mCRL2
Loading...
Searching...
No Matches
liblts_bisim_gj.h
Go to the documentation of this file.
1// Author(s): Jan Friso Groote and David N. Jansen
2//
3// Copyright: see the accompanying file COPYING or copy at
4// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
5//
6// Distributed under the Boost Software License, Version 1.0.
7// (See accompanying file LICENSE_1_0.txt or copy at
8// http://www.boost.org/LICENSE_1_0.txt)
9
10/// \file lts/detail/liblts_bisim_gj.h
11///
12/// \brief O(m log n)-time branching bisimulation algorithm similar to liblts_bisim_dnj.h
13/// which does not use bunches, i.e., partitions of transitions. This algorithm
14/// should be slightly faster, but in particular use less memory than liblts_bisim_dnj.h.
15/// Otherwise the functionality is exactly the same.
16
17#ifndef LIBLTS_BISIM_GJ_H
18#define LIBLTS_BISIM_GJ_H
19
20// If INIT_WITHOUT_BLC_SETS is defined, initialization does not call
21// stabilizeB() but runs a separate algorithm that does not rely on BLC sets
22// to create an initial partition. Only towards the end of initialization, the
23// BLC sets are generated, and then stabilizeB() is called to handle new bottom
24// states that have been found during the separate algorithm.
25#define INIT_WITHOUT_BLC_SETS
26
27#include <iomanip> // for std::fixed, std::setprecision(), std::setw()
28#include <ctime> // for std::clock_t, std::clock()
29#include "mcrl2/lts/detail/liblts_scc.h"
30#include "mcrl2/lts/detail/liblts_merge.h"
31#include "mcrl2/lts/detail/check_complexity.h"
32#include "mcrl2/lts/detail/fixed_vector.h"
33#include "mcrl2/lts/detail/simple_list.h"
34#define linked_list simple_list
35
36namespace mcrl2
37{
38namespace lts
39{
40namespace detail
41{
42
43template <class LTS_TYPE> class bisim_partitioner_gj;
44
46{
47
48// Forward declaration.
49struct state_type_gj;
50struct block_type;
52struct transition_type;
54
55typedef std::size_t state_index;
56typedef std::size_t transition_index;
57
58
59typedef std::size_t label_index;
64
69constexpr block_type* null_block=nullptr;
70
71/// default counter value if the counter field of a state is not in use currently
73
74 /// \brief the number of counter values that can be used for one subblock
75 /// \details There are three singular values (`undefined`, `marked_NewBotSt`,
76 /// and `marked_HitSmall`), and the other values needs to be distributed over
77 /// three subblocks (ReachAlw, AvoidLrg, and AvoidSml).
79 (std::numeric_limits<transition_index>::max()-2)/3;
80
81 enum subblocks { ReachAlw=0,// states that can reach always all splitters
82 AvoidSml, // states that cannot inertly reach the small
83 // splitter (while it is not empty)
84 AvoidLrg, // states that cannot inertly reach the
85 // large splitter (while it is not empty)
86 NewBotSt}; // states that can inertly reach multiple of
87 // the above subblocks
88 // The following values are used only for temporary marking
89 // and are not really associated with a subblock:
90 // HitSmall -- states that can (non-inertly) reach the small
91 // splitter; they can be in any subblock except
92 // AvoidSml. Necessary for correctness.
93
94 /// \brief base marking value for a subblock
95 /// \details If the counter has this value, the state definitely belongs
96 /// to the respective subblock.
97 static inline constexpr transition_index marked(enum subblocks subblock)
98 {
99 return assert(ReachAlw==subblock || AvoidSml==subblock ||
100 AvoidLrg==subblock || NewBotSt==subblock),
101 marked_range*subblock+1;
102 }
103
104 /// counter value to indicate that a state is in the NewBotSt subset
105 constexpr transition_index marked_NewBotSt=marked(NewBotSt); static_assert(marked_NewBotSt<std::numeric_limits<transition_index>::max());
106
107 /// counter value to indicate that a state has a transition in the small
108 ///splitter (so it cannot become part of AvoidSml)
110
111 /// \brief checks whether a counter value is a marking for a given subblock
112 static inline constexpr bool is_in_marked_range_of
113 (transition_index counter, enum subblocks subblock)
114 {
115 return assert(ReachAlw==subblock || AvoidSml==subblock || AvoidLrg==subblock),
116 counter-marked(subblock)<marked_range;
117 }
118
119/// The function clear() takes care that a container frees memory when it is
120/// cleared and it is large.
121template <class CONTAINER>
122static inline void clear(CONTAINER& c)
123{
124 if (c.size()>1000) { c=CONTAINER(); } else { c.clear(); }
125}
126
127// The struct below facilitates to walk through a LBC_list starting from an
128// arbitrary transition.
129typedef transition_index* BLC_list_iterator; // should not be nullptr
131typedef const transition_index* BLC_list_const_iterator; // should not be nullptr
132
133/// information about a transition stored in m_outgoing_transitions
135{
136 /// pointer to the corresponding entry in m_BLC_transitions
138 {
139 /// \brief transition index (used during initialisation)
141 /// \brief pointer to the corresponding entry in `m_BLC_transitions` (used during main part of the algorithm)
143 /// \brief Construct the object as a transition index
145 : transitions()
146 {}
147 /// \brief Convert the object from counter to iterator
149 {
152 }
153 /// \brief Destruct the object as an iterator
155 } ref;
156
157 /// this pointer is used to find transitions with the same source state, action label, and target constellation
158 /// (Transitions are grouped according to these in m_outgoing_transitions.)
159 /// For most transitions, it points to the last transition with the same source state, action label, and target constellation;
160 /// but if this transition is the last one in the group, start_same_saC points to the first transition in the group.
162
163 // The default initialiser does not initialize the fields of this struct.
165 {}
166
168 : ref(),
169 start_same_saC(sssaC)
170 {}
171};
172
173/// a pointer to a state, i.e. a reference to a state
175{
176 state_in_block_pointer(fixed_vector<state_type_gj>::iterator new_ref_state)
178 {}
179
181 {}
182
184
185 bool operator==(const state_in_block_pointer& other) const
186 {
187 return ref_state==other.ref_state;
188 }
189
190 bool operator!=(const state_in_block_pointer& other) const
191 {
192 return ref_state!=other.ref_state;
193 }
194};
195
196/// a vector with an additional (internal) field to indicate how much work has been
197/// done already on it.
199{
202
203 public:
205 #ifndef NDEBUG
206 bool find(const state_in_block_pointer s) const
207 {
208 return std::find(m_vec.begin(), m_vec.end(), s)!=m_vec.end();
209 }
210 #endif
212 { assert(!find(s));
213 m_vec.push_back(s);
214 }
215
216 std::size_t todo_is_empty() const
217 {
218 return m_vec.size()==m_todo_indicator;
219 }
220
221 // Move a state from the todo part to the definitive vector.
223 { assert(!todo_is_empty());
224 state_in_block_pointer result=m_vec[m_todo_indicator];
226 return result;
227 }
228
229 void swap_vec(std::vector<state_in_block_pointer>& other_vec)
230 {
231 m_vec.swap(other_vec);
233 }
234
235 std::size_t size() const
236 {
237 return m_vec.size();
238 }
239
240 std::size_t empty() const
241 {
242 return m_vec.empty();
243 }
244
246 {
247 return m_vec.begin();
248 }
249
251 {
252 return m_vec.end();
253 }
254
256 {
257 return m_vec.data();
258 }
259
261 {
262 return m_vec.data() + m_vec.size();
263 }
264
266 {
267 return m_vec.front();
268 }
269
270 //const state_in_block_pointer& back() const
271 //{
272 // return m_vec.back();
273 //}
274
275 void reserve(std::vector<state_in_block_pointer>::size_type new_cap)
276 {
277 m_vec.reserve(new_cap);
278 }
279
281
283 {
284 return m_vec.begin();
285 }
286
288 {
289 return m_vec.end();
290 }
291
292 // add all elements in [begin, end) to the vector
293 void add_todo(iterator begin, iterator end)
294 {
295 m_vec.insert(m_vec.end(), begin, end);
296 }
297
298 void clear()
299 {
301 bisimulation_gj::clear(m_vec);
302 }
303};
304
305
306
307// Below the four main data structures are listed.
308/// information about a state
310{
311 /// block of the state
313 /// first incoming transition
315 /// first outgoing transition
317 /// pointer to the corresponding entry in m_states_in_blocks
319 /// number of outgoing block-inert transitions
321 /// counter used during splitting
322 /// If this counter is set to undefined (0), it is considered to be not yet
323 /// visited.
324 /// If this counter is a positive number, it is the number of outgoing
325 /// block-inert transitions that have not yet been handled.
327 #ifndef NDEBUG
328 /// \brief print a short state identification for debugging
329 template<class LTS_TYPE>
330 std::string debug_id_short(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
331 {
332 assert(partitioner.m_states.data()<=this);
333 assert(this<partitioner.m_states.data_end());
334 return std::to_string(this-partitioner.m_states.data());
335 }
336
337 /// \brief print a state identification for debugging
338 template<class LTS_TYPE>
339 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
340 {
341 return "state " + debug_id_short(partitioner);
342 }
343 #endif
344 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
346 #endif
347};
348
349/// The following type gives the start and end indications of the transitions
350/// for the same block, label and constellation in the array m_BLC_transitions.
352{
354
355 // If the source block of the BLC_indicator has new bottom states,
356 // it is undefined whether the BLC_indicator should be regarded as stable or
357 // unstable. Otherwise, the BLC_indicator is regarded as stable if and only
358 // if start_marked_BLC is ==nullptr.
361
363 : start_same_BLC(start),
364 start_marked_BLC(is_stable ? nullptr : end),
365 end_same_BLC(end)
366 { assert(nullptr!=start_same_BLC); assert(nullptr!=end_same_BLC);
368 }
369
370 bool is_stable() const
371 { assert(nullptr!=start_same_BLC); assert(nullptr!=end_same_BLC);
375 return nullptr==start_marked_BLC;
376 }
377
378 /// This function returns true iff the BLC set contains at least one
379 /// marked transition.
381 {
382 if (is_stable())
383 {
384 return false;
385 }
387 }
388
390 { assert(!is_stable());
391 start_marked_BLC=nullptr;
392 }
393
395 { assert(is_stable());
397 }
398
399 bool operator==(const BLC_indicators& other) const
400 {
401 return start_same_BLC==other.start_same_BLC &&
404 }
405
406 bool operator!=(const BLC_indicators& other) const
407 {
408 return !operator==(other);
409 }
410 #ifndef NDEBUG
411 /// \brief print a B_to_C slice identification for debugging
412 /// \details This function is only available if compiled in Debug mode.
413 template<class LTS_TYPE>
414 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner,
415 const block_type* from_block=nullptr) const
416 {
417 assert(partitioner.m_BLC_transitions.data()<=start_same_BLC);
421 assert(end_same_BLC<=partitioner.m_BLC_transitions.data_end());
422 std::string result("BLC set ["+std::to_string(std::distance<BLC_list_const_iterator>(&*partitioner.m_BLC_transitions.begin(), start_same_BLC))+","+std::to_string(std::distance<BLC_list_const_iterator>(&*partitioner.m_BLC_transitions.begin(), end_same_BLC))+")");
424 {
425 return "Empty "+result;
426 }
427 result += " from "+(nullptr==from_block ? partitioner.m_states[partitioner.m_aut.get_transitions()[*start_same_BLC].from()].block : from_block)->debug_id(partitioner);
428 result += " to ";
429 result += partitioner.m_states[partitioner.m_aut.get_transitions()[*start_same_BLC].to()].block->c.onstellation->debug_id(partitioner);
430 result += " containing the ";
431 if (std::distance(start_same_BLC, end_same_BLC)>1)
432 {
433 result+=std::to_string(std::distance(start_same_BLC, end_same_BLC));
434 result += " transitions ";
435 }
436 else
437 {
438 result += "transition ";
439 }
441 if (start_marked_BLC == iter)
442 {
443 result += "| ";
444 }
445 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
446 if (std::distance(start_same_BLC, end_same_BLC)>4)
447 {
448 ++iter;
449 result += start_marked_BLC == iter ? " | " : ", ";
450 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
451 result += std::next(iter) == start_marked_BLC ? " | ..."
452 : (!is_stable() && start_marked_BLC>std::next(iter) && start_marked_BLC<=end_same_BLC-3 ? ", ..|.." : ", ...");
453 iter = end_same_BLC-3;
454 }
455 while (++iter!=end_same_BLC)
456 {
457 result += start_marked_BLC == iter ? " | " : ", ";
458 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
459 }
460 if (start_marked_BLC == iter)
461 {
462 result += " |";
463 }
464 return result;
465 }
466 #endif
467 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
469 #endif
470};
471
472/// information about a transition
473/// The source, label and target of the transition are not stored here but in
474/// m_aut.get_transitions(), to save memory.
476{
477 // The position of the transition type corresponds to m_aut.get_transitions().
478 // std::size_t from, label, to are found in m_aut.get_transitions().
480 outgoing_transitions_it ref_outgoing_transitions; // This refers to the position of this transition in m_outgoing_transitions.
481 // During initialisation m_outgoing_transitions contains the indices of this
482 // transition. After initialisation m_outgoing_transitions refers to the corresponding
483 // entry in m_BLC_transitions, of which the field transition contains the index
484 // of this transition.
485 #ifndef NDEBUG
486 /// \brief print a short transition identification for debugging
487 /// \details This function is only available if compiled in Debug mode.
488 template<class LTS_TYPE>
489 std::string debug_id_short(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
490 {
491 assert(partitioner.m_transitions.data()<=this);
492 assert(this<partitioner.m_transitions.data_end());
493 const transition& t=partitioner.m_aut.get_transitions()
494 [this-partitioner.m_transitions.data()];
495 return partitioner.m_states[t.from()].debug_id_short(partitioner) + " -" +
496 pp(partitioner.m_aut.action_label(t.label())) + "-> " +
497 partitioner.m_states[t.to()].debug_id_short(partitioner);
498 }
499
500 /// \brief print a transition identification for debugging
501 /// \details This function is only available if compiled in Debug mode.
502 template<class LTS_TYPE>
503 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
504 {
505 return "transition " + debug_id_short(partitioner);
506 }
507 #endif
508 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
510 #endif
511};
512
513/// information about a block
514/// \details A block is mainly described through the set of states it contains.
515/// For this we have `fixed_vector<state_in_block_pointer> m_states_in_blocks`,
516/// where states are kept grouped by block. The fields `start_bottom_states`,
517/// `sta.rt_non_bottom_states` and `end_states` are pointers into that array.
518///
519/// Some fields get a second life (to save memory) during initialisation or
520/// during finalising; that is the purpose of the unions.
521///
522/// A block should be trivially destructible because we want it to be allocated
523/// using the pool allocator `linked_list<BLC_indicators>::get_pool()`. This
524/// is why there are no iterator fields.
526{
528 {
529 /// constellation that the block is in
531
532 /// \brief used during initialisation for the first unmarked bottom state
534
536 :onstellation(new_c)
537 {}
538 } c;
539
540 /// first state of the block in m_states_in_blocks
541 /// States in [start_bottom_states, sta.rt_non_bottom_states) are bottom
542 /// states in the block
544
546 {
547 /// first non-bottom state of the block in m_states_in_blocks
548 /// States in [sta.rt_non_bottom_states, end_states) are non-bottom states
549 /// in the block.
550 ///
551 /// If m_branching==false, we have sta.rt_non_bottom_states==end_states.
553
554 /// \brief used during finalizing for the state index in the reduced LTS
555 /// \details After partition refinement has finished, the boundary between
556 /// bottom and non-bottom states is no longer needed. Therefore, we use
557 /// the same space to store a block number instead. This block number is
558 /// the same as the state number in the reduced LTS.
560
563 {}
564 } sta;
565
566 /// pointer past the last state in the block
568
569 union btc_R
570 {
571 /// \brief list of descriptors of all BLC sets that contain transitions starting in the block
572 /// \details If the block has inert transitions, they are always in the
573 /// first element of the list.
574 ///
575 /// During the main/co-split phase, a main splitter immediately follows the
576 /// corresponding co-splitter in the list.
577 /// During `stabilize()`, BLC sets that are regarded as unstable are near
578 /// the end of the list.
581 /// \brief used during initialisation for a pointer to a vector of marked states
582 /// \details During initialisation (when there is only one constellation)
583 /// the same space as `to_constellation` is actually used for something
584 /// else.
585 ///
586 /// In the initial refinement in `create_initial_partition()`, blocks
587 /// are split according to which action labels they can (inertly) reach.
588 /// If R!=nullptr, this block has been registered as a block where some
589 /// state has a transition with the current label under investigation.
590 /// Such states, if they are non-bottom, are inserted into the vector
591 /// `*R`. If R!=nullptr, the block has also been inserted into
592 /// the vector `blocks_that_need_refinement` (a local variable of
593 /// `create_initial_partition()`).
595
596 /// \brief constructor
597 /// \details Note: `if_R_is_nullptr_then_to_constellation_is_empty_list()`
598 /// depends on the fact that the constructor creates the variant field `R`.
600 : R(nullptr)
601 {}
602
603 /// \brief indicates whether the default values of the union members agree
604 /// \details If this function returns `false`, it is necesssary to
605 /// explicitly construct every `block.to_constellation` list during
606 /// initialisation. Otherwise, it would be enough to just keep `R` as
607 /// `nullptr`. The function is not a constexpr but it is optimized away
608 /// completely (at least by my compiler, DNJ).
610 {
611 btc_R test_should_be_empty_BLC_list=btc_R(); assert(nullptr==test_should_be_empty_BLC_list.R);
612 if constexpr (sizeof(test_should_be_empty_BLC_list.R)!=
613 sizeof(test_should_be_empty_BLC_list.to_constellation))
614 {
615 return false;
616 }
617 if (test_should_be_empty_BLC_list.to_constellation.empty() &&
618 test_should_be_empty_BLC_list.to_constellation==
619 linked_list<BLC_indicators>())
620 {
621 // no need to change `test_should_be_empty_BLC_list` from a pointer
622 // to a linked_list explicitly, as the two seem to have the same bit
623 // pattern; the destructor will work fine.
624 return true;
625 }
626 // The destructor expects a linked list:
627 new (&test_should_be_empty_BLC_list) linked_list<BLC_indicators>();
628 return false;
629 }
630 } block;
631
632 /// \brief copy constructor. Required by MSCV.
633 block_type(const block_type& other)
634 : c(other.c.onstellation),
636 sta(other.sta.rt_non_bottom_states),
640 {}
641
642 /// \brief a boolean that is true iff the block contains new bottom states
643 /// \details If a block contains new bottom states, it will be ignored until
644 /// `stabilizeB()` handles all blocks with new bottom states. Such a block
645 /// must also be added to the list `m_blocks_with_new_bottom_states`.
647
648 /// constructor
650 state_in_block_pointer* start_non_bottom,
652 constellation_type* new_c)
653 : c(new_c),
654 start_bottom_states(start_bottom),
655 sta(start_non_bottom),
656 end_states(end),
657 block(),
659 { assert(start_bottom<=start_non_bottom); assert(start_non_bottom<=end);
660 }
661 #ifndef NDEBUG
662 /// \brief print a block identification for debugging
663 template<class LTS_TYPE>
664 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
665 { assert(partitioner.m_states_in_blocks.data()<=start_bottom_states);
668 assert(end_states<=partitioner.m_states_in_blocks.data_end());
669 return"block ["+std::to_string
670 (std::distance<const state_in_block_pointer*>
671 (partitioner.m_states_in_blocks.data(), start_bottom_states))+","+
672 std::to_string
673 (std::distance<const state_in_block_pointer*>
674 (partitioner.m_states_in_blocks.data(), end_states))+")";
675 }
676 #endif
677 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
679 #endif
680};
681
682/// information about a constellation
684{
685 /// points to the first state in m_states_in_blocks
687
688 /// points past the last state in m_states_in_blocks
690
692 state_in_block_pointer* const new_end)
693 : start_const_states(new_start),
694 end_const_states(new_end)
695 {}
696 #ifndef NDEBUG
697 /// \brief print a constellation identification for debugging
698 template<class LTS_TYPE>
699 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
700 { assert(partitioner.m_states_in_blocks.data()<=start_const_states);
702 assert(end_const_states<=partitioner.m_states_in_blocks.data_end());
703 return "constellation ["+std::to_string
704 (std::distance<const state_in_block_pointer*>
705 (partitioner.m_states_in_blocks.data(), start_const_states))+","+
706 std::to_string
707 (std::distance<const state_in_block_pointer*>
708 (partitioner.m_states_in_blocks.data(), end_const_states))+")";
709 }
710 #endif
711};
712
713} // end namespace bisimulation_gj
714
715
716/*=============================================================================
717= main class =
718=============================================================================*/
719
720
721using namespace mcrl2::lts::detail::bisimulation_gj;
722
723/// \class bisim_partitioner_gj
724/// \brief implements the main algorithm for the branching bisimulation quotient
725template <class LTS_TYPE>
726class bisim_partitioner_gj
727{
728 private:
729
732 #ifndef NDEBUG
733 public: // needed for the debugging functions, e.g. debug_id().
734 #endif
735 /// \brief automaton that is being reduced
736 LTS_TYPE& m_aut;
737
738 // Generic data structures.
739 /// \brief information about states
741
742 /// \brief transitions ordered per source state
743 /// \details This array is used to go through the outgoing transitions of a
744 /// state. The transitions of a given source state are further grouped per
745 /// action label, and within every action label per target constellation.
746 /// The invisible label (tau) is always the first label.
748 // During refining this contains the index in m_BLC_transition, of which
749 // the transition field contains the index of the transition.
755 private:
757
758 /// The following variable contains all non-trivial constellations.
760
763
764 /// \brief true iff branching (not strong) bisimulation has been requested
765 const bool m_branching;
766
767 /// \brief true iff divergence-preserving branching bisimulation has been
768 /// requested
769 /// \details Note that this field must be false if strong bisimulation has
770 /// been requested. There is no such thing as divergence-preserving strong
771 /// bisimulation.
773
774 /// The auxiliary function below can be removed, but is now used to express
775 /// that the hidden_label_map does not need to be applied, while still
776 /// leaving it in the code.
778 (typename LTS_TYPE::labels_size_type l)
779 {
780 return l;
781 }
782
783 /// The function assumes that m_branching is true and tests whether
784 /// transition t is inert during initialisation under that condition
786 { assert(m_branching);
789 }
790
791 /// The function tests whether transition t is inert during initialisation,
792 /// i.e. when there is only one source/target block.
793 bool is_inert_during_init(const transition& t) const
794 {
796 }
797
798 /// The function calculates the label index of transition t, where
799 /// tau-self-loops get the special index `divergent_label` if
800 /// divergence needs to be preserved
802 const label_index divergent_label=-2
803 /* different from null_action */) const
804 {
805 label_index result = m_aut_apply_hidden_label_map(t.label()); assert(divergent_label!=result); assert(null_action!=divergent_label);
806 if (m_preserve_divergence && ( assert(m_branching),
807 t.from() == t.to()) &&
808 m_aut.is_tau(result))
809 {
810 return divergent_label;
811 }
812 return result;
813 }
814 #ifndef NDEBUG
815 /// \brief Checks whether the transition data structure is correct
816 /// \returns true iff all checks pass
817 /// \details Checks whether the pointers incoming transitions -> outgoing
818 /// transitions -> BLC transitions -> incoming transitions are consistent;
819 /// whether the pointers from states to incoming and outgoing transitions are
820 /// consistent; whether the pointers from BLC indicators to BLC sets are
821 /// consistent.
822 ///
823 /// If `check_block_to_constellation`, it also checks whether every
824 /// transition is in one BLC set of its source block.
825 ///
826 /// If `check_temporary_complexity_counters`, it also checks that no more
827 /// work is accounted for in temporary complexity counters. If
828 /// `initialisation` holds, all states are treated as non-bottom states (so
829 /// that later one might handle all bottom states as new bottom states in the
830 /// very first call to `stabilizeB()`). In any case, the BLC sets need to be
831 /// fully initialised.
832 void check_transitions(const bool initialisation,
833 const bool check_temporary_complexity_counters,
834 const bool check_block_to_constellation = true) const
835 {
836 for(transition_index ti=0; ti<m_transitions.size(); ++ti)
837 {
838 const BLC_list_const_iterator btc_ti=
839 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions;
840 assert(*btc_ti==ti);
841
842 const transition& t=m_aut.get_transitions()[ti];
843 assert(&*m_states[t.to()].start_incoming_transitions<=&t);
844 if (t.to()+1!=m_aut.num_states())
845 {
846 assert(&t<=&*std::prev(m_states[t.to()+1].start_incoming_transitions));
847 }
848 else
849 {
850 assert(&t<=&m_aut.get_transitions().back());
851 }
852
853 assert(m_states[t.from()].start_outgoing_transitions<=
854 m_transitions[ti].ref_outgoing_transitions);
855 if (t.from()+1==m_aut.num_states())
856 {
857 assert(m_transitions[ti].ref_outgoing_transitions<
858 m_outgoing_transitions.end());
859 }
860 else
861 {
862 assert(m_transitions[ti].ref_outgoing_transitions<
863 m_states[t.from() + 1].start_outgoing_transitions);
864 }
865
866 assert(m_transitions[ti].
867 transitions_per_block_to_constellation->start_same_BLC<=btc_ti);
868 assert(btc_ti<m_transitions[ti].
869 transitions_per_block_to_constellation->end_same_BLC);
870
871 if (!check_block_to_constellation)
872 continue;
873
874 block_type* const b=m_states[t.from()].block;
875
876 const label_index t_label = label_or_divergence(t);
877 bool found=false;
878 for(const BLC_indicators& blc: b->block.to_constellation)
879 {
880 if (!blc.is_stable())
881 {
882 assert(blc.start_same_BLC<=blc.start_marked_BLC);
883 assert(blc.start_marked_BLC<=blc.end_same_BLC);
884 }
885 assert(blc.start_same_BLC<blc.end_same_BLC);
886 transition& first_t = m_aut.get_transitions()[*blc.start_same_BLC];
887 assert(b == m_states[first_t.from()].block);
888 if (t_label == label_or_divergence(first_t) &&
889 m_states[first_t.to()].block->c.onstellation ==
890 m_states[t.to()].block->c.onstellation)
891 {
892 assert(!found); assert(blc.start_same_BLC <= btc_ti);
893 assert(btc_ti<blc.end_same_BLC);
894 assert(&blc == &*m_transitions[ti].transitions_per_block_to_constellation);
895 found = true;
896 }
897 }
898 assert(found);
899 if (check_temporary_complexity_counters)
900 {
901 block_type* const targetb = m_states[t.to()].block;
902 const unsigned max_sourceB = check_complexity::log_n-
903 check_complexity::ilog2(number_of_states_in_block(*b));
904 const unsigned max_targetC = check_complexity::log_n-
905 check_complexity::ilog2(number_of_states_in_constellation
906 (*targetb->c.onstellation));
907 const unsigned max_targetB = check_complexity::log_n-
908 check_complexity::ilog2(number_of_states_in_block(*targetb));
909 mCRL2complexity(&m_transitions[ti],
910 no_temporary_work(max_sourceB, max_targetC, max_targetB,
911 !initialisation &&
912 0==m_states[t.from()].no_of_outgoing_block_inert_transitions),
913 *this);
914 }
915 }
916 }
917
918 /// \brief Checks whether data structures are consistent
919 /// \returns true iff all checks pass
920 /// \details Checks whether states are in their blocks; the pointers outgoing
921 /// transition (-> BLC transition) -> incoming transition -> outgoing
922 /// transition are consistent; whether the saC slices (source state, action,
923 /// target constellation) are correct; whether blocks are correct.
924 [[nodiscard]]
925 bool check_data_structures(const std::string& tag, const bool initialisation=false, const bool check_temporary_complexity_counters=true) const
926 {
927 mCRL2log(log::debug) << "Check data structures: " << tag << ".\n";
928 assert(m_states.size()==m_aut.num_states());
929 assert(m_states_in_blocks.size()==m_aut.num_states());
930 assert(m_transitions.size()==m_aut.num_transitions());
931 assert(m_outgoing_transitions.size()==m_aut.num_transitions());
932 assert(m_BLC_transitions.size()==m_aut.num_transitions());
933
934 // Check that the elements in m_states are well formed.
935 for (fixed_vector<state_type_gj>::iterator si=
936 const_cast<fixed_vector<state_type_gj>&>(m_states).begin();
937 si<m_states.cend(); si++)
938 {
939 const state_type_gj& s=*si;
940
941 assert(s.counter==undefined);
942 assert(s.block->start_bottom_states< s.block->sta.rt_non_bottom_states);
943 assert(s.block->sta.rt_non_bottom_states<=s.block->end_states);
944
945 // In the following line we need that si is an iterator (not a const_iterator)
946 assert(std::find(s.block->start_bottom_states,
947 s.block->end_states,
948 state_in_block_pointer(si))!=s.block->end_states);
949
950 assert(s.ref_states_in_blocks->ref_state==si);
951
952 // ensure that in the incoming transitions we first have the transitions
953 // with label tau, and then the other transitions:
954 bool maybe_tau=true;
955 const std::vector<transition>::const_iterator end_it1=
956 std::next(si)>=m_states.end() ? m_aut.get_transitions().end()
957 : std::next(si)->start_incoming_transitions;
958 for (std::vector<transition>::const_iterator
959 it=s.start_incoming_transitions; it!=end_it1; ++it)
960 {
961 const transition& t=*it;
962 if (m_aut.is_tau(m_aut_apply_hidden_label_map(t.label())))
963 {
964 assert(maybe_tau);
965 }
966 else
967 {
968 maybe_tau=false;
969 }
970 // potentially we might test that the transitions are grouped per label
971 }
972
973 // Check that for each state the outgoing transitions satisfy the
974 // following invariant: First there are (originally) inert transitions
975 // (inert transitions may be separated over multiple constellations, so
976 // we cannot require that the inert transitions come before other
977 // tau-transitions). Then there are other transitions sorted per label
978 // and constellation.
979 std::unordered_set<std::pair<label_index, const constellation_type*> >
980 constellations_seen;
981
982 maybe_tau=true;
983 // The construction below is to enable translation on Windows.
984 const outgoing_transitions_const_it end_it2=
985 std::next(si)>=m_states.end() ? m_outgoing_transitions.cend()
986 : std::next(si)->start_outgoing_transitions;
987 for(outgoing_transitions_const_it it=s.start_outgoing_transitions;
988 it!=end_it2; ++it)
989 {
990 const transition& t=m_aut.get_transitions()[!initialisation
991 ? *it->ref.BLC_transitions : it->ref.transitions];
992 assert(m_states.cbegin()+t.from()==si);
993 assert(m_transitions[!initialisation ? *it->ref.BLC_transitions
994 : it->ref.transitions].ref_outgoing_transitions==it);
995 assert((it->start_same_saC>it &&
996 it->start_same_saC<m_outgoing_transitions.end() &&
997 ((it+1)->start_same_saC==it->start_same_saC ||
998 (it+1)->start_same_saC<=it)) ||
999 (it->start_same_saC<=it &&
1000 (it+1==m_outgoing_transitions.end() ||
1001 (it+1)->start_same_saC>it)));
1002 const label_index t_label = label_or_divergence(t);
1003 // The following for loop is only executed if it is the last transition in the saC-slice.
1004 for(outgoing_transitions_const_it itt=it->start_same_saC;
1005 itt<it->start_same_saC->start_same_saC; ++itt)
1006 {
1007 const transition& t1=m_aut.get_transitions()[!initialisation
1008 ? *itt->ref.BLC_transitions : itt->ref.transitions];
1009 assert(m_states.cbegin()+t1.from()==si);
1010 assert(label_or_divergence(t1) == t_label);
1011 assert(m_states[t.to()].block->c.onstellation==
1012 m_states[t1.to()].block->c.onstellation);
1013 }
1014
1015 const label_index label = label_or_divergence(t);
1016 // Check that if the target constellation, if not new, is equal to the
1017 // target constellation of the previous outgoing transition.
1018 const constellation_type* t_to_constellation=
1019 m_states[t.to()].block->c.onstellation;
1020 if (constellations_seen.count(std::pair(label,t_to_constellation))>0)
1021 {
1022 assert(it!=s.start_outgoing_transitions);
1023 const transition& old_t=m_aut.get_transitions()[!initialisation
1024 ? *std::prev(it)->ref.BLC_transitions
1025 : std::prev(it)->ref.transitions];
1026 assert(label_or_divergence(old_t)==label);
1027 assert(t_to_constellation==
1028 m_states[old_t.to()].block->c.onstellation);
1029 }
1030 else
1031 {
1032 if (m_branching && m_aut.is_tau(label))
1033 {
1034 assert(maybe_tau);
1035 }
1036 else
1037 {
1038 maybe_tau=false;
1039 }
1040 constellations_seen.emplace(label,t_to_constellation);
1041 }
1042 }
1043 }
1044 // Check that the elements in m_transitions are well formed.
1045 if (!initialisation)
1046 {
1047 check_transitions(initialisation, check_temporary_complexity_counters);
1048 }
1049 // Check that the elements in m_blocks are well formed.
1050 {
1051 set_of_transitions_type all_transitions;
1052 transition_index actual_no_of_non_constellation_inert_BLC_sets=0;
1053 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1054 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1055 {
1056 const block_type& b=*si->ref_state->block;
1057 const constellation_type& c=*b.c.onstellation;
1058 assert(m_states_in_blocks.data()<=c.start_const_states);
1059 assert(c.start_const_states<=b.start_bottom_states);
1060 assert(b.start_bottom_states<b.sta.rt_non_bottom_states);
1061 assert(b.sta.rt_non_bottom_states<=b.end_states);
1062 assert(b.end_states<=c.end_const_states);
1063 assert(c.end_const_states<=m_states_in_blocks.data_end());
1064
1065 unsigned char const max_B=check_complexity::log_n-
1066 check_complexity::ilog2(number_of_states_in_block(b));
1067 unsigned char const max_C=check_complexity::log_n-check_complexity::
1068 ilog2(number_of_states_in_constellation(*b.c.onstellation));
1069 for (const state_in_block_pointer*
1070 is=b.start_bottom_states; is!=b.sta.rt_non_bottom_states; ++is)
1071 {
1072 assert(is->ref_state->block==&b);
1073 assert(is->ref_state->no_of_outgoing_block_inert_transitions==0);
1074 if (check_temporary_complexity_counters)
1075 {
1076 // During initialisation, new bottom state counters must remain 0
1077 mCRL2complexity(is->ref_state, no_temporary_work(max_B,
1078 !initialisation), *this);
1079 }
1080 }
1081 for (const state_in_block_pointer*
1082 is=b.sta.rt_non_bottom_states; is!=b.end_states; ++is)
1083 {
1084 assert(is->ref_state->block==&b);
1085 assert(is->ref_state->no_of_outgoing_block_inert_transitions>0);
1086 // Because there cannot be new bottom states among non-bottom states,
1087 // we can always check the temporary work of non-bottom states:
1088 mCRL2complexity(is->ref_state,no_temporary_work(max_B,false),*this);
1089 }
1090 // Because a block has no temporary or new-bottom-state-related
1091 // counters, we can always check its temporary work:
1092 mCRL2complexity(&b, no_temporary_work(max_C, max_B), *this);
1093
1094 if (!initialisation)
1095 {
1096 assert(b.block.to_constellation.check_linked_list());
1097 for (linked_list<BLC_indicators>::const_iterator
1098 ind=b.block.to_constellation.begin();
1099 ind!=b.block.to_constellation.end(); ++ind)
1100 {
1101 assert(ind->start_same_BLC<ind->end_same_BLC);
1102 const transition& first_transition=
1103 m_aut.get_transitions()[*(ind->start_same_BLC)];
1104 const label_index first_transition_label=
1105 label_or_divergence(first_transition);
1106 if(!is_inert_during_init(first_transition) ||
1107 m_states[first_transition.from()].block->c.onstellation!=
1108 m_states[first_transition.to()].block->c.onstellation)
1109 {
1110 ++actual_no_of_non_constellation_inert_BLC_sets;
1111 }
1112 for(BLC_list_const_iterator i=ind->start_same_BLC;
1113 i<ind->end_same_BLC; ++i)
1114 {
1115 const transition& t=m_aut.get_transitions()[*i];
1116 assert(m_transitions[*i].transitions_per_block_to_constellation==
1117 ind);
1118 all_transitions.emplace(*i);
1119 assert(m_states[t.from()].block==&b);
1120 assert(m_states[t.to()].block->c.onstellation==
1121 m_states[first_transition.to()].block->c.onstellation);
1122 assert(label_or_divergence(t)==first_transition_label);
1123 if (is_inert_during_init(t) && b.c.onstellation==
1124 m_states[t.to()].block->c.onstellation)
1125 {
1126 // The inert transitions should be in the first element of
1127 // `block.to_constellation`:
1128 assert(b.block.to_constellation.begin()==ind);
1129 }
1130 }
1131 if (check_temporary_complexity_counters)
1132 {
1133 mCRL2complexity(ind, no_temporary_work(max_C,
1134 check_complexity::log_n-check_complexity::ilog2
1135 (number_of_states_in_constellation(*m_states
1136 [first_transition.to()].block->c.onstellation))), *this);
1137 }
1138 }
1139 }
1140 }
1141 if (!initialisation) {
1142 assert(all_transitions.size()==m_transitions.size());
1143 assert(actual_no_of_non_constellation_inert_BLC_sets==
1145 }
1146 // destruct `all_transitions` here
1147 }
1148
1149 // TODO: Check that the elements in m_constellations are well formed.
1150
1151 // Check that the states in m_states_in_blocks refer to with ref_states_in_block to the right position.
1152 // and that a state is correctly designated as a (non-)bottom state.
1153 for (const state_in_block_pointer*
1154 si=m_states_in_blocks.data(); si<m_states_in_blocks.data_end(); ++si)
1155 {
1156 assert(si==si->ref_state->ref_states_in_blocks);
1157 }
1158
1159 // Check that the blocks in m_blocks_with_new_bottom_states are bottom states.
1160 for(const block_type* bi: m_blocks_with_new_bottom_states)
1161 {
1162 assert(bi->contains_new_bottom_states);
1163 }
1164
1165 // Check that the non-trivial constellations are non trivial.
1166 for(const constellation_type* ci: m_non_trivial_constellations)
1167 {
1168 // There are at least two blocks in a non-trivial constellation.
1169 const block_type* const first_bi=ci->start_const_states->ref_state->block;
1170 const block_type* const last_bi=std::prev(ci->end_const_states)->ref_state->block;
1171 assert(first_bi != last_bi);
1172 }
1173 return true;
1174 }
1175
1176 /// \brief Checks the main invariant of the partition refinement algorithm
1177 /// \returns true iff the main invariant holds
1178 /// \details Checks the following invariant:
1179 /// If a block has a constellation-non-inert transition, then every
1180 /// bottom state has a constellation-non-inert transition with the same
1181 /// label to the same target constellation.
1182 /// It is assumed that the BLC data structure is correct, so we conveniently
1183 /// use that to verify the invariant.
1184 ///
1185 /// The function can also check a partial invariant while stabilisation has
1186 /// not yet finished. If calM != nullptr, then we have:
1187 /// The above invariant may be violated for BLC sets that are still to
1188 /// be stabilized, as given by the main splitters in calM.
1189 /// (calM_elt indicates how far stabilization has handled calM already.)
1190 /// (block_label_to_cotransition indicates the co-splitters that belong
1191 /// to the main splitters in calM.)
1192 /// It may also be violated for blocks that contain new bottom states,
1193 /// as indicated by m_blocks_with_new_bottom_states.
1194 ///
1195 /// Additionally, the function ensures that only transitions in BLC sets
1196 /// satisfying the above conditions are marked:
1197 /// Transitions may only be marked in BLC sets that are still to be
1198 /// stabilized, as given by calM (including co-splitters); they may
1199 /// also be marked if they start in new bottom states, as indicated by
1200 /// m_blocks_with_new_bottom_states, or if they start in a singleton
1201 /// block.
1202 [[nodiscard]]
1203 bool check_stability(const std::string& tag,
1204 const std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> >*
1205 calM=nullptr,
1206 const std::pair<BLC_list_iterator,BLC_list_iterator>* calM_elt=nullptr,
1207 const constellation_type* const old_constellation=null_constellation,
1208 const constellation_type* const new_constellation=null_constellation)
1209 const
1210 {
1211 assert((old_constellation==null_constellation &&
1212 new_constellation==null_constellation ) ||
1213 (old_constellation!=null_constellation &&
1214 new_constellation!=null_constellation &&
1215 old_constellation!=new_constellation ));
1216 mCRL2log(log::debug) << "Check stability: " << tag << ".\n";
1217 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1218 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1219 {
1220 const block_type& b=*si->ref_state->block;
1221 bool previous_stable=true;
1222 for (linked_list<BLC_indicators>::const_iterator
1223 ind=b.block.to_constellation.begin();
1224 ind!=b.block.to_constellation.end(); ++ind)
1225 {
1226 set_of_states_type all_source_bottom_states;
1227
1228 assert(ind->start_same_BLC<ind->end_same_BLC);
1229 const transition&first_t=m_aut.get_transitions()[*ind->start_same_BLC];
1230 const label_index first_t_label=label_or_divergence(first_t);
1231 const bool all_transitions_in_BLC_are_inert =
1232 is_inert_during_init(first_t) && b.c.onstellation==
1233 m_states[first_t.to()].block->c.onstellation;
1234 assert(!all_transitions_in_BLC_are_inert ||
1235 b.block.to_constellation.begin()==ind);
1236 for (BLC_list_const_iterator i=ind->start_same_BLC;
1237 i<ind->end_same_BLC; ++i)
1238 {
1239 assert(m_BLC_transitions.data()<=i);
1240 assert(i<m_BLC_transitions.data_end());
1241 const transition& t=m_aut.get_transitions()[*i];
1242 assert(m_states[t.from()].block == &b);
1243 assert(label_or_divergence(t) == first_t_label);
1244 assert(m_states[t.to()].block->c.onstellation==
1245 m_states[first_t.to()].block->c.onstellation);
1246 if (is_inert_during_init(t) && b.c.onstellation==
1247 m_states[t.to()].block->c.onstellation)
1248 {
1249 assert(all_transitions_in_BLC_are_inert);
1250 }
1251 else
1252 {
1253 // This is a constellation-non-inert transition.
1254 assert(!all_transitions_in_BLC_are_inert);
1255 if (0 == m_states[t.from()].no_of_outgoing_block_inert_transitions)
1256 {
1257 assert(b.start_bottom_states<=
1258 m_states[t.from()].ref_states_in_blocks);
1259 assert(m_states[t.from()].ref_states_in_blocks<
1260 b.sta.rt_non_bottom_states);
1261 all_source_bottom_states.emplace(t.from());
1262 }
1263 else
1264 {
1265 assert(b.sta.rt_non_bottom_states<=
1266 m_states[t.from()].ref_states_in_blocks);
1267 assert(m_states[t.from()].ref_states_in_blocks < b.end_states);
1268 }
1269 }
1270 }
1271 assert(all_source_bottom_states.size()<=static_cast<std::size_t>
1272 (std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)));
1273 // check that every bottom state has a transition in this BLC entry:
1274 bool eventual_instability_is_ok = true;
1275 bool eventual_marking_is_ok = true;
1276 if (!all_transitions_in_BLC_are_inert &&
1277 all_source_bottom_states.size()!=static_cast<std::size_t>
1278 (std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)))
1279 {
1280 // only splitters should be instable.
1281 mCRL2log(log::debug) << "Not all "
1282 << std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)
1283 << (m_branching ? " bottom states have a transition in the "
1284 : " states have a transition in the ")
1285 << ind->debug_id(*this) << ": transitions found from states";
1286 for (set_of_states_type::iterator
1287 asbc_it=all_source_bottom_states.begin();
1288 asbc_it!=all_source_bottom_states.end() ; ++asbc_it)
1289 { mCRL2log(log::debug) << ' ' << *asbc_it; }
1290 mCRL2log(log::debug) << '\n';
1291 eventual_instability_is_ok = false;
1292 }
1293 if (!ind->is_stable())
1294 {
1295 // only splitters should contain marked transitions.
1296 mCRL2log(log::debug) << ind->debug_id(*this) << " contains " << std::distance(ind->start_marked_BLC, ind->end_same_BLC) << " marked transitions.\n";
1297 eventual_marking_is_ok = false;
1298 }
1299 if (b.contains_new_bottom_states)
1300 {
1301 if (!(eventual_instability_is_ok && eventual_marking_is_ok))
1302 {
1303 mCRL2log(log::debug) << " This is ok because " << b.debug_id(*this) << " contains new bottom states.\n";
1304 eventual_instability_is_ok = true;
1305 eventual_marking_is_ok = true;
1306 }
1307 }
1308 if (!(eventual_instability_is_ok && eventual_marking_is_ok) && nullptr != calM && calM->begin() != calM->end())
1309 {
1310 std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> >::const_iterator calM_iter = calM->begin();
1311 if (nullptr != calM_elt)
1312 {
1313 for(;;)
1314 {
1315 assert(calM->end() != calM_iter);
1316 if (calM_iter->first <= calM_elt->first && calM_elt->second <= calM_iter->second)
1317 {
1318 break;
1319 }
1320 ++calM_iter;
1321 }
1322 if (calM_elt->first<=ind->start_same_BLC && ind->end_same_BLC<=calM_elt->second)
1323 {
1324 mCRL2log(log::debug) <<" This is ok because the BLC set ("
1325 << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label())
1326 << "-> " << m_states[first_t.to()].
1327 block->c.onstellation->debug_id(*this)
1328 << ") is soon going to be a main splitter.\n";
1329 eventual_instability_is_ok = true;
1330 eventual_marking_is_ok = true;
1331 }
1332 else
1333 {
1334 if (old_constellation==
1335 m_states[first_t.to()].block->c.onstellation)
1336 {
1337 const linked_list<BLC_indicators>::const_iterator main_splitter=b.block.to_constellation.next(ind);
1338 if (main_splitter!=b.block.to_constellation.end())
1339 {
1340 assert(main_splitter->start_same_BLC < main_splitter->end_same_BLC);
1341 const transition& main_t = m_aut.get_transitions()[*main_splitter->start_same_BLC];
1342 assert(m_states[main_t.from()].block == &b);
1343 if(label_or_divergence(first_t)==label_or_divergence(main_t)
1344 && m_states[main_t.to()].block->c.onstellation==
1345 new_constellation)
1346 {
1347 if (calM_elt->first<=main_splitter->start_same_BLC && main_splitter->end_same_BLC<=calM_elt->second)
1348 {
1349 assert(new_constellation==
1350 m_states[main_t.to()].block->c.onstellation);
1351 mCRL2log(log::debug) << " This is ok because the BLC set (" << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label()) << "-> " << old_constellation->debug_id(*this) << ") is soon going to be a co-splitter.\n";
1352 eventual_instability_is_ok = true;
1353 eventual_marking_is_ok = true;
1354 }
1355 }
1356 }
1357 }
1358 }
1359 ++calM_iter;
1360 }
1361 for(; !(eventual_instability_is_ok && eventual_marking_is_ok) && calM->end() != calM_iter; ++calM_iter)
1362 {
1363 if (calM_iter->first<=ind->start_same_BLC && ind->end_same_BLC<=calM_iter->second)
1364 {
1365 mCRL2log(log::debug) <<" This is ok because the BLC set ("
1366 << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label())
1367 << "-> "
1368 << m_states[first_t.to()].block->c.onstellation->debug_id(*this)
1369 << ") is going to be a main splitter later.\n";
1370 eventual_instability_is_ok = true;
1371 eventual_marking_is_ok = true;
1372 }
1373 else
1374 {
1375 if (old_constellation==
1376 m_states[first_t.to()].block->c.onstellation)
1377 {
1378 const linked_list<BLC_indicators>::const_iterator main_splitter=b.block.to_constellation.next(ind);
1379 if (main_splitter != b.block.to_constellation.end())
1380 {
1381 assert(main_splitter->start_same_BLC < main_splitter->end_same_BLC);
1382 const transition& main_t = m_aut.get_transitions()[*main_splitter->start_same_BLC];
1383 assert(m_states[main_t.from()].block == &b);
1384 if(label_or_divergence(first_t)==label_or_divergence(main_t)
1385 && m_states[main_t.to()].block->c.onstellation==
1386 new_constellation)
1387 {
1388 if (calM_iter->first<=main_splitter->start_same_BLC && main_splitter->end_same_BLC<=calM_iter->second)
1389 {
1390 assert(new_constellation==
1391 m_states[main_t.to()].block->c.onstellation);
1392 mCRL2log(log::debug) << " This is ok because the BLC "
1393 "set (" << b.debug_id(*this) << " -"
1394 << m_aut.action_label(first_t.label())
1395 << "-> " << old_constellation->debug_id(*this)
1396 << ") is going to be a co-splitter later.\n";
1397 eventual_instability_is_ok = true;
1398 eventual_marking_is_ok = true;
1399 }
1400 }
1401 }
1402 }
1403 }
1404 }
1405 }
1406 if (1>=number_of_states_in_block(b))
1407 {
1408 if (!eventual_marking_is_ok)
1409 {
1410 mCRL2log(log::debug) << " (This is ok because the source block contains only 1 state.)\n";
1411 eventual_marking_is_ok = true;
1412 }
1413 }
1414 else if (1<no_of_constellations /* i.e. !initialisation */ &&
1415 !b.contains_new_bottom_states)
1416 {
1417 assert(eventual_marking_is_ok); assert(eventual_instability_is_ok);
1418 if (null_constellation==old_constellation && ind->is_stable()) {
1419 assert(previous_stable);
1420 }
1421 else
1422 {
1423 previous_stable=false;
1424 }
1425 }
1426 }
1427 }
1428 mCRL2log(log::debug) << "Check stability finished: " << tag << ".\n";
1429 return true;
1430 }
1431
1432 /// \brief Prints the list of BLC sets as debug output
1433 void display_BLC_list(const block_type* const bi) const
1434 {
1435 mCRL2log(log::debug) << "\n BLC_List\n";
1436 for(const BLC_indicators& blc_it: bi->block.to_constellation)
1437 {
1438 const transition& first_t=m_aut.get_transitions()[*blc_it.start_same_BLC];
1439 const label_index l=label_or_divergence(first_t, (label_index) -2);
1440 mCRL2log(log::debug) << "\n BLC set "
1441 << std::distance<BLC_list_const_iterator>(m_BLC_transitions.data(),
1442 blc_it.start_same_BLC) << " -- "
1443 << std::distance<BLC_list_const_iterator>(m_BLC_transitions.data(),
1444 blc_it.end_same_BLC)
1445 << " of " << ((label_index)-2==l ? "divergent self-loop "
1446 : pp(m_aut.action_label(l))+"-")
1447 << "transitions to " << m_states[first_t.to()].block->c.onstellation->debug_id(*this) << ":\n";
1448 for (BLC_list_const_iterator i=blc_it.start_same_BLC; ; ++i)
1449 {
1450 if (i == blc_it.start_marked_BLC)
1451 {
1452 mCRL2log(log::debug) << " (The BLC set is unstable, and the "
1453 " following transitions are marked.)\n";
1454 }
1455 if (i>=blc_it.end_same_BLC)
1456 {
1457 break;
1458 }
1459 const transition& t=m_aut.get_transitions()[*i];
1460 mCRL2log(log::debug) << " " << t.from() << " -"
1461 << m_aut.action_label(t.label()) << "-> " << t.to();
1462 if (is_inert_during_init(t) &&
1463 m_states[t.from()].block==m_states[t.to()].block)
1464 {
1465 mCRL2log(log::debug) << " (block-inert)";
1466 }
1467 else if (is_inert_during_init(t) &&
1468 m_states[t.from()].block->c.onstellation==
1469 m_states[t.to()].block->c.onstellation)
1470 {
1471 mCRL2log(log::debug) << " (constellation-inert)";
1472 }
1473 mCRL2log(log::debug) << '\n';
1474 }
1475 }
1476 mCRL2log(log::debug) << " BLC_List end\n";
1477 }
1478
1479 /// \brief Prints the partition refinement data structure as debug output
1480 void print_data_structures(const std::string& header,
1481 const bool initialisation=false) const
1482 {
1483 if (!mCRL2logEnabled(log::debug)) { return; }
1484 mCRL2log(log::debug) << "========= PRINT DATASTRUCTURE: " << header << " =======================================\n"
1485 "++++++++++++++++++++ States ++++++++++++++++++++++++++++\n";
1486 for(state_index si=0; si<m_aut.num_states(); ++si)
1487 {
1488 mCRL2log(log::debug) << "State " << si <<" (" << m_states[si].block->debug_id(*this) << "):\n"
1489 " #Inert outgoing transitions: " << m_states[si].no_of_outgoing_block_inert_transitions << "\n"
1490
1491 " Incoming transitions:\n";
1492 std::vector<transition>::const_iterator end=(si+1==m_aut.num_states()?m_aut.get_transitions().end():m_states[si+1].start_incoming_transitions);
1493 for(std::vector<transition>::const_iterator it=m_states[si].start_incoming_transitions; it!=end; ++it)
1494 {
1495 mCRL2log(log::debug) << " " << ptr(*it) << "\n";
1496 }
1497
1498 mCRL2log(log::debug) << " Outgoing transitions:\n";
1499 label_index t_label=m_aut.tau_label_index();
1500 const constellation_type* to_constln=null_constellation;
1501 for(outgoing_transitions_const_it it=m_states[si].start_outgoing_transitions;
1502 it!=m_outgoing_transitions.end() &&
1503 (si+1>=m_aut.num_states() || it!=m_states[si+1].start_outgoing_transitions);
1504 ++it)
1505 {
1506 const transition& t=m_aut.get_transitions()[!initialisation
1507 ? *it->ref.BLC_transitions : it->ref.transitions];
1508 bool start_same_saC_valid=
1509 m_outgoing_transitions.cbegin()<=it->start_same_saC &&
1510 it->start_same_saC<m_outgoing_transitions.end();
1511 if (start_same_saC_valid &&
1512 it->start_same_saC->start_same_saC==it &&
1513 it->start_same_saC >= it)
1514 {
1515 // it is at the beginning of a saC slice
1516 const label_index old_t_label=t_label;
1517 t_label=label_or_divergence(t, (label_index) -2);
1518 to_constln=m_states[t.to()].block->c.onstellation;
1519 mCRL2log(log::debug) << " - - - - saC slice of "
1520 << ((label_index) -2==t_label ? "divergent self-loop "
1521 : pp(m_aut.action_label(t_label))+"-")
1522 << "transitions to " << to_constln->debug_id(*this)
1523 << (m_aut.is_tau(t_label) && !m_aut.is_tau(old_t_label)
1524 ? " -- error: tau-transitions should come first\n"
1525 : ":\n");
1526 }
1527 mCRL2log(log::debug) << " " << ptr(t);
1528 if (start_same_saC_valid)
1529 {
1530 if (label_or_divergence(t, (label_index) -2)!=t_label)
1531 {
1532 mCRL2log(log::debug) << " -- error: different label";
1533 }
1534 if (!initialisation && m_states[t.to()].block->c.onstellation!=to_constln)
1535 {
1536 mCRL2log(log::debug) << " -- error: different target " << m_states[t.to()].block->c.onstellation->debug_id(*this);
1537 }
1538 if (it->start_same_saC->start_same_saC == it)
1539 {
1540 // Transition t must be the beginning and/or the end of a saC-slice
1541 if (it->start_same_saC >= it && it > m_outgoing_transitions.cbegin())
1542 {
1543 // Transition t must be the beginning of a saC-slice
1544 const transition& prev_t=m_aut.get_transitions()[
1545 !initialisation ? *std::prev(it)->ref.BLC_transitions
1546 : std::prev(it)->ref.transitions];
1547 if (prev_t.from()==t.from() &&
1548 label_or_divergence(prev_t)==t_label &&
1549 (initialisation ||
1550 m_states[prev_t.to()].block->c.onstellation==
1551 m_states[t.to()].block->c.onstellation))
1552 {
1553 mCRL2log(log::debug) << " -- error: not the beginning of a saC-slice";
1554 }
1555 }
1556 if (it->start_same_saC <= it && std::next(it) < m_outgoing_transitions.end())
1557 {
1558 // Transition t must be the end of a saC-slice
1559 const transition& next_t=m_aut.get_transitions()[
1560 !initialisation ? *std::next(it)->ref.BLC_transitions
1561 : std::next(it)->ref.transitions];
1562 if (next_t.from()==t.from() &&
1563 label_or_divergence(next_t)==t_label &&
1564 (initialisation ||
1565 m_states[next_t.to()].block->c.onstellation==
1566 m_states[t.to()].block->c.onstellation))
1567 {
1568 mCRL2log(log::debug) << " -- error: not the end of a saC-slice";
1569 }
1570 }
1571 }
1572 else if (it->start_same_saC > it ? it->start_same_saC->start_same_saC > it : it->start_same_saC->start_same_saC < it)
1573 {
1574 mCRL2log(log::debug) << " -- error: not pointing to its own saC-slice";
1575 }
1576 }
1577 mCRL2log(log::debug) << '\n';
1578 }
1579 mCRL2log(log::debug) << " Ref states in blocks: " << std::distance<fixed_vector<state_type_gj>::const_iterator>(m_states.cbegin(), m_states[si].ref_states_in_blocks->ref_state) << ". Must be " << si <<".\n";
1580 mCRL2log(log::debug) << "---------------------------------------------------\n";
1581 }
1582 mCRL2log(log::debug) << "++++++++++++++++++++ Transitions ++++++++++++++++++++++++++++\n";
1583 for(transition_index ti=0; ti<m_transitions.size(); ++ti)
1584 {
1585 const transition& t=m_aut.get_transitions()[ti];
1586 mCRL2log(log::debug) << "Transition " << ti <<": " << t.from()
1587 << " -" << m_aut.action_label(t.label()) << "-> "
1588 << t.to() << "\n";
1589 }
1590
1591 mCRL2log(log::debug) << "++++++++++++++++++++ Blocks ++++++++++++++++++++++++++++\n";
1592 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1593 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1594 {
1595 block_type* const bi=si->ref_state->block;
1596 mCRL2log(log::debug) << " Block " << bi;
1597 if (!initialisation) {
1598 mCRL2log(log::debug) << " (" << bi->c.onstellation->debug_id(*this) << ')';
1599 }
1600 mCRL2log(log::debug) << ":\n " << std::distance(bi->start_bottom_states,
1601 bi->sta.rt_non_bottom_states)
1602 << (m_branching ? " Bottom state" : " State")
1603 << (1==std::distance(bi->start_bottom_states,
1604 bi->sta.rt_non_bottom_states) ? ": " : "s: ");
1605 for (const state_in_block_pointer*
1606 sit=bi->start_bottom_states; sit!=bi->sta.rt_non_bottom_states; ++sit)
1607 {
1608 mCRL2log(log::debug) << sit->ref_state->debug_id_short(*this) << " ";
1609 }
1610 if (m_branching)
1611 {
1612 mCRL2log(log::debug) << "\n " << std::distance
1613 (bi->sta.rt_non_bottom_states, bi->end_states)
1614 << " Non-bottom state" << (1==std::distance
1615 (bi->sta.rt_non_bottom_states, bi->end_states)
1616 ? ": " : "s: ");
1617 for (const state_in_block_pointer*
1618 sit=bi->sta.rt_non_bottom_states; sit!=bi->end_states; ++sit)
1619 {
1620 mCRL2log(log::debug) << sit->ref_state->debug_id_short(*this) << " ";
1621 }
1622 }
1623 else
1624 {
1625 assert(bi->sta.rt_non_bottom_states==bi->end_states);
1626 }
1627 if (!initialisation)
1628 {
1629 display_BLC_list(bi);
1630 }
1631 mCRL2log(log::debug) << "\n";
1632 }
1633
1634 mCRL2log(log::debug) << "++++++++++++++++++++ Constellations ++++++++++++++++++++++++++++\n";
1635 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1636 m_states_in_blocks.data_end()!=si;
1637 si=si->ref_state->block->c.onstellation->end_const_states)
1638 {
1639 const constellation_type* const ci=si->ref_state->block->c.onstellation;
1640 mCRL2log(log::debug) << " " << ci->debug_id(*this) << ":\n";
1641 mCRL2log(log::debug) << " Blocks in constellation:";
1642 for (const state_in_block_pointer*
1643 constln_it=ci->start_const_states;
1644 constln_it<ci->end_const_states; )
1645 {
1646 const block_type* const bi=constln_it->ref_state->block;
1647 mCRL2log(log::debug) << " " << bi->debug_id(*this);
1648 constln_it = bi->end_states;
1649 }
1650 mCRL2log(log::debug) << "\n";
1651 }
1652 mCRL2log(log::debug) << "Non-trivial constellations:";
1653 for (const constellation_type* ci: m_non_trivial_constellations)
1654 {
1655 mCRL2log(log::debug) << " " << ci->debug_id(*this);
1656 }
1657
1659 "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
1660 "Outgoing transitions:\n";
1661
1662 for (outgoing_transitions_const_it pi = m_outgoing_transitions.cbegin();
1663 pi < m_outgoing_transitions.cend(); ++pi)
1664 {
1665 const transition& t=m_aut.get_transitions()[!initialisation
1666 ? *pi->ref.BLC_transitions : pi->ref.transitions];
1667 mCRL2log(log::debug) << " " << t.from() << " -"
1668 << m_aut.action_label(t.label()) << "-> " << t.to();
1669 if (m_outgoing_transitions.cbegin()<=pi->start_same_saC &&
1670 pi->start_same_saC<m_outgoing_transitions.end())
1671 {
1672 const transition& t1=m_aut.get_transitions()[!initialisation
1673 ? *pi->start_same_saC->ref.BLC_transitions
1674 : pi->start_same_saC->ref.transitions];
1675 mCRL2log(log::debug) << " \t(same saC: " << t1.from() << " -" << m_aut.action_label(t1.label()) << "-> " << t1.to();
1676 const label_index t_label = label_or_divergence(t);
1677 if (pi->start_same_saC->start_same_saC == pi)
1678 {
1679 // Transition t must be the beginning and/or the end of a saC-slice
1680 if (pi->start_same_saC >= pi && pi > m_outgoing_transitions.cbegin())
1681 {
1682 // Transition t must be the beginning of a saC-slice
1683 const transition& prev_t=m_aut.get_transitions()[
1684 !initialisation ? *std::prev(pi)->ref.BLC_transitions
1685 : std::prev(pi)->ref.transitions];
1686 if (prev_t.from()==t.from() &&
1687 label_or_divergence(prev_t)==t_label &&
1688 (initialisation ||
1689 m_states[prev_t.to()].block->c.onstellation==
1690 m_states[t.to()].block->c.onstellation))
1691 {
1692 mCRL2log(log::debug) << " -- error: not the beginning of a saC-slice";
1693 }
1694 }
1695 if (pi->start_same_saC <= pi && std::next(pi) < m_outgoing_transitions.end())
1696 {
1697 // Transition t must be the end of a saC-slice
1698 const transition& next_t=m_aut.get_transitions()[
1699 !initialisation ? *std::next(pi)->ref.BLC_transitions
1700 : std::next(pi)->ref.transitions];
1701 if (next_t.from()==t.from() &&
1702 label_or_divergence(next_t)==t_label &&
1703 (initialisation ||
1704 m_states[next_t.to()].block->c.onstellation==
1705 m_states[t.to()].block->c.onstellation))
1706 {
1707 mCRL2log(log::debug) << " -- error: not the end of a saC-slice";
1708 }
1709 }
1710 }
1711 else if (pi->start_same_saC > pi ? pi->start_same_saC->start_same_saC > pi : pi->start_same_saC->start_same_saC < pi)
1712 {
1713 mCRL2log(log::debug) << " -- error: not in its own saC-slice";
1714 }
1715 mCRL2log(log::debug) << ')';
1716 }
1717 mCRL2log(log::debug) << '\n';
1718 }
1719 mCRL2log(log::debug) << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
1720 "New bottom blocks to be investigated:";
1721
1722 for(const block_type* bi: m_blocks_with_new_bottom_states)
1723 {
1724 mCRL2log(log::debug) << " " << bi->debug_id(*this) << '\n';
1725 }
1726
1727 mCRL2log(log::debug) << "\n========= END PRINT DATASTRUCTURE: " << header << " =======================================\n";
1728 }
1729 #endif // ifndef NDEBUG
1730 public:
1731 /// \brief Calculate the number of equivalence classes
1732 /// \details The number of equivalence classes (which is valid after the
1733 /// partition has been constructed) is equal to the number of states in the
1734 /// bisimulation quotient.
1735 std::size_t num_eq_classes() const
1736 {
1737 return no_of_blocks;
1738 }
1739
1740
1741 /// \brief Get the equivalence class of a state
1742 /// \details After running the minimisation algorithm, this function
1743 /// produces the number of the equivalence class of a state. This number
1744 /// is the same as the number of the state in the minimised LTS to which
1745 /// the original state is mapped.
1746 /// \param s state whose equivalence class needs to be found
1747 /// \returns sequence number of the equivalence class of state s
1749 { assert(si<m_states.size());
1750 return m_states[si].block->sta.te_in_reduced_LTS;
1751 }
1752
1753
1754 /// \brief Adapt the LTS after minimisation
1755 /// \details After the efficient branching bisimulation minimisation, the
1756 /// information about the quotient LTS is only stored in the partition data
1757 /// structure of the partitioner object. This function exports the
1758 /// information back to the LTS by adapting its states and transitions: it
1759 /// updates the number of states and adds those transitions that are
1760 /// mandated by the partition data structure. If desired, it also creates
1761 /// a vector containing an arbritrary (example) original state per
1762 /// equivalence class.
1763 ///
1764 /// The main parameter and return value are implicit with this function: a
1765 /// reference to the LTS was stored in the object by the constructor.
1767 {
1768 // Assign numbers to the blocks (i.e. to the states of the reduced LTS)
1769 // One could devise a fancy scheme where the block containing state i
1770 // tries to get block number i; but let's just do something simple now.
1771 // We no longer need sta.rt_non_bottom_states at this moment, so we can
1772 // reuse that field to store the block number:
1773 state_index block_number=0;
1774 for (state_in_block_pointer*
1775 si=m_states_in_blocks.data(); m_states_in_blocks.data_end()!=si;
1776 si=si->ref_state->block->end_states)
1777 {
1778 block_type* const bi=si->ref_state->block;
1779 // destruct bi->sta.rt_non_bottom_states; -- trivial
1780 new (&bi->sta.te_in_reduced_LTS) state_index(block_number);
1781 ++block_number;
1782 }
1783
1784 {
1785 // The transitions are most efficiently directly extracted from the
1786 // block.to_constellation lists in blocks.
1787 typename std::remove_reference<decltype(m_aut.get_transitions())>::type
1788 T;
1789 for (state_in_block_pointer*
1790 si=m_states_in_blocks.data(); m_states_in_blocks.data_end()!=si;
1791 si=si->ref_state->block->end_states)
1792 {
1793 const block_type& B=*si->ref_state->block; //mCRL2complexity(&B, add_work(..., 1), *this);
1794 // Because every block is touched exactly once, we do not store a
1795 // physical counter for this.
1796 for(const BLC_indicators blc_ind: B.block.to_constellation)
1797 { // mCRL2complexity(&blc_ind, add_work(..., 1), *this);
1798 // Because every BLC set is touched exactly once, we do not store
1799 // a physical counter for this.
1800 assert(blc_ind.start_same_BLC<blc_ind.end_same_BLC);
1801 const transition&
1802 t=m_aut.get_transitions()[*blc_ind.start_same_BLC];
1803 const state_index new_to=get_eq_class(t.to());
1804 if (!is_inert_during_init(t) || B.sta.te_in_reduced_LTS!=new_to)
1805 {
1806 T.emplace_back(B.sta.te_in_reduced_LTS, t.label(), new_to);
1807 }
1808 }
1809 }
1810 m_aut.get_transitions()=std::move(T);
1811 }
1812 //
1813 // Merge the states, by setting the state labels of each state to the
1814 // concatenation of the state labels of its equivalence class.
1815
1816 if (m_aut.has_state_info()) // If there are no state labels
1817 { // this step is not needed
1818 /* Create a vector for the new labels */
1819 typename std::remove_reference<decltype(m_aut.state_labels())>::type
1820 new_labels(num_eq_classes());
1821
1822 for(std::size_t i=0; i<m_aut.num_states(); ++i)
1823 { //mCRL2complexity(&m_states[i], add_work(..., 1), *this);
1824 // Because every state is touched exactly once, we do not store a
1825 // physical counter for this.
1826 const state_index new_index(get_eq_class(i));
1827 new_labels[new_index]=new_labels[new_index]+m_aut.state_label(i);
1828 }
1829
1830 m_aut.set_num_states(num_eq_classes(), false); assert(0==m_aut.num_state_labels());
1831 //m_aut.clear_state_labels();
1832 m_aut.state_labels()=std::move(new_labels);
1833 }
1834 else
1835 {
1836 m_aut.set_num_states(num_eq_classes(), false);
1837 }
1838
1839 m_aut.set_initial_state(get_eq_class(m_aut.initial_state()));
1840 }
1841
1842
1843 /// \brief Check whether two states are in the same equivalence class.
1844 /// \param s first state that needs to be compared.
1845 /// \param t second state that needs to be compared.
1846 /// \returns true iff the two states are in the same equivalence class.
1847 bool in_same_class(state_index const s, state_index const t) const
1848 {
1849 return get_eq_class(s) == get_eq_class(t);
1850 }
1851 private:
1852 #ifndef NDEBUG
1853 std::string ptr(const transition& t) const
1854 {
1855 return std::to_string(t.from())+" -"+pp(m_aut.action_label(t.label()))+
1856 "-> "+std::to_string(t.to());
1857 }
1858 #endif
1859 /*--------------------------- main algorithm ----------------------------*/
1860
1861 /*----------------- splitB -- Algorithm 3 of [GJ 2024] -----------------*/
1862
1863 /// \brief return the number of states in block `B`
1866 return std::distance(B.start_bottom_states, B.end_states);
1867 }
1868
1869 /// \brief return the number of states in constellation `C`
1871 const
1873 return std::distance(C.start_const_states, C.end_const_states);
1874 }
1875
1876 /// \brief swap the contents of `pos1` and `pos2`, assuming they are different
1879 { assert(m_states_in_blocks.data()<=pos1);
1880 std::swap(*pos1,*pos2); assert(pos1<m_states_in_blocks.data_end());
1881 pos1->ref_state->ref_states_in_blocks=pos1; assert(m_states_in_blocks.data()<=pos2);
1882 pos2->ref_state->ref_states_in_blocks=pos2; assert(pos2<m_states_in_blocks.data_end()); assert(pos1!=pos2);
1883 }
1884
1885 /// \brief swap the contents of `pos1` and `pos2` if they are different
1888 {
1889 if (pos1!=pos2)
1890 {
1892 }
1893 }
1894
1895 /// \brief Move the contents of `pos1` to `pos2`, those of `pos2` to `pos3` and those of `pos3` to `pos1`
1896 /// \details The function requires that `pos3` lies in between `pos1` and
1897 /// `pos2`. It also requires that `pos2` and `pos3` are different.
1902 { assert(m_states_in_blocks.data()<=pos2); assert(pos2<pos3);
1903 assert(pos1<m_states_in_blocks.data_end());
1904 if (pos1==pos3)
1905 {
1906 std::swap(*pos1,*pos2);
1907 }
1908 else
1909 { assert(pos3<pos1);
1910 const state_in_block_pointer temp=*pos1;
1911 *pos1=*pos3;
1912 *pos3=*pos2;
1913 *pos2=temp;
1914
1915 pos3->ref_state->ref_states_in_blocks=pos3;
1916 }
1917 pos1->ref_state->ref_states_in_blocks=pos1;
1918 pos2->ref_state->ref_states_in_blocks=pos2;
1919 }
1920
1921 /// \brief Move the contents of `pos1` to `pos2`, those of `pos2` to `pos3` and those of `pos3` to `pos1`
1922 /// \details The function requires that `pos3` lies in between `pos1` and
1923 /// `pos2`. The swap is only executed if the positions are different.
1928 {
1929 if (pos2==pos3)
1930 {
1931 swap_states_in_states_in_block(pos1,pos2);
1932 }
1933 else
1934 {
1936 }
1937 }
1938
1939 /// \brief Swap the range [`pos1`, `pos1` + `count`) with the range [`pos2`, `pos2` + `count`)
1940 /// \details `pos1` must come before `pos2`.
1941 /// (If the ranges overlap, only swap the non-overlapping part.)
1942 /// The function requires `count` > 0 and `pos1` < `pos2`
1943 /// (this is sufficient for how it's used below: to swap new bottom states
1944 /// into their proper places; also, the work counters assume that
1945 /// [`assign_work_to`, `assign_work_to` + `count`) is assigned the work.)
1949 state_index count
1950 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
1951 , const state_in_block_pointer* assign_work_to,
1952 unsigned char const max_B,
1955 #endif
1956 )
1957 { assert(count<m_aut.num_states()); assert(m_states_in_blocks.data()<=pos1);
1958 /* if (pos1 > pos2) std::swap(pos1, pos2); */ assert(pos1<pos2); assert(pos2<=m_states_in_blocks.data_end()-count);
1959 {
1960 std::make_signed<state_index>::type
1961 overlap=std::distance(pos2, pos1)+count;
1962 if (overlap > 0)
1963 {
1964 count -= overlap;
1965 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
1966 // If we do not change `assign_work_to`, then there should be no overlap
1967 // between the area starting at `pos2` and the one at `assign_work_to`;
1968 // otherwise it may happen that work is assigned to unexpected counters.
1969 if (pos2==assign_work_to) {
1970 assign_work_to+=overlap;
1971 } else { assert(assign_work_to+count<=pos2+overlap ||
1972 pos2+overlap+count<=assign_work_to); }
1973 #endif
1974 pos2 += overlap;
1975 }
1976 } assert(0 < count);
1977 state_in_block_pointer temp=*pos1;
1978 while (--count > 0)
1979 { mCRL2complexity(assign_work_to->ref_state, add_work(ctr, max_B), *this);
1980 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
1981 ++assign_work_to;
1982 #endif
1983 *pos1 = *pos2;
1984 pos1->ref_state->ref_states_in_blocks=pos1;
1985 ++pos1;
1986 *pos2 = *pos1;
1987 pos2->ref_state->ref_states_in_blocks=pos2;
1988 ++pos2;
1989 }
1990 *pos1 = *pos2;
1991 pos1->ref_state->ref_states_in_blocks=pos1;
1992 *pos2 = temp;
1993 pos2->ref_state->ref_states_in_blocks=pos2;
1994 #ifndef NDEBUG
1995 for (fixed_vector<state_type_gj>::const_iterator
1996 si=m_states.cbegin(); si<m_states.cend(); ++si)
1997 {
1998 assert(si==si->ref_states_in_blocks->ref_state);
1999 }
2000 #endif
2001 }
2002
2003 /// \brief marks the transition indicated by `out_pos`.
2004 /// \details (We use an outgoing_transitions_it because it points to the
2005 /// m_BLC_transitions entry that needs to be updated.)
2007 {
2008 BLC_list_iterator old_pos = out_pos->ref.BLC_transitions;
2009 linked_list<BLC_indicators>::iterator ind =
2010 m_transitions[*old_pos].transitions_per_block_to_constellation; assert(ind->start_same_BLC<=old_pos);
2011 assert(old_pos<m_BLC_transitions.data_end());
2012 assert(old_pos<ind->end_same_BLC); assert(!ind->is_stable());
2013 if (old_pos < ind->start_marked_BLC)
2014 {
2015 /* The transition is not marked */ assert(ind->start_same_BLC<ind->start_marked_BLC);
2016 BLC_list_iterator new_pos = std::prev(ind->start_marked_BLC); assert(ind->start_same_BLC<=new_pos); assert(new_pos<ind->end_same_BLC);
2017 assert(new_pos<m_BLC_transitions.data_end());
2018 if (old_pos < new_pos)
2019 {
2020 std::swap(*old_pos, *new_pos);
2021 m_transitions[*old_pos].ref_outgoing_transitions->
2022 ref.BLC_transitions = old_pos; assert(out_pos==m_transitions[*new_pos].ref_outgoing_transitions);
2023 out_pos->ref.BLC_transitions = new_pos;
2024 }
2025 ind->start_marked_BLC--;
2026 }
2027
2028 #ifndef NDEBUG
2029 for (BLC_list_const_iterator it=m_BLC_transitions.data();
2030 it<m_BLC_transitions.data_end(); ++it)
2031 {
2032 assert(m_transitions[*it].ref_outgoing_transitions->ref.BLC_transitions==
2033 it);
2034 assert(m_transitions[*it].transitions_per_block_to_constellation->
2035 start_same_BLC<=it);
2036 assert(it<
2037 m_transitions[*it].transitions_per_block_to_constellation->end_same_BLC);
2038 }
2039 #endif
2040 }
2041
2042 /// \brief Move the content of i1 to i2, i2 to i3 and i3 to i1.
2044 const BLC_list_iterator i1,
2045 const BLC_list_iterator i2,
2046 const BLC_list_iterator i3)
2047 { assert(i3<=i2); assert(i2<=i1);
2048 if (i1==i3)
2049 {
2050 return;
2051 }
2052 if ((i1==i2)||(i2==i3))
2053 {
2054 std::swap(*i1,*i3);
2055 m_transitions[*i1].ref_outgoing_transitions->ref.BLC_transitions = i1;
2056 m_transitions[*i3].ref_outgoing_transitions->ref.BLC_transitions = i3;
2057 }
2058 else // swap all three elements.
2059 {
2060 transition_index temp = *i1;
2061 *i1=*i2;
2062 *i2=*i3;
2063 *i3=temp;
2064 m_transitions[*i1].ref_outgoing_transitions->ref.BLC_transitions = i1;
2065 m_transitions[*i2].ref_outgoing_transitions->ref.BLC_transitions = i2;
2066 m_transitions[*i3].ref_outgoing_transitions->ref.BLC_transitions = i3;
2067 }
2068 }
2069
2070 /// \brief Swap transition `ti` from BLC set `old_BLC_block` to BLC set `new_BLC_block`
2071 /// \param ti transition that needs to be swapped
2072 /// \param new_BLC_block new BLC set, where the transition should go to
2073 /// \param old_BLC_block old BLC set, where the transition was in originally
2074 /// \returns true iff the last element of `old_BLC_block` has been removed
2075 /// \details It is assumed that the new BLC set is located precisely before
2076 /// the old BLC set in `m_BLC_transitions`.
2077 /// This routine cannot be used in the initialisation phase, but only
2078 /// during refinement.
2079 ///
2080 /// This variant of the swap routine assumes that transition `ti` is only
2081 /// marked if it is in a singleton block or in a block containing new
2082 /// bottom states. In both cases, it is not necessary to maintain
2083 /// transition markings; so `ti` will always be treated as unmarked, and
2084 /// the new BLC set must be stable.
2085 /// (However, it may happen that other transitions in `old_BLC_block` are
2086 /// marked, and then their marking must be kept.)
2087 [[nodiscard]]
2089 const transition_index ti,
2090 linked_list<BLC_indicators>::iterator new_BLC_block,
2091 linked_list<BLC_indicators>::iterator old_BLC_block)
2092 { assert(new_BLC_block->is_stable());
2093 BLC_list_iterator old_position=
2094 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions; assert(old_BLC_block->start_same_BLC <= old_position);
2095 assert(old_position<old_BLC_block->end_same_BLC);
2096 assert(new_BLC_block->end_same_BLC==old_BLC_block->start_same_BLC);
2097 assert(m_transitions[ti].transitions_per_block_to_constellation==old_BLC_block);
2098 assert(ti == *old_position); assert(old_BLC_block->is_stable());
2099 if (old_position!=old_BLC_block->start_same_BLC)
2100 {
2101 std::swap(*old_position,*old_BLC_block->start_same_BLC);
2102 m_transitions[*old_position].ref_outgoing_transitions->
2103 ref.BLC_transitions = old_position;
2104 m_transitions[*old_BLC_block->start_same_BLC].
2105 ref_outgoing_transitions->ref.BLC_transitions =
2106 old_BLC_block->start_same_BLC;
2107 }
2108 new_BLC_block->end_same_BLC=++old_BLC_block->start_same_BLC;
2109 m_transitions[ti].transitions_per_block_to_constellation=new_BLC_block;
2110 return old_BLC_block->start_same_BLC==old_BLC_block->end_same_BLC;
2111 }
2112
2113 /// \brief Move transition `t` with transition index `ti` to a new BLC set
2114 /// \param index_block_B block forming a new constellation, at the same time target of `t`
2115 /// \param t transition that needs to be moved
2116 /// \param ti (redundant) transition index of t
2117 /// \returns true iff a new BLC set for non-constellation-inert transitions has been created
2118 /// \details Called if the target state of transition `t` switches to a new
2119 /// constellation; at the moment of calling, the new constellation only
2120 /// contains block `index_block_B`.
2121 ///
2122 /// If the transition is not constellation-inert (or does not remain
2123 /// constellation-inert), it is moved to a BLC set just after the current
2124 /// BLC set in its list of BLC sets. If no suitable BLC set exists yet, it
2125 /// will be created in that position of the list. In this way, a main
2126 /// splitter (i.e. a BLC set with transitions to the new constellation)
2127 /// will always immediately succeed its co-splitter.
2128 ///
2129 /// Counting the number of BLC sets requires that the new block still has
2130 /// the old constellation number.
2131 [[nodiscard]]
2133 block_type* const index_block_B,
2134 const transition& t,
2135 const transition_index ti)
2136 { assert(m_states[t.to()].block==index_block_B);
2137 block_type* const from_block=m_states[t.from()].block; assert(&m_aut.get_transitions()[ti] == &t);
2138 bool new_block_created = false; assert(from_block->block.to_constellation.check_linked_list());
2139 linked_list<BLC_indicators>::iterator this_block_to_constellation=
2140 m_transitions[ti].transitions_per_block_to_constellation; assert(this_block_to_constellation->is_stable());
2141 #ifndef NDEBUG
2142 // Check whether this_block_to_constellation is in the corresponding list
2143 for (linked_list<BLC_indicators>::const_iterator i=from_block->block.to_constellation.begin();
2144 i!=this_block_to_constellation; ++i)
2145 {
2146 assert(i!=from_block->block.to_constellation.end());
2147 }
2148 #endif
2149 assert(this_block_to_constellation!=from_block->block.to_constellation.end());
2150 assert(this_block_to_constellation->start_same_BLC <= m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions);
2151 linked_list<BLC_indicators>::iterator next_block_to_constellation;
2152 // if this transition is inert, it is inserted in a block in front.
2153 // Otherwise, it is inserted after the current element in the list.
2154 if (is_inert_during_init(t) && from_block==index_block_B)
2155 {
2156 next_block_to_constellation=from_block->block.to_constellation.begin(); assert(next_block_to_constellation->start_same_BLC <
2157 next_block_to_constellation->end_same_BLC);
2158 assert(m_states[m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].from()].block==index_block_B);
2159 assert(m_aut.is_tau(m_aut_apply_hidden_label_map(m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].label())));
2160 if (next_block_to_constellation==this_block_to_constellation)
2161 {
2162 // Make a new entry in the list block.to_constellation, at the
2163 // beginning;
2164
2165 next_block_to_constellation=from_block->block.to_constellation.
2166 emplace_front(//first_block_to_constellation,
2167 this_block_to_constellation->start_same_BLC,
2168 this_block_to_constellation->start_same_BLC,true);
2169 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2170 next_block_to_constellation->work_counter = this_block_to_constellation->work_counter;
2171 #endif
2172 } else {
2173 assert(m_states[m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].to()].block==index_block_B);
2174 }
2175 }
2176 else
2177 {
2178 // The transition is not constellation-inert.
2179 // The transition will be placed in a BLC set immediately after the BLC
2180 // set it came from, so that main splitters (with transitions to the
2181 // new constellation) come after co-splitters (with transitions to the
2182 // old constellation).
2183
2184 // This method also ensures that transitions from the old constellation
2185 // to the old constellation will remain at the beginning of their
2186 // respective BLC set.
2187 next_block_to_constellation=from_block->
2188 block.to_constellation.next(this_block_to_constellation);
2189 const transition* first_t;
2190 if (next_block_to_constellation==
2191 from_block->block.to_constellation.end() ||
2192 (first_t=&m_aut.get_transitions()
2193 [*(next_block_to_constellation->start_same_BLC)], assert(m_states[first_t->from()].block==from_block),
2194 m_states[first_t->to()].block!=index_block_B) ||
2195 label_or_divergence(*first_t)!=label_or_divergence(t))
2196 {
2197 // Make a new entry in the list next_block_to_constellation, after the current list element.
2198 new_block_created = true;
2199 next_block_to_constellation=from_block->block.to_constellation.
2200 emplace_after(this_block_to_constellation,
2201 this_block_to_constellation->start_same_BLC,
2202 this_block_to_constellation->start_same_BLC,true);
2203 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2204 /* The entry will be marked as unstable later */ next_block_to_constellation->work_counter=
2205 this_block_to_constellation->work_counter;
2206 #endif
2208 }
2209 }
2210
2211 if (swap_in_the_doubly_linked_list_LBC_in_blocks_new_constellation(ti,
2212 next_block_to_constellation, this_block_to_constellation))
2213 {
2214 from_block->block.to_constellation.erase(this_block_to_constellation);
2215 if (!is_inert_during_init(t) ||
2216 from_block->c.onstellation!=index_block_B->c.onstellation)
2217 // i.e. if transition t is not a (formerly) inert transition (in
2218 // that case, the old BLC set would havold BLC set did *not*
2219 // contain the (formerly) inert tau-transitions from the old to the
2220 // new constellation, or the still-inert tau-transitions of the
2221 // old constellation
2222 {
2224 }
2225 }
2226 #ifndef NDEBUG
2227 check_transitions(false, false, false);
2228 #endif
2229 return new_block_created;
2230 }
2231
2232 /// \brief Swap transition `ti` from BLC set `old_BLC_block` to BLC set `new_BLC_block`
2233 /// \param ti transition that needs to be swapped
2234 /// \param new_BLC_block new BLC set, where the transition should go to
2235 /// \param old_BLC_block old BLC set, where the transition was in originally
2236 /// \returns true iff the last element of `old_BLC_block` has been removed
2237 /// \details It is assumed that the new BLC set is located precisely before
2238 /// the old BLC set in `m_BLC_transitions`.
2239 /// This routine cannot be used in the initialisation phase, but only
2240 /// during refinement.
2241 ///
2242 /// The stability state of old and new BLC set is always the same.
2243 [[nodiscard]]
2245 const transition_index ti,
2246 linked_list<BLC_indicators>::iterator new_BLC_block,
2247 linked_list<BLC_indicators>::iterator old_BLC_block)
2248 { assert(new_BLC_block->end_same_BLC==old_BLC_block->start_same_BLC);
2249 BLC_list_iterator old_position =
2250 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions; assert(old_BLC_block->start_same_BLC<=old_position);
2251 assert(old_position<old_BLC_block->end_same_BLC); assert(ti==*old_position);
2252 assert(m_transitions[ti].transitions_per_block_to_constellation==
2253 old_BLC_block);
2254 //assert(new block==m_states[m_aut.get_transitions()[ti].from()].block);
2255 if (old_BLC_block->is_stable())
2256 { assert(new_BLC_block->is_stable());
2257 if (old_position!=old_BLC_block->start_same_BLC)
2258 {
2259 std::swap(*old_position, *old_BLC_block->start_same_BLC);
2260 m_transitions[*old_position].ref_outgoing_transitions->
2261 ref.BLC_transitions=old_position;
2262 m_transitions[*old_BLC_block->start_same_BLC].
2263 ref_outgoing_transitions->ref.BLC_transitions=
2264 old_BLC_block->start_same_BLC;
2265 }
2266 }
2267 else
2268 { assert(!new_BLC_block->is_stable());
2269 assert(new_BLC_block->start_same_BLC<=new_BLC_block->start_marked_BLC);
2270 assert(new_BLC_block->start_marked_BLC<=new_BLC_block->end_same_BLC);
2271 if (old_position<old_BLC_block->start_marked_BLC)
2272 { assert(old_BLC_block->start_marked_BLC<=old_BLC_block->end_same_BLC);
2273 swap_three_iterators_and_update_m_transitions(old_position,
2274 old_BLC_block->start_same_BLC, new_BLC_block->start_marked_BLC);
2275 ++new_BLC_block->start_marked_BLC;
2276 }
2277 else
2278 { assert(old_BLC_block->start_same_BLC<=old_BLC_block->start_marked_BLC);
2279 swap_three_iterators_and_update_m_transitions(old_position,
2280 old_BLC_block->start_marked_BLC, old_BLC_block->start_same_BLC);
2281 ++old_BLC_block->start_marked_BLC;
2282 }
2283 }
2284 m_transitions[ti].transitions_per_block_to_constellation=new_BLC_block;
2285 new_BLC_block->end_same_BLC=++old_BLC_block->start_same_BLC;
2286 return old_BLC_block->start_same_BLC==old_BLC_block->end_same_BLC;
2287 }
2288
2289 /// \brief Update the BLC list of transition `ti`, which now starts in block `new_bi`
2290 /// \param old_bi the former block where the source state of `ti` was in
2291 /// \param new_bi the current block where the source state of `ti` moves to
2292 /// \param ti index of the transition whose source state moved to a new block
2293 /// \param old_constellation target constellation of co-splitters
2294 /// \details If the transition was part of a stable BLC set, or is
2295 /// constellation-inert, the new BLC set where it goes to is also stable.
2296 /// If the transition is part of an unstable BLC set, the order of
2297 /// main/co-splitters is maintained. This order states that a co-splitter
2298 /// (i.e. any BLC set with non-constellation-inert transitions whose target
2299 /// state is in `old_constellation`) immediately precedes its corresponding
2300 /// main splitter (i.e. a BLC set with non-constellation-inert transitions
2301 /// whose target state is in the newest constellation, with the same action
2302 /// labels as the co-splitter).
2303 ///
2304 /// To maintain the order, it may happen that the old BLC set (where `ti`
2305 /// comes from) needs to be kept even if it becomes empty; then it will be
2306 /// added to `m_BLC_indicators_to_be_deleted` for deletion after all
2307 /// transitions of `new_bi` have been handled.
2309 block_type* const old_bi,
2310 block_type* const new_bi,
2311 const transition_index ti,
2312 constellation_type* old_constellation,
2313 constellation_type*const new_constellation
2314 // used to maintain the order of BLC sets:
2315 // main splitter BLC sets (target constellation == new constellation) follow immediately
2316 // after co-splitter BLC sets (target constellation == old_constellation) in the BLC sets
2317 )
2318 { assert(old_bi->block.to_constellation.check_linked_list());
2319 const transition& t=m_aut.get_transitions()[ti]; assert(new_bi->block.to_constellation.check_linked_list());
2320 assert(m_states[t.from()].block==new_bi);
2321 linked_list<BLC_indicators>::iterator this_block_to_constellation=
2322 m_transitions[ti].transitions_per_block_to_constellation;
2323 #ifndef NDEBUG
2324 // Check whether this_block_to_constellation is in the corresponding list
2325 for (linked_list<BLC_indicators>::const_iterator i=old_bi->block.to_constellation.begin();
2326 i!=this_block_to_constellation; ++i)
2327 {
2328 assert(i!=old_bi->block.to_constellation.end());
2329 }
2330 #endif
2332 constellation_type* const to_constln=
2333 m_states[t.to()].block->c.onstellation;
2334 linked_list<BLC_indicators>::iterator new_BLC_block;
2335 const bool t_is_inert=is_inert_during_init(t);
2336 if (t_is_inert && to_constln==new_bi->c.onstellation)
2337 {
2338 /* Before correcting the BLC lists, we already inserted an empty */ assert(this_block_to_constellation==old_bi->block.to_constellation.begin());
2339 /* BLC_indicator into the list to take the constellation-inert */
2340 /* transitions. */ assert(!new_bi->block.to_constellation.empty());
2341 new_BLC_block=new_bi->block.to_constellation.begin(); assert(this_block_to_constellation->start_same_BLC==new_BLC_block->end_same_BLC);
2342 #ifndef NDEBUG
2343 if (new_BLC_block->start_same_BLC<new_BLC_block->end_same_BLC) {
2344 const transition& inert_t=m_aut.get_transitions()[*new_BLC_block->start_same_BLC];
2345 assert(new_bi==m_states[inert_t.from()].block);
2346 assert(a==label_or_divergence(inert_t));
2347 assert(to_constln==m_states[inert_t.to()].block->c.onstellation);
2348 }
2349 #endif
2350 }
2351 else
2352 {
2353 transition_index perhaps_new_BLC_block_transition;
2354 const transition* perhaps_new_BLC_t;
2355 if (this_block_to_constellation->start_same_BLC!=
2356 m_BLC_transitions.data() &&
2357 (perhaps_new_BLC_block_transition=
2358 *std::prev(this_block_to_constellation->start_same_BLC),
2359 perhaps_new_BLC_t=
2360 &m_aut.get_transitions()[perhaps_new_BLC_block_transition],
2361 m_states[perhaps_new_BLC_t->from()].block==new_bi) &&
2362 a==label_or_divergence(*perhaps_new_BLC_t) &&
2363 to_constln==m_states
2364 [perhaps_new_BLC_t->to()].block->c.onstellation)
2365 {
2366 // Found the entry where the transition should go to
2367 // Move the current transition to the new list.
2368 new_BLC_block=m_transitions[perhaps_new_BLC_block_transition].
2369 transitions_per_block_to_constellation;
2370 #ifndef NDEBUG
2371 if (this_block_to_constellation->is_stable()) { assert(new_BLC_block->is_stable()); }
2372 else { assert(!new_BLC_block->is_stable()); }
2373 #endif
2374 }
2375 else
2376 {
2377 // Make a new entry in the list next_block_to_constellation;
2378
2379 // We first calculate the position where the new BLC set should go to
2380 // in new_position.
2381 // Default position: at the beginning.
2382 linked_list<BLC_indicators>::iterator new_position=
2383 new_bi->block.to_constellation.end(); assert(!is_inert_during_init(t)||to_constln!=new_bi->c.onstellation);
2384 if (new_bi->block.to_constellation.empty())
2385 { assert(!m_branching);
2386 /* This is the first transition that is moved. */ assert(new_bi->block.to_constellation.end()==new_position);
2387 }
2388 else
2389 {
2390 // default position: place it at the end of the list
2391 new_position=new_bi->block.to_constellation.before_end(); assert(new_bi->block.to_constellation.end()!=new_position);
2392 }
2393 if (null_constellation!=old_constellation)
2394 {
2395 if (t_is_inert &&
2396 ((to_constln==new_constellation &&
2397 new_bi->c.onstellation==old_constellation) ||
2398 // < The transition goes from the old constellation to
2399 // the splitter block and was constellation-inert before.
2400 // It is in a main splitter without (unstable)
2401 // co-splitter. We do not need to find the co-splitter.
2402 (to_constln==old_constellation &&
2403 new_bi->c.onstellation==new_constellation)))
2404 // < The formerly constellation-inert transition goes
2405 // from the new constellation to the old constellation,
2406 // it is in a co-splitter without (unstable) main
2407 // splitter, and this co-splitter was handled as the
2408 // first splitting action.
2409 {
2410 old_constellation=null_constellation;
2411 }
2412 else
2413 { assert(old_constellation!=new_constellation);
2414 // The following comments are all formulated for the case that
2415 // this_block_to_constellation is a main splitter (except when
2416 // indicated explicitly).
2417 linked_list<BLC_indicators>::const_iterator old_co_splitter;
2418 constellation_type* co_to_constln;
2419 if ((old_constellation==to_constln &&
2420 // i.e. `this_block_to_constellation` is a co-splitter
2421 (old_co_splitter=old_bi->block.to_constellation.
2422 next(this_block_to_constellation),
2423 co_to_constln=new_constellation, true)) ||
2424 (new_constellation==to_constln &&
2425 // i.e. `this_block_to_constellation` is a main splitter
2426 (old_co_splitter=old_bi->block.to_constellation.
2427 prev(this_block_to_constellation),
2428 co_to_constln=old_constellation, true)))
2429 {
2430 if (old_bi->block.to_constellation.end()!=old_co_splitter)
2431 {
2432 // If the co-splitter belonging to
2433 // `this_block_to_constellation` exists, then it is
2434 // `old_co_splitter` (but if there is no such co-splitter,
2435 // `old_co_splitter` could be a different main splitter, a
2436 // different co-splitter without main splitter, or a
2437 // completely unrelated splitter).
2438
2439 // Try to find out whether there is already a corresponding
2440 // co-splitter in `new_bi->block.to_constellation`
2441 // This co-splitter would be just before `old_co_splitter`
2442 // in `m_BLC_transitions`.
2443 if (new_bi->block.to_constellation.end()!=new_position &&
2444 m_BLC_transitions.data()<old_co_splitter->start_same_BLC)
2445 // i.e. this is not the first transition -- neither the
2446 // first to be moved to the new block nor the first in
2447 // m_BLC_transitions
2448 {
2449 // Check the transition in the potential corresponding
2450 // new co-splitter:
2451 const transition_index perhaps_new_co_spl_transition=
2452 *std::prev(old_co_splitter->start_same_BLC);
2453 const transition& perhaps_new_co_spl_t=
2454 m_aut.get_transitions()[perhaps_new_co_spl_transition];
2455 if(new_bi==m_states[perhaps_new_co_spl_t.from()].block &&
2456 a==label_or_divergence(perhaps_new_co_spl_t) &&
2457 co_to_constln==m_states
2458 [perhaps_new_co_spl_t.to()].block->c.onstellation)
2459 {
2460 // `perhaps_new_co_spl_transition` is in the
2461 // corresponding new co-splitter; place the new BLC set
2462 // immediately after this co-splitter in the list
2463 // `new_bi->block.to_constellation`.
2464 new_position=m_transitions
2465 [perhaps_new_co_spl_transition].
2466 transitions_per_block_to_constellation;
2467 if (old_constellation==to_constln)
2468 {
2469 // (`this_block_to_constellation` was a co-splitter:)
2470 // `perhaps_new_co_spl_transition` is in the new main
2471 // splitter; place the new BLC set immediately before
2472 // this main splitter in the list
2473 // `new_bi->block.to_constellation`.
2474 new_position=new_bi->block.to_constellation.
2475 prev(new_position);
2476 }
2477 #ifndef NDEBUG
2478 /* The new co-splitter was found, and */ if (old_co_splitter->start_same_BLC<old_co_splitter->end_same_BLC)
2479 /* `old_co_splitter` must have been the old */ {
2480 /* co-splitter. */ const transition& co_t=m_aut.get_transitions()
2481 [*old_co_splitter->start_same_BLC];
2482 /* Now the new main splitter is about to be created. */ assert(old_bi==m_states[co_t.from()].block ||
2483 /* In this case it is ok to delete */ new_bi==m_states[co_t.from()].block);
2484 /* `this_block_to_constellation` when it becomes */ assert(a==label_or_divergence(co_t));
2485 /* empty; therefore we set `old_constellation` in a */ assert(co_to_constln==m_states[co_t.to()].block->c.onstellation);
2486 /* way that it's going to delete it immediately: */ }
2487 #endif
2488 old_constellation=null_constellation;
2489 // We should not use `old_constellation` for anything
2490 // else after this point.
2491 }
2492 }
2493 }
2494 else
2495 {
2496 // this_block_to_constellation is a main splitter
2497 // but it has no corresponding co-splitter.
2498 // If it becomes empty, one can immediately delete it.
2499 old_constellation=null_constellation;
2500 }
2501 }
2502 else
2503 {
2504 // this_block_to_constellation is neither a main splitter nor
2505 // a co-splitter. If it becomes empty, one can immediately
2506 // delete it.
2507 old_constellation=null_constellation;
2508 }
2509 }
2510 }
2511 else if (this_block_to_constellation->is_stable())
2512 {
2513 // default position during new bottom splits: at the beginning of
2514 // the list (but after the BLC set of inert transitions)
2515 new_position=m_branching ? new_bi->block.to_constellation.begin()
2516 : new_bi->block.to_constellation.end();
2517 } assert(!m_branching || new_bi->block.to_constellation.end()!=new_position);
2518 const BLC_list_iterator old_BLC_start=this_block_to_constellation->start_same_BLC;
2519 new_BLC_block=new_bi->block.to_constellation.emplace_after
2520 (new_position, old_BLC_start, old_BLC_start,
2521 this_block_to_constellation->is_stable());
2522 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2523 new_BLC_block->work_counter=this_block_to_constellation->work_counter;
2524 #endif
2526 }
2527 }
2528 const bool last_element_removed=
2529 swap_in_the_doubly_linked_list_LBC_in_blocks_new_block(ti,
2530 new_BLC_block, this_block_to_constellation);
2531
2532 if (last_element_removed)
2533 {
2534 if (null_constellation != old_constellation)
2535 {
2536 // Sometimes we could still remove this_block_to_constellation
2537 // immediately (namely if the new main splitter and the new
2538 // co-splitter already exist, or if the old co-splitter does not
2539 // exist at all). A few such cases are handled above, but other
2540 // cases would require additional, possibly extensive, checks:
2541 // if (co_block_found) {
2542 // copy more or less the code from above that decides
2543 // whether this_block_to_constellation is a main splitter
2544 // that has an old co-splitter but not a new co-splitter
2545 // or vice versa.
2546 // }
2547 m_BLC_indicators_to_be_deleted.push_back
2548 (this_block_to_constellation);
2549 }
2550 else
2551 {
2552 // Remove this element.
2553 old_bi->block.to_constellation.erase(this_block_to_constellation);
2554 }
2555 if (!t_is_inert || to_constln!=new_bi->c.onstellation)
2556 {
2558 }
2559 } assert(old_bi->block.to_constellation.check_linked_list());
2560 #ifndef NDEBUG
2561 assert(new_bi->block.to_constellation.check_linked_list());
2563 #endif
2564 return;
2565 }
2566
2567 /// \brief reset a range of state counters to `undefined`
2568 /// \details The function is prepared for a situation when we join the
2569 /// `block` and `counter` fields together into one `block_plus_counter`.
2570 /// That is why it checks that only counters of states in block `bi` are
2571 /// reset.
2573 std::vector<state_in_block_pointer>::const_iterator begin,
2574 std::vector<state_in_block_pointer>::const_iterator const end,
2575 block_type* const block)
2576 {
2577 (void) block; // avoid unused parameter warning
2578 while (begin!=end)
2579 { assert(block==begin->ref_state->block);
2580 begin->ref_state->counter=undefined;
2581 ++begin;
2582 }
2583 }
2584
2585 /// \brief Moves the former non-bottom state `si` to the bottom states
2586 /// \details The block of si is not yet inserted into the set of blocks
2587 /// with new bottom states.
2589 const fixed_vector<state_type_gj>::iterator si)
2590 { assert(m_states.begin()<=si);
2591 block_type* bi = si->block; assert(si<m_states.end());
2592 swap_states_in_states_in_block(si->ref_states_in_blocks,
2593 bi->sta.rt_non_bottom_states); assert(0 == si->no_of_outgoing_block_inert_transitions);
2596 }
2597
2598 /// \brief Makes splitter stable and moves it to the beginning of the list
2600 const linked_list<BLC_indicators>::iterator splitter)
2601 { assert(from_block->block.to_constellation.end()!=splitter);
2602 splitter->make_stable(); assert(splitter->start_same_BLC<splitter->end_same_BLC);
2603 #ifndef NDEBUG
2604 const transition& t=m_aut.get_transitions()[*splitter->start_same_BLC];
2605 assert(from_block==m_states[t.from()].block);
2606 #endif
2607 linked_list<BLC_indicators>& btc=from_block->block.to_constellation; assert(!btc.empty());
2608 if (splitter!=btc.begin())
2609 {
2610 linked_list<BLC_indicators>::iterator move_splitter_after=btc.end();
2611 if (m_branching)
2612 { // The following assertion may fail because we sometimes make a splitter
2613 const transition& perhaps_inert_t= // stable before all BLC sets are split:
2614 m_aut.get_transitions()[*btc.begin()->start_same_BLC]; // assert(m_states[perhaps_inert_t.from()].block==from_block);
2615 if (is_inert_during_init_if_branching(perhaps_inert_t) &&
2616 m_states[perhaps_inert_t.to()].block->c.onstellation==
2617 from_block->c.onstellation)
2618 {
2619 move_splitter_after=btc.begin();
2620 }
2621 }
2622 btc.splice_to_after(move_splitter_after, btc, splitter);
2623 }
2624 }
2625
2626 /// \brief Move states in a set to a specific position in `m_states_in_block`
2627 /// \param R vector of states that need to be moved
2628 /// \param to_pos position where the first state in `R` needs to move to
2629 /// \details The work on this is assigned to the states in vector `R`.
2632 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2633 , state_index new_block_bottom_size
2634 #endif
2635 )
2636 {
2637 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2638 unsigned char const max_B=check_complexity::log_n-
2639 check_complexity::ilog2(new_block_bottom_size+R.size());
2640 #endif
2641 for (state_in_block_pointer st: R)
2642 { mCRL2complexity(st.ref_state, add_work(check_complexity::
2643 split_block_B_into_R_and_BminR__carry_out_split, max_B), *this);
2644 swap_states_in_states_in_block(to_pos++,
2645 st.ref_state->ref_states_in_blocks);
2646 }
2647 return;
2648 }
2649
2650 /// \brief Update all BLC sets after a new block has been created
2651 /// \param old_bi index of the old block from which states have been taken
2652 /// \param new_bi index of the new block
2653 /// \param old_constellation old constellation that was split most recently
2654 /// \details The old constellation is used to maintain the order of
2655 /// main/co-splitter pairs in the list of BLC sets (remember that we should
2656 /// have the main splitter immediately before its co-splitter).
2658 block_type* const new_bi,
2659 constellation_type* const old_constellation,
2660 constellation_type* const new_constellation)
2661 {
2662 // Algorithm 2, Line 2.42
2663 // adapt the BLC sets of a new block B in a way that they are consistent
2664 /* with the previous version... */ assert(!old_bi->block.to_constellation.empty());
2665 if (m_branching)
2666 {
2667 BLC_list_iterator start_inert_BLC=
2668 old_bi->block.to_constellation.begin()->start_same_BLC; // if there are inert transitions, they are here
2669 linked_list<BLC_indicators>::iterator new_inert_BLC_set=
2670 new_bi->block.to_constellation.emplace_front(start_inert_BLC,
2671 start_inert_BLC, true);
2672 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2673 assert(start_inert_BLC<old_bi->block.to_constellation.begin()->end_same_BLC);
2674 const transition& perhaps_inert_t=m_aut.get_transitions()[*start_inert_BLC];
2675 assert(m_states[perhaps_inert_t.from()].block==old_bi ||
2676 m_states[perhaps_inert_t.from()].block==new_bi);
2677 if (is_inert_during_init(perhaps_inert_t) &&
2678 m_states[perhaps_inert_t.to()].block->c.onstellation==
2679 old_bi->c.onstellation)
2680 {
2681 // This are really the inert transitions, so we should copy the work
2682 // counter
2683 new_inert_BLC_set->work_counter=
2684 old_bi->block.to_constellation.begin()->work_counter;
2685 }
2686 #else
2687 (void) new_inert_BLC_set; // avoid unused variable warning
2688 #endif
2689 }
2690
2691 const state_in_block_pointer* const it_end=new_bi->end_states;
2693 it=new_bi->start_bottom_states; it_end!=it; ++it)
2694 { assert(new_bi==it->ref_state->block);
2695 outgoing_transitions_const_it const out_it_end=
2696 std::next(it->ref_state)==m_states.end()
2697 ? m_outgoing_transitions.end()
2698 : std::next(it->ref_state)->start_outgoing_transitions;
2699 for (outgoing_transitions_it out_it=it->ref_state->
2700 start_outgoing_transitions; out_it_end!=out_it; ++out_it)
2701 {
2703 *out_it->ref.BLC_transitions, old_constellation, new_constellation);
2704 }
2705 }
2706
2707 if (m_branching)
2708 { assert(!new_bi->block.to_constellation.empty());
2709 // If the dummy set inserted before the loop is still empty, we remove
2710 // it again.
2711 // Before the loop we inserted an empty BLC set for the inert
2712 // transitions into new_bi->block.to_constellation.
2713 // If it is still empty, we have to remove it again.
2714 linked_list<BLC_indicators>::iterator
2715 inert_ind=new_bi->block.to_constellation.begin();
2716 if (inert_ind->start_same_BLC==inert_ind->end_same_BLC)
2717 { assert(inert_ind->is_stable());
2718 new_bi->block.to_constellation.erase(inert_ind);
2719 }
2720 }
2721
2722 for (std::vector<linked_list<BLC_indicators>::iterator>::iterator
2723 it=m_BLC_indicators_to_be_deleted.begin();
2724 it<m_BLC_indicators_to_be_deleted.end(); ++it)
2725 { assert((*it)->start_same_BLC==(*it)->end_same_BLC);
2726 // the work in this loop can be attributed to the operation that added this BLC
2727 old_bi->block.to_constellation.erase(*it); // set to m_BLC_indicators_to_be_deleted
2728 }
2729 clear(m_BLC_indicators_to_be_deleted);
2730
2731 // Actually it is not necessary to maintain the order (first stable, then
2732 // unstable) in the BLC list during the main/co-split phase; this order
2733 // is only needed in the new bottom split phase. So it would probably be
2734 // ok to just leave these co-splitters where they are actually and only
2735 // make them stable.
2736 return new_bi;
2737 }
2738
2739 /// \brief create a new block and adapt the BLC sets, and reset state counters
2740 /// \param start_bottom_states pointer to the first bottom state of the new block in `m_states_in_blocks`
2741 /// \param start_non_bottom_states pointer to the first non-bottom state of the new block in `m_states_in_blocks`
2742 /// \param end_states pointer past the last state of the new block in `m_states_in_blocks`
2743 /// \param constellation constellation of the new block
2744 /// \param old_constellation old constellation of the most recent constellation-split, used to update the BLC sets
2745 template<bool initialisation = false>
2747 state_in_block_pointer* start_bottom_states,
2748 state_in_block_pointer* const start_non_bottom_states,
2749 state_in_block_pointer* const end_states,
2750 block_type* const old_block_index,
2751 constellation_type* const old_constellation,
2752 constellation_type* const new_constellation)
2753 {
2754 // Algorithm 2, Line 2.41
2755 constellation_type* const constellation=old_block_index->c.onstellation; assert(constellation->start_const_states<=start_bottom_states);
2756 assert(start_bottom_states<end_states);
2757 block_type* const new_block_index=
2758 #ifdef USE_POOL_ALLOCATOR
2759 simple_list<BLC_indicators>::get_pool().
2760 template construct<block_type>
2761 #else
2762 new block_type
2763 #endif
2764 (start_bottom_states,
2765 start_non_bottom_states, end_states, constellation); assert(end_states<=constellation->end_const_states);
2766 ++no_of_blocks;
2767 #ifndef NDEBUG
2768 new_block_index->work_counter=old_block_index->work_counter;
2769 #endif
2770 for(; start_bottom_states<start_non_bottom_states; ++start_bottom_states)
2771 { assert(0==
2772 start_bottom_states->ref_state->no_of_outgoing_block_inert_transitions);
2773 assert(old_block_index==start_bottom_states->ref_state->block);
2774 start_bottom_states->ref_state->block=new_block_index; assert(start_bottom_states->ref_state->counter==undefined);
2775 }
2776 for (; start_bottom_states<end_states; ++start_bottom_states)
2777 { assert(old_block_index==start_bottom_states->ref_state->block);
2778 start_bottom_states->ref_state->block=new_block_index; assert(0!=
2779 start_bottom_states->ref_state->no_of_outgoing_block_inert_transitions);
2780 start_bottom_states->ref_state->counter=undefined;
2781 }
2782
2783 if constexpr (initialisation)
2784 {
2785 return new_block_index;
2786 }
2787 // Algorithm 2, Line 2.42
2788 return update_BLC_sets_new_block(old_block_index, new_block_index,
2789 old_constellation, new_constellation);
2790 }
2791
2792 /// \brief makes incoming transitions from block `NewBotSt_block_index` non-block-inert
2794 block_type* NewBotSt_block_index,
2795 state_in_block_pointer* start_bottom,
2796 state_in_block_pointer* const end_non_bottom)
2797 {
2798 for (; start_bottom!=end_non_bottom; ++start_bottom)
2799 {
2800 std::vector<transition>::const_iterator const in_it_end=
2801 std::next(start_bottom->ref_state)>=m_states.end()
2802 ? m_aut.get_transitions().end()
2803 : std::next(start_bottom->ref_state)->start_incoming_transitions; assert(start_bottom->ref_state->block!=NewBotSt_block_index);
2804 for (std::vector<transition>::iterator
2805 in_it=start_bottom->ref_state->start_incoming_transitions;
2806 in_it!=in_it_end &&
2807 m_aut.is_tau(m_aut_apply_hidden_label_map(in_it->label()));
2808 ++in_it)
2809 {
2810 const fixed_vector<state_type_gj>::iterator
2811 from=m_states.begin()+in_it->from(); assert(m_states[in_it->to()].ref_states_in_blocks==start_bottom);
2812 if (NewBotSt_block_index==from->block)
2813 {
2814 // make_transition_non_block_inert(*in_it);
2815 if (0== --from->no_of_outgoing_block_inert_transitions)
2816 {
2817 change_non_bottom_state_to_bottom_state(from);
2818 }
2819 }
2820 }
2821 }
2822 }
2823
2824 /// \brief find the next constellation after `splitter_it`'s in the `same_saC` slice of the outgoing transitions
2825 /// \details Assumes that the BLC sets are fully initialized.
2829 { assert(m_states.begin()+m_aut.get_transitions()[*splitter_it].from()==
2830 src.ref_state);
2832 out_it=m_transitions[*splitter_it].ref_outgoing_transitions;
2833 if (out_it<out_it->start_same_saC)
2834 {
2835 out_it=out_it->start_same_saC;
2836 }
2837 ++out_it;
2839 out_it_end=std::next(src.ref_state)>=m_states.end()
2840 ? m_outgoing_transitions.end()
2841 : std::next(src.ref_state)->start_outgoing_transitions;
2842 if (out_it<out_it_end)
2843 {
2844 return m_transitions[*out_it->ref.BLC_transitions].
2845 transitions_per_block_to_constellation;
2846 }
2847 else
2848 {
2849 return linked_list<BLC_indicators>::end();
2850 }
2851 }
2852
2853 /// \brief split a block (using main and co-splitter) into up to four subblocks
2854 /// \details The function can be used in the following ways:
2855 ///
2856 /// 1. If `has_small_splitter`: `small_splitter` contains transitions to
2857 /// the newest constellation `new_constellation` (or occasionally
2858 /// transitions from the newest constellation); this serves to stabilize
2859 /// block `bi` after constellation `new_constellation` was split off
2860 /// from `old_constellation`. Because the new constellation is known to
2861 /// be *small*, the main splitter can be read completely.
2862 ///
2863 /// If `has_large_splitter`, this is a true four-way-split, and
2864 /// `large_splitter` consists of transitions with the same label as
2865 /// `small_splitter` but with target constellation `old_constellation`.
2866 /// It is unknown whether `large_splitter` is small or large, but we are
2867 /// able to quickly determine, for states that have a transition in
2868 /// `small_splitter`, whether they have one in the co-splitter as well
2869 /// (using the `start_same_saC` pointers).
2870 ///
2871 /// If `!has_large_splitter`, then `small_splitter` contains transitions
2872 /// that have just become non-constellation-inert by splitting the
2873 /// constellation (and the co-splitter would contain transitions that
2874 /// are still constellation-inert).
2875 ///
2876 /// 2. If `!has_small_splitter && has_large_splitter`: `bi` is a block in
2877 /// which every bottom state is new, and it needs to be stabilized under
2878 /// all splitters; in the current call, it is split under
2879 /// `large_splitter`, which can be treated similar to a co-splitter in
2880 /// the first case. (So there is still no guarantee that
2881 /// `large_splitter` is small.)
2882 ///
2883 /// We require that every bottom state with a transition in
2884 /// `large_splitter` has a *marked* transition in `large_splitter`, so
2885 /// that we still can split up the bottom states quickly. (There cannot
2886 /// be many new bottom states, as a state becomes a new bottom state at
2887 /// most once during the whole algorithm.) In all other cases, marked
2888 /// transitions are not required.
2889 ///
2890 /// 3. If `!has_small_splitter && !has_large_splitter`: This is a split
2891 /// during initialisation. Block `bi` is split, and the relevant
2892 /// information about ReachAlw-states is given in the block descriptor.
2893 /// The parameters `small_splitter` and `large_splitter` can be ignored.
2894 /// (For bookkeeping, the only constellation present is given in
2895 /// `new_constellation`.)
2896 ///
2897 /// The function refines `bi` into up to four subblocks consisting of the
2898 /// following states:
2899 /// - **ReachAlw:** states that can reach always all splitters provided,
2900 /// and their block-inert predecessors
2901 /// - **AvoidSml:** states that cannot inertly reach `small_splitter`,
2902 /// although `small_splitter!=nullptr`, and their block-inert
2903 /// predecessors
2904 /// - **AvoidLrg:** states that cannot inertly reach `large_splitter`,
2905 /// although `large_splitter!=nullptr`, and their block-inert
2906 /// predecessors
2907 /// - **NewBotSt:** states that can block-inertly reach multiple of the
2908 /// above subsets. This will include new bottom states and will later
2909 /// need to be stabilized under all outgoing BLC sets.
2910 ///
2911 /// The bottom states can always be distributed over the first three
2912 /// subsets; after that, one has to find block-inert predecessors for each
2913 /// subset to extend it. NewBotSt mostly starts out empty, but sometimes a
2914 /// search in one of the other subsets adds a state to it.
2915 ///
2916 /// To ensure that the search through block-inert predecessors is quick, it
2917 /// is broken off after three subblocks have been completed; all remaining
2918 /// states then must be in the unfinished subblock. In this way, every
2919 /// action during the search for block-inert predecessors can be assigned
2920 /// to a _small_ subblock: either to a state in it, or an incoming or an
2921 /// outgoing transition.
2922 ///
2923 /// \param bi index of the block being split
2924 /// \param small_splitter small BLC set under which the block needs to be stabilized
2925 /// \param large_splitter (possibly large) BLC set under which the block needs to be stabilized
2926 /// \param old_constellation target constellation of all co-splitters
2927 /// \param new_constellation newest constellation (target of main splitters), used for bookkeeping
2928 /// \returns block index of the ReachAlw subblock if it exists; or `null_block` if ReachAlw is empty
2929 template <bool has_small_splitter, bool has_large_splitter>
2931 linked_list<BLC_indicators>::iterator const small_splitter,
2932 linked_list<BLC_indicators>::iterator const large_splitter,
2933 constellation_type* const old_constellation,
2934 constellation_type* const new_constellation)
2935 {
2936 assert(1<number_of_states_in_block(*bi));
2938 /// \brief potential non-bottom states
2939 /// \details These vectors contain non-bottom states that have been found
2940 /// when going through predecessors of a subblock.
2941 ///
2942 /// The variable is declared `static` to avoid repeated deallocations and
2943 /// reallocations while the algorithm runs many refinements.
2944 static std::vector<state_in_block_pointer>potential_non_bottom_states[3]; assert(potential_non_bottom_states[ReachAlw].empty());
2945 assert(potential_non_bottom_states[AvoidSml].empty());
2946 assert(potential_non_bottom_states[AvoidLrg].empty());
2947 static std::vector<state_in_block_pointer>
2948 potential_non_bottom_states_HitSmall; assert(potential_non_bottom_states_HitSmall.empty());
2949
2950 /// \brief proven non-bottom states
2951 /// \details These vectors contain all non-bottom states of which the
2952 /// procedure has proven that they are in the respective subblock, unless
2953 /// the corresponding coroutine has been aborted; all their block-inert
2954 /// successors are already in the subblock.
2955 ///
2956 /// The variable is declared `static` to avoid repeated deallocations and
2957 /// reallocations while the algorithm runs many refinements.
2958 ///
2959 /// The fourth entry in this array is for NewBotSt; it should be in the
2960 /// same array to allow to find the three other arrays with coroutine^1,
2961 /// coroutine^2 and coroutine^3.
2962 static todo_state_vector non_bottom_states[4];
2963
2964 #define non_bottom_states_NewBotSt non_bottom_states[3]
2965
2966 // Non-bottom states have a `counter` field that indicates their subblock
2967 // status: the field contains the sum of a base value, that indicates
2968 // which subblock they are (potentially) in, and a counter that indicates
2969 // how many block-inert successors still neeed to be checked.
2970
2971 /// \brief distribution of bottom states
2972 /// \details Bottom states are distributed over the subblocks by placing
2973 /// them in a specific slice of the bottom states of block `bi`: at the
2974 /// beginning there will be ReachAlw-bottom states, then AvoidLrg-bottom
2975 /// states and at the end AvoidSml-bottom states. The iterators indicate
2976 /// the place where every slice starts; at the same time, this is the end
2977 /// of the previous slice.
2978 state_in_block_pointer* start_bottom_states[4]; assert(non_bottom_states[ReachAlw].empty());
2979 start_bottom_states[ReachAlw]=bi->start_bottom_states; assert(non_bottom_states[AvoidSml].empty());
2980 start_bottom_states[AvoidSml]=bi->start_bottom_states; assert(non_bottom_states[AvoidLrg].empty());
2981 start_bottom_states[AvoidLrg+1]=bi->sta.rt_non_bottom_states; assert(non_bottom_states_NewBotSt.empty());
2982 #define bottom_size(coroutine) ( assert(ReachAlw==(coroutine)||AvoidSml==(coroutine)||AvoidLrg==(coroutine)),
2983 assert(start_bottom_states[(coroutine)]<=start_bottom_states[(coroutine)+1]),
2984 static_cast<state_type>
2985 (std::distance(start_bottom_states[(coroutine)],
2986 start_bottom_states[(coroutine)+1])))
2987 #define bottom_and_non_bottom_size(coroutine) ( assert(aborted!=status[(coroutine)]),
2988 bottom_size((coroutine))+non_bottom_states[(coroutine)].size())
2989
2990 /// \brief next unhandled co-splitter transition
2991 /// \details NewBotSt may go through the co-splitter transitions at some
2992 /// point of the algorithm; this iterator is used to store which
2993 /// transition NewBotSt will handle next. (The variable is already
2994 /// declared here just for initialisation.)
2995 BLC_list_iterator large_splitter_iter_NewBotSt;
2996 BLC_list_const_iterator large_splitter_iter_end_NewBotSt;
2997
2998 if (has_small_splitter /* needed for correctness */)
2999 { assert(bi->block.to_constellation.end()!=small_splitter);
3000 // by default states are in AvoidSml:
3001 start_bottom_states[AvoidLrg]=bi->sta.rt_non_bottom_states; assert(small_splitter->is_stable());
3002 #ifndef NDEBUG
3003 /* 1. All transitions in the main splitter are looked through. */ const transition&
3004 /* For each state with a transition in the main splitter, it */ main_t=m_aut.get_transitions()[*small_splitter->start_same_BLC];
3005 /* is possible to check whether it has a transition in the */ assert(bi==m_states[main_t.from()].block);
3006 /* co-splitter or not, using the `start_same_saC` pointer. */
3007 /* We distribute the states as described above: */
3008 #endif
3009 /* - bottom states are moved to ReachAlw, AvoidLrg, or AvoidSml, */
3010 /* depending on the transitions to the main and co-splitter */
3011 /* - non-bottom states are moved to potentially-ReachAlw if they */
3012 /* have a transition in all splitters provided. (If there is */
3013 /* no co-splitter, that means: if they have a transition in the */
3014 /* main splitter.) */
3015 /* - non-bottom with a transition in the main splitter but not in */
3016 /* the co-splitter, even though the latter is provided, are */
3017 /* moved to HitSmall temporarily. They will not become part of */
3018 /* AvoidSml. */
3019 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
3020 /* The running time for this is assigned to the transitions in */ const unsigned char max_C=check_complexity::log_n-check_complexity::
3021 /* the main splitter, which contains transitions to the new small */ ilog2(number_of_states_in_constellation(*new_constellation));
3022 /* constellation. */
3023 #endif
3024 if (has_large_splitter /* needed for correctness */)
3025 { assert(bi->block.to_constellation.end()!=large_splitter);
3026 mCRL2complexity(small_splitter, add_work(check_complexity::
3027 four_way_splitB__handle_transitions_in_main_splitter, max_C), *this);
3028 #ifndef NDEBUG
3029 /* This is a normal main/co-split (where `small_splitter` */ const transition& co_t=m_aut.get_transitions()[*large_splitter->start_same_BLC];
3030 /* contains transitions to the _small_ new constellation and */ assert(bi==m_states[co_t.from()].block);
3031 /* `large_splitter` transitions from the same block with the same */ assert(label_or_divergence(main_t)==label_or_divergence(co_t));
3032 /* label to the old constellation). None of these transitions are */ assert(!is_inert_during_init(main_t) ||
3033 /* constellation-inert. */ (new_constellation!=bi->c.onstellation &&
3034 old_constellation!=bi->c.onstellation));
3035 #endif
3036 large_splitter_iter_NewBotSt=large_splitter->start_same_BLC; assert(new_constellation==m_states[main_t.to()].block->c.onstellation);
3037 large_splitter_iter_end_NewBotSt=large_splitter->end_same_BLC; assert(old_constellation==m_states[co_t.to()].block->c.onstellation);
3038 }
3039 else
3040 { assert(bi->block.to_constellation.end()==large_splitter);
3041 assert((1==max_C) ^ is_inert_during_init(main_t));
3042 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
3043 /* This is a tau co-split (where `small_splitter` contains */ if (old_constellation==bi->c.onstellation) {
3044 /* tau-transitions from the _small_ new constellation to the old */ // This is still a normal split with tau-transitions
3045 /* constellation), or it is a tau main split of the old */ mCRL2complexity(small_splitter, add_work(check_complexity::
3046 /* constellation (where `small_splitter` contains tau-transitions */ four_way_splitB__handle_transitions_in_main_splitter, max_C), *this);
3047 /* from the old constellation to the _small_ new constellation). */ assert(new_constellation==m_states[main_t.to()].block->c.onstellation);
3048 /* The other splitter is missing because these transitions are */ } else {
3049 /* still constellation-inert. */ // This is a tau co-split
3050 mCRL2complexity(small_splitter, add_work(check_complexity::
3051 refine_partition_until_it_becomes_stable__prepare_cosplit,max_C),*this);
3052 assert(new_constellation==bi->c.onstellation);
3053 if (1<max_C) {
3054 assert(number_of_states_in_block(*bi)==
3055 number_of_states_in_constellation(*new_constellation));
3056 assert(old_constellation==m_states[main_t.to()].block->c.onstellation);
3057 }
3058 }
3059 #endif
3060 large_splitter_iter_NewBotSt=m_BLC_transitions.data_end();
3061 large_splitter_iter_end_NewBotSt=m_BLC_transitions.data_end();
3062 }
3063
3064 // Algorithm 2, Lines 2.2--2.10
3065 // Move source states of transitions in the small splitter to their
3066 // respective subblock:
3067 BLC_list_iterator splitter_it=small_splitter->start_same_BLC; assert(splitter_it!=small_splitter->end_same_BLC);
3068 do
3069 { // mCRL2complexity(&m_transitions[*splitter_it], add_work(...), *this);
3070 state_in_block_pointer const src=m_states.begin()+ // is subsumed in the above call
3071 m_aut.get_transitions()[*splitter_it].from();
3072 if (0==src.ref_state->no_of_outgoing_block_inert_transitions)
3073 { assert(bi->start_bottom_states<=src.ref_state->ref_states_in_blocks);
3074 /* src is a ReachAlw-bottom state or an AvoidLrg-bottom state */ assert(src.ref_state->ref_states_in_blocks<bi->sta.rt_non_bottom_states);
3075 if (src.ref_state->ref_states_in_blocks<
3076 start_bottom_states[AvoidSml])
3077 {
3078 #ifndef NDEBUG
3079 if (has_large_splitter) {
3080 /* source state is already in ReachAlw-bottom */ assert(next_target_constln_in_same_saC(src, splitter_it)==large_splitter);
3081 }
3082 #endif
3083 }
3084 else if (!has_large_splitter /* needed for correctness */)
3085 {
3086 /* Algorithm 2, Line 2.2: state belongs to ReachAlw */ assert(ReachAlw+1==AvoidSml);
3087 swap_states_in_states_in_block(start_bottom_states[AvoidSml],
3088 src.ref_state->ref_states_in_blocks);
3089 ++start_bottom_states[AvoidSml];
3090 }
3091 else if (start_bottom_states[AvoidSml+1]<=
3092 src.ref_state->ref_states_in_blocks)
3093 {
3094 #ifndef NDEBUG
3095 assert(bi->block.to_constellation.end()!=large_splitter);
3096 outgoing_transitions_const_it const out_it_end=std::next(src.ref_state)>=
3097 /* source state is already in AvoidLrg-bottom */ m_states.end() ? m_outgoing_transitions.end()
3098 : std::next(src.ref_state)->start_outgoing_transitions;
3099 for (outgoing_transitions_const_it out_it=src.ref_state->
3100 start_outgoing_transitions; out_it!=out_it_end; ++out_it)
3101 {
3102 assert(m_transitions[*out_it->ref.BLC_transitions].
3103 transitions_per_block_to_constellation!=large_splitter);
3104 }
3105 #endif
3106 }
3107 else if (next_target_constln_in_same_saC(src, splitter_it)==
3108 large_splitter)
3109 {
3110 /* Algorithm 2, Line 2.2: state belongs to ReachAlw */ assert(ReachAlw+1==AvoidSml);
3111 swap_states_in_states_in_block(start_bottom_states[AvoidSml],
3112 src.ref_state->ref_states_in_blocks);
3113 ++start_bottom_states[AvoidSml];
3114 }
3115 else
3116 {
3117 /* Algorithm 2, Line 2.3: state belongs to AvoidLrg */ assert(AvoidSml+1==AvoidLrg);
3118 --start_bottom_states[AvoidSml+1];
3119 swap_states_in_states_in_block(start_bottom_states[AvoidSml+1],
3120 src.ref_state->ref_states_in_blocks);
3121 }
3122 }
3123 else
3124 {
3125 /* src has outgoing tau transitions; it might end in the */ assert(bi->sta.rt_non_bottom_states<=src.ref_state->ref_states_in_blocks);
3126 /* NewBotSt-subblock. */ assert(src.ref_state->ref_states_in_blocks<bi->end_states);
3127 if (undefined==src.ref_state->counter)
3128 {
3129 if (!has_large_splitter /* needed for correctness */ ||
3130 next_target_constln_in_same_saC(src, splitter_it)==
3131 large_splitter)
3132 {
3133 // Algorithm 2, Line 2.9
3134 src.ref_state->counter=marked(ReachAlw)+
3135 src.ref_state->no_of_outgoing_block_inert_transitions; assert(is_in_marked_range_of(src.ref_state->counter, ReachAlw));
3136 // Algorithm 2, Line 2.7
3137 potential_non_bottom_states[ReachAlw].push_back(src);
3138 }
3139 else
3140 {
3141 // Algorithm 2, Line 2.8
3142 src.ref_state->counter=marked_HitSmall;
3143 potential_non_bottom_states_HitSmall.push_back(src);
3144 #ifndef NDEBUG
3145 outgoing_transitions_const_it const out_it_end=std::next(src.ref_state)>=
3146 m_states.end() ? m_outgoing_transitions.end()
3147 : std::next(src.ref_state)->start_outgoing_transitions;
3148 for (outgoing_transitions_const_it out_it=src.ref_state->
3149 start_outgoing_transitions; out_it!=out_it_end; ++out_it)
3150 {
3151 assert(has_small_splitter || has_large_splitter),
3152 assert(m_transitions[*out_it->ref.BLC_transitions].
3153 transitions_per_block_to_constellation!=large_splitter);
3154 }
3155 #endif
3156 }
3157 }
3158 #ifndef NDEBUG
3159 else if (marked_HitSmall==src.ref_state->counter) {
3160 assert(bi->block.to_constellation.end()!=large_splitter);
3161 } else {
3162 assert(is_in_marked_range_of(src.ref_state->counter, ReachAlw));
3163 if (bi->block.to_constellation.end()!=large_splitter) {
3164 assert(has_small_splitter || has_large_splitter),
3165 assert(next_target_constln_in_same_saC(src,splitter_it)==large_splitter);
3166 }
3167 }
3168 #endif
3169 }
3170 ++splitter_it;
3171 }
3172 while (splitter_it!=small_splitter->end_same_BLC);
3173 }
3174 else if (has_large_splitter /* needed for correctness */)
3175 { assert(bi->block.to_constellation.end()==small_splitter);
3176 /* This is a bottom state split. We can only go through the */ assert(bi->block.to_constellation.end()!=large_splitter);
3177 /* transitions from (new) bottom states in the co-splitter before */ assert(!large_splitter->is_stable());
3178 /* starting the coroutines. These transitions must have been marked */ assert(null_constellation==old_constellation);
3179 /* by the caller. */
3180
3181 // AvoidSml is empty; AvoidLrg is the default subblock for bottom
3182 // states without a transition in the co-splitter. But we will set
3183 // `start_bottom_states[AvoidLrg]` only after the for loop below.
3184
3185 large_splitter_iter_NewBotSt=large_splitter->start_same_BLC;
3186 large_splitter_iter_end_NewBotSt=large_splitter->start_marked_BLC;
3187
3188 for (BLC_list_iterator co_splitter_it=large_splitter->start_marked_BLC;
3189 co_splitter_it!=large_splitter->end_same_BLC; ++co_splitter_it)
3190 { // We can assign the work to the marked transition and do not need a counter
3191 state_in_block_pointer const src=m_states.begin()+ // for this loop.
3192 m_aut.get_transitions()[*co_splitter_it].from(); assert(0==src.ref_state->no_of_outgoing_block_inert_transitions);
3193 assert(bi->start_bottom_states<=src.ref_state->ref_states_in_blocks);
3194 /* Algorithm 2, Line 2.2: src is a ReachAlw-bottom state */ assert(src.ref_state->ref_states_in_blocks<bi->sta.rt_non_bottom_states);
3195 if (start_bottom_states[AvoidSml]<=
3196 src.ref_state->ref_states_in_blocks)
3197 {
3198 swap_states_in_states_in_block(start_bottom_states[AvoidSml],
3199 src.ref_state->ref_states_in_blocks);
3200 ++start_bottom_states[AvoidSml];
3201 }
3202 }
3203 // Algorithm 2, Line 2.4
3204 start_bottom_states[AvoidLrg]=start_bottom_states[AvoidSml];
3205 make_stable_and_move_to_start_of_BLC(bi, large_splitter);
3206 }
3207 else
3208 { assert(bi->block.to_constellation.end()==small_splitter);
3209 /* This is a refinement during initialisation. */ assert(bi->block.to_constellation.end()==large_splitter);
3210 /* During initialisation, we do not maintain BLC sets, so the */
3211 /* splitters cannot be given as such sets. The information needed */
3212 /* to split the block is instead provided within the block_type of */
3213 /* `*bi`. As there is only one constellation, the split actually */
3214 /* considers all transitions with a specific label out of `*bi`. */
3215 /* The field `c.first_unmarked_bottom_state` indicates which bottom */
3216 /* have such a transition (namely the bottom states before that */
3217 /* pointer), and the field `block.R` points to a vector containing */
3218 /* the non-bottom states with such a transition. */
3219
3220 start_bottom_states[AvoidSml]=bi->c.first_unmarked_bottom_state;
3221 start_bottom_states[AvoidSml+1]=bi->sta.rt_non_bottom_states; assert(nullptr!=bi->block.R);
3222 potential_non_bottom_states[ReachAlw].swap(*bi->block.R); assert(null_constellation==old_constellation);
3223
3224 // reset the information in bi so it looks like a normal block:
3225 delete bi->block.R;
3226 bi->block.R=nullptr; assert(null_constellation!=new_constellation);
3227 // destroy bi->c.first_unmarked_bottom_state; -- trivial
3228 new (&bi->c) block_type::
3230 #ifndef NDEBUG
3231 for (state_in_block_pointer st: potential_non_bottom_states[ReachAlw]) {
3232 assert(0<st.ref_state->no_of_outgoing_block_inert_transitions);
3233 assert(st.ref_state->counter==marked(ReachAlw)+
3234 st.ref_state->no_of_outgoing_block_inert_transitions);
3235 assert(is_in_marked_range_of(st.ref_state->counter, ReachAlw));
3236 }
3237 #endif
3238 large_splitter_iter_NewBotSt=m_BLC_transitions.data_end();
3239 large_splitter_iter_end_NewBotSt=m_BLC_transitions.data_end();
3240 }
3241
3242 /* 2. If the block does not contain non-bottom states, all states have */ assert(bi->start_bottom_states==start_bottom_states[ReachAlw]);
3243 /* been distributed. Finalize the refinement and return. (There */ assert(start_bottom_states[ReachAlw]<=start_bottom_states[AvoidSml]);
3244 /* may be up to three subblocks, namely ReachAlw/AvoidSml/AvoidLrg. */ assert(start_bottom_states[AvoidSml]<=start_bottom_states[AvoidLrg]);
3245 /* Pick the first and the last subblock and split off the smaller */ assert(start_bottom_states[AvoidLrg]<=start_bottom_states[AvoidLrg+1]);
3246 /* of the two. Then compare the remaining two subblocks and again */ assert(start_bottom_states[AvoidLrg+1]==bi->sta.rt_non_bottom_states);
3247 // split off the smaller one.)
3248 if (bi->sta.rt_non_bottom_states==bi->end_states)
3249 {
3250 // Algorithm 2, Line 2.41--2.42
3251 block_type* ReachAlw_block_index=null_block;
3252 constellation_type* const constellation=bi->c.onstellation;
3253 bool constellation_was_trivial=
3254 constellation->start_const_states->ref_state->block==
3255 std::prev(constellation->end_const_states)->ref_state->block;
3256 bool constellation_becomes_nontrivial=false;
3257 if (has_large_splitter && bottom_size(ReachAlw)<bottom_size(AvoidLrg))
3258 { assert(bi->start_bottom_states==start_bottom_states[ReachAlw]);
3259 if (!has_small_splitter || 0<bottom_size(ReachAlw))
3260 { assert(0<bottom_size(ReachAlw));
3261 bi->start_bottom_states=start_bottom_states[ReachAlw+1];
3262 ReachAlw_block_index=create_new_block
3263 <!has_small_splitter && !has_large_splitter>
3264 (start_bottom_states[ReachAlw],
3265 start_bottom_states[ReachAlw+1],
3266 start_bottom_states[ReachAlw+1], bi,
3267 old_constellation, new_constellation);
3268 constellation_becomes_nontrivial=true;
3269 }
3270 if (!has_small_splitter||bottom_size(AvoidSml)<bottom_size(AvoidLrg))
3272 assert(bi->start_bottom_states==start_bottom_states[AvoidSml]);
3273 if (has_small_splitter && 0<bottom_size(AvoidSml))
3274 {
3275 bi->start_bottom_states=start_bottom_states[AvoidSml+1];
3276 create_new_block<!has_small_splitter && !has_large_splitter>
3277 (start_bottom_states[AvoidSml],
3278 start_bottom_states[AvoidSml+1],
3279 start_bottom_states[AvoidSml+1], bi,
3280 old_constellation, new_constellation);
3281 constellation_becomes_nontrivial=true;
3282 } else { assert(0==bottom_size(AvoidSml)); }
3283 }
3284 else if (0<bottom_size(AvoidLrg))
3285 { assert(bi->end_states==start_bottom_states[AvoidLrg+1]);
3286 bi->sta.rt_non_bottom_states=start_bottom_states[AvoidLrg];
3287 bi->end_states=start_bottom_states[AvoidLrg];
3288 create_new_block<!has_small_splitter && !has_large_splitter>
3289 (start_bottom_states[AvoidLrg],
3290 start_bottom_states[AvoidLrg+1],
3291 start_bottom_states[AvoidLrg+1], bi,
3292 old_constellation, new_constellation);
3293 constellation_becomes_nontrivial=true;
3294 }
3295 }
3296 else
3298 assert(bi->end_states==start_bottom_states[AvoidLrg+1]);
3299 if (has_large_splitter && 0<bottom_size(AvoidLrg))
3300 {
3301 bi->sta.rt_non_bottom_states=start_bottom_states[AvoidLrg];
3302 bi->end_states=start_bottom_states[AvoidLrg];
3303 create_new_block<!has_small_splitter && !has_large_splitter>
3304 (start_bottom_states[AvoidLrg],
3305 start_bottom_states[AvoidLrg+1],
3306 start_bottom_states[AvoidLrg+1], bi,
3307 old_constellation, new_constellation);
3308 constellation_becomes_nontrivial=true;
3309 } else { assert(0==bottom_size(AvoidLrg)); }
3310 if ((has_small_splitter || !has_large_splitter) &&
3312 { assert(bi->start_bottom_states==start_bottom_states[ReachAlw]);
3313 bi->start_bottom_states=start_bottom_states[ReachAlw+1]; assert(0<bottom_size(ReachAlw));
3314 ReachAlw_block_index=create_new_block
3315 <!has_small_splitter && !has_large_splitter>
3316 (start_bottom_states[ReachAlw],
3317 start_bottom_states[ReachAlw+1],
3318 start_bottom_states[ReachAlw+1], bi,
3319 old_constellation, new_constellation);
3320 constellation_becomes_nontrivial=true;
3321 }
3322 else
3324 ReachAlw_block_index=bi;
3325 if ((has_small_splitter || !has_large_splitter) &&
3327 { assert(bi->end_states==start_bottom_states[AvoidSml+1]);
3328 bi->sta.rt_non_bottom_states=start_bottom_states[AvoidSml];
3329 bi->end_states=start_bottom_states[AvoidSml];
3330 create_new_block<!has_small_splitter && !has_large_splitter>
3331 (start_bottom_states[AvoidSml],
3332 start_bottom_states[AvoidSml+1],
3333 start_bottom_states[AvoidSml+1], bi,
3334 old_constellation, new_constellation);
3335 constellation_becomes_nontrivial=true;
3336 } else { assert(0==bottom_size(AvoidSml)); }
3337 }
3338 }
3339
3340 if (constellation_becomes_nontrivial && constellation_was_trivial)
3341 { assert(std::find(m_non_trivial_constellations.begin(),
3342 /* This constellation was trivial, as it will be split add it to */ m_non_trivial_constellations.end(),
3343 /* the non-trivial constellations. */ constellation)==m_non_trivial_constellations.end());
3344 m_non_trivial_constellations.emplace_back(constellation);
3345 }
3346 // Algorithm 2, Line 2.44
3347 return ReachAlw_block_index;
3348 } assert(m_branching);
3349
3350 // 3. We distinguish situations where some of these subblocks are empty:
3351 // - If there are no AvoidSml-bottom states, then AvoidSml will be
3352 // empty.
3353 // - It may also happen that there are no AvoidLrg-bottom states but
3354 // there are ReachAlw-bottom states because every bottom state with
3355 // a transition in the main splitter also has a transition in the
3356 // co-splitter; then it is clear from the start that AvoidLrg is
3357 // empty. Potential-AvoidLrg non-bottom states are in NewBotSt
3358 // instead.
3359 // - It may be that there are no ReachAlw-bottom states but there are
3360 // AvoidLrg-bottom states because no bottom state with a transition
3361 // in the main splitter has a transition in the co-splitter; then it
3362 // is clear from the start that ReachAlw is empty.
3363 // Potential-ReachAlw non-bottom states are in NewBotSt instead.
3364 // Empty subblocks are considered finished.
3365
3366 // 4. We decide whether one of the subblocks is already too large (more
3367 // than 50% of the unfinished states); if yes, this subblock is
3368 // immediately aborted. At most one subblock can be aborted at any
3369 // time. The aborted subblock is *not* considered finished.
3370 /* (We use variable `no_of_unfinished_states_in_block` to record the*/ assert(non_bottom_states[ReachAlw].empty());
3371 /* number of unfinished states as long as there is no aborted */ assert(non_bottom_states[AvoidSml].empty());
3372 /* subblock; as soon as a subblock is aborted, it is set to the */ assert(non_bottom_states[AvoidLrg].empty());
3373 /* largest possible value to avoid aborting another subblock.) */ assert(non_bottom_states_NewBotSt.empty());
3374
3375 enum { state_checking,
3376 incoming_inert_transition_checking,
3377 outgoing_constellation_checking,
3378 aborted, finished } status[3], status_NewBotSt;
3379 state_in_block_pointer* current_bottom_state_iter[3];
3380
3381 // the number of states in the block that are not yet in finished
3382 // subblocks; but if some process has been aborted already, it is equal
3383 // to `std::numeric_limits<state_index>::max()`:
3384 state_index no_of_unfinished_states_in_block=
3386
3387 /// \brief Abort if there are too many bottom states in a subblock, used before the coroutines start
3388 /// \details This macro applies to ReachAlw, AvoidSml, or AvoidLrg.
3389 ///
3390 /// If the bottom states alone already cover more than half of
3391 /// a block, the corresponding coroutine does not need to start.
3392 #define abort_if_bottom_size_too_large(coroutine)
3393 (( assert(non_bottom_states[(coroutine)].empty()),
3394 bottom_size((coroutine))>no_of_unfinished_states_in_block/2) &&
3395 ( assert(std::numeric_limits<state_index>::max()!=
3396 no_of_unfinished_states_in_block),
3397 no_of_unfinished_states_in_block=
3398 std::numeric_limits<state_index>::max(), assert(m_aut.num_states()<no_of_unfinished_states_in_block/2),
3399 status[(coroutine)]=aborted,
3400 true))
3401
3402 /// \brief Abort if there are too many states in subblock NewBotSt
3403 /// \details: If the states, possibly after adding i additional states,
3404 /// cover more than half of the states in the unfinished subblocks,
3405 /// NewBotSt can be aborted. The parameter i allows to apply the test
3406 /// even before adding a state, to avoid storing data that is immediately
3407 /// going to be abolished.
3408 ///
3409 /// NewBotSt has only non-bottom states, so we need a macro that
3410 /// is different from the other subblocks.
3411 ///
3412 /// This macro can be used before the coroutines start or while they run.
3413 #define abort_if_non_bottom_size_too_large_NewBotSt(i)
3414 (( assert(aborted!=status_NewBotSt),
3415 non_bottom_states_NewBotSt.size()+(i)>
3416 no_of_unfinished_states_in_block/2) &&
3417 (/* Algorithm 2, Line 2.12 */ assert(std::numeric_limits<state_index>::max()!=
3418 no_of_unfinished_states_in_block),
3419 no_of_unfinished_states_in_block=
3420 std::numeric_limits<state_index>::max(), assert(m_aut.num_states()<no_of_unfinished_states_in_block/2),
3421 status_NewBotSt=aborted,
3422 true))
3423
3424 /// \brief Abort if there are too many states in a subblock
3425 /// \details: If the states, possibly after adding i additional states,
3426 /// cover more than half of the states in the unfinished subblocks, the
3427 /// coroutine can be aborted. The parameter i allows to apply the test
3428 /// even before adding a state, to avoid storing data that is immediately
3429 /// going to be abolished.
3430 ///
3431 /// If the coroutine is aborted, its non-bottom state vector is
3432 /// immediately cleared, as it is of no use any more. (Marked counters
3433 /// can be found through `potential_non_bottom_states`.)
3434 ///
3435 /// This macro can be used while the coroutines run.
3436 #define abort_if_size_too_large(coroutine, i)
3437 (bottom_and_non_bottom_size((coroutine))+(i)>
3438 no_of_unfinished_states_in_block/2 &&
3439 (/* Algorithm 2, Line 2.12 */ assert(std::numeric_limits<state_index>::max()!=
3440 no_of_unfinished_states_in_block),
3441 no_of_unfinished_states_in_block=
3442 std::numeric_limits<state_index>::max(), assert(m_aut.num_states()<no_of_unfinished_states_in_block/2),
3443 status[(coroutine)]=aborted,
3444 non_bottom_states[(coroutine)].clear(),
3445 true))
3446
3447 int no_of_finished_searches=0; // including the NewBotSt-search
3448 int no_of_running_searches=0; // does not include the NewBotSt-search
3449 enum subblocks running_searches[3]; // does not include the NewBotSt-search
3450
3451 if ((!has_small_splitter && has_large_splitter) ||
3453 { assert(0==bottom_size(AvoidSml));
3454 /* AvoidSml is empty and finishes early. There are no states that */ assert(potential_non_bottom_states[AvoidSml].empty());
3455 // might be moved to NewBotSt.
3456 if (!has_large_splitter || 0==bottom_size(AvoidLrg))
3457 { assert(0==bottom_size(AvoidLrg));
3458 //++no_of_finished_searches;
3459 //status_NewBotSt=finished;
3460 // This is a trivial split and nothing needs to be done.
3461 // If AvoidLrg were not yet finished, it could still happen that
3462 // some states are found to have a transition in the co-splitter,
3463 // so they would yet be added to NewBotSt.
3464
3465 clear_state_counters(potential_non_bottom_states[ReachAlw].begin(),
3466 potential_non_bottom_states[ReachAlw].end(), bi);
3467 clear(potential_non_bottom_states[ReachAlw]);
3468 if (has_small_splitter && has_large_splitter)
3469 {
3470 clear_state_counters(potential_non_bottom_states_HitSmall.begin(),
3471 potential_non_bottom_states_HitSmall.end(), bi);
3472 clear(potential_non_bottom_states_HitSmall);
3473 } else { assert(potential_non_bottom_states_HitSmall.empty()); }
3474 // Algorithm 2, Line 2.44
3475 return bi;
3476 }
3477 ++no_of_finished_searches;
3478 status[AvoidSml]=finished;
3479 }
3481 {
3482 running_searches[no_of_running_searches]=AvoidSml;
3483 ++no_of_running_searches;
3484 current_bottom_state_iter[AvoidSml]=start_bottom_states[AvoidSml];
3485 status[AvoidSml]=state_checking;
3486 }
3487
3488 if (!has_large_splitter || 0==bottom_size(AvoidLrg))
3489 { assert(0==bottom_size(AvoidLrg));
3490 /* AvoidLrg is empty and finishes early. */ assert(potential_non_bottom_states[AvoidLrg].empty());
3491 ++no_of_finished_searches;
3492 status[AvoidLrg]=finished;
3493 }
3495 {
3496 running_searches[no_of_running_searches]=AvoidLrg;
3497 ++no_of_running_searches;
3498 current_bottom_state_iter[AvoidLrg]=start_bottom_states[AvoidLrg];
3499 status[AvoidLrg]=state_checking;
3500 }
3501
3502 status_NewBotSt=state_checking;
3503 if (0==bottom_size(ReachAlw))
3504 {
3505 // ReachAlw is empty and finishes early. Its non-bottom states are
3506 // actually in NewBotSt (because they can inertly reach a AvoidLrg- or
3507 /* AvoidSml-bottom-state). */ assert(non_bottom_states_NewBotSt.empty());
3508 // Algorithm 2, Line 2.35 left
3510 (potential_non_bottom_states[ReachAlw]);
3511 if (!has_large_splitter || finished==status[AvoidLrg])
3512 { assert(finished==status[AvoidLrg]);
3513 // Algorithm 2, Line 2.37 left
3514 // both ReachAlw and AvoidLrg are empty. So the HitSmall states must
3515 // be in NewBotSt. (NewBotSt has not yet been aborted.)
3516 if (has_small_splitter && has_large_splitter)
3517 {
3519 {
3521 (potential_non_bottom_states_HitSmall.begin(),
3522 potential_non_bottom_states_HitSmall.end());
3523 clear(potential_non_bottom_states_HitSmall);
3524 }
3525 else
3526 {
3528 (potential_non_bottom_states_HitSmall);
3529 }
3530 } else { assert(potential_non_bottom_states_HitSmall.empty()); }
3531 }
3532 for (state_in_block_pointer st: non_bottom_states_NewBotSt)
3533 { // The work can be assigned to the same main splitter transition(s) that made
3534 // the state get into ReachAlw (depending on whether the source or target
3535 st.ref_state->counter=marked_NewBotSt; // constellation are new, see above).
3536 }
3537 ++no_of_finished_searches;
3538 status[ReachAlw]=finished;
3540 }
3542 {
3543 running_searches[no_of_running_searches]=ReachAlw;
3544 ++no_of_running_searches;
3545 current_bottom_state_iter[ReachAlw]=start_bottom_states[ReachAlw];
3546 status[ReachAlw]=state_checking;
3547 }
3548
3549 // 5. We start the coroutines for the non-empty, non-aborted subblocks.
3550 // Every coroutine executes one step in turn. The coroutines stop as
3551 // soon as three of them have finished (including empty subblocks).
3552 // Generally the X-coroutine finds predecessors of states that are
3553 // determined to be in the X-subblock and adds them first to the
3554 // potentially-X states; as soon as every successor of a state is
3555 // known to be in the X-subblock, the state is determined to be in the
3556 // X-subblock itself.
3557 // There are two twists here:
3558 // - The coroutine for the AvoidLrg-subblock needs to check, when all
3559 // successors are known to be in the AvoidLrg-subblock, whether the
3560 // state has a transition in the co-splitter; if yes, the state is
3561 // actually a new bottom state in the NewBotSt-subblock (all its
3562 // inert successors are in AvoidLrg but the state itself is in
3563 // NewBotSt).
3564 // - Predecessors of NewBotSt-states are immediately added to the
3565 // NewBotSt-subblock because for them, having one NewBotSt-successor
3566 // is enough. There is no set of potentially-NewBotSt states.
3567
3568 std::vector<transition>::iterator current_source_iter[3],
3569 current_source_iter_NewBotSt;
3570 std::vector<transition>::const_iterator current_source_iter_end[3],
3571 current_source_iter_end_NewBotSt;
3572
3573 state_in_block_pointer current_source_AvoidLrg;
3574 outgoing_transitions_const_it current_outgoing_iter_start_AvoidLrg;
3575 outgoing_transitions_const_it current_outgoing_iter_AvoidLrg; assert(large_splitter_iter_NewBotSt<=large_splitter_iter_end_NewBotSt);
3576 for (;;)
3577 { assert(2>=no_of_finished_searches);
3578 state_in_block_pointer* new_start_bottom_states_plus_one[3];
3579 state_in_block_pointer* new_end_bottom_states_plus_one[2];
3580 #define new_start_bottom_states(idx) (assert(1<=(idx)), assert((idx)<=3), new_start_bottom_states_plus_one[(idx)-1])
3581 #define new_end_bottom_states(idx) (assert(1<=(idx)), assert((idx)<=2), new_end_bottom_states_plus_one[(idx)-1])
3582 #define new_end_bottom_states_NewBotSt (new_start_bottom_states_plus_one[2])
3583 for (int current_search_index=0; current_search_index<
3584 no_of_running_searches; ++current_search_index)
3585 {
3586 const enum subblocks
3587 current_search=running_searches[current_search_index]; assert(0<=current_search); assert(current_search<NewBotSt);
3588
3589 if (incoming_inert_transition_checking==status[current_search])
3590 { assert(current_source_iter[current_search]<
3591 /* Algorithm 2, Line 2.15 left */ current_source_iter_end[current_search]);
3592 mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().begin(),
3593 current_source_iter[current_search])], add_work(check_complexity::
3594 simple_splitB_U__handle_transition_to_U_state, 1), *this);
3595 const transition& tr=*current_source_iter[current_search]++; assert(m_aut.is_tau(m_aut_apply_hidden_label_map(tr.label())));
3596 state_in_block_pointer const src=m_states.begin()+tr.from(); assert(m_states[tr.to()].block==bi);
3597 // Algorithm 2, Line 2.16 left
3598 if (src.ref_state->block==bi &&
3600 { assert(!non_bottom_states[ReachAlw].find(src));
3601 assert(!non_bottom_states[AvoidSml].find(src));
3602 assert(!non_bottom_states[AvoidLrg].find(src));
3603 const transition_index current_counter=src.ref_state->counter;
3604 // Algorithm 2, Line 2.17 left
3605 if( ( ( undefined==current_counter
3606 || ( has_small_splitter && has_large_splitter
3607 && marked_HitSmall==current_counter
3608 && AvoidSml!=current_search ) || (assert(marked_HitSmall!=current_counter || AvoidSml==current_search),false)
3609 )
3610 && (// Algorithm 2, Line 2.23 left
3611 src.ref_state->counter=marked(current_search)+
3612 src.ref_state->no_of_outgoing_block_inert_transitions, assert(std::find(potential_non_bottom_states[current_search].begin(),
3613 potential_non_bottom_states[current_search].end(), src)==
3614 /* Algorithm 2, Line 2.22 left */ potential_non_bottom_states[current_search].end()),
3615 potential_non_bottom_states[current_search].
3616 push_back(src),
3617 true ))
3618 || is_in_marked_range_of(current_counter, current_search) )
3619 { assert(is_in_marked_range_of(src.ref_state->counter, current_search));
3620 // Algorithm 2, Line 2.24 left
3621 --src.ref_state->counter; assert(is_in_marked_range_of(src.ref_state->counter, current_search));
3622 /* Algorithm 2, Line 2.25 left */ assert(!non_bottom_states_NewBotSt.find(src));
3623 if (marked(current_search)==src.ref_state->counter)
3624 { if (!has_large_splitter) {
3625 /* all inert transitions of src point to the current */ assert(AvoidLrg!=current_search);
3626 /* subblock */ if (!has_small_splitter) { assert(marked_HitSmall!=current_counter); }
3627 /* Algorithm 2, Line 2.26 left */ }
3628 if (has_large_splitter &&
3629 AvoidLrg==current_search &&
3630 large_splitter_iter_NewBotSt!=
3631 large_splitter_iter_end_NewBotSt)
3632 { assert(bi->block.to_constellation.end()!=large_splitter);
3633 // but AvoidLrg needs to check whether src has a transition
3634 // in the large splitter
3635 // (This can be avoided if we remember that the state had
3636 // been in HitSmall earlier; then we know that it had a
3637 // transition in the small splitter but none in the
3638 // large splitter.)
3639 current_source_AvoidLrg=src;
3640 status[AvoidLrg]=outgoing_constellation_checking;
3641 current_outgoing_iter_start_AvoidLrg=
3642 src.ref_state->start_outgoing_transitions;
3643 current_outgoing_iter_AvoidLrg=
3644 std::next(src.ref_state)>=m_states.end()
3645 ? m_outgoing_transitions.end()
3646 : std::next(src.ref_state)->start_outgoing_transitions; assert(current_outgoing_iter_start_AvoidLrg<current_outgoing_iter_AvoidLrg);
3647 continue;
3648 } else { assert(AvoidLrg!=current_search ||
3649 /* Algorithm 2, Line 2.12 */ large_splitter_iter_NewBotSt==large_splitter_iter_end_NewBotSt); }
3650 if (abort_if_size_too_large(current_search, 1))
3651 { assert(running_searches[current_search_index]==current_search);
3652 --no_of_running_searches; assert(current_search_index<=no_of_running_searches);
3653 running_searches[current_search_index]=
3654 running_searches[no_of_running_searches]; assert(std::find(potential_non_bottom_states[current_search].begin(),
3655 potential_non_bottom_states[current_search].end(), src)!=
3656 potential_non_bottom_states[current_search].end());
3657 --current_search_index;
3658 continue;
3659 }
3660 // Algorithm 2, Line 2.31 left
3661 non_bottom_states[current_search].add_todo(src);
3662 }
3663 }
3664 // Algorithm 2, Line 2.18 left
3665 else if (marked_NewBotSt!=src.ref_state->counter)
3666 {
3667 // The state has block-inert transitions to multiple
3668 // subblocks (or it is HitSmall and the current search is
3669 /* AvoidSml). It should be added to NewBotSt. */ assert(!non_bottom_states_NewBotSt.find(src));
3670 // Algorithm 2, Line 2.20 left
3671 if (aborted!=status_NewBotSt &&
3673 {
3674 // but actually if NewBotSt is already aborted, there is no
3675 // need to add the state to NewBotSt. (If the current search
3676 // ends first or second, the state will be added to NewBotSt
3677 // later anyway, but if the current search ends as third we
3678 // have saved the assignment.)
3679 src.ref_state->counter=marked_NewBotSt;
3681 }
3682 } else {assert(aborted==status_NewBotSt||non_bottom_states_NewBotSt.find(src));}
3683 }
3684
3685 if (current_source_iter[current_search]!=
3686 current_source_iter_end[current_search] &&
3687 m_aut.is_tau(m_aut_apply_hidden_label_map
3688 (current_source_iter[current_search]->label())))
3689 {
3690 continue;
3691 }
3692 status[current_search]=state_checking;
3693 }
3694 else if (!has_large_splitter||state_checking==status[current_search])
3695 { assert(state_checking==status[current_search]);
3696 // Algorithm 2, Line 2.14 left
3697 state_in_block_pointer const tgt=
3698 current_bottom_state_iter[current_search]<
3699 start_bottom_states[current_search+1]
3700 ? *current_bottom_state_iter[current_search]++
3701 : non_bottom_states[current_search].move_from_todo(); assert(!non_bottom_states[current_search^1].find(tgt));
3702
3703 /* Prepare for the sources of tgt to be added to the subblock */ mCRL2complexity(tgt.ref_state,
3705 current_source_iter[current_search]=
3706 tgt.ref_state->start_incoming_transitions; assert(!non_bottom_states[current_search^2].find(tgt));
3707 current_source_iter_end[current_search]=
3708 std::next(tgt.ref_state)>=m_states.end()
3709 ? m_aut.get_transitions().end()
3710 : std::next(tgt.ref_state)->start_incoming_transitions; assert(!non_bottom_states[current_search^3].find(tgt));
3711 if (current_source_iter[current_search]<
3712 current_source_iter_end[current_search] &&
3713 m_aut.is_tau(m_aut_apply_hidden_label_map
3714 (current_source_iter[current_search]->label())))
3715 {
3716 status[current_search]=incoming_inert_transition_checking;
3717 continue;
3718 }
3719 }
3720 else
3721 { assert(AvoidLrg==current_search);
3722 /* Algorithm 2, Line 2.27 left */ assert(outgoing_constellation_checking==status[AvoidLrg]);
3723 assert(current_outgoing_iter_start_AvoidLrg<current_outgoing_iter_AvoidLrg);
3724 assert(m_outgoing_transitions.end()==current_outgoing_iter_AvoidLrg ||
3725 current_outgoing_iter_start_AvoidLrg<
3726 current_outgoing_iter_AvoidLrg->start_same_saC);
3727 --current_outgoing_iter_AvoidLrg; assert(current_outgoing_iter_AvoidLrg->start_same_saC<=
3728 current_outgoing_iter_AvoidLrg);
3729 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
3730 // Assign the work to the transitions in the same_saC slice
3732 current_outgoing_iter_AvoidLrg->start_same_saC;
3733 assert(has_small_splitter || has_large_splitter);
3734 mCRL2complexity(&m_transitions[*out_it->ref.BLC_transitions],
3735 add_work(check_complexity::
3736 simple_splitB_U__handle_transition_from_potential_U_state, 1), *this);
3737 #ifndef NDEBUG
3738 while (++out_it<=current_outgoing_iter_AvoidLrg) {
3739 assert(has_small_splitter || has_large_splitter);
3740 mCRL2complexity(&m_transitions[*out_it->ref.BLC_transitions],
3741 add_work_notemporary(check_complexity::
3742 simple_splitB_U__handle_transition_from_potential_U_state, 1), *this);
3743 }
3744 #endif
3745 #endif
3746 assert(!non_bottom_states[ReachAlw].find(current_source_AvoidLrg));
3747 assert(!non_bottom_states[AvoidLrg].find(current_source_AvoidLrg));
3748 assert(!non_bottom_states[AvoidSml].find(current_source_AvoidLrg));
3749 assert(marked(AvoidLrg)==current_source_AvoidLrg.ref_state->counter ||
3750 marked_NewBotSt==current_source_AvoidLrg.ref_state->counter);
3751 /* Algorithm 2, Line 2.28 left */ assert(has_small_splitter || has_large_splitter);
3752 linked_list<BLC_indicators>::const_iterator const current_splitter=
3753 m_transitions[
3754 *current_outgoing_iter_AvoidLrg->ref.BLC_transitions].
3755 transitions_per_block_to_constellation; assert(bi->block.to_constellation.end()!=large_splitter);
3756 if (current_splitter==large_splitter)
3757 {
3758 // The state has a transition in the large splitter, so it should
3759 // not be added to AvoidLrg. Instead, add it to NewBotSt:
3760 // Algorithm 2, Line 2.29 left
3761 if (marked_NewBotSt!=current_source_AvoidLrg.ref_state->counter)
3762 {
3763 // It doesn't happen often that the source is marked NewBotSt
3764 // exactly while AvoidLrg is running this search -- so we do
3765 /* not test this very often. */ assert(!non_bottom_states_NewBotSt.find(current_source_AvoidLrg));
3766 if (aborted!=status_NewBotSt &&
3768 { assert(aborted!=status_NewBotSt);
3769 // but actually if NewBotSt is already aborted, there is no
3770 // need to add the state to NewBotSt. (If the current search
3771 // ends first or second, the state will be added to NewBotSt
3772 // later anyway, but if the current search ends as third we
3773 // have saved the assignment.)
3774 current_source_AvoidLrg.ref_state->counter=marked_NewBotSt;
3775 non_bottom_states_NewBotSt.add_todo(current_source_AvoidLrg);
3776 }
3777 } else { assert(non_bottom_states_NewBotSt.find(current_source_AvoidLrg)); }
3778 }
3779 else if (current_outgoing_iter_AvoidLrg=
3780 current_outgoing_iter_AvoidLrg->start_same_saC,
3781 current_outgoing_iter_start_AvoidLrg==
3782 current_outgoing_iter_AvoidLrg
3783 // We have searched all outgoing transitions but found
3784 // none in the co-splitter
3785 // (We tried several options to accelerate this test,
3786 // e.g. remembering whether `current_source_AvoidLrg`
3787 // had been in HitSmall earlier; letting the
3788 // NewBotSt-coroutine go through the co-splitter
3789 // transitions instead of only waiting to mark them
3790 // as "cannot be in AvoidLrg"; even just comparing
3791 // `current_splitter==small_splitter`. But none of these
3792 // options would have much effect, so we decided to stick
3793 // with the simpler code.)
3794 )
3795 { assert(marked(AvoidLrg)==current_source_AvoidLrg.ref_state->counter);
3796 // Algorithm 2, Line 2.12
3798 { assert(running_searches[current_search_index]==AvoidLrg);
3799 --no_of_running_searches; assert(current_search_index<=no_of_running_searches);
3800 running_searches[current_search_index]=
3801 running_searches[no_of_running_searches]; assert(std::find(potential_non_bottom_states[AvoidLrg].begin(),
3802 potential_non_bottom_states[AvoidLrg].end(), current_source_AvoidLrg)!=
3803 potential_non_bottom_states[AvoidLrg].end());
3804 --current_search_index;
3805 continue;
3806 }
3807 // Algorithm 2, Line 2.31 left
3808 non_bottom_states[AvoidLrg].add_todo(current_source_AvoidLrg);
3809 }
3810 else
3811 {
3812 continue;
3813 }
3814 // At this point the search for outgoing transitions has finished
3815 // (and AvoidLrg is still running). We can go back to the previous
3816 // status.
3817 if (current_source_iter[AvoidLrg]!=
3818 current_source_iter_end[AvoidLrg] &&
3819 m_aut.is_tau(m_aut_apply_hidden_label_map
3820 (current_source_iter[AvoidLrg]->label())))
3821 {
3822 status[AvoidLrg]=incoming_inert_transition_checking;
3823 continue;
3824 }
3825 status[AvoidLrg]=state_checking;
3826 }
3827
3828 /* Now we have done one step in the handling of this subblock. If */ assert(state_checking==status[current_search]);
3829 /* we reach this point, it is time to check whether the subblock is*/ assert(NewBotSt!=current_search);
3830 // finished.
3831 if (current_bottom_state_iter[current_search]==
3832 start_bottom_states[current_search+1] &&
3833 non_bottom_states[current_search].todo_is_empty())
3834 {
3835 // the current search is completed. Finish the subblock:
3836 // Algorithm 2, Line 2.32 left
3837 status[current_search]=finished;
3838 ++no_of_finished_searches;
3839 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
3840 // Finalise the work distribution here:
3841 // Forget the balance of earlier processes that finished:
3842 // (If NewBotSt is unfinished, the third process does enough work to tilt the
3843 // balance into the positive. If another process is unfinished, then
3844 // NewBotSt and the last process that finished before NewBotSt together
3845 // should provide enough credit.)
3847 // move the work from temporary state counters to final ones
3848 const unsigned char max_new_B=check_complexity::log_n-
3850 for (const state_in_block_pointer* s=start_bottom_states[current_search];
3851 (s!=start_bottom_states[current_search+1] ||
3852 (s=non_bottom_states[current_search].data(), true)) &&
3853 s!=non_bottom_states[current_search].data_end(); ++s)
3854 {
3855 mCRL2complexity(s->ref_state, finalise_work(check_complexity::
3858 // incoming tau-transitions of s
3859 const std::vector<transition>::const_iterator in_ti_end=
3860 std::next(s->ref_state)>=m_states.end() ? m_aut.get_transitions().end()
3861 : std::next(s->ref_state)->start_incoming_transitions;
3862 for (std::vector<transition>::const_iterator
3863 ti=s->ref_state->start_incoming_transitions; ti!=in_ti_end; ++ti)
3864 {
3865 if (!m_aut.is_tau(m_aut_apply_hidden_label_map(ti->label()))) { break; }
3866 mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().
3867 cbegin(), ti)], finalise_work(check_complexity::
3868 simple_splitB_U__handle_transition_to_U_state, check_complexity::
3869 simple_splitB__handle_transition_to_R_or_U_state, max_new_B), *this);
3870 }
3871 if (has_large_splitter && AvoidLrg==current_search &&
3872 0!=s->ref_state->no_of_outgoing_block_inert_transitions)
3873 {
3874 // outgoing transitions of s
3875 const outgoing_transitions_const_it out_ti_end=
3876 std::next(s->ref_state)>=m_states.end() ? m_outgoing_transitions.end()
3877 : std::next(s->ref_state)->start_outgoing_transitions;
3879 ti=s->ref_state->start_outgoing_transitions; ti!=out_ti_end; ++ti)
3880 {
3881 assert(has_small_splitter || has_large_splitter);
3882 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
3883 finalise_work(check_complexity::
3884 simple_splitB_U__handle_transition_from_potential_U_state,
3885 check_complexity::
3886 simple_splitB__handle_transition_from_R_or_U_state,
3887 max_new_B), *this);
3888 }
3889 } else { assert(AvoidLrg!=current_search ||
3890 0==s->ref_state->no_of_outgoing_block_inert_transitions); }
3891 }
3892 if (has_large_splitter && AvoidLrg==current_search)
3893 {
3894 // Also handle the work for states that were potentially in AvoidLrg but
3895 // turned out to be new bottom states. The states that ended up actually
3896 // in AvoidLrg have already been handled above. We just go over all states
3897 // again, as only the non-AvoidLrg-states have the relevant counter !=0.
3898 // (We cannot only go over non_bottom_states_NewBotSt because some states
3899 // may have been handled by AvoidLrg after NewBotSt became too large.)
3900 for (const state_in_block_pointer*
3901 s=bi->sta.rt_non_bottom_states; s!=bi->end_states; ++s)
3902 {
3903 // outgoing transitions of s
3904 const outgoing_transitions_const_it out_ti_end=
3905 std::next(s->ref_state)>=m_states.end() ? m_outgoing_transitions.end()
3906 : std::next(s->ref_state)->start_outgoing_transitions;
3908 ti=s->ref_state->start_outgoing_transitions; ti!=out_ti_end; ++ti)
3909 {
3910 assert(has_small_splitter || has_large_splitter);
3911 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions], finalise_work
3912 (check_complexity::
3913 simple_splitB_U__handle_transition_from_potential_U_state,
3914 check_complexity::
3915 simple_splitB__test_outgoing_transitions_found_new_bottom_state,
3916 1), *this);
3917 // At this point we have not yet identified the new bottom states,
3918 // so we cannot be more specific than giving ``1'' as the new counter
3919 // value to be assigned if there has been work. After identifying the
3920 // new bottom states, we could be more strict and require ``0'' in
3921 // states that are still non-bottom.
3922 }
3923 }
3924 } else { assert(AvoidLrg!=current_search); }
3925 #endif
3926 // Algorithm 2, Line 2.33 left
3927 if (3>no_of_finished_searches)
3928 {
3929 // Algorithm 2, Line 2.35 left
3930 /* If NewBotSt is not empty, then the following reserve() call */ assert(finished!=status_NewBotSt);
3931 // would reserve an overapproximation of the needed space,
3932 // because some states likely have moved from current_search to
3933 // NewBotSt already. Therefore I do not include it. Only if
3934 // NewBotSt.size() is less than what is added to it there may be
3935 // multiple reallocations. Only if NewBotSt.size() is less than
3936 // 1/3 of what is added to it there will be multiple
3937 // reallocations.
3939 {
3941 (// non_bottom_states_NewBotSt.size()
3942 +potential_non_bottom_states[current_search].size()
3943 -non_bottom_states[current_search].size());
3944 }
3945 for (state_in_block_pointer st:
3946 potential_non_bottom_states[current_search])
3947 { // The work in this loop can be assigned to the same transition(s) that made
3948 // st go into `potential_non_bottom_states[current_search]`. (It can now be
3949 // a final counter, as we know for sure the subblock is not aborted.)
3950 if (marked_NewBotSt!=st.ref_state->counter)
3951 { assert(is_in_marked_range_of(st.ref_state->counter, current_search));
3952 if (marked(current_search)!=st.ref_state->counter)
3953 { assert(!non_bottom_states_NewBotSt.find(st));
3954 /* We always add state st to non_bottom_states_NewBotSt, */ assert(!non_bottom_states[ReachAlw].find(st));
3955 // even if NewBotSt is aborted, because we want to clear
3956 // potential_non_bottom_states[current_search]. The
3957 // alternative would be to reset the counter to undefined,
3958 // but as state st must have an unexplored block-inert
3959 // transition to a different subblock, then that subblock
3960 // would add it to its own potential_non_bottom_states
3961 // later.
3962 non_bottom_states_NewBotSt.add_todo(st); assert(!non_bottom_states[AvoidLrg].find(st));
3963 st.ref_state->counter=marked_NewBotSt; assert(!non_bottom_states[AvoidSml].find(st));
3964 } else { assert(non_bottom_states[current_search].find(st)); }
3965 } else { assert(!non_bottom_states[current_search].find(st)); }
3966 } assert(running_searches[current_search_index]==current_search);
3967 clear(potential_non_bottom_states[current_search]);
3968 --no_of_running_searches; assert(current_search_index<=no_of_running_searches);
3969 running_searches[current_search_index]=
3970 running_searches[no_of_running_searches];
3971 --current_search_index; /* is now -1, 0 or +1 */ assert((has_small_splitter && has_large_splitter) ||
3972 /* Algorithm 2, Line 2.36 left */ potential_non_bottom_states_HitSmall.empty());
3973 if (has_small_splitter && has_large_splitter &&
3974 finished==status[ReachAlw] && finished==status[AvoidLrg] &&
3975 aborted!=status_NewBotSt)
3976 { assert(1>=no_of_running_searches);
3977 // Algorithm 2, Line 2.37 left
3978 /* The HitSmall states can be assigned to NewBotSt because */ assert(finished!=status[AvoidSml]);
3979 /* they cannot be in ReachAlw or AvoidLrg */ assert(finished!=status_NewBotSt);
3980 for (state_in_block_pointer st:
3981 potential_non_bottom_states_HitSmall)
3982 { assert(0<st.ref_state->no_of_outgoing_block_inert_transitions);
3983 // The work in this loop can be assigned to the same transitions in
3984 // the main splitter as the one(s) that made st become a member of
3985 // `potential_non_bottom_states_HitSmall`.
3986 assert(!non_bottom_states[AvoidSml].find(st));
3987 if (marked_HitSmall==st.ref_state->counter)
3988 { assert(!non_bottom_states_NewBotSt.find(st));
3989 non_bottom_states_NewBotSt.add_todo(st); assert(!non_bottom_states[ReachAlw].find(st));
3990 st.ref_state->counter=marked_NewBotSt; assert(!non_bottom_states[AvoidLrg].find(st));
3991 } else { assert(marked(ReachAlw)==st.ref_state->counter ||
3992 marked(AvoidLrg)==st.ref_state->counter ||
3993 marked_NewBotSt==st.ref_state->counter); }
3994 }
3995 clear(potential_non_bottom_states_HitSmall);
3996 } else { assert(finished!=status[ReachAlw] || finished!=status[AvoidLrg] ||
3997 aborted==status_NewBotSt ||
3998 potential_non_bottom_states_HitSmall.empty()); }
3999 if (std::numeric_limits<state_index>::max()!=
4000 no_of_unfinished_states_in_block)
4001 { assert(0<no_of_running_searches); assert(no_of_running_searches<=2);
4002 /* Algorithm 2, Line 2.12 */ assert(aborted!=status[ReachAlw]); assert(aborted!=status[AvoidLrg]);
4003 no_of_unfinished_states_in_block-=
4004 bottom_and_non_bottom_size(current_search); assert(aborted!=status[running_searches[0]]);
4005 /* Try to find out whether some other process needs to be */ assert(finished!=status[running_searches[0]]);
4006 /* aborted, now that we have a more strict size bound. */ assert(aborted!=status[AvoidSml]); assert(aborted!=status_NewBotSt);
4007 if (abort_if_size_too_large(running_searches[0], 0))
4008 {
4009 // The if test is not necessary, as the result will just
4010 // be ignored if 1==no_of_running_searches, because we will
4011 // have 0==no_of_running_searches after the decrement a few
4012 // lines further down.
4013 // if (1<no_of_running_searches)
4014 // {
4015 running_searches[0]=running_searches[1];
4016 // }
4017 if (0==current_search_index)
4018 {
4019 --current_search_index;
4020 }
4021 --no_of_running_searches; // is now 0 or 1
4022 }
4023 else if (1<no_of_running_searches && ( assert(aborted!=status[running_searches[1]]),
4024 assert(finished!=status[running_searches[1]]),
4025 abort_if_size_too_large(running_searches[1], 0)))
4026 {
4027 // if (1==current_search_index) { --current_search_index; }
4028 // < will be ignored, because the new search index will
4029 // then become 1 again, which is >= the number of running
4030 // searches, so the inner main loop will be exited anyway.
4031 --no_of_running_searches; assert(1==no_of_running_searches);
4032 }
4033 else
4034 {
4036 }
4037 }
4038 continue;
4039 }
4040
4041 // Algorithm 2, Line 2.34
4042 /* All three subblocks ReachAlw/AvoidLrg/AvoidSml are finished. */ assert(finished==status[AvoidSml]); assert(finished==status[AvoidLrg]);
4043 /* NewBotSt is unfinished. */ assert(finished==status[ReachAlw]);
4044
4045 /* Calculate the placement of subblocks: */
4047 start_bottom_states[ReachAlw+1]+
4048 non_bottom_states[ReachAlw].size();
4053 non_bottom_states[AvoidSml].size();
4057 non_bottom_states[AvoidLrg].size();
4058 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4059 // Finish the accounting. First check that there were not too many waiting
4060 // cycles: (This check may have been done in NewBotSt but we cannot be sure;
4061 // NewBotSt may have been aborted earlier.)
4063 // After this check we are no longer allowed to wait, and we are allowed to
4064 // cancel work.
4065 if (has_large_splitter) {
4066 // Cancel work in the whole block. Actually only the work in NewBotSt needs
4067 // to be cancelled, but the states may not yet have moved there.
4068 for (const state_in_block_pointer*
4069 s=bi->start_bottom_states; s!=bi->sta.rt_non_bottom_states; ++s)
4070 {
4071 // outgoing transitions of s
4072 const outgoing_transitions_it out_ti_end=
4073 std::next(s->ref_state)>=m_states.end() ? m_outgoing_transitions.end()
4074 : std::next(s->ref_state)->start_outgoing_transitions;
4076 ti=s->ref_state->start_outgoing_transitions; ti!=out_ti_end; ++ti)
4077 {
4078 assert(has_small_splitter || has_large_splitter);
4079 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
4080 cancel_work(check_complexity::
4081 simple_splitB_R__handle_transition_from_R_state), *this);
4082 }
4083 }
4084 }
4085 for (const state_in_block_pointer*
4086 s=bi->sta.rt_non_bottom_states; s!=bi->end_states; ++s)
4087 {
4088 mCRL2complexity(s->ref_state, cancel_work
4090 // incoming tau-transitions of s
4091 const std::vector<transition>::iterator in_ti_end=
4092 std::next(s->ref_state)>=m_states.end() ? m_aut.get_transitions().end()
4093 : std::next(s->ref_state)->start_incoming_transitions;
4094 for (std::vector<transition>::iterator
4095 ti=s->ref_state->start_incoming_transitions; ti!=in_ti_end; ++ti)
4096 {
4097 if (!m_aut.is_tau(m_aut_apply_hidden_label_map(ti->label()))) { break; }
4098 mCRL2complexity(&m_transitions[std::distance(m_aut.
4099 get_transitions().begin(), ti)], cancel_work(check_complexity::
4100 simple_splitB_R__handle_transition_to_R_state), *this);
4101 }
4102 if (has_large_splitter) {
4103 // outgoing transitions of s
4104 const outgoing_transitions_it out_ti_end=
4105 std::next(s->ref_state)>=m_states.end() ? m_outgoing_transitions.end()
4106 : std::next(s->ref_state)->start_outgoing_transitions;
4108 ti=s->ref_state->start_outgoing_transitions; ti!=out_ti_end; ++ti)
4109 {
4110 assert(has_small_splitter || has_large_splitter);
4111 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
4112 cancel_work(check_complexity::
4113 simple_splitB_R__handle_transition_from_R_state), *this);
4114 }
4115 }
4116 }
4117 // Reset the work balance counters:
4118 /* Algorithm 2, Line 2.39 */ check_complexity::check_temporary_work();
4119 #endif
4121 {
4122 // Algorithm 2, Line 2.40
4123 // As NewBotSt is empty, we do not need to split off one of the
4124 // other (non-empty) subblocks. Choose the largest one.
4125 enum subblocks max_process=AvoidLrg;
4127 if (!has_large_splitter ||
4128 (has_small_splitter &&
4130 { assert(max_size<bottom_and_non_bottom_size(AvoidSml));
4132 max_process=AvoidSml;
4133 } else { assert(bottom_and_non_bottom_size(AvoidSml)<=max_size); }
4135 {
4137 max_process=ReachAlw;
4138 }
4139 status_NewBotSt=finished;
4140 status[max_process]=aborted;
4141 // we need to swap the vectors for clearing the state counters:
4142 clear(potential_non_bottom_states[current_search]); assert(potential_non_bottom_states[max_process].empty());
4143 non_bottom_states[max_process].swap_vec
4144 (potential_non_bottom_states[max_process]);
4145 #ifndef NDEBUG
4146 for (state_in_block_pointer st: potential_non_bottom_states_HitSmall) {
4147 assert(has_small_splitter); assert(has_large_splitter);
4148 /* All HitSmall states must have been assigned to some */ assert(marked(ReachAlw)==st.ref_state->counter ||
4149 /* subblock, so there is no need to clear these state counters */ marked(AvoidLrg)==st.ref_state->counter);
4150 /* as well: */ }
4151 #endif
4152 if (has_small_splitter && has_large_splitter)
4153 {
4154 clear(potential_non_bottom_states_HitSmall);
4155 }
4156 goto end_for_empty_NewBotSt_subblock;
4157 }
4158
4159 constellation_type* const constellation=bi->c.onstellation;
4160 if (constellation->start_const_states->ref_state->block==
4161 std::prev(constellation->end_const_states)->ref_state->block)
4162 { assert(std::find(m_non_trivial_constellations.begin(),
4163 /* This constellation was trivial, as it will be split add it */ m_non_trivial_constellations.end(),
4164 /* to the non-trivial constellations. */ constellation)==m_non_trivial_constellations.end());
4165 m_non_trivial_constellations.emplace_back(constellation);
4166 }
4167
4168 // Algorithm 2, Line 2.41
4169 // Split off NewBotSt -- actually just make *bi smaller
4170 block_type* const NewBotSt_block_index=bi;
4173 // We have to clear state counters of the current search because
4174 // some of these states may be actually NewBotSt-states that have
4175 // not yet been identified as such:
4176 clear_state_counters
4177 (potential_non_bottom_states[current_search].begin(),
4178 potential_non_bottom_states[current_search].end(), bi);
4179 clear(potential_non_bottom_states[current_search]); assert(potential_non_bottom_states[ReachAlw].empty());
4180 /* The other processes have finished earlier and transferred */ assert(potential_non_bottom_states[AvoidLrg].empty());
4181 /* their states in potential_non_bottom_states to NewBotSt. */ assert(potential_non_bottom_states[AvoidSml].empty());
4182 clear_state_counters(non_bottom_states_NewBotSt.begin(),
4183 non_bottom_states_NewBotSt.end(), bi);
4185 // Some HitSmall states may also be not-yet-found NewBotSt states,
4186 // so we have to clear these state counters as well.
4187 clear_state_counters
4188 (potential_non_bottom_states_HitSmall.begin(),
4189 potential_non_bottom_states_HitSmall.end(), bi);
4190 clear(potential_non_bottom_states_HitSmall); assert(has_large_splitter ||
4192 /* Split off the third subblock (AvoidLrg) */ static_assert(2==AvoidLrg); assert(finished==status[AvoidLrg]);
4193 if (has_large_splitter &&
4196 { assert(0<bottom_size(AvoidLrg));
4197 move_nonbottom_states_to(non_bottom_states[AvoidLrg],
4199 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4201 #endif
4202 );
4203 if (start_bottom_states[AvoidLrg]!=
4205 {
4207 (start_bottom_states[AvoidLrg],
4209 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4210 , start_bottom_states[AvoidLrg],
4213 #endif
4214 );
4215 }
4216 non_bottom_states[AvoidLrg].clear(); // cannot clear before the above call to bottom_and_non_bottom_size(2)
4217 create_new_block<!has_small_splitter && !has_large_splitter>
4221 old_constellation, new_constellation);
4223 (NewBotSt_block_index,
4226 } else {
4229 assert(0==bottom_size(AvoidLrg));assert(non_bottom_states[AvoidLrg].empty());
4230 }
4231 /* Split off the second subblock (AvoidSml) */ static_assert(1==AvoidSml); assert(finished==status[AvoidSml]);
4232 if (!has_large_splitter ||
4233 (has_small_splitter &&
4237 assert(0<bottom_size(AvoidSml));
4238 move_nonbottom_states_to(non_bottom_states[AvoidSml],
4240 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4242 #endif
4243 );
4244 if (start_bottom_states[AvoidSml]!=
4246 {
4248 (start_bottom_states[AvoidSml],
4250 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4251 , start_bottom_states[AvoidSml],
4254 #endif
4255 );
4256 }
4257 non_bottom_states[AvoidSml].clear(); // cannot clear before the above call to bottom_and_non_bottom_size(AvoidLrg)
4258 create_new_block<!has_small_splitter && !has_large_splitter>
4262 old_constellation, new_constellation);
4264 (NewBotSt_block_index,
4267 } else {
4270 assert(0==bottom_size(AvoidSml));assert(non_bottom_states[AvoidSml].empty());
4271 }
4272
4273 /* Split off the first subblock (ReachAlw) */ static_assert(0==ReachAlw); assert(finished==status[ReachAlw]);
4274 block_type* ReachAlw_block_index=null_block;
4275 if (start_bottom_states[ReachAlw]!=
4277 { assert(0<bottom_size(ReachAlw));
4278 move_nonbottom_states_to(non_bottom_states[ReachAlw],
4279 start_bottom_states[ReachAlw+1]
4280 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4282 #endif
4283 );
4284 non_bottom_states[ReachAlw].clear();
4285 ReachAlw_block_index=create_new_block
4286 <!has_small_splitter && !has_large_splitter>
4287 (start_bottom_states[ReachAlw],
4288 start_bottom_states[ReachAlw+1],
4290 old_constellation, new_constellation);
4292 (NewBotSt_block_index,
4293 start_bottom_states[ReachAlw],
4295 } else {
4296 assert(0==bottom_size(ReachAlw));assert(non_bottom_states[ReachAlw].empty());
4297 /* Algorithm 2, Line 2.43 */ }
4298 NewBotSt_block_index->contains_new_bottom_states=true; assert(NewBotSt_block_index->start_bottom_states<
4299 NewBotSt_block_index->sta.rt_non_bottom_states);
4300 m_blocks_with_new_bottom_states.push_back(NewBotSt_block_index);
4301 // Algorithm 2, Line 2.44
4302 return ReachAlw_block_index;
4303 }
4304 } // end of inner coroutine loop for the ReachAlw/AvoidLrg/AvoidSml-states
4305
4306 // Now do one step for the NewBotSt-states:
4307
4308 if (incoming_inert_transition_checking==status_NewBotSt)
4309 { assert(current_source_iter_NewBotSt<current_source_iter_end_NewBotSt);
4310 /* Algorithm 2, Line 2.15 right */ mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().begin(),
4311 current_source_iter_NewBotSt)], add_work(check_complexity::
4312 simple_splitB_R__handle_transition_to_R_state, 1), *this);
4313 const transition& tr=*current_source_iter_NewBotSt++; assert(m_aut.is_tau(m_aut_apply_hidden_label_map(tr.label())));
4314 state_in_block_pointer const src=m_states.begin()+tr.from(); assert(m_states[tr.to()].block==bi);
4315 // Algorithm 2, Line 2.16 right
4316 if (src.ref_state->block==bi &&
4318 {
4319 // Algorithm 2, Line 2.17 right
4320 if (marked_NewBotSt!=src.ref_state->counter)
4322 // Algorithm 2, Line 2.12
4324 {
4325 // but actually if NewBotSt is already aborted, there is no
4326 // need to add the state to NewBotSt. (If the state has
4327 // block-inert transitions to other subblocks, it will be added
4328 // to NewBotSt later anyway, but otherwise we have saved the
4329 // assignment.)
4330 continue;
4331 }
4332 src.ref_state->counter=marked_NewBotSt;
4334 } else { assert(non_bottom_states_NewBotSt.find(src)); }
4335 }
4336 if (current_source_iter_NewBotSt==current_source_iter_end_NewBotSt ||
4337 !m_aut.is_tau(m_aut_apply_hidden_label_map
4338 (current_source_iter_NewBotSt->label())))
4339 {
4340 status_NewBotSt=state_checking;
4341 }
4342 }
4343 else if (state_checking==status_NewBotSt)
4344 {
4345 // Algorithm 2, Line 2.14 right
4347 {
4350 /* Prepare for the sources of tgt to be added to the subblock */ mCRL2complexity(tgt.ref_state,
4352 current_source_iter_NewBotSt=
4353 tgt.ref_state->start_incoming_transitions;
4354 current_source_iter_end_NewBotSt=
4355 std::next(tgt.ref_state)>=m_states.end()
4356 ? m_aut.get_transitions().end()
4357 : std::next(tgt.ref_state)->start_incoming_transitions;
4358 if(current_source_iter_NewBotSt<current_source_iter_end_NewBotSt &&
4359 m_aut.is_tau(m_aut_apply_hidden_label_map
4360 (current_source_iter_NewBotSt->label())))
4361 {
4362 status_NewBotSt=incoming_inert_transition_checking;
4363 }
4364 continue;
4365 }
4366 // Algorithm 2, Line 2.18 right
4367 if (1>=no_of_finished_searches)
4368 {
4369 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4370 /* Nothing can be done now for the NewBotSt-subblock; we just */ check_complexity::wait();
4371 // have to wait for another subblock to give us some initial
4372 // NewBotSt-state.
4373 #endif
4374 continue;
4375 }
4376 // Algorithm 2, Line 2.19 right
4377 if (has_large_splitter && finished!=status[AvoidLrg] &&
4378 large_splitter_iter_NewBotSt!=large_splitter_iter_end_NewBotSt)
4379 { assert(finished==status[ReachAlw]); assert(finished==status[AvoidSml]);
4380 // Because we have nothing else to do, we handle one transition in
4381 // the co-splitter.
4382 do
4383 {
4384 // Algorithm 2, Line 2.23 right
4385 const transition&
4386 t=m_aut.get_transitions()[*large_splitter_iter_NewBotSt]; mCRL2complexity(&m_transitions[*large_splitter_iter_NewBotSt],
4387 add_work(check_complexity::
4388 simple_splitB_R__handle_transition_from_R_state, 1), *this);
4389 ++large_splitter_iter_NewBotSt;
4390 state_in_block_pointer src=m_states.begin()+t.from(); assert(src.ref_state->block==bi);
4391 // Algorithm 2, Line 2.25 right
4392 if (0==src.ref_state->no_of_outgoing_block_inert_transitions)
4393 { assert(!(start_bottom_states[AvoidLrg]<=src.ref_state->ref_states_in_blocks &&
4394 src.ref_state->ref_states_in_blocks<start_bottom_states[AvoidLrg+1]));
4395 }
4396 else
4397 {
4398 // Algorithm 2, Line 2.24 right
4399 if ((undefined==src.ref_state->counter) ||
4400 is_in_marked_range_of(src.ref_state->counter, AvoidLrg))
4401 { assert(!non_bottom_states[ReachAlw].find(src));
4402 /* The only subblocks that src could go to are AvoidLrg */ assert(!non_bottom_states[AvoidSml].find(src));
4403 /* and NewBotSt. But because it has a transition in the */ assert(!non_bottom_states[AvoidLrg].find(src));
4404 /* co-splitter, it cannot go to AvoidLrg. */ assert(!non_bottom_states_NewBotSt.find(src));
4405 // Algorithm 2, Line 2.26 right
4406 src.ref_state->counter=marked_NewBotSt;
4408 if (0==no_of_running_searches)
4409 {
4410 // NewBotSt is the only running search (and AvoidLrg is not
4411 // finished, so it must be aborted), so we can as well
4412 // continue this loop until we've found all such states.
4413 // We also know that NewBotSt cannot become too large.
4414 continue;
4415 }
4416 // We must add state src to NewBotSt even if NewBotSt is
4417 // about to be aborted: it may happen that this was exactly
4418 // the last transition in the co-splitter, and then the
4419 // AvoidLrg-coroutine could add state src erroneously.
4421 break;
4422 } else { assert(marked_HitSmall!=src.ref_state->counter); }
4423 }
4424 if (0!=no_of_running_searches)
4425 {
4426 break;
4427 }
4428 }
4429 while ( assert(0==no_of_running_searches), assert(aborted==status[AvoidLrg]),
4430 large_splitter_iter_NewBotSt!=large_splitter_iter_end_NewBotSt);
4431 }
4432 else
4433 { assert(finished==status[AvoidLrg] ||
4434 large_splitter_iter_NewBotSt==large_splitter_iter_end_NewBotSt);
4435 // Now check that there were not too many waiting cycles:
4436 #ifndef NDEBUG
4438 // After this check we are no longer allowed to wait (and we are allowed to
4439 // cancel work).
4440 #endif
4441 // If finished==status[AvoidLrg]:
4442 // At most one of AvoidSml and ReachAlw is not finished.
4443 // If AvoidSml is not finished, all states with non-exclusive
4444 // block-inert transitions to AvoidLrg or ReachAlw have
4445 // been added to NewBotSt. Also all states that would
4446 // be in AvoidLrg except for their transition in the
4447 // co-splitter have been added to NewBotSt. The search for
4448 // AvoidSml-predecessors will not add any further states to
4449 // NewBotSt.
4450 // If ReachAlw is not finished, the situation is similar.
4451 // Therefore, we can finish NewBotSt.
4452 // If finished!=status[AvoidLrg] &&
4453 // large_splitter_iter_NewBotSt==large_splitter_iter_end_NewBotSt:
4454 // Until now, AvoidLrg and NewBotSt were still running, and it
4455 // was unclear which of the two was smaller. Now it has turned
4456 // out that NewBotSt has finished all it can do, so AvoidLrg
4457 // shall be aborted.
4458 // Algorithm 2, Line 2.21 right
4459 status_NewBotSt=finished; assert(3==++no_of_finished_searches);
4460
4461 // Algorithm 2, Line 2.41
4462 // Calculate the placement of subblocks, and also clear state
4463 // counters of the aborted subblock:
4466
4467 if (!has_large_splitter || finished==status[AvoidLrg])
4468 { assert(finished==status[AvoidLrg]);
4471 non_bottom_states[AvoidLrg].size();
4474 if ((!has_small_splitter && has_large_splitter) ||
4475 finished==status[AvoidSml])
4476 { assert(finished==status[AvoidSml]); assert(finished!=status[ReachAlw]);
4479 non_bottom_states[AvoidSml].size();
4482 // clear the state counters of the aborted subblock:
4483 non_bottom_states[ReachAlw].clear();
4484 clear_state_counters
4485 (potential_non_bottom_states[ReachAlw].begin(),
4486 potential_non_bottom_states[ReachAlw].end(), bi);
4487 clear(potential_non_bottom_states[ReachAlw]);
4488 // Some HitSmall states may still linger around in the aborted
4489 // subblock. So we also have to clear these state counters.
4490 if (has_small_splitter && has_large_splitter)
4491 {
4492 clear_state_counters
4493 (potential_non_bottom_states_HitSmall.begin(),
4494 potential_non_bottom_states_HitSmall.end(), bi);
4495 } else { assert(potential_non_bottom_states_HitSmall.empty()); }
4496 }
4497 else
4498 { assert(finished==status[ReachAlw]);
4500 start_bottom_states[ReachAlw+1]+
4501 non_bottom_states[ReachAlw].size();
4504 // clear the state counters of the aborted subblock:
4505 non_bottom_states[AvoidSml].clear();
4506 clear_state_counters
4507 (potential_non_bottom_states[AvoidSml].begin(),
4508 potential_non_bottom_states[AvoidSml].end(), bi);
4509 clear(potential_non_bottom_states[AvoidSml]);
4510 // All HitSmall states must have been captured by another
4511 // subblock. So we can just delete them.
4512 }
4513 }
4514 else
4515 { assert(finished==status[ReachAlw]);
4517 start_bottom_states[ReachAlw+1]+
4518 non_bottom_states[ReachAlw].size(); assert(finished==status[AvoidSml]);
4523 non_bottom_states[AvoidSml].size();
4526 // clear the state counters of the aborted subblock:
4527 non_bottom_states[AvoidLrg].clear();
4528 clear_state_counters
4529 (potential_non_bottom_states[AvoidLrg].begin(),
4530 potential_non_bottom_states[AvoidLrg].end(), bi);
4531 clear(potential_non_bottom_states[AvoidLrg]);
4532 if (has_small_splitter)
4533 {
4534 // Some HitSmall states may still linger around.
4535 clear_state_counters
4536 (potential_non_bottom_states_HitSmall.begin(),
4537 potential_non_bottom_states_HitSmall.end(), bi);
4538 } else { assert(potential_non_bottom_states_HitSmall.empty()); }
4539 }
4540 if (has_small_splitter && has_large_splitter)
4541 {
4542 clear(potential_non_bottom_states_HitSmall);
4543 } else { assert(potential_non_bottom_states_HitSmall.empty()); }
4544 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4545 // Finish the accounting.
4546 // (We have already called `check_complexity::check_waiting_cycles()`, so we
4547 // are no longer allowed to wait, and we are allowed to cancel work.)
4548 // Cancel work in the whole block (actually only work in the aborted subblock
4549 // will be cancelled, but we go through the whole block because the states
4550 // have not yet been positioned correctly; also, most likely not all its
4551 // non-bottom states will be in `non_bottom_states[...]`).
4552 {
4553 state_type max_NcludeCo_size=std::distance(
4555 max_NcludeCo_size=std::max<state_type>(max_NcludeCo_size, std::distance(
4556 start_bottom_states[ReachAlw], new_start_bottom_states(ReachAlw+1)));
4557 max_NcludeCo_size=std::max<state_type>(max_NcludeCo_size, std::distance(
4559 const unsigned char max_NcludeCo_B=
4562 do {
4563 mCRL2complexity(s->ref_state, cancel_work
4565 // incoming tau-transitions of s
4566 const std::vector<transition>::const_iterator in_ti_end=
4567 std::next(s->ref_state)>=m_states.end() ? m_aut.get_transitions().end()
4568 : std::next(s->ref_state)->start_incoming_transitions;
4569 for (std::vector<transition>::const_iterator
4570 ti=s->ref_state->start_incoming_transitions; ti!=in_ti_end; ++ti)
4571 {
4572 if(!m_aut.is_tau(m_aut_apply_hidden_label_map(ti->label()))) { break; }
4573 mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().
4574 cbegin(), ti)], cancel_work(check_complexity::
4575 simple_splitB_U__handle_transition_to_U_state), *this);
4576 }
4577 if (has_large_splitter && finished!=status[AvoidLrg]) {
4578 // outgoing transitions of s
4579 const outgoing_transitions_const_it out_ti_end=
4580 std::next(s->ref_state)>=m_states.end() ? m_outgoing_transitions.end()
4581 : std::next(s->ref_state)->start_outgoing_transitions;
4583 ti=s->ref_state->start_outgoing_transitions; ti!=out_ti_end; ++ti)
4584 {
4585 assert(has_small_splitter || has_large_splitter);
4586 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
4587 cancel_work(check_complexity::
4588 simple_splitB_U__handle_transition_from_potential_U_state), *this);
4589 // We should also finalise the co-splitter transitions handled by
4590 // NewBotSt (which may exist even if NewBotSt is empty):
4591 mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
4592 finalise_work(check_complexity::
4593 simple_splitB_R__handle_transition_from_R_state,
4594 check_complexity::
4595 simple_splitB__handle_transition_from_R_or_U_state,
4596 max_NcludeCo_B), *this);
4597 }
4598 } else { assert(finished==status[AvoidLrg]); }
4599 } while (++s!=bi->end_states);
4600 }
4601 #endif
4602 // split off NewBotSt
4603 // This can be done only after the aborted subblock has cleared
4604 // its state counters. But it should be done before the other
4605 /* splits, so it is easy to detect which transitions are no */ assert((state_index) std::distance(new_end_bottom_states_NewBotSt,
4606 /* longer block-inert. */ bi->end_states)==non_bottom_states_NewBotSt.size());
4609 /* As NewBotSt is not empty, a trivial constellation will */ assert(bi->start_bottom_states<new_end_bottom_states_NewBotSt);
4610 // become non-trivial. (The condition in if() needs to be
4611 // checked before the subblock for NewBotSt is created.)
4612 constellation_type* const constellation=bi->c.onstellation;
4613 if (constellation->start_const_states->ref_state->block==
4614 std::prev(constellation->end_const_states)->ref_state->block)
4615 { assert(std::find(m_non_trivial_constellations.begin(),
4616 /* This constellation was trivial, as it will be split add it*/ m_non_trivial_constellations.end(),
4617 /* to the non-trivial constellations. */ constellation)==m_non_trivial_constellations.end());
4618 m_non_trivial_constellations.emplace_back(constellation);
4619 }
4620
4623 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4624 , 0
4625 #endif
4626 );
4628 block_type* const NewBotSt_block_index=
4629 create_new_block<!has_small_splitter && !has_large_splitter>
4632 bi->end_states, bi,
4634 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4635 // Finalise the work in NewBotSt. This should be done after calling `
4636 // check_complexity::check_waiting_cycles()` so NewBotSt cannot make its own
4637 // waiting time appear small.
4638 const unsigned char max_new_B=check_complexity::log_n-check_complexity::ilog2
4641 do {
4642 mCRL2complexity(s->ref_state, finalise_work(check_complexity::
4645 // incoming tau-transitions of s
4646 const std::vector<transition>::iterator in_ti_end=
4647 std::next(s->ref_state)>=m_states.end() ? m_aut.get_transitions().end()
4648 : std::next(s->ref_state)->start_incoming_transitions;
4649 for (std::vector<transition>::iterator
4650 ti=s->ref_state->start_incoming_transitions; ti!=in_ti_end; ++ti)
4651 {
4652 if (!m_aut.is_tau(m_aut_apply_hidden_label_map(ti->label()))) { break; }
4653 mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().
4654 begin(), ti)], finalise_work(check_complexity::
4655 simple_splitB_R__handle_transition_to_R_state, check_complexity::
4656 simple_splitB__handle_transition_to_R_or_U_state, max_new_B), *this);
4657 }
4658 // outgoing transitions of s -- already done above if necessary
4659 ++s;
4660 } while (s!=bi->end_states);
4661 // Reset the work balance counters:
4663 #endif
4664 // check transitions that have become non-block-inert:
4666 do
4667 {
4668 outgoing_transitions_const_it const out_it_end=
4669 std::next(nst_it->ref_state)>=m_states.end()
4670 ? m_outgoing_transitions.end()
4671 : std::next(nst_it->ref_state)->start_outgoing_transitions;
4672 outgoing_transitions_it out_it=nst_it->ref_state->
4673 start_outgoing_transitions; assert(out_it!=out_it_end);
4674 const transition* tr=&m_aut.get_transitions()
4675 [has_small_splitter || has_large_splitter /* needed for correctness */
4676 ?*out_it->ref.BLC_transitions :out_it->ref.transitions]; assert(0<nst_it->ref_state->no_of_outgoing_block_inert_transitions);
4677 do
4678 { assert(m_states.begin()+tr->from()==nst_it->ref_state);
4680 if (m_states[tr->to()].block==bi)
4681 { assert(is_inert_during_init(*tr));
4682 /* This is a transition that has become non-block-inert. */ assert(bi->start_bottom_states<=m_states[tr->to()].ref_states_in_blocks);
4683 /* (However, it is still constellation-inert.) */
4684 /* make_transition_non_inert(*tr) */ assert(m_states[tr->to()].ref_states_in_blocks<new_end_bottom_states_NewBotSt);
4685 /* < would just execute the decrement "--" below: */ assert(0<nst_it->ref_state->no_of_outgoing_block_inert_transitions);
4686 if (0== --nst_it->ref_state->
4687 no_of_outgoing_block_inert_transitions)
4688 {
4689 // The state at nst_it has become a bottom_state.
4690 change_non_bottom_state_to_bottom_state
4691 (nst_it->ref_state);
4692 break;
4693 }
4694 } else {
4696 m_states[tr->to()].ref_states_in_blocks ||
4697 m_states[tr->to()].ref_states_in_blocks<start_bottom_states[ReachAlw]);
4698 }
4699 ++out_it;
4700 }
4701 while (out_it!=out_it_end &&
4702 (tr=&m_aut.get_transitions()
4703 [has_small_splitter || has_large_splitter /* needed for correctness */
4704 ?*out_it->ref.BLC_transitions :out_it->ref.transitions],
4706 ++nst_it;
4707 }
4708 while (nst_it!=bi->end_states); assert(NewBotSt_block_index->start_bottom_states<
4709 /* Algorithm 2, Line 2.43 */ NewBotSt_block_index->sta.rt_non_bottom_states);
4710 NewBotSt_block_index->contains_new_bottom_states=true;
4711 m_blocks_with_new_bottom_states.push_back(NewBotSt_block_index);
4712 }
4713 else
4714 {
4715 #ifndef NDEBUG
4716 // Reset the work balance counters:
4718 #endif
4719 end_for_empty_NewBotSt_subblock: assert(non_bottom_states_NewBotSt.empty());
4720 constellation_type* const constellation=bi->c.onstellation;
4721 if (constellation->start_const_states->ref_state->block==
4722 std::prev(constellation->end_const_states)->ref_state->block)
4723 { assert(std::find(m_non_trivial_constellations.begin(),
4724 /* This constellation was trivial, as it will be split add */ m_non_trivial_constellations.end(),
4725 /* it to the non-trivial constellations. */ constellation)==m_non_trivial_constellations.end());
4726 m_non_trivial_constellations.emplace_back(constellation); assert((start_bottom_states[ReachAlw]!=new_start_bottom_states(ReachAlw+1))+
4731 }
4732 } assert(finished!=status[AvoidLrg] || static_cast<state_index>(std::distance
4733 /* Algorithm 2, Line 2.41 */ (new_start_bottom_states(AvoidLrg), new_start_bottom_states(AvoidLrg+1)))==
4734 /* Split off the third subblock (AvoidLrg) */ bottom_and_non_bottom_size(AvoidLrg));
4735 if (has_large_splitter && new_start_bottom_states(AvoidLrg)!=
4737 { assert(0!=bottom_size(AvoidLrg));
4738 if (start_bottom_states[AvoidLrg]!=
4740 {
4741 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4742 const state_in_block_pointer* acct_iter;
4743 state_index acct_B_size;
4744 if (finished==status[AvoidLrg]) {
4745 acct_iter=start_bottom_states[AvoidLrg];
4747 } else {
4748 // If AvoidLrg is aborted, the work can be assigned to the non-bottom
4749 // states of ReachAlw and AvoidSml.
4750 assert(non_bottom_states[AvoidLrg].empty());
4751 assert(finished==status[ReachAlw]); assert(finished==status[AvoidSml]);
4753 std::distance(start_bottom_states[AvoidLrg],
4755 if (non_bottom_states[AvoidSml].size()>=count) {
4756 acct_iter=non_bottom_states[AvoidSml].data();
4758 } else if (non_bottom_states[ReachAlw].size()>=count) {
4759 acct_iter=non_bottom_states[ReachAlw].data();
4761 } else {
4762 assert(count<=non_bottom_states[AvoidSml].size()+
4763 non_bottom_states[ReachAlw].size());
4764 // As we are not going to use `non_bottom_states[AvoidLrg]` for anything
4765 // else, we just replace its content by the relevant states.
4766 non_bottom_states[AvoidLrg]=non_bottom_states[AvoidSml];
4767 non_bottom_states[AvoidLrg].add_todo(non_bottom_states[ReachAlw].begin(),
4768 non_bottom_states[ReachAlw].begin()
4769 +(count-non_bottom_states[AvoidLrg].size()));
4770 acct_iter=non_bottom_states[AvoidLrg].data();
4771 acct_B_size=std::max(bottom_and_non_bottom_size(AvoidSml),
4773 }
4774 }
4775 #endif
4777 (start_bottom_states[AvoidLrg],
4779 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4781 finished==status[AvoidLrg]
4785 #endif
4786 );
4787 }
4788
4789 if (finished==status[AvoidLrg])
4790 { assert(potential_non_bottom_states[AvoidLrg].empty());
4791 move_nonbottom_states_to(non_bottom_states[AvoidLrg],
4793 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4795 #endif
4796 );
4797 non_bottom_states[AvoidLrg].clear();
4798 create_new_block<!has_small_splitter && !has_large_splitter>
4802 old_constellation, new_constellation);
4803 }
4804 else
4805 {
4806 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4807 // delete what we've stored in non_bottom_states[AvoidLrg] just for
4808 // accounting
4809 non_bottom_states[AvoidLrg].clear();
4810 #endif
4814 }
4815 } else {
4818 assert(0==bottom_size(AvoidLrg));assert(non_bottom_states[AvoidLrg].empty());
4819 assert(finished==status[AvoidLrg]);
4820 }
4821 /* Split off the second subblock (AvoidSml) */ assert(finished!=status[AvoidSml] || static_cast<state_index>(std::distance
4824 if ((has_small_splitter || !has_large_splitter) &&
4827 { assert(0!=bottom_size(AvoidSml));
4828 // If AvoidSml is aborted, then swapping these bottom states can
4829 // be accounted for by the non-bottom states of ReachAlw.
4830 // The function will not execute more swaps than their size.
4831 if (start_bottom_states[AvoidSml]!=
4833 {
4835 (start_bottom_states[AvoidSml],
4837 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4838 , finished==status[AvoidSml] ? start_bottom_states[AvoidSml]
4839 : non_bottom_states[ReachAlw].data(),
4841 (finished==status[AvoidSml] ? bottom_and_non_bottom_size(AvoidSml)
4843 finished==status[AvoidSml]
4847 #endif
4848 );
4849 }
4850 if (finished==status[AvoidSml])
4851 { assert(potential_non_bottom_states[AvoidSml].empty());
4852 move_nonbottom_states_to(non_bottom_states[AvoidSml],
4854 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4856 #endif
4857 );
4858 non_bottom_states[AvoidSml].clear();
4859 create_new_block<!has_small_splitter && !has_large_splitter>
4863 old_constellation, new_constellation);
4864 }
4865 else
4866 {
4870 }
4871 } else {
4874 assert(0==bottom_size(AvoidSml));assert(non_bottom_states[AvoidSml].empty());
4875 assert(finished==status[AvoidSml]);
4876 }
4877 /* Split off the first subblock (ReachAlw) */ assert(finished!=status[ReachAlw] || static_cast<state_index>(std::distance
4878 (start_bottom_states[ReachAlw], new_start_bottom_states(ReachAlw+1)))==
4880 block_type* ReachAlw_block_index=null_block;
4881 if (start_bottom_states[ReachAlw]!=
4883 { assert(0<bottom_size(ReachAlw));
4884 if (finished==status[ReachAlw])
4885 { assert(potential_non_bottom_states[ReachAlw].empty());
4886 move_nonbottom_states_to(non_bottom_states[ReachAlw],
4887 start_bottom_states[ReachAlw+1]
4888 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4890 #endif
4891 );
4892 non_bottom_states[ReachAlw].clear();
4893 ReachAlw_block_index=create_new_block
4894 <!has_small_splitter && !has_large_splitter>
4895 (start_bottom_states[ReachAlw],
4896 start_bottom_states[ReachAlw+1],
4898 old_constellation, new_constellation);
4899 }
4900 else
4901 { assert(bi->start_bottom_states==start_bottom_states[ReachAlw]);
4902 bi->sta.rt_non_bottom_states=start_bottom_states[ReachAlw+1]; assert(bi->start_bottom_states<bi->sta.rt_non_bottom_states);
4904 ReachAlw_block_index=bi;
4905 }
4906 } else {
4907 assert(0==bottom_size(ReachAlw));assert(non_bottom_states[ReachAlw].empty());
4908 /* Algorithm 2, Line 2.44 */ }
4909 return ReachAlw_block_index; // leave the function completely, as we have finished.
4910 }
4911 } else {
4912 assert(aborted==status_NewBotSt);
4913 }
4914 } // end of outer coroutine loop for ReachAlw/AvoidSml/AvoidLrg and NewBotSt together
4915
4916 #undef bottom_size
4917 #undef abort_if_bottom_size_too_large
4918 #undef abort_if_non_bottom_size_too_large_NewBotSt
4919 #undef abort_if_size_too_large
4920 #undef bottom_and_non_bottom_size
4921 }
4922
4923//================================================= Create initial partition ========================================================
4925 std::vector<transition_index>& action_counter,
4926 const std::vector<label_index>& todo_stack) const
4927 {
4928 transition_index sum=0;
4929 for(label_index index: todo_stack)
4930 { // The work in this loop is attributed to the transitions with label `index`
4931 transition_index n=sum;
4932 sum=sum+action_counter[index];
4933 action_counter[index]=n;
4934 }
4935 return sum;
4936 }
4937
4938 /// \brief create one BLC set for the block starting at `pos`
4939 /// \details The BLC set is created, inserted into the list
4940 /// `block.to_constellation` of the block, and the pointers from
4941 /// transitions to it are adapted. The function also adapts the
4942 /// `ref.BLC_transitions` pointer of the transitions in the BLC set.
4944 state_in_block_pointer* const pos,
4945 BLC_list_iterator start_same_BLC,
4946 const BLC_list_iterator end_same_BLC)
4947 { assert(start_same_BLC<end_same_BLC);
4948 block_type* const bi=pos->ref_state->block; assert(pos==bi->start_bottom_states);
4949 linked_list<BLC_indicators>::iterator blc=bi->
4950 block.to_constellation.emplace_back(start_same_BLC,end_same_BLC,true);
4951 if (!is_inert_during_init(m_aut.get_transitions()[*start_same_BLC]))
4952 {
4954 }
4955 do
4956 { assert(bi==m_states[m_aut.get_transitions()[*start_same_BLC].from()].block);
4957 m_transitions[*start_same_BLC].transitions_per_block_to_constellation=
4958 blc; mCRL2complexity(&m_transitions[*start_same_BLC], add_work(check_complexity::
4959 order_BLC_transitions__sort_transition, check_complexity::log_n), *this);
4960 m_transitions[*start_same_BLC].ref_outgoing_transitions->
4961 ref.convert_to_iterator(start_same_BLC);
4962 }
4963 while (++start_same_BLC<end_same_BLC);
4964 }
4965
4966 /// \brief order `m_BLC_transition` entries according to source block
4967 /// \param start_same_BLC first transition to be handled
4968 /// \param end_same_BLC iterator past the last transition to be handled
4969 /// \param min_block lower bound to the block `start_bottom_states` that can be expected
4970 /// \param max_block upper bound to the block `start_bottom_states` that can be expected
4971 /// \details This function assumes that all transitions in the range
4972 /// [`start_same_BLC`, `end_same_BLC`) have the same label and the same
4973 /// target constellation. They have source blocks whose field
4974 /// `start_bottom_states` is in the range
4975 /// [`min_block`, `max_block`]. It groups these transitions according
4976 /// to their source blocks and inserts the corresponding
4977 /// `linked_list<BLC_indicators>` entries in the source blocks. The
4978 /// algorithm used is similar to quicksort, but the pivot value is
4979 /// determined by numeric calculations instead of selection from the data.
4980 ///
4981 /// The function is intended to be used during initialisation, if one does
4982 /// not use `m_BLC_transitions` during the first refinements.
4983 void order_BLC_transitions(const BLC_list_iterator start_same_BLC,
4984 const BLC_list_iterator end_same_BLC,
4985 state_in_block_pointer* min_block,
4986 state_in_block_pointer* max_block)
4987 { assert(start_same_BLC<end_same_BLC);
4988 assert(min_block->ref_state->block->start_bottom_states==min_block);
4989 assert(max_block->ref_state->block->start_bottom_states==max_block);
4990 if (min_block==max_block)
4991 {
4993 start_same_BLC, end_same_BLC);
4994 return;
4995 } else { assert(min_block<max_block); }
4996 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
4997 const unsigned char max_sort=check_complexity::log_n-
4998 check_complexity::ilog2(max_block-min_block+1);
4999 #endif
5000 state_in_block_pointer* pivot=min_block+(max_block-min_block+1)/2;
5001 pivot=pivot->ref_state->block->start_bottom_states; // round down
5002 state_in_block_pointer* min_below_pivot=pivot;
5003 state_in_block_pointer* max_above_pivot=pivot;
5004 #define max_below_pivot min_block
5005 #define min_above_pivot max_block
5006 // move transitions with source_block==pivot to the beginning,
5007 // transitions with source_block<pivot to the middle,
5008 // transitions with source_block>pivot to the end
5009 // (similar to quicksort with equal keys)
5010 BLC_list_iterator end_equal_to_pivot=start_same_BLC;
5011 BLC_list_iterator end_smaller_than_pivot=start_same_BLC;
5012 BLC_list_iterator begin_larger_than_pivot=end_same_BLC;
5013 for (;;)
5014 {
5015 for (;;)
5016 { assert(end_smaller_than_pivot<begin_larger_than_pivot);
5017 #ifndef NDEBUG
5018 { const state_in_block_pointer* sb;
5019 BLC_list_const_iterator it=start_same_BLC;
5020 assert(it<=end_equal_to_pivot);
5021 for (; it<end_equal_to_pivot; ++it) {
5022 assert(m_states[m_aut.get_transitions()[*it].from()].block->
5023 start_bottom_states==pivot);
5024 }
5025 assert(it<=end_smaller_than_pivot);
5026 for (; it<end_smaller_than_pivot; ++it) {
5027 assert(max_below_pivot<pivot);
5028 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5029 start_bottom_states;
5030 assert(sb>=min_below_pivot); assert(sb<=max_below_pivot);
5031 }
5032 assert(it<begin_larger_than_pivot);
5033 for (it=begin_larger_than_pivot; it<end_same_BLC; ++it) {
5034 assert(pivot<min_above_pivot);
5035 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5036 start_bottom_states;
5037 assert(sb>=min_above_pivot); assert(sb<=max_above_pivot);
5038 }
5039 }
5040 #endif
5041 mCRL2complexity(&m_transitions[*end_smaller_than_pivot], add_work(
5042 check_complexity::order_BLC_transitions__sort_transition, max_sort), *this);
5043 state_in_block_pointer* const source_block=
5044 m_states[m_aut.get_transitions()
5045 [*end_smaller_than_pivot].from()].block->start_bottom_states;
5046 if (source_block==pivot)
5047 {
5048 std::swap(*end_equal_to_pivot++, *end_smaller_than_pivot);
5049 }
5050 else if (source_block>pivot)
5051 {
5052 if (source_block<min_above_pivot)
5053 {
5054 min_above_pivot=source_block;
5055 }
5056 if (source_block>max_above_pivot)
5057 {
5058 max_above_pivot=source_block;
5059 }
5060 break;
5061 }
5062 else
5063 {
5064 if (source_block<min_below_pivot)
5065 {
5066 min_below_pivot=source_block;
5067 }
5068 if (source_block>max_below_pivot)
5069 {
5070 max_below_pivot=source_block;
5071 }
5072 }
5073 ++end_smaller_than_pivot;
5074 if (end_smaller_than_pivot>=begin_larger_than_pivot)
5075 {
5076 goto break_two_loops;
5077 }
5078 }
5079 // Now *end_smaller_than_pivot contains an element with
5080 // source_block > pivot
5081 for (;;)
5082 { assert(end_smaller_than_pivot<begin_larger_than_pivot);
5083 #ifndef NDEBUG
5084 { const state_in_block_pointer* sb;
5085 BLC_list_const_iterator it=start_same_BLC;
5086 assert(it<=end_equal_to_pivot);
5087 for (; it<end_equal_to_pivot; ++it) {
5088 assert(m_states[m_aut.get_transitions()[*it].from()].block->
5089 start_bottom_states==pivot);
5090 }
5091 assert(it<=end_smaller_than_pivot);
5092 for (; it<end_smaller_than_pivot; ++it) {
5093 assert(max_below_pivot<pivot);
5094 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5095 start_bottom_states;
5096 assert(sb>=min_below_pivot); assert(sb<=max_below_pivot);
5097 }
5098 assert(it<begin_larger_than_pivot); assert(pivot<min_above_pivot);
5099 sb=m_states[m_aut.get_transitions()[*it].from()].
5100 block->start_bottom_states;
5101 assert(sb>=min_above_pivot); assert(sb<=max_above_pivot);
5102 for (it=begin_larger_than_pivot; it<end_same_BLC; ++it) {
5103 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5104 start_bottom_states;
5105 assert(sb>=min_above_pivot); assert(sb<=max_above_pivot);
5106 }
5107 }
5108 #endif
5109 --begin_larger_than_pivot;
5110 if (end_smaller_than_pivot>=begin_larger_than_pivot)
5111 {
5112 goto break_two_loops;
5113 } mCRL2complexity(&m_transitions[*begin_larger_than_pivot], add_work(
5114 check_complexity::order_BLC_transitions__sort_transition, max_sort), *this);
5115 state_in_block_pointer* const source_block=
5116 m_states[m_aut.get_transitions()
5117 [*begin_larger_than_pivot].from()].block->start_bottom_states;
5118 if (source_block==pivot)
5119 { assert(end_smaller_than_pivot<begin_larger_than_pivot);
5120 transition_index temp=*begin_larger_than_pivot; assert(end_equal_to_pivot<=end_smaller_than_pivot);
5121 *begin_larger_than_pivot=*end_smaller_than_pivot;
5122 *end_smaller_than_pivot=*end_equal_to_pivot;
5123 *end_equal_to_pivot=temp;
5124 ++end_equal_to_pivot;
5125 ++end_smaller_than_pivot;
5126 if (end_smaller_than_pivot>=begin_larger_than_pivot)
5127 {
5128 goto break_two_loops;
5129 }
5130 break;
5131 }
5132 if (source_block<pivot)
5133 {
5134 if (source_block<min_below_pivot)
5135 {
5136 min_below_pivot=source_block;
5137 }
5138 if (source_block>max_below_pivot)
5139 {
5140 max_below_pivot=source_block;
5141 }
5142 std::swap(*end_smaller_than_pivot, *begin_larger_than_pivot);
5143 ++end_smaller_than_pivot;
5144 if (end_smaller_than_pivot>=begin_larger_than_pivot)
5145 {
5146 goto break_two_loops;
5147 }
5148 break;
5149 } assert(min_above_pivot<=max_above_pivot);
5150 if (source_block<min_above_pivot)
5151 {
5152 min_above_pivot=source_block;
5153 }
5154 else if (source_block>max_above_pivot)
5155 {
5156 max_above_pivot=source_block;
5157 }
5158 }
5159 }
5160 break_two_loops: ; assert(end_smaller_than_pivot==begin_larger_than_pivot);
5161 #ifndef NDEBUG
5162 { const state_in_block_pointer* sb;
5163 BLC_list_const_iterator it=start_same_BLC;
5164 assert(it<=end_equal_to_pivot);
5165 for (; it<end_equal_to_pivot; ++it) {
5166 assert(m_states[m_aut.get_transitions()[*it].from()].block->
5167 start_bottom_states==pivot);
5168 }
5169 assert(it<=end_smaller_than_pivot);
5170 for (; it<end_smaller_than_pivot; ++it) {
5171 assert(max_below_pivot<pivot);
5172 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5173 start_bottom_states;
5174 assert(sb>=min_below_pivot); assert(sb<=max_below_pivot);
5175 }
5176 assert(it==begin_larger_than_pivot); assert(it<=end_same_BLC);
5177 for (; it<end_same_BLC; ++it) {
5178 assert(pivot<min_above_pivot);
5179 sb=m_states[m_aut.get_transitions()[*it].from()].block->
5180 start_bottom_states;
5181 assert(sb>=min_above_pivot); assert(sb<=max_above_pivot);
5182 }
5183 }
5184 #endif
5185 if (start_same_BLC<end_equal_to_pivot)
5186 {
5188 start_same_BLC, end_equal_to_pivot);
5189 }
5190 // Now try to use only tail recursion:
5191 if (min_above_pivot>=max_above_pivot)
5192 {
5193 if (begin_larger_than_pivot<end_same_BLC)
5194 { assert(min_above_pivot==max_above_pivot);
5196 begin_larger_than_pivot, end_same_BLC);
5197 }
5198 if (end_equal_to_pivot<begin_larger_than_pivot)
5199 {
5200 order_BLC_transitions(end_equal_to_pivot, begin_larger_than_pivot,
5201 min_below_pivot, max_below_pivot);
5202 }
5203 return;
5204 }
5205 if (min_below_pivot>=max_below_pivot)
5206 {
5207 if (end_equal_to_pivot<begin_larger_than_pivot)
5208 { assert(min_below_pivot==max_below_pivot);
5210 end_equal_to_pivot, begin_larger_than_pivot);
5211 }
5212 if (begin_larger_than_pivot<end_same_BLC)
5213 {
5214 order_BLC_transitions(begin_larger_than_pivot, end_same_BLC,
5215 min_above_pivot, max_above_pivot);
5216 }
5217 return;
5218 } assert(end_equal_to_pivot<begin_larger_than_pivot);
5219 assert(min_below_pivot<max_below_pivot);
5220 assert(begin_larger_than_pivot<end_same_BLC);
5221 /* Here we cannot do tail recursion */ assert(min_above_pivot<max_above_pivot);
5222 order_BLC_transitions(end_equal_to_pivot, begin_larger_than_pivot,
5223 min_below_pivot, max_below_pivot);
5224 // Hopefully the compiler turns this tail recursion into iteration
5225 order_BLC_transitions(begin_larger_than_pivot, end_same_BLC,
5226 min_above_pivot, max_above_pivot);
5227 #undef max_below_pivot
5228 #undef min_above_pivot
5229 }
5230
5231 // Algorithm 3. Stabilize the current partition with respect to the current constellation
5232 // given that the blocks in m_blocks_with_new_bottom_states do contain new bottom states.
5233 // Stabilisation is always called after initialisation, i.e., m_aut.get_transitions()[ti].transition refers
5234 // to a position in m_BLC_transitions, where the transition index of this transition can be found.
5235
5236 template <bool initialization=false>
5238 {
5239 if (m_blocks_with_new_bottom_states.empty() ||
5240 (initialization && m_BLC_transitions.empty()))
5241 {
5242 return;
5243 }
5244 // Qhat contains the slices of BLC transitions that still need stabilization
5245 // Algorithm 3, Line 3.2
5246 std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> > Qhat;
5247 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5248 std::vector<std::pair<BLC_list_const_iterator, BLC_list_const_iterator> >
5249 initialize_qhat_work_to_assign_later;
5250 std::vector<std::pair<BLC_list_const_iterator, BLC_list_const_iterator> >
5251 stabilize_work_to_assign_later;
5252 #endif
5253 /* Algorithm 3, Line 3.3 */ assert(!m_blocks_with_new_bottom_states.empty());
5254 for(block_type* const bi: m_blocks_with_new_bottom_states)
5255 { assert(bi->contains_new_bottom_states);
5256 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5257 // The work in this loop is assigned to the (new) bottom states in bi
5258 // It cannot be assigned to the block bi because there may be more new bottom
5259 // states later.
5260 const state_in_block_pointer* new_bott_it=bi->start_bottom_states;
5261 assert(new_bott_it < bi->sta.rt_non_bottom_states);
5262 do
5263 {
5264 mCRL2complexity(new_bott_it->ref_state,
5265 add_work(check_complexity::stabilizeB__prepare_block, 1), *this);
5266 }
5267 /* Algorithm 3, Line 3.7 */ while (++new_bott_it<bi->sta.rt_non_bottom_states);
5268 #endif
5269 bi->contains_new_bottom_states=false; assert(!bi->block.to_constellation.empty());
5270 if (1>=number_of_states_in_block(*bi))
5271 {
5272 // blocks with only 1 state do not need to be stabilized further
5273 continue;
5274 }
5275 typename linked_list<BLC_indicators>::iterator
5276 ind=bi->block.to_constellation.begin(); assert(ind->start_same_BLC<ind->end_same_BLC);
5277 const transition* first_t;
5278 if(!initialization ||
5279 (first_t=&m_aut.get_transitions()[*ind->start_same_BLC], assert(m_states[first_t->from()].block==bi),
5280 is_inert_during_init(*first_t) &&
5281 bi->c.onstellation==m_states[first_t->to()].block->c.onstellation))
5282 {
5283 #ifndef NDEBUG
5284 if (!initialization) { first_t=&m_aut.get_transitions()[*ind->start_same_BLC]; }
5285 assert(m_states[first_t->from()].block==bi);
5286 assert(is_inert_during_init(*first_t) &&
5287 bi->c.onstellation==m_states[first_t->to()].block->c.onstellation);
5288 /* The first BLC-set is constellation-inert, so skip it */ assert(ind->is_stable());
5289 if constexpr (initialization)
5290 {
5291 assert(m_BLC_transitions.data()==ind->start_same_BLC);
5292 assert(bi->block.to_constellation.end()==std::next(ind) ||
5293 ind->end_same_BLC==std::next(ind)->start_same_BLC);
5294 }
5295 #endif
5296 ++ind;
5297 }
5298 if (initialization && bi->block.to_constellation.end()!=ind)
5299 {
5300 Qhat.emplace_back(ind->start_same_BLC, m_BLC_transitions.data_end());
5301 }
5302 for (; bi->block.to_constellation.end()!=ind; ++ind)
5303 { assert(ind->is_stable());
5304 ind->start_marked_BLC=ind->end_same_BLC;
5305 #ifndef NDEBUG
5306 assert(!ind->has_marked_transitions());
5307 assert(ind->start_same_BLC<ind->end_same_BLC);
5308 const transition& first_t = m_aut.get_transitions()[*ind->start_same_BLC];
5309 assert(m_states[first_t.from()].block == bi);
5310 /* The BLC set transitions are not constellation-inert, so we */ assert(!is_inert_during_init(first_t) ||
5311 /* need to stabilize under them */ bi->c.onstellation!=m_states[first_t.to()].block->c.onstellation);
5312 #endif
5313 if constexpr (!initialization)
5314 {
5315 // Algorithm 3, Line 3.4
5316 Qhat.emplace_back(ind->start_same_BLC, ind->end_same_BLC);
5317 }
5318 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5319 // The work is assigned to the transitions out of new bottom states in ind.
5320 // Try to find a new bottom state to which to assign it.
5321 bool work_assigned = false;
5322 // assign the work to the transitions out of bottom states in this BLC-set
5323 for (BLC_list_const_iterator work_it = ind->start_same_BLC;
5324 work_it<ind->end_same_BLC; ++work_it)
5325 {
5326 // assign the work to this transition
5327 if (0==m_states[m_aut.get_transitions()
5328 [*work_it].from()].no_of_outgoing_block_inert_transitions)
5329 {
5330 #ifndef NDEBUG
5331 if (work_assigned)
5332 {
5333 mCRL2complexity(&m_transitions[*work_it], add_work_notemporary(
5334 check_complexity::stabilizeB__initialize_Qhat, 1), *this);
5335 continue;
5336 }
5337 #endif
5338 mCRL2complexity(&m_transitions[*work_it], add_work(
5339 check_complexity::stabilizeB__initialize_Qhat, 1), *this);
5340 work_assigned = true;
5341 #ifdef NDEBUG
5342 break;
5343 #endif
5344 }
5345 }
5346 if (!work_assigned)
5347 {
5348 // We register that we still have to find a transition from a new bottom
5349 // state in this slice.
5350 initialize_qhat_work_to_assign_later.emplace_back(ind->start_same_BLC,
5351 ind->end_same_BLC);
5352 }
5353 #endif
5354 }
5355
5356// 2. Administration: Mark all transitions out of (new) bottom states
5357 if constexpr (!initialization)
5358 {
5359 // Algorithm 3, Line 3.5
5360 state_in_block_pointer* si=bi->start_bottom_states; assert(si<bi->sta.rt_non_bottom_states);
5361 do
5362 { mCRL2complexity(si->ref_state, add_work(
5363 /* Algorithm 3, Line 3.6 */ check_complexity::stabilizeB__distribute_states_over_Phat, 1), *this);
5364 outgoing_transitions_it end_it=
5365 std::next(si->ref_state)>=m_states.end()
5366 ? m_outgoing_transitions.end()
5367 : std::next(si->ref_state)->start_outgoing_transitions; assert(si->ref_state->block==bi);
5368 for (outgoing_transitions_it ti=
5369 si->ref_state->start_outgoing_transitions; ti<end_it; ++ti)
5370 { // mCRL2complexity(&m_transitions[m_BLC_transitions[ti->transition]],
5371 // add_work(..., 1), *this);
5372 const transition& t= // subsumed under the above counter
5373 m_aut.get_transitions()[*ti->ref.BLC_transitions]; assert(m_states.begin()+t.from()==si->ref_state);
5374 if (!is_inert_during_init(t) ||
5375 bi->c.onstellation!=m_states[t.to()].block->c.onstellation)
5376 {
5377 // the transition is not constellation-inert, so mark it
5378 mark_BLC_transition(ti);
5379 }
5380 /* Actually it's enough to mark one transition per saC slice: */ assert(ti <= ti->start_same_saC);
5381 ti = ti->start_same_saC;
5382 }
5383 ++si;
5384 }
5385 while (si<bi->sta.rt_non_bottom_states);
5386 }
5387 }
5388 // Algorithm 3, Line 3.7
5389 clear(m_blocks_with_new_bottom_states);
5390
5391 bool small_splitter_used_up=false;
5392 constellation_type* new_constellation=null_constellation;
5393 #ifndef NDEBUG
5394 // during initialization, we need to provide the new constellation:
5395 if (initialization) { new_constellation=m_states[0].block->c.onstellation; }
5396 #endif
5397 // Algorithm 3, line 3.8
5398 for (;;)
5399 { // mCRL2complexity(all bottom states, add_work(..., 1), *this);
5400 // not necessary, as the inner loop is always executed
5401// 3. As long as there are registered slices in m_BLC_transitions, select any one of them.
5402// Take the first BLC_indicator that has transitions in this slice; remove it from the slice;
5403// if the slice is now empty remove it from the register.
5404// Do a normal splitB() under this splitter.
5405// If more new bottom states are created, store them in the new m_blocks_with_new_bottom_states.
5406
5407 // inner loop to be executed until further new bottom states are found:
5408 do
5409 {
5410 if (Qhat.empty())
5411 { assert(check_data_structures("End of stabilizeB()"));
5412 /* nothing needs to be stabilized any more. */ assert(check_stability("End of stabilizeB()"));
5413 // Therefore, it is impossible that further new bottom states are
5414 // found in these rounds. So all work must have been accounted for:
5415 assert(initialize_qhat_work_to_assign_later.empty());
5416 assert(stabilize_work_to_assign_later.empty());
5417 return;
5418 } // mCRL2complexity(..., add_work(..., max_C), *this);
5419 // Algorithm 3, line 3.9 // not needed as the inner loop is always executed at least once.
5420 //print_data_structures("New bottom state loop");
5421 assert(check_data_structures("New bottom state loop", false, false));
5422 std::pair<BLC_list_iterator,BLC_list_iterator>& Qhat_elt=Qhat.back(); assert(check_stability("New bottom state loop", &Qhat));
5423 assert(Qhat_elt.first<Qhat_elt.second);
5424 const linked_list<BLC_indicators>::iterator splitter=
5425 m_transitions[*std::prev(Qhat_elt.second)].
5426 transitions_per_block_to_constellation; assert(splitter->end_same_BLC==Qhat_elt.second);
5427 // Algorithm 3, Line 3.10
5428 Qhat_elt.second=splitter->start_same_BLC; assert(splitter->start_same_BLC<splitter->end_same_BLC);
5429 const transition& first_t=
5430 m_aut.get_transitions()[*splitter->start_same_BLC];
5431 block_type* const from_block_index=m_states[first_t.from()].block; assert(!from_block_index->contains_new_bottom_states);
5432 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5433 // The work is assigned to the transitions out of new bottom states in splitter.
5434 bool work_assigned=false;
5435 for (BLC_list_const_iterator work_it=splitter->start_same_BLC;
5436 work_it<splitter->end_same_BLC; ++work_it)
5437 {
5438 // assign the work to this transition
5439 if (0==m_states[m_aut.get_transitions()[*work_it].from()].
5440 no_of_outgoing_block_inert_transitions)
5441 {
5442 #ifndef NDEBUG
5443 if (work_assigned)
5444 {
5445 mCRL2complexity(&m_transitions[*work_it], add_work_notemporary(
5446 check_complexity::stabilizeB__main_loop, 1), *this);
5447 continue;
5448 }
5449 #endif
5450 mCRL2complexity(&m_transitions[*work_it],
5451 add_work(check_complexity::stabilizeB__main_loop, 1), *this);
5452 work_assigned=true;
5453 #ifdef NDEBUG
5454 break;
5455 #endif
5456 }
5457 }
5458 if (!work_assigned)
5459 {
5460 // We register that we still have to find a transition from a new bottom
5461 // state in this slice.
5462 stabilize_work_to_assign_later.emplace_back(splitter->start_same_BLC,
5463 splitter->end_same_BLC);
5464 /* Algorithm 3, Line 3.11 */ }
5465 #endif
5466 if (std::distance(from_block_index->start_bottom_states,
5467 from_block_index->end_states)<=1)
5468 {
5469 // a block with 1 state does not need to be split
5470 // We still need to make the splitter stable because it is
5471 // expected that all BLC sets are stable by the main/co-split
5472 // phase.
5473 // The BLC set will not be inserted into Qhat another time, so
5474 // it is not necessary to maintain the order of BLC sets (first
5475 // stable ones, then unstable ones).
5476 splitter->make_stable();
5477 }
5478 else
5479 { assert(!is_inert_during_init(first_t) || from_block_index->c.onstellation!=
5480 /* Algorithm 3, Line 3.12 */ m_states[first_t.to()].block->c.onstellation);
5481 if (initialization && !small_splitter_used_up && 1==Qhat.size())
5482 {
5483 // During initialization, we visit every transition once as if it
5484 // were in a small splitter. However, in this case, we need to
5485 // supply the new constellation if in debug mode.
5486 make_stable_and_move_to_start_of_BLC(from_block_index, splitter);
5487 four_way_splitB<true, false>(from_block_index, splitter,
5488 from_block_index->block.to_constellation.end(),
5489 null_constellation, new_constellation);
5490 if (Qhat_elt.first==Qhat_elt.second)
5491 {
5492 // It occasionally happens that the very last split leads to a
5493 // block with new bottom states; that block cannot be handled
5494 // as if it had small splitters.
5495 small_splitter_used_up=true;
5496 }
5497 }
5498 else
5499 {
5500 four_way_splitB<false, true>(from_block_index, from_block_index->
5501 block.to_constellation.end(), splitter,
5502 null_constellation, null_constellation);
5503 }
5504 } assert(Qhat_elt.first<=Qhat_elt.second);
5505 if (Qhat_elt.first==Qhat_elt.second)
5506 {
5507 Qhat.pop_back(); // invalidates Qhat_elt
5508 }
5509 // Algorithm 3, Line 3.13
5510 }
5511 while (m_blocks_with_new_bottom_states.empty()); assert(1==m_blocks_with_new_bottom_states.size());
5512 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5513 // Further new bottom states have been found, so we now have a chance at
5514 // assigning the initialization of Qhat that had not yet been assigned
5515 // earlier.
5516 for (std::vector<std::pair<BLC_list_const_iterator,BLC_list_const_iterator> >
5517 ::iterator qhat_it=initialize_qhat_work_to_assign_later.begin();
5518 qhat_it!=initialize_qhat_work_to_assign_later.end(); )
5519 {
5520 bool new_bottom_state_with_transition_found=false;
5521 for (BLC_list_const_iterator work_it=qhat_it->first;
5522 work_it<qhat_it->second; ++work_it)
5523 {
5524 const state_index t_from=m_aut.get_transitions()[*work_it].from();
5525 if (0==m_states[t_from].no_of_outgoing_block_inert_transitions)
5526 {
5527 // t_from is a new bottom state, so we can assign the work to this
5528 // transition
5529 #ifndef NDEBUG
5530 if (new_bottom_state_with_transition_found)
5531 {
5532 mCRL2complexity(&m_transitions[*work_it], add_work_notemporary
5533 (check_complexity::
5534 stabilizeB__initialize_Qhat_afterwards, 1), *this);
5535 continue;
5536 }
5537 #endif
5538 mCRL2complexity(&m_transitions[*work_it], add_work(check_complexity::
5539 stabilizeB__initialize_Qhat_afterwards, 1), *this);
5540 new_bottom_state_with_transition_found=true;
5541 #ifdef NDEBUG
5542 break;
5543 #endif
5544 }
5545 }
5546 if (new_bottom_state_with_transition_found)
5547 {
5548 // The work has been assigned successfully, so we can replace this
5549 // entry of initialize_qhat_work_to_assign_later with the last one.
5550 *qhat_it=initialize_qhat_work_to_assign_later.back();
5551 if (std::next(qhat_it)==initialize_qhat_work_to_assign_later.end())
5552 {
5553 initialize_qhat_work_to_assign_later.pop_back();
5554 break;
5555 }
5556 else
5557 {
5558 initialize_qhat_work_to_assign_later.pop_back();
5559 }
5560 }
5561 else
5562 {
5563 ++qhat_it;
5564 }
5565 }
5566
5567 // We shall also try and find further new bottom states to which to assign
5568 // the main loop iterations that had not yet been assigned earlier.
5569 for (std::vector<std::pair<BLC_list_const_iterator,BLC_list_const_iterator> >
5570 ::iterator stabilize_it=stabilize_work_to_assign_later.begin();
5571 stabilize_it!=stabilize_work_to_assign_later.end(); )
5572 {
5573 bool new_bottom_state_with_transition_found=false;
5574 for (BLC_list_const_iterator work_it=stabilize_it->first;
5575 work_it<stabilize_it->second; ++work_it)
5576 {
5577 const state_index t_from=m_aut.get_transitions()[*work_it].from();
5578 if (0==m_states[t_from].no_of_outgoing_block_inert_transitions)
5579 {
5580 // t_from is a new bottom state, so we can assign the work to this
5581 // transition
5582 #ifndef NDEBUG
5583 if (new_bottom_state_with_transition_found)
5584 {
5585 mCRL2complexity(&m_transitions[*work_it], add_work_notemporary(
5586 check_complexity::stabilizeB__main_loop_afterwards, 1), *this);
5587 continue;
5588 }
5589 #endif
5590 mCRL2complexity(&m_transitions[*work_it], add_work(check_complexity::
5591 stabilizeB__main_loop_afterwards, 1), *this);
5592 new_bottom_state_with_transition_found=true;
5593 #ifdef NDEBUG
5594 break;
5595 #endif
5596 }
5597 }
5598 if (new_bottom_state_with_transition_found)
5599 {
5600 // The work has been assigned successfully, so we can replace this
5601 // entry of stabilize_work_to_assign_later with the last one.
5602 *stabilize_it=stabilize_work_to_assign_later.back();
5603 if (std::next(stabilize_it) == stabilize_work_to_assign_later.end())
5604 {
5605 stabilize_work_to_assign_later.pop_back();
5606 break;
5607 }
5608 else
5609 {
5610 stabilize_work_to_assign_later.pop_back();
5611 }
5612 }
5613 else
5614 {
5615 ++stabilize_it;
5616 }
5617 }
5618 #endif
5619 block_type* const bi=m_blocks_with_new_bottom_states.front(); assert(bi->contains_new_bottom_states);
5620 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5621 // The work in this loop is assigned to the (new) bottom states in bi
5622 // It cannot be assigned to the block bi because there may be more new bottom
5623 // states later.
5624 const state_in_block_pointer* new_bott_it=bi->start_bottom_states;
5625 assert(new_bott_it < bi->sta.rt_non_bottom_states);
5626 do
5627 {
5628 mCRL2complexity(new_bott_it->ref_state,
5630 }
5631 /* Algorithm 3, Line 3.17 */ while (++new_bott_it<bi->sta.rt_non_bottom_states);
5632 #endif
5634 clear(m_blocks_with_new_bottom_states);
5636 {
5637 // blocks with only 1 state do not need to be stabilized further
5638 continue;
5639 }
5640 typename linked_list<BLC_indicators>::iterator
5641 ind=bi->block.to_constellation.begin(); assert(!bi->block.to_constellation.empty());
5642 assert(ind->start_same_BLC<ind->end_same_BLC);
5643 #ifndef NDEBUG
5644 const transition& first_t=m_aut.get_transitions()[*ind->start_same_BLC];
5645 assert(m_states[first_t.from()].block==bi);
5646 assert(is_inert_during_init_if_branching(first_t) &&
5647 bi->c.onstellation==m_states[first_t.to()].block->c.onstellation);
5648 #endif
5649 /* The first BLC-set is constellation-inert, so skip it */ assert(ind->is_stable());
5650 ++ind;
5651 for (; bi->block.to_constellation.end()!=ind; ++ind)
5652 {
5653 if (!ind->is_stable())
5654 {
5655 #ifndef NDEBUG
5656 /* This is a new bottom block that was found during */ // Check that all other BLC sets are already unstable
5657 /* stabilizeB(). Therefore, the subsequent BLC sets are */ while (++ind!=bi->block.to_constellation.end())
5658 /* already somewhere in Qhat, and stabilizing for them two */ {
5659 /* times is not needed. */ assert(!ind->is_stable());
5660 // marked transitions would start in new bottom states found
5661 // earlier:
5662 assert(!ind->has_marked_transitions());
5663 }
5664 #endif
5665 break;
5666 }
5667 ind->start_marked_BLC=ind->end_same_BLC;
5668 #ifndef NDEBUG
5669 assert(!ind->has_marked_transitions());
5670 assert(ind->start_same_BLC<ind->end_same_BLC);
5671 const transition& first_t = m_aut.get_transitions()[*ind->start_same_BLC];
5672 /* Algorithm 3, Line 3.14 */ assert(m_states[first_t.from()].block == bi);
5673 /* The BLC set transitions are not constellation-inert, so we */ assert(!is_inert_during_init_if_branching(first_t) ||
5674 /* need to stabilize under them */ bi->c.onstellation!=m_states[first_t.to()].block->c.onstellation);
5675 #endif
5676 Qhat.emplace_back(ind->start_same_BLC, ind->end_same_BLC);
5677 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5678 // The work is assigned to the transitions out of new bottom states in ind.
5679 // Try to find a new bottom state to which to assign it.
5680 bool work_assigned = false;
5681 // assign the work to the transitions out of bottom states in this BLC-set
5682 for (BLC_list_const_iterator work_it = ind->start_same_BLC;
5683 work_it<ind->end_same_BLC; ++work_it)
5684 {
5685 // assign the work to this transition
5686 if (0==m_states[m_aut.get_transitions()
5687 [*work_it].from()].no_of_outgoing_block_inert_transitions)
5688 {
5689 #ifndef NDEBUG
5690 if (work_assigned)
5691 {
5692 mCRL2complexity(&m_transitions[*work_it], add_work_notemporary(
5693 check_complexity::stabilizeB__initialize_Qhat, 1), *this);
5694 continue;
5695 }
5696 #endif
5697 mCRL2complexity(&m_transitions[*work_it], add_work(
5698 check_complexity::stabilizeB__initialize_Qhat, 1), *this);
5699 work_assigned = true;
5700 #ifdef NDEBUG
5701 break;
5702 #endif
5703 }
5704 }
5705 if (!work_assigned)
5706 {
5707 // We register that we still have to find a transition from a new bottom
5708 // state in this slice.
5709 initialize_qhat_work_to_assign_later.emplace_back(ind->start_same_BLC,
5710 ind->end_same_BLC);
5711 }
5712 #endif
5713 }
5714
5715// 2. Administration: Mark all transitions out of (new) bottom states
5716 // Algorithm 3, 3.15
5718 do
5719 { mCRL2complexity(si->ref_state, add_work(
5720 /* Algorithm 3, Line 3.16 */ check_complexity::stabilizeB__distribute_states_over_Phat, 1), *this);
5722 std::next(si->ref_state)>=m_states.end()
5723 ? m_outgoing_transitions.end()
5724 : std::next(si->ref_state)->start_outgoing_transitions; assert(si->ref_state->block==bi);
5726 si->ref_state->start_outgoing_transitions; ti<end_it; ++ti)
5727 { // mCRL2complexity(&m_transitions[m_BLC_transitions[ti->transition]],
5728 // add_work(..., 1), *this);
5729 const transition& t= // subsumed under the above counter
5730 m_aut.get_transitions()[*ti->ref.BLC_transitions]; assert(m_states.begin()+t.from()==si->ref_state);
5731 if (!is_inert_during_init_if_branching(t) ||
5732 bi->c.onstellation!=m_states[t.to()].block->c.onstellation)
5733 {
5734 // the transition is not constellation-inert, so mark it
5736 }
5737 /* Actually it's enough to mark one transition per saC slice: */ assert(ti <= ti->start_same_saC);
5738 ti = ti->start_same_saC;
5739 }
5740 ++si;
5741 }
5742 while (si<bi->sta.rt_non_bottom_states);
5743 } assert(0); // unreachable
5744 }
5745
5747 {
5748 mCRL2log(log::verbose) << "An O(m log n) "
5749 << (m_branching ? (m_preserve_divergence
5750 ? "divergence-preserving branching "
5751 : "branching ")
5752 : "")
5753 << "bisimulation partitioner created for " << m_aut.num_states()
5754 << " states and " << m_transitions.size()
5755 << " transitions (using the experimental algorithm GJ2025).\n";
5756 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
5757 /* Algorithm 1, Line 1.2 */ check_complexity::init(2 * m_aut.num_states());
5758 // we need ``2*'' because there is one additional call to splitB during initialisation
5759 #endif
5760 group_transitions_on_tgt_label(m_aut);
5761
5762 /* Count the number of occurring action labels. */ assert((unsigned) m_preserve_divergence <= 1);
5763 mCRL2log(log::verbose) << "Start initialisation of the BLC list in the "
5764 "initialisation, after sorting.\n";
5765 constellation_type* const initial_constellation=
5766 #ifdef USE_POOL_ALLOCATOR
5767 simple_list<BLC_indicators>::get_pool().
5768 template construct<constellation_type>
5769 #else
5770 new constellation_type
5771 #endif
5772 (m_states_in_blocks.data(), m_states_in_blocks.data_end()); assert(1==no_of_constellations);
5773 block_type* const initial_block=
5774 #ifdef USE_POOL_ALLOCATOR
5775 simple_list<BLC_indicators>::get_pool().
5776 template construct<block_type>
5777 #else
5778 new block_type
5779 #endif
5780 (m_states_in_blocks.data(), m_states_in_blocks.data_end(),
5781 m_states_in_blocks.data_end(), initial_constellation); assert(1==no_of_blocks);
5782 #ifndef INIT_WITHOUT_BLC_SETS
5783 #define temporary_BLC_list (initial_block->block.to_constellation)
5784 #else
5786 temporary_BLC_list=linked_list<BLC_indicators>();
5787 #endif
5788 {
5789 std::vector<label_index> todo_stack_actions;
5790 std::vector<transition_index> count_transitions_per_action
5791 (m_aut.num_action_labels() + (unsigned) m_preserve_divergence, 0);
5792 if (m_branching)
5793 {
5794 // ensure that inert transitions come first and set the number of
5795 // transitions to a nonzero value so it doesn't trigger
5796 // todo_stack_actions.push_back(...) in the loop
5797 todo_stack_actions.push_back(m_aut.tau_label_index());
5798 count_transitions_per_action[m_aut.tau_label_index()] = 1;
5799 }
5800 for (transition_index ti=0; ti<m_transitions.size(); ++ti)
5801 {
5802 const transition& t=m_aut.get_transitions()[ti]; // mCRL2complexity(&m_transitions[ti], add_work(..., 1), *this);
5803 // Because every transition is touched exactly once, we do not store a physical counter for this.
5804 const label_index label=label_or_divergence(t,
5805 m_aut.num_action_labels()); assert(m_aut.apply_hidden_label_map(t.label())==t.label());
5806 transition_index& c=count_transitions_per_action[label];
5807 if (c==0)
5808 {
5809 todo_stack_actions.push_back(label);
5810 }
5811 c++;
5812 }
5813 if (m_branching)
5814 { assert(m_aut.is_tau(todo_stack_actions.front()));
5815 --count_transitions_per_action[m_aut.tau_label_index()];
5816 }
5817 accumulate_entries(count_transitions_per_action, todo_stack_actions);
5818 for (transition_index ti=0; ti<m_transitions.size(); ++ti)
5819 { // mCRL2complexity(&m_transitions[ti], add_work(..., 1), *this);
5820 // Because every transition is touched exactly once, we do not store a physical counter for this.
5821 const transition& t=m_aut.get_transitions()[ti];
5822 const label_index label = label_or_divergence(t,
5823 m_aut.num_action_labels());
5824 transition_index& c=count_transitions_per_action[label]; assert(0 <= c); assert(c < m_transitions.size());
5825 m_BLC_transitions[c]=ti;
5826 c++;
5827 }
5828
5829 // create BLC_indicators for every action label:
5830 std::vector<label_index>::const_iterator
5831 a_it=todo_stack_actions.begin();
5832 if (a_it!=todo_stack_actions.end() &&
5833 (0!=count_transitions_per_action[*a_it] || (assert(m_branching), assert(m_aut.is_tau(*a_it)),
5834 ++a_it!=todo_stack_actions.end())) )
5835 {
5836 BLC_list_iterator start_index=m_BLC_transitions.data();
5837 do
5838 { // mCRL2complexity(..., add_work(..., 1), *this);
5839 const label_index a=*a_it; // not needed because the inner loop is always executed
5840 const BLC_list_iterator end_index=
5841 m_BLC_transitions.data()+count_transitions_per_action[a]; assert(end_index<=m_BLC_transitions.data_end());
5842 // create a BLC_indicator and insert it into the list...
5843 temporary_BLC_list.emplace_back(start_index, end_index, true); assert(start_index<end_index);
5844 start_index=end_index;
5845 }
5846 while (++a_it!=todo_stack_actions.end()); assert(start_index==m_BLC_transitions.data_end());
5847 }
5848 // destroy and deallocate `todo_stack_actions` and
5849 // `count_transitions_per_action` here.
5850 }
5851
5852 // Group transitions per outgoing state.
5853 mCRL2log(log::verbose) << "Start setting outgoing transitions\n";
5854 {
5855 fixed_vector<transition_index> count_outgoing_transitions_per_state
5856 (m_aut.num_states(), 0);
5857 for(const transition& t: m_aut.get_transitions())
5858 { // mCRL2complexity(&m_transitions[std::distance
5859 // (m_aut.get_transitions().data(), &t)], add_work(..., 1), *this);
5860 count_outgoing_transitions_per_state[t.from()]++; // Because every transition is touched exactly once,
5861 if (is_inert_during_init(t)) // we do not store a physical counter for this.
5862 {
5863 m_states[t.from()].no_of_outgoing_block_inert_transitions++;
5864 }
5865 }
5866
5867 // We now set the outgoing transition per state pointer to the first
5868 // non-inert transition.
5869 // The counters for outgoing transitions calculated above are reset to
5870 // 0 and will later contain the number of transitions already stored.
5871 // Every time an inert transition is stored, the outgoing transition
5872 // per state pointer is reduced by one.
5873 outgoing_transitions_it current_outgoing_transitions=
5874 m_outgoing_transitions.begin();
5875
5876 // place transitions and set pointers to incoming/outgoing transitions
5877 for (state_index s=0; s<m_aut.num_states(); ++s)
5878 { // mCRL2complexity(&m_states[s], add_work(..., 1), *this);
5879 if (marked_range<=m_states[s].no_of_outgoing_block_inert_transitions) // Because every state is touched exactly once,
5880 { // we do not store a physical counter for this.
5881 mCRL2log(log::error) << "State " << s << " has "
5882 << m_states[s].no_of_outgoing_block_inert_transitions
5883 << " outgoing block-inert transitions. However, the "
5884 "four-way-split can handle at most " << (marked_range-1)
5885 << " outgoing block-inert transitions per state. "
5886 "Aborting now.\n";
5887 exit(EXIT_FAILURE);
5888 }
5889 m_states[s].start_outgoing_transitions=current_outgoing_transitions+
5890 m_states[s].no_of_outgoing_block_inert_transitions;
5891 current_outgoing_transitions+=
5892 count_outgoing_transitions_per_state[s];
5893 count_outgoing_transitions_per_state[s]=0;
5894 // meaning of this counter changes to: number of outgoing transitions
5895 // already stored
5896 } assert(m_outgoing_transitions.end()==current_outgoing_transitions);
5897
5898 mCRL2log(log::verbose) << "Moving incoming and outgoing transitions\n";
5899
5900 for (BLC_list_iterator ti=m_BLC_transitions.data();
5901 ti<m_BLC_transitions.data_end(); ++ti)
5902 { // mCRL2complexity(&m_transitions[*ti], add_work(..., 1), *this);
5903 const transition& t=m_aut.get_transitions()[*ti]; // Because every transition is touched exactly once,
5904 if (is_inert_during_init(t)) // we do not store a physical counter for this.
5905 {
5906 m_transitions[*ti].ref_outgoing_transitions =
5907 --m_states[t.from()].start_outgoing_transitions;
5908 }
5909 else
5910 {
5911 m_transitions[*ti].ref_outgoing_transitions =
5912 m_states[t.from()].start_outgoing_transitions +
5913 count_outgoing_transitions_per_state[t.from()];
5914 }
5916 m_transitions[*ti].ref_outgoing_transitions->ref.transitions=*ti;
5917 #else
5918 m_transitions[*ti].ref_outgoing_transitions->ref.BLC_transitions=ti;
5919 #endif
5920 ++count_outgoing_transitions_per_state[t.from()];
5921 }
5922 // destroy and deallocate count_outgoing_transitions_per_state here.
5923 }
5924
5925 state_index current_state=null_state; assert(current_state + 1 == 0);
5926 // bool tau_transitions_passed=true;
5927 // TODO: This should be combined with another pass through all transitions.
5928 for(std::vector<transition>::iterator it=m_aut.get_transitions().begin();
5929 it!=m_aut.get_transitions().end(); it++)
5930 { // mCRL2complexity(&m_transitions[std::distance
5931 // (m_aut.get_transitions().begin(), it)], add_work(..., 1), *this);
5932 const transition& t=*it; // Because every transition is touched exactly once,
5933 if (t.to()!=current_state) // we do not store a physical counter for this.
5934 {
5935 for (state_index i=current_state+1; i<=t.to(); ++i)
5936 { // ensure that every state is visited at most once:
5937 mCRL2complexity(&m_states[i], add_work(check_complexity::
5938 create_initial_partition__set_start_incoming_transitions, 1), *this);
5939 m_states[i].start_incoming_transitions=it;
5940 }
5941 current_state=t.to();
5942 }
5943 }
5944 for (state_index i=current_state+1; i<m_aut.num_states(); ++i)
5945 { mCRL2complexity(&m_states[i], add_work(check_complexity::
5946 create_initial_partition__set_start_incoming_transitions, 1), *this);
5947 m_states[i].start_incoming_transitions=m_aut.get_transitions().end();
5948 }
5949
5950 // Set the start_same_saC fields in m_outgoing_transitions.
5951 outgoing_transitions_it it = m_outgoing_transitions.end();
5952 if (m_outgoing_transitions.begin() < it)
5953 {
5954 --it;
5955 const transition& t=m_aut.get_transitions()[
5957 it->ref.transitions
5958 #else
5959 *it->ref.BLC_transitions
5960 #endif
5961 ];
5962 state_index current_state = t.from();
5963 label_index current_label = label_or_divergence(t);
5964 outgoing_transitions_it current_end_same_saC = it;
5965 while (m_outgoing_transitions.begin() < it)
5966 {
5967 --it; // mCRL2complexity(&m_transitions[*it->ref.BLC_transitions or
5968 // it->ref.transitions], add_work(..., 1), *this);
5969 const transition& t=m_aut.get_transitions()[
5971 it->ref.transitions
5972 #else
5973 *it->ref.BLC_transitions
5974 #endif
5975 ]; // Because every transition is touched exactly once,
5976 const label_index new_label = label_or_divergence(t); // we do not store a physical counter for this.
5977 if (current_state == t.from() && current_label == new_label)
5978 {
5979 // We encounter a transition with the same saC.
5980 // Let it refer to the end.
5981 it->start_same_saC = current_end_same_saC;
5982 }
5983 else
5984 {
5985 // We encounter a transition with a different saC.
5986 current_state = t.from();
5987 current_label = new_label;
5988 current_end_same_saC->start_same_saC = std::next(it);
5989 current_end_same_saC = it;
5990 }
5991 }
5992 current_end_same_saC->start_same_saC = m_outgoing_transitions.begin();
5993 } assert(m_states_in_blocks.size()==m_aut.num_states());
5994 state_in_block_pointer* lower_i=m_states_in_blocks.data(); assert(initial_block->start_bottom_states==lower_i);
5995 state_in_block_pointer* upper_i=m_states_in_blocks.data_end(); assert(initial_block->end_states==upper_i);
5996 for (fixed_vector<state_type_gj>::iterator i=m_states.begin();
5997 i<m_states.end(); ++i)
5998 { // mCRL2complexity(&m_states[i], add_work(..., 1), *this);
5999 if (0<i->no_of_outgoing_block_inert_transitions) // Because every state is touched exactly once,
6000 { // we do not store a physical counter for this.
6001 --upper_i;
6002 upper_i->ref_state=i;
6003 i->ref_states_in_blocks=upper_i;
6004 }
6005 else
6006 {
6007 lower_i->ref_state=i;
6008 i->ref_states_in_blocks=lower_i;
6009 ++lower_i;
6010 }
6011 i->block=initial_block;
6012 } assert(lower_i == upper_i);
6013 initial_block->sta.rt_non_bottom_states = lower_i;
6015 mCRL2log(log::verbose) << "Start refining in the initialisation WITH BLC sets\n";
6016 for (linked_list<BLC_indicators>::iterator
6017 blc_it=temporary_BLC_list.begin();
6018 temporary_BLC_list.end()!=blc_it; ++blc_it)
6019 { assert(blc_it->start_same_BLC<blc_it->end_same_BLC);
6020 BLC_list_iterator it=blc_it->start_same_BLC; // mCRL2complexity(blc_it, add_work(...), *this);
6021 if (!is_inert_during_init(m_aut.get_transitions()[*it])) // Because every BLC set (= set of transitions with the same label)
6022 { // is touched exactly once, we do not store a physicaal counter for it.
6023 ++no_of_non_constellation_inert_BLC_sets;
6024 }
6025 do
6026 {
6027 m_transitions[*it].transitions_per_block_to_constellation=blc_it;
6028 ++it;
6029 }
6030 while (it!=blc_it->end_same_BLC);
6031 }
6032 #undef temporary_BLC_list
6033 // Algorithm 1, Line 1.2
6034 initial_block->contains_new_bottom_states = true;
6035 m_blocks_with_new_bottom_states.push_back(initial_block);
6036 /* Everything except `m_BLC_transitions` is now completely initialized.*/ //print_data_structures("After initial reading before splitting in the initialisation", true);
6037 assert(check_data_structures("After initial reading before splitting in the initialisation", false, false));
6038#else
6039 /* Everything except `m_BLC_transitions` is now completely initialized.*/ //print_data_structures("After initial reading before splitting in the initialisation", true);
6040 assert(check_data_structures("After initial reading before splitting in the initialisation", true, false));
6041 // The initial partition has been constructed. Continue with the initialisation.
6042 mCRL2log(log::verbose) << "Start refining in the initialisation WITHOUT BLC sets\n";
6043
6044 // We have not yet fully instantiated the BLC sets.
6045 // Therefore, we run a kind of simplified stabilisation: we do not need
6046 // to check the target constellation but only the action of the
6047 // transition.
6048 if (!temporary_BLC_list.empty())
6049 {
6050 linked_list<BLC_indicators>::iterator blc_it=
6051 temporary_BLC_list.begin(); assert(blc_it->start_same_BLC<blc_it->end_same_BLC);
6052 if (!is_inert_during_init
6053 (m_aut.get_transitions()[*blc_it->start_same_BLC]) ||
6054 ++blc_it!=temporary_BLC_list.end())
6055 {
6056 do
6057 { // mCRL2complexity(blc_it, add_work(...), *this);
6058 std::vector<block_type*> blocks_that_need_refinement; // not needed because the inner loop is always executed at least once
6059 BLC_list_iterator trans_it=blc_it->start_same_BLC; assert(trans_it<blc_it->end_same_BLC);
6060 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
6061 const label_index a=label_or_divergence(m_aut.get_transitions()[*trans_it]);
6062 #endif
6063 do
6064 {
6065 // mark the source state of *trans_it:
6066 const transition& t=m_aut.get_transitions()[*trans_it]; assert(label_or_divergence(t)==a);
6067 const state_in_block_pointer s(m_states.begin()+t.from()); // mCRL2complexity(&m_transitions[*trans_it], add_work(...), *this);
6068 block_type& B=*s.ref_state->block; // because every transition is touched exactly once,
6069 if (nullptr==B.block.R) // we do not include a physical counter for this
6070 { assert(std::find(blocks_that_need_refinement.begin(),
6071 blocks_that_need_refinement.end(), s.ref_state->block)==
6072 blocks_that_need_refinement.end());
6073 if (B.contains_new_bottom_states ||
6074 number_of_states_in_block(B)<=1)
6075 {
6076 continue;
6077 }
6078 B.block.R=new std::vector<state_in_block_pointer>();
6079 blocks_that_need_refinement.push_back(s.ref_state->block);
6080 // B.c.on.~constellation_and_new_bottom_states(); -- trivial
6081 B.c.first_unmarked_bottom_state=B.start_bottom_states;
6082 } else { assert(std::find(blocks_that_need_refinement.begin(),
6083 blocks_that_need_refinement.end(), s.ref_state->block)!=
6084 blocks_that_need_refinement.end()); }
6085 state_in_block_pointer* const
6086 pos_s=s.ref_state->ref_states_in_blocks; assert(B.start_bottom_states<=pos_s); assert(pos_s<B.end_states);
6087 if (B.c.first_unmarked_bottom_state<=pos_s)
6088 {
6089 if (0==s.ref_state->no_of_outgoing_block_inert_transitions)
6090 { assert(pos_s<B.sta.rt_non_bottom_states);
6091 swap_states_in_states_in_block
6092 (B.c.first_unmarked_bottom_state, pos_s); assert(undefined==s.ref_state->counter);
6093 B.c.first_unmarked_bottom_state++;
6094 }
6095 else
6096 {
6097 if (undefined==s.ref_state->counter)
6098 {
6099 B.block.R->push_back(s);
6100 s.ref_state->counter=marked(ReachAlw)+
6101 s.ref_state->no_of_outgoing_block_inert_transitions; assert(B.sta.rt_non_bottom_states<=pos_s);
6102 } else { assert(B.sta.rt_non_bottom_states<=pos_s); }
6103 assert(is_in_marked_range_of(s.ref_state->counter, ReachAlw));
6104 }
6105 }
6106 }
6107 while (++trans_it<blc_it->end_same_BLC);
6108
6109 for (block_type* const bi : blocks_that_need_refinement)
6110 { assert(nullptr!=bi->block.R);
6111 four_way_splitB<false, false>(bi,
6112 linked_list<BLC_indicators>::end(), // no main splitter
6113 linked_list<BLC_indicators>::end(), // no co-splitter
6114 null_constellation, initial_constellation);
6115 // The function will retrieve first_unmarked_bottom_state
6116 // and the vector of potential ReachAlw-states from *bi.
6117
6118 // The only parameter that we still have to convey is the
6119 // initial constellation, so four_way_splitB() can make
6120 // the block look normal after it has retrieved the
6121 // information.
6122 }
6123 }
6124 while (++blc_it!=temporary_BLC_list.end());
6125 }
6126 }
6129 {
6130 // We need to explicitly convert the null pointers stored in block.R
6131 // to empty lists for block.to_constellation.
6132 state_in_block_pointer* st_it=m_states_in_blocks.data(); assert(m_states_in_blocks.data_end()!=st_it);
6133 do
6134 {
6135 block_type* const blk_it=st_it->ref_state->block; assert(nullptr==blk_it->block.R);
6136 // delete blk_it->block.R; -- not needed, as it should be nullptr
6137 // destroy blk_it->block.R; -- not needed, as it is a pointer and
6138 // has a trivial destructor.
6139 new(&blk_it->block.to_constellation) linked_list<BLC_indicators>();
6140 st_it=blk_it->end_states;
6141 }
6142 while (m_states_in_blocks.data_end()!=st_it);
6143 }
6144 // Now create the correct BLC sets
6146 last_block_start=std::prev(m_states_in_blocks.end())->
6147 ref_state->block->start_bottom_states;
6148 linked_list<BLC_indicators>::iterator blc_it;
6149 while(blc_it=temporary_BLC_list.begin(),temporary_BLC_list.end()!=blc_it)
6150 { // mCRL2complexity(blc_it, add_work(...), *this);
6151 order_BLC_transitions(blc_it->start_same_BLC, blc_it->end_same_BLC, // Because every BLC set (= set of transitions with the same label)
6152 m_states_in_blocks.data(), last_block_start); // is touched exactly once, we do not store a physicaal counter for it.
6153 // erase the elements from the list as we go (this is needed so the
6154 // pool allocator adds them to the free list)
6155 temporary_BLC_list.erase(blc_it);
6156 }
6157#endif
6158 /* Algorithm 1, line 1.3 */ //print_data_structures("End initialisation");
6159 assert(check_stability("End initialisation"));
6160 mCRL2log(log::verbose) << "Start stabilizing in the initialisation\n"; assert(check_data_structures("End initialisation", false, false));
6162 stabilizeB();
6163 #else
6164 stabilizeB<true>(); // perhaps this is always possible?
6165 #endif
6166 }
6167
6168 /// \brief find a splitter for the tau-transitions from the new constellation to the old constellation
6169 /// \param index_block_B block that forms the new constellation
6170 /// \param old_constellation index of the old constellation
6171 /// \returns splitter that contains the tau-transitions from `index_block_B` to `old_constellation`
6172 /// \details If no such splitter exists,
6173 /// `linked_list<BLC_indicators>::end()` is returned.
6174 ///
6175 /// The function uses the fact that the first element of the list
6176 /// `block.to_constellation` contains the inert transitions (if there are
6177 /// any), and just after splitting the new constellation off from the old
6178 /// one, the element immediately after that the tau-transitions from the
6179 /// new to the old constellation.
6183 const constellation_type* const new_constellation) const
6184 {
6185 linked_list< BLC_indicators >::iterator
6186 btc_it=index_block_B->block.to_constellation.begin();
6187 if (btc_it == index_block_B->block.to_constellation.end())
6188 {
6189 // The new constellation has no outgoing transitions at all.
6190 return index_block_B->block.to_constellation.end();
6191 } assert(btc_it->start_same_BLC<btc_it->end_same_BLC);
6192 const transition& btc_t=
6193 m_aut.get_transitions()[*(btc_it->start_same_BLC)];
6195 {
6196 // The new constellation has no outgoing tau-transitions at all (except
6197 // possibly tau-self-loops, for divergence-preserving branching
6198 // bisimulation).
6199 return index_block_B->block.to_constellation.end();
6200 }
6201 if (m_states[btc_t.to()].block->c.onstellation==old_constellation)
6202 {
6203 // The new constellation has no inert transitions but it does have
6204 // tau-transitions to the old constellation (which were inert before).
6205 return btc_it;
6206 }
6207 if (m_states[btc_t.to()].block->c.onstellation!=new_constellation)
6208 {
6209 // The new constellation, before it was separated from the old one,
6210 // had no constellation-inert outgoing transitions.
6211 return index_block_B->block.to_constellation.end();
6212 }
6213 // *btc_it is the BLC_indicator for the inert transitions of the new
6214 // constellation. Try the second element in the list:
6215 btc_it=index_block_B->block.to_constellation.next(btc_it);
6216 if (btc_it == index_block_B->block.to_constellation.end())
6217 {
6218 // The new constellation has no other outgoing transitions.
6219 return index_block_B->block.to_constellation.end();
6220 } assert(btc_it->start_same_BLC<btc_it->end_same_BLC);
6221 const transition& btc2_t=
6222 m_aut.get_transitions()[*(btc_it->start_same_BLC)];
6223 if (!is_inert_during_init_if_branching(btc2_t) ||
6224 old_constellation!=m_states[btc2_t.to()].block->c.onstellation)
6225 {
6226 // The new constellation has no tau-transitions to the old
6227 // constellation.
6228 return index_block_B->block.to_constellation.end();
6229 }
6230 return btc_it;
6231 }
6232
6233 /// \brief Select a block that is not the largest block in a non-trivial constellation.
6234 /// \returns the index of such a block
6235 /// \details Either the first or the last block of a constellation is
6236 /// selected; also, the constellation bounds are adapted accordingly.
6237 /// However, the caller will have to create a new constellation and set the
6238 /// block's `constellation` field.
6239 ///
6240 /// To ensure the time complexity bounds, it is necessary that the
6241 /// block returned contains at most 50% of the states in its constellation.
6242 /// The smaller the better.
6244 { assert(!m_non_trivial_constellations.empty());
6245 // Algorithm 1, Line 1.5
6246 // Do the minimal checking, i.e., only check two blocks in a constellation.
6247 constellation_type* const ci=m_non_trivial_constellations.back();
6248 block_type* index_block_B=ci->start_const_states->ref_state->block; // The first block.
6249 block_type* second_block_B=
6250 std::prev(ci->end_const_states)->ref_state->block; // The last block.
6251
6252 if (number_of_states_in_block(*index_block_B)<=
6253 number_of_states_in_block(*second_block_B))
6254 {
6255 ci->start_const_states=index_block_B->end_states;
6256 }
6257 else
6258 {
6260 index_block_B=second_block_B;
6261 }
6262 return index_block_B;
6263 }
6264
6265// =================================================================================================================================
6266//
6267// refine_partition_until_it_becomes_stable.
6268//
6269// =================================================================================================================================
6270
6271 /// \brief number of new bottom states found after constructing the initial partition
6272 /// \details This count includes all states that were non-bottom state in
6273 /// the (unstable) trivial partition with a single block.
6275
6276 /// \brief number of non-inert BLC sets in the partition
6277 /// \details The sets that are in `m_BLC_indicators_to_be_deleted` are not
6278 /// included in this count. Nor are sets that contain constellation-inert
6279 /// transitions.
6281
6283 {
6284 // This implements the while loop in Algorithm 1 from line 1.4 to 1.19.
6285
6286 // The instruction below has complexity O(|Act|);
6287 // calM will contain the m_BLC_transitions slices that need stabilization:
6288 std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> > calM;
6289 // Algorithm 1, line 1.4
6290 clock_t next_print_time = clock();
6291 const clock_t rounded_start_time = next_print_time - CLOCKS_PER_SEC/2;
6292 while (true)
6293 { //print_data_structures("MAIN LOOP");
6294 assert(check_data_structures("MAIN LOOP"));
6295 assert(check_stability("MAIN LOOP"));
6297 {
6298 if (std::clock_t now = std::clock(); next_print_time <= now ||
6299 m_non_trivial_constellations.empty())
6300 {
6301
6302 /* - - - - -print progress information- - - - - */
6303
6304 // The formula below should ensure that `next_print_time`
6305 // increases by a whole number of minutes, so that the
6306 // progress information is printed every minute (or, if
6307 // one iteration takes more than one minute, after a whole
6308 // number of minutes).
6309 next_print_time+=((now-next_print_time)/(60*CLOCKS_PER_SEC)
6310 + 1) * (60*CLOCKS_PER_SEC);
6311 now = (now - rounded_start_time) / CLOCKS_PER_SEC;
6312 if (0 != now)
6313 {
6314 if (60 <= now)
6315 {
6316 if (3600 <= now)
6317 {
6318 mCRL2log(log::verbose) << now / 3600 << " h ";
6319 now %= 3600;
6320 }
6321 mCRL2log(log::verbose) << now / 60 << " min ";
6322 now %= 60;
6323 }
6324 mCRL2log(log::verbose) << now
6325 << " sec passed since starting the main loop.\n";
6326 }
6327 #define PRINT_SG_PL(counter, sg_string, pl_string)
6328 (counter) << (1 == (counter) ? (sg_string) : (pl_string))
6329 mCRL2log(log::verbose)
6330 << (m_non_trivial_constellations.empty()
6331 ? "The reduced LTS contains "
6332 : "The reduced LTS contains at least ")
6333 << PRINT_SG_PL(no_of_blocks, " state and ", " states and ")
6334 << PRINT_SG_PL(no_of_non_constellation_inert_BLC_sets,
6335 " transition.", " transitions.");
6336 if (1 < no_of_blocks)
6337 {
6338 #define PRINT_INT_PERCENTAGE(num,denom)
6339 (((num) * 200 + (denom)) / (denom) / 2)
6340 mCRL2log(log::verbose) << " Estimated "
6342 no_of_blocks - 1)
6343 << "% done.";
6344 #undef PRINT_INT_PERCENTAGE
6345 }
6347 // << " Logarithmic estimate: "
6348 // << (int)(100.5+std::log((double) no_of_constellations/
6349 // no_of_blocks)
6350 // *log_initial_nr_of_blocks)
6351 // << "% done."
6352 << "\nThe current partition contains ";
6353 if (m_branching)
6354 {
6357 " new bottom state and ", " new bottom states and ");
6358 } else { assert(0==no_of_new_bottom_states); }
6359 mCRL2log(log::verbose)
6360 << PRINT_SG_PL(no_of_constellations,
6361 " constellation (of which ", " constellations (of which ")
6362 << PRINT_SG_PL(m_non_trivial_constellations.size(),
6363 " is nontrivial).\n", " are nontrivial).\n");
6364 #undef PRINT_SG_PL
6365 }
6366 }
6367 if (m_non_trivial_constellations.empty())
6368 {
6369 break;
6370 }
6371 // Algorithm 1, line 1.5
6372 block_type* index_block_B=
6374 constellation_type* const old_constellation=
6375 index_block_B->c.onstellation;
6376
6377 // Algorithm 1, line 1.6
6378 if (old_constellation->start_const_states->ref_state->block==
6379 std::prev(old_constellation->end_const_states)->ref_state->block)
6380 { assert(m_non_trivial_constellations.back()==old_constellation);
6381 // Constellation has become trivial.
6382 m_non_trivial_constellations.pop_back();
6383 }
6384 constellation_type* const new_constellation=
6385 #ifdef USE_POOL_ALLOCATOR
6386 simple_list<BLC_indicators>::get_pool().
6387 template construct<constellation_type>
6388 #else
6389 new constellation_type
6390 #endif
6391 (index_block_B->start_bottom_states,
6392 index_block_B->end_states);
6394 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
6395 /* Block index_block_B is moved to the new constellation but we shall*/ // new_constellation->work_counter=old_constellation->work_counter;
6396 /* not yet assign */ unsigned char const max_C=check_complexity::log_n-check_complexity::
6397 /* index_block_B->c.onstellation=new_constellation; */ ilog2(number_of_states_in_constellation(*new_constellation));
6398 mCRL2complexity(index_block_B, add_work(check_complexity::
6400 #endif
6401 // Here the variables block.to_constellation and the doubly linked list
6402 // L_B->C in blocks must be still be updated.
6403 // This happens further below.
6404
6405 for (state_in_block_pointer* i=index_block_B->start_bottom_states;
6406 i!=index_block_B->end_states; ++i)
6407 { // mCRL2complexity(m_states[*i], add_work(..., max_C), *this);
6408 // and visit the incoming transitions. // subsumed under the above counter
6409 const std::vector<transition>::iterator end_it=
6410 (std::next(i->ref_state)==m_states.end())
6411 ? m_aut.get_transitions().end()
6412 : std::next(i->ref_state)->start_incoming_transitions;
6413 for(std::vector<transition>::iterator
6414 j=i->ref_state->start_incoming_transitions; j!=end_it; ++j)
6415 {
6416 const transition& t=*j;
6417 const transition_index t_index=
6418 std::distance(m_aut.get_transitions().begin(), j);
6419 // Update the state-action-constellation (saC) references in // mCRL2complexity(&m_transitions[t_index], add_work(..., max_C), *this);
6420 // m_outgoing_transitions. // subsumed under the above counter
6421 const outgoing_transitions_it old_pos=
6422 m_transitions[t_index].ref_outgoing_transitions;
6423 const outgoing_transitions_it end_same_saC=
6424 old_pos->start_same_saC < old_pos
6425 ? old_pos : old_pos->start_same_saC;
6426 const outgoing_transitions_it new_pos=end_same_saC->start_same_saC; assert(m_states[t.from()].start_outgoing_transitions<=new_pos);
6427 assert(new_pos<=old_pos);
6428 if (old_pos != new_pos)
6429 {
6430 std::swap(old_pos->ref.BLC_transitions,
6431 new_pos->ref.BLC_transitions);
6432 m_transitions[*old_pos->ref.BLC_transitions].
6433 ref_outgoing_transitions=old_pos;
6434 m_transitions[*new_pos->ref.BLC_transitions].
6435 ref_outgoing_transitions=new_pos;
6436 }
6437 if (new_pos < end_same_saC)
6438 {
6439 end_same_saC->start_same_saC = std::next(new_pos);
6440 }
6441 // correct start_same_saC provisionally: make them at least point
6442 // at each other. In the new saC-slice, all transitions point to
6443 // the first one, except the first one: that shall point at the
6444 // last one.
6445 new_pos->start_same_saC = new_pos;
6446 if (m_states[t.from()].start_outgoing_transitions<new_pos)
6447 {
6448 // Check if t is the first transition in the new saC slice:
6449 const transition& prev_t = m_aut.get_transitions()
6450 [*std::prev(new_pos)->ref.BLC_transitions]; assert(prev_t.from() == t.from());
6451 if (m_states[prev_t.to()].block == index_block_B &&
6452 label_or_divergence(prev_t) == label_or_divergence(t))
6453 {
6454 // prev_t also belongs to the new saC slice.
6455 new_pos->start_same_saC = std::prev(new_pos)->start_same_saC; assert(m_states[t.from()].start_outgoing_transitions<=new_pos->start_same_saC);
6456 assert(new_pos->start_same_saC<new_pos);
6457 assert(std::prev(new_pos)==new_pos->start_same_saC->start_same_saC);
6458 new_pos->start_same_saC->start_same_saC = new_pos;
6459 }
6460 }
6461 }
6462 }
6463 calM.clear();
6464
6465 // Walk through all states in block B
6466 for (state_in_block_pointer* i=index_block_B->start_bottom_states;
6467 i!=index_block_B->end_states; ++i)
6468 { // mCRL2complexity(m_states[*i], add_work(..., max_C), *this);
6469 // and visit the incoming transitions. // subsumed under the above counter
6470 const std::vector<transition>::iterator end_it=
6471 (std::next(i->ref_state)==m_states.end())
6472 ? m_aut.get_transitions().end()
6473 : std::next(i->ref_state)->start_incoming_transitions;
6474 for(std::vector<transition>::iterator
6475 j=i->ref_state->start_incoming_transitions; j!=end_it; ++j)
6476 {
6477 const transition& t=*j;
6478 const transition_index t_index=
6479 std::distance(m_aut.get_transitions().begin(), j); assert(m_states[t.to()].block == index_block_B);
6480 bool source_block_is_singleton=
6481 (1>=number_of_states_in_block(*m_states[t.from()].block)); // mCRL2complexity(&m_transitions[t_index], add_work(..., max_C), *this);
6482 // subsumed under the above counter
6483 // Give the saC slice of this transition its final correction
6484 const outgoing_transitions_it out_pos=
6485 m_transitions[t_index].ref_outgoing_transitions;
6486 const outgoing_transitions_it start_new_saC=
6487 out_pos->start_same_saC;
6488 if (start_new_saC < out_pos)
6489 {
6490 // not the first transition in the saC-slice
6491 if (out_pos < start_new_saC->start_same_saC)
6492 {
6493 // not the last transition in the saC-slice
6494 out_pos->start_same_saC = start_new_saC->start_same_saC;
6495 }
6496 }
6497
6498 // Update the doubly linked list L_B->C in blocks as the constellation is split in B and C\B.
6499 if (update_the_doubly_linked_list_LBC_new_constellation(index_block_B, t, t_index) &&
6500 !source_block_is_singleton &&
6501 (!is_inert_during_init(t) || index_block_B!=m_states[t.from()].block))
6502 {
6503 // a new BLC set has been constructed, insert its start position into calM.
6504 // (unless the source block is a singleton)
6505 BLC_list_iterator BLC_pos=m_transitions[t_index].
6506 ref_outgoing_transitions->ref.BLC_transitions; assert(t_index == *BLC_pos);
6507 // Algorithm 1, Line 1.7
6508 calM.emplace_back(BLC_pos, BLC_pos);
6509 // The end-position (the second element in the pair) will need to be corrected later.
6510 }
6511 }
6512 }
6513 index_block_B->c.onstellation=new_constellation;
6514
6515 // Algorithm 1, Line 1.7
6516 // correct the end-positions of calM entries
6517 if (calM.begin()!=calM.end())
6518 {
6519 for (std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> >::
6520 iterator calM_elt=calM.begin();; )
6521 {
6522 linked_list <BLC_indicators>::iterator ind=m_transitions
6523 [*calM_elt->first].transitions_per_block_to_constellation; mCRL2complexity(ind, add_work(check_complexity::
6524 /* Algorithm 1, Line 1.17 */ refine_partition_until_it_becomes_stable__correct_end_of_calM,max_C),*this);
6525 /* check if all transitions were moved to the new constellation, */ assert(ind->start_same_BLC==calM_elt->first);
6526 /* or some transitions to the old constellation have remained: */ assert(!ind->has_marked_transitions());
6527 const transition& last_t=
6528 m_aut.get_transitions()[*std::prev(ind->end_same_BLC)]; assert(m_states[last_t.to()].block->c.onstellation==new_constellation);
6529 assert(ind->start_same_BLC<ind->end_same_BLC);
6530 const transition* next_t=nullptr;
6531 if ((is_inert_during_init(last_t) &&
6532 m_states[last_t.from()].block->c.onstellation==
6533 old_constellation && (assert(m_states[last_t.from()].block!=index_block_B), true)
6534 ) ||
6535 (ind->end_same_BLC<m_BLC_transitions.data_end() &&
6536 (next_t=&m_aut.get_transitions()[*ind->end_same_BLC],
6537 m_states[last_t.from()].block==
6538 m_states[next_t->from()].block &&
6539 label_or_divergence(last_t)==label_or_divergence(*next_t) &&
6540 old_constellation==
6541 m_states[next_t->to()].block->c.onstellation)))
6542 {
6543 // there are some transitions to the corresponding co-splitter,
6544 // so we will have to stabilize the block
6545 calM_elt->second = ind->end_same_BLC;
6546 ++calM_elt;
6547 if (calM_elt==calM.end())
6548 {
6549 break;
6550 }
6551 }
6552 else
6553 {
6554 // all transitions in the old BLC set have moved to the new BLC
6555 // set; as the old BLC set was stable, so is the new one.
6556 // We can skip this element.
6557 if (std::prev(calM.end())==calM_elt)
6558 {
6559 // to avoid protests by the MSVC compiler we have to do this
6560 // check beforehand (if calM_elt points to the last element of
6561 // the vector, the standard mandates that the iterator becomes
6562 // invalid.)
6563 calM.pop_back();
6564 break;
6565 }
6566 else
6567 {
6568 calM_elt->first=calM.back().first;
6569 calM.pop_back();
6570 }
6571 }
6572 }
6573 }
6574
6575 // ---------------------------------------------------------------------------------------------
6576 // First carry out a co-split of B with respect to C\B and an action tau.
6577 if (m_branching)
6578 {
6579 linked_list<BLC_indicators>::iterator tau_co_splitter=
6580 find_inert_co_transition_for_block(index_block_B,
6581 old_constellation, new_constellation);
6582
6583 // Algorithm 1, Line 1.8
6584 if (index_block_B->block.to_constellation.end()!=tau_co_splitter)
6585 {
6586 // The tau co-splitter contains transitions that have just become
6587 // non-inert.
6589 if (number_of_states_in_block(*index_block_B) > 1)
6590 { assert(tau_co_splitter->is_stable());
6591 four_way_splitB<true, false>(index_block_B, tau_co_splitter,
6592 index_block_B->block.to_constellation.end(),
6593 old_constellation, // needed, because index_block_B
6594 // might be split again later under other labels.
6595 new_constellation
6596 );
6597 }
6598 }
6599 }
6600 // Algorithm 1, Line 1.9
6601 for (std::pair<BLC_list_iterator, BLC_list_iterator> calM_elt: calM)
6602 { // mCRL2complexity(..., add_work(..., max_C), *this);
6603 // not needed as the inner loop is always executed at least once.
6604 //print_data_structures("Main loop");
6605 assert(check_stability("Main loop", &calM, &calM_elt, old_constellation, new_constellation));
6606 assert(check_data_structures("Main loop", false, false));
6607 /* Algorithm 1, Line 1.10 */ assert(calM_elt.first < calM_elt.second);
6608 do
6609 {
6610 linked_list<BLC_indicators>::iterator splitter=
6611 m_transitions[*std::prev(calM_elt.second)].
6612 transitions_per_block_to_constellation; mCRL2complexity(splitter, add_work(check_complexity::
6613 refine_partition_until_it_becomes_stable__execute_main_split,max_C),*this);
6614 /* Algorithm 1, Line 1.11 */ assert(splitter->end_same_BLC==calM_elt.second); assert(splitter->is_stable());
6615 calM_elt.second = splitter->start_same_BLC; assert(splitter->start_same_BLC<splitter->end_same_BLC);
6616
6617 const transition& first_t=
6618 m_aut.get_transitions()[*splitter->start_same_BLC];
6619 const label_index a=label_or_divergence(first_t); assert(m_states[first_t.to()].block->c.onstellation==new_constellation);
6620 block_type* Bpp=m_states[first_t.from()].block; assert(Bpp->c.onstellation!=new_constellation ||
6621 /* Algorithm 1, Line 1.12 */ !is_inert_during_init(first_t));
6622 if (number_of_states_in_block(*Bpp) <= 1)
6623 {
6624 // a block with 1 state does not need to be split
6625 }
6626 else if (Bpp->contains_new_bottom_states)
6627 {
6628 // The block Bpp contains new bottom states, and it is not
6629 // necessary to spend any work on it now.
6630 // We will later stabilize it in stabilizeB().
6631 }
6632 // Algorithm 1, Line 1.13
6633 else if (is_inert_during_init(first_t) &&
6634 old_constellation==Bpp->c.onstellation)
6635 {
6636 // The co-splitter would be constellation-inert, so no co-split
6637 // is needed
6638 // Algorithm 1, Line 1.14
6639 four_way_splitB<true, false>(Bpp, splitter,
6640 Bpp->block.to_constellation.end(),
6641 old_constellation, new_constellation);
6642 }
6643 else
6644 {
6645 // Algorithm 1, Line 1.16
6646 linked_list<BLC_indicators>::iterator co_splitter=
6647 Bpp->block.to_constellation.prev(splitter);
6648 const transition* co_t;
6649 // Algorithm 1, Line 1.17
6650 if (Bpp->block.to_constellation.end()!=co_splitter &&
6651 ( assert(co_splitter->is_stable()),
6652 assert(co_splitter->start_same_BLC<co_splitter->end_same_BLC),
6653 co_t=&m_aut.get_transitions()[*co_splitter->start_same_BLC], assert(m_states[co_t->from()].block==Bpp),
6654 a==label_or_divergence(*co_t) &&
6655 old_constellation==
6656 m_states[co_t->to()].block->c.onstellation))
6657 {
6658 // Algorithm 1, Line 1.18
6659 four_way_splitB<true, true>(Bpp, splitter, co_splitter,
6660 old_constellation, new_constellation);
6661 }
6662 }
6663 // Algorithm 1, Line 1.9
6664 }
6665 while (calM_elt.first < calM_elt.second);
6666 } //print_data_structures("Before stabilize");
6667 assert(check_data_structures("Before stabilize", false, false));
6668 /* Algorithm 1, Line 1.19 */ assert(check_stability("Before stabilize"));
6669 stabilizeB();
6670 }
6671 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
6673 #endif
6674 }
6675
6676 public:
6677 /// time measurement after creating the initial partition (but before the first call to `stabilizeB()`)
6679
6680 /// \brief constructor
6681 /// \details The constructor constructs the data structures and immediately
6682 /// calculates the partition corresponding with the bisimulation quotient.
6683 /// It does not adapt the LTS to represent the quotient's transitions.
6684 /// It is assumed that there are no tau-loops in aut.
6685 /// \param aut LTS that needs to be reduced
6686 /// \param branching If true branching bisimulation is used,
6687 /// otherwise strong bisimulation is
6688 /// applied.
6689 /// \param preserve_divergence If true and branching is true, preserve
6690 /// tau loops on states.
6692 const bool branching = false,
6693 const bool preserve_divergence = false)
6694 : m_aut(aut),
6699 no_of_blocks(1),
6702 m_branching(branching),
6703 m_preserve_divergence(preserve_divergence),
6706 { assert(m_branching || !m_preserve_divergence);
6708 mCRL2log(log::verbose) << "Start initialisation.\n";
6710 end_initial_part=std::clock();
6711 mCRL2log(log::verbose) << "After initialisation there are "
6712 << no_of_blocks << " equivalence classes. Start refining. \n";
6714 }
6715};
6716
6717
6718
6719
6720
6721/* ************************************************************************* */
6722/* */
6723/* I N T E R F A C E */
6724/* */
6725/* ************************************************************************* */
6726
6727
6728
6729
6730
6731/// \brief nonmember functions serving as interface with the rest of mCRL2
6732/// \details These functions are copied, almost without changes, from
6733/// liblts_bisim_gw.h, which was written by Anton Wijs.
6734
6735/// \brief Reduce transition system l with respect to strong or
6736/// (divergence-preserving) branching bisimulation.
6737/// \param[in,out] l The transition system that is reduced.
6738/// \param branching If true branching bisimulation is
6739/// applied, otherwise strong bisimulation.
6740/// \param preserve_divergence Indicates whether loops of internal
6741/// actions on states must be preserved. If
6742/// false these are removed. If true these
6743/// are preserved.
6744template <class LTS_TYPE>
6745void bisimulation_reduce_gj(LTS_TYPE& l, const bool branching = false,
6746 const bool preserve_divergence = false)
6747{
6748 if (1 >= l.num_states())
6749 {
6750 mCRL2log(log::warning) << "There is only 1 state in the LTS. It is not "
6751 "guaranteed that branching bisimulation minimisation runs in "
6752 "time O(m log n).\n";
6753 }
6754 // Algorithm 1, Line 1.1: Find tau-SCCs and contract each of them to a
6755 // single state
6756 const std::clock_t start_SCC=std::clock();
6757 mCRL2log(log::verbose) << "Start SCC\n";
6758 if (branching)
6759 {
6760 scc_reduce(l, preserve_divergence);
6761 }
6762
6763 // Now apply the branching bisimulation reduction algorithm. If there
6764 // are no taus, this will automatically yield strong bisimulation.
6765 const std::clock_t start_part=std::clock();
6766 mCRL2log(log::verbose) << "Start Partitioning\n";
6767 bisim_partitioner_gj<LTS_TYPE> bisim_part(l,branching,preserve_divergence);
6768
6769 // Assign the reduced LTS
6770 const std::clock_t end_part=std::clock();
6771 mCRL2log(log::verbose) << "Start finalizing\n";
6772 bisim_part.finalize_minimized_LTS();
6773
6775 {
6776 const std::clock_t end_finalizing=std::clock();
6777 const int prec=static_cast<int>
6778 (std::log10(CLOCKS_PER_SEC)+0.69897000433602);
6779 // For example, if CLOCKS_PER_SEC>= 20: >=2 digits
6780 // If CLOCKS_PER_SEC>= 200: >=3 digits
6781 // If CLOCKS_PER_SEC>=2000000: >=7 digits
6782
6783 double runtime[5];
6784 runtime[0]=(double) (end_finalizing - start_SCC)/CLOCKS_PER_SEC; // total time
6785 runtime[1]=(double) ( start_part-start_SCC)/CLOCKS_PER_SEC;
6786 runtime[2]=(double) ( bisim_part.end_initial_part-start_part )/CLOCKS_PER_SEC;
6787 runtime[3]=(double) ( end_part-bisim_part.end_initial_part )/CLOCKS_PER_SEC;
6788 runtime[4]=(double) (end_finalizing-end_part )/CLOCKS_PER_SEC;
6789 if (runtime[0]>=60.0)
6790 {
6791 int min[sizeof(runtime)/sizeof(runtime[0])];
6792 for (unsigned i = 0; i < sizeof(runtime)/sizeof(runtime[0]); ++i)
6793 {
6794 min[i] = static_cast<int>(runtime[i]) / 60;
6795 runtime[i] -= 60 * min[i];
6796 }
6797 if (min[0]>=60)
6798 {
6799 int h[sizeof(runtime)/sizeof(runtime[0])];
6800 for (unsigned i=0; i < sizeof(runtime)/sizeof(runtime[0]); ++i)
6801 {
6802 h[i] = min[i] / 60;
6803 min[i] %= 60;
6804 }
6805 int width = static_cast<int>(std::log10(h[0])) + 1;
6806
6807 mCRL2log(log::verbose) << std::fixed << std::setprecision(prec)
6808 << "Time spent on contracting SCCs: " << std::setw(width) << h[1] << "h " << std::setw(2) << min[1] << "min " << std::setw(prec+3) << runtime[1] << "s\n"
6809 "Time spent on initial partition:" << std::setw(width) << h[2] << "h " << std::setw(2) << min[2] << "min " << std::setw(prec+3) << runtime[2] << "s\n"
6810 "Time spent on stabilize+refine: " << std::setw(width) << h[3] << "h " << std::setw(2) << min[3] << "min " << std::setw(prec+3) << runtime[3] << "s\n"
6811 "Time spent on finalizing: " << std::setw(width) << h[4] << "h " << std::setw(2) << min[4] << "min " << std::setw(prec+3) << runtime[4] << "s\n"
6812 "Total CPU time: " << std::setw(width) << h[0] << "h " << std::setw(2) << min[0] << "min " << std::setw(prec+3) << runtime[0] << "s\n"
6813 "BENCHMARK TIME: " << static_cast<double>(end_part-start_part)/CLOCKS_PER_SEC << "\n"
6814 << std::defaultfloat;
6815 }
6816 else
6817 {
6818 mCRL2log(log::verbose) << std::fixed << std::setprecision(prec)
6819 << "Time spent on contracting SCCs: " << std::setw(2) << min[1] << "min " << std::setw(prec+3) << runtime[1] << "s\n"
6820 "Time spent on initial partition:" << std::setw(2) << min[2] << "min " << std::setw(prec+3) << runtime[2] << "s\n"
6821 "Time spent on stabilize+refine: " << std::setw(2) << min[3] << "min " << std::setw(prec+3) << runtime[3] << "s\n"
6822 "Time spent on finalizing: " << std::setw(2) << min[4] << "min " << std::setw(prec+3) << runtime[4] << "s\n"
6823 "Total CPU time: " << std::setw(2) << min[0] << "min " << std::setw(prec+3) << runtime[0] << "s\n"
6824 "BENCHMARK TIME: " << static_cast<double>(end_part-start_part)/CLOCKS_PER_SEC << "\n"
6825 << std::defaultfloat;
6826 }
6827 }
6828 else
6829 {
6830 mCRL2log(log::verbose) << std::fixed << std::setprecision(prec)
6831 << "Time spent on contracting SCCs: " << std::setw(prec+3) << runtime[1] << "s\n"
6832 "Time spent on initial partition:" << std::setw(prec+3) << runtime[2] << "s\n"
6833 "Time spent on stabilize+refine: " << std::setw(prec+3) << runtime[3] << "s\n"
6834 "Time spent on finalizing: " << std::setw(prec+3) << runtime[4] << "s\n"
6835 "Total CPU time: " << std::setw(prec+3) << runtime[0] << "s\n"
6836 "BENCHMARK TIME: " << static_cast<double>(end_part-start_part)/CLOCKS_PER_SEC << "\n"
6837 << std::defaultfloat;
6838 }
6839 }
6840}
6841
6842
6843/// \brief Checks whether the two initial states of two LTSs are strong or
6844/// (divergence-preserving) branching bisimilar.
6845/// \details This routine uses the experimental O(m log n) branching
6846/// bisimulation algorithm developed in 2024 by Jan Friso Groote and David N.
6847/// Jansen. It runs in O(m log n) time and uses O(m) memory, where n is the
6848/// number of states and m is the number of transitions.
6849///
6850/// The LTSs l1 and l2 are not usable anymore after this call.
6851/// \param[in,out] l1 A first transition system.
6852/// \param[in,out] l2 A second transistion system.
6853/// \param branching If true branching bisimulation is used,
6854/// otherwise strong bisimulation is
6855/// applied.
6856/// \param preserve_divergence If true and branching is true, preserve
6857/// tau loops on states.
6858/// \param generate_counter_examples (non-functional, only in the
6859/// interface for historical reasons)
6860/// \returns True iff the initial states of the transition systems l1 and l2
6861/// are ((divergence-preserving) branching) bisimilar.
6862template <class LTS_TYPE>
6863bool destructive_bisimulation_compare_gj(LTS_TYPE& l1, LTS_TYPE& l2,
6864 const bool branching = false, const bool preserve_divergence = false,
6865 const bool generate_counter_examples = false,
6866 const std::string& /*counter_example_file*/ = "",
6867 bool /*structured_output*/ = false)
6868{
6869 if (generate_counter_examples)
6870 {
6871 mCRL2log(log::warning) << "The GJ24 branching bisimulation "
6872 "algorithm does not generate counterexamples.\n";
6873 }
6874 std::size_t init_l2(l2.initial_state() + l1.num_states());
6875 detail::merge(l1, std::move(l2));
6876 l2.clear(); // No use for l2 anymore.
6877
6878 if (branching)
6879 {
6880 detail::scc_partitioner<LTS_TYPE> scc_part(l1);
6881 scc_part.replace_transition_system(preserve_divergence);
6882 init_l2 = scc_part.get_eq_class(init_l2);
6883 } else { assert(!preserve_divergence); }
6884 assert(1 < l1.num_states());
6885 bisim_partitioner_gj<LTS_TYPE>bisim_part(l1,branching,preserve_divergence);
6886
6887 return bisim_part.in_same_class(l1.initial_state(), init_l2);
6888}
6889
6890
6891/// \brief Checks whether the two initial states of two LTSs are strong or
6892/// (divergence-preserving) branching bisimilar.
6893/// \details The LTSs l1 and l2 are first duplicated and subsequently reduced
6894/// modulo bisimulation. If memory is a concern, one could consider to use
6895/// destructive_bisimulation_compare(). This routine uses the O(m log n)
6896/// branching bisimulation algorithm developed in 2018 by David N. Jansen. It
6897/// runs in O(m log n) time and uses O(m) memory, where n is the number of
6898/// states and m is the number of transitions.
6899/// \param l1 A first transition system.
6900/// \param l2 A second transistion system.
6901/// \param branching If true branching bisimulation is used,
6902/// otherwise strong bisimulation is applied.
6903/// \param preserve_divergence If true and branching is true, preserve tau
6904/// loops on states.
6905/// \retval True iff the initial states of the transition systems l1 and l2
6906/// are ((divergence-preserving) branching) bisimilar.
6907template <class LTS_TYPE>
6908inline bool bisimulation_compare_gj(const LTS_TYPE& l1, const LTS_TYPE& l2,
6909 const bool branching = false, const bool preserve_divergence = false)
6910{
6911 LTS_TYPE l1_copy(l1);
6912 LTS_TYPE l2_copy(l2);
6913 return destructive_bisimulation_compare_gj(l1_copy, l2_copy, branching,
6914 preserve_divergence);
6915}
6916
6917
6918} // end namespace detail
6919} // end namespace lts
6920} // end namespace mcrl2
6921
6922#undef linked_list
6923#endif // ifndef LIBLTS_BISIM_GJ_H
#define mCRL2complexity(unit, call, info_for_debug)
Assigns work to a counter and checks for errors.
const aterm & operator[](const size_type i) const
Returns the i-th argument.
Definition aterm.h:187
aterm & operator=(const aterm &other) noexcept=default
const function_symbol & function() const
Returns the function symbol belonging to an aterm.
Definition aterm.h:144
aterm(const aterm &other) noexcept=default
This class has user-declared copy constructor so declare default copy and move operators.
const function_symbol & function() const noexcept
Definition aterm_core.h:55
bool operator!=(const function_symbol &f) const
Inequality test.
bool operator==(const function_symbol &f) const
Equality test.
unprotected_aterm_core m_stack[maximal_size_of_stack]
static constexpr std::size_t maximal_size_of_stack
void initialise(const term_balanced_tree< Term > &tree)
const Term & dereference() const
Dereference operator.
bool equal(const iterator &other) const
Equality operator.
iterator(const term_balanced_tree< Term > &tree)
void increment()
Increments the iterator.
bool is_node() const
Returns true iff the tree is a node with a left and right subtree.
friend void make_term_balanced_tree(term_balanced_tree< Term1 > &result, ForwardTraversalIterator p, const std::size_t size, Transformer transformer)
static void make_tree_helper(aterm &result, ForwardTraversalIterator &p, const std::size_t size, Transformer transformer)
term_balanced_tree & operator=(const term_balanced_tree &) noexcept=default
Assignment operator.
size_type size() const
Returns the size of the term_balanced_tree.
term_balanced_tree(term_balanced_tree &&) noexcept=default
Move constructor.
bool empty() const
Returns true if tree is empty.
Term & reference
Reference to T.
static const aterm & empty_tree()
static void make_tree(aterm &result, ForwardTraversalIterator &p, const std::size_t size, Transformer transformer)
term_balanced_tree(ForwardTraversalIterator first, const std::size_t size)
Creates an term_balanced_tree with a copy of a range.
std::size_t size_type
An unsigned integral type.
static const function_symbol & tree_single_node_function()
const aterm & left_branch() const
Get the left branch of the tree.
term_balanced_tree(const term_balanced_tree &) noexcept=default
Copy constructor.
Term value_type
The type of object, T stored in the term_balanced_tree.
term_balanced_tree(ForwardTraversalIterator first, const std::size_t size, Transformer transformer)
Creates an term_balanced_tree with a copy of a range, where a transformer is applied to each term bef...
static const function_symbol & tree_node_function()
const Term & operator[](std::size_t position) const
Element indexing operator.
iterator begin() const
Returns an iterator pointing to the beginning of the term_balanced_tree.
iterator end() const
Returns an iterator pointing to the end of the term_balanced_tree.
term_balanced_tree()
Default constructor. Creates an empty tree.
const aterm & right_branch() const
Get the left branch of the tree.
term_balanced_tree & operator=(term_balanced_tree &&) noexcept=default
Move assign operator.
const Term const_reference
Const reference to T.
term_balanced_tree(const aterm &tree)
Construction from aterm.
const Term & element_at(std::size_t position, std::size_t size) const
Get an element at the indicated position.
static const function_symbol & tree_empty_function()
term_balanced_tree(detail::_term_appl *t)
ptrdiff_t difference_type
A signed integral type.
A list of aterm objects.
Definition aterm_list.h:24
An unprotected term does not change the reference count of the shared term when it is copied or moved...
Definition aterm_core.h:32
void swap(unprotected_aterm_core &t) noexcept
Swaps this term with its argument.
Definition aterm_core.h:152
bool operator==(const unprotected_aterm_core &t) const
Comparison operator.
Definition aterm_core.h:83
bool defined() const
Returns true if this term is not equal to the term assigned by the default constructor of aterms,...
Definition aterm_core.h:143
const detail::_aterm * m_term
Definition aterm_core.h:36
bool operator!=(const unprotected_aterm_core &t) const
Inequality operator on two unprotected aterms.
Definition aterm_core.h:92
const function_symbol & function() const
Yields the function symbol in an aterm.
Definition aterm_core.h:161
action_formula(action_formula &&) noexcept=default
action_formula & operator=(const action_formula &) noexcept=default
action_formula(const atermpp::aterm &term)
action_formula(const data::data_expression &x)
\brief Constructor Z6.
action_formula(const action_formula &) noexcept=default
Move semantics.
action_formula & operator=(action_formula &&) noexcept=default
action_formula(const data::untyped_data_parameter &x)
\brief Constructor Z6.
action_formula()
\brief Default constructor X3.
action_formula(const process::untyped_multi_action &x)
\brief Constructor Z6.
\brief The and operator for action formulas
and_ & operator=(const and_ &) noexcept=default
and_ & operator=(and_ &&) noexcept=default
and_(const action_formula &left, const action_formula &right)
\brief Constructor Z14.
and_()
\brief Default constructor X3.
and_(and_ &&) noexcept=default
const action_formula & left() const
and_(const atermpp::aterm &term)
and_(const and_ &) noexcept=default
Move semantics.
const action_formula & right() const
\brief The at operator for action formulas
at(const atermpp::aterm &term)
const data::data_expression & time_stamp() const
at & operator=(at &&) noexcept=default
const action_formula & operand() const
at(const at &) noexcept=default
Move semantics.
at(at &&) noexcept=default
at()
\brief Default constructor X3.
at & operator=(const at &) noexcept=default
at(const action_formula &operand, const data::data_expression &time_stamp)
\brief Constructor Z14.
\brief The existential quantification operator for action formulas
exists(const atermpp::aterm &term)
exists & operator=(exists &&) noexcept=default
exists(exists &&) noexcept=default
exists(const exists &) noexcept=default
Move semantics.
exists()
\brief Default constructor X3.
const data::variable_list & variables() const
exists & operator=(const exists &) noexcept=default
const action_formula & body() const
exists(const data::variable_list &variables, const action_formula &body)
\brief Constructor Z14.
\brief The value false for action formulas
false_(const atermpp::aterm &term)
false_()
\brief Default constructor X3.
false_(false_ &&) noexcept=default
false_(const false_ &) noexcept=default
Move semantics.
false_ & operator=(const false_ &) noexcept=default
false_ & operator=(false_ &&) noexcept=default
\brief The universal quantification operator for action formulas
forall & operator=(const forall &) noexcept=default
const action_formula & body() const
forall & operator=(forall &&) noexcept=default
forall(const atermpp::aterm &term)
const data::variable_list & variables() const
forall()
\brief Default constructor X3.
forall(const data::variable_list &variables, const action_formula &body)
\brief Constructor Z14.
forall(const forall &) noexcept=default
Move semantics.
forall(forall &&) noexcept=default
\brief The implication operator for action formulas
const action_formula & left() const
imp(const imp &) noexcept=default
Move semantics.
imp(imp &&) noexcept=default
imp & operator=(imp &&) noexcept=default
imp(const action_formula &left, const action_formula &right)
\brief Constructor Z14.
imp()
\brief Default constructor X3.
imp & operator=(const imp &) noexcept=default
imp(const atermpp::aterm &term)
const action_formula & right() const
\brief The multi action for action formulas
multi_action(const multi_action &) noexcept=default
Move semantics.
multi_action(multi_action &&) noexcept=default
multi_action(const process::action_list &actions)
\brief Constructor Z14.
multi_action(const atermpp::aterm &term)
multi_action & operator=(const multi_action &) noexcept=default
multi_action()
\brief Default constructor X3.
const process::action_list & actions() const
multi_action & operator=(multi_action &&) noexcept=default
\brief The not operator for action formulas
not_(const action_formula &operand)
\brief Constructor Z14.
not_()
\brief Default constructor X3.
const action_formula & operand() const
not_(const atermpp::aterm &term)
not_(not_ &&) noexcept=default
not_(const not_ &) noexcept=default
Move semantics.
not_ & operator=(const not_ &) noexcept=default
not_ & operator=(not_ &&) noexcept=default
\brief The or operator for action formulas
or_ & operator=(const or_ &) noexcept=default
or_(or_ &&) noexcept=default
or_()
\brief Default constructor X3.
or_ & operator=(or_ &&) noexcept=default
or_(const action_formula &left, const action_formula &right)
\brief Constructor Z14.
or_(const atermpp::aterm &term)
or_(const or_ &) noexcept=default
Move semantics.
const action_formula & right() const
const action_formula & left() const
\brief The value true for action formulas
true_(true_ &&) noexcept=default
true_ & operator=(const true_ &) noexcept=default
true_()
\brief Default constructor X3.
true_(const true_ &) noexcept=default
Move semantics.
true_(const atermpp::aterm &term)
true_ & operator=(true_ &&) noexcept=default
data_expression & operator=(const data_expression &) noexcept=default
data_expression & operator=(data_expression &&) noexcept=default
sort_expression sort() const
Returns the sort of the data expression.
Definition data.cpp:108
data_expression(const data_expression &) noexcept=default
Move semantics.
data_expression(data_expression &&) noexcept=default
Rewriter that operates on data expressions.
Definition rewriter.h:81
data_expression operator()(const data_expression &d) const
Rewrites a data expression.
Definition rewriter.h:158
void add_sort(const basic_sort &s)
Adds a sort to this specification.
\brief A data variable
Definition variable.h:28
Class for logging messages.
Definition logger.h:129
static void set_reporting_level(const log_level_t level)
Set reporting level.
Definition logger.h:210
logger(const log_level_t l)
Default constructor.
Definition logger.h:164
Read-only singly linked list of action rename rules.
\brief A timed multi-action
multi_action(const multi_action &) noexcept=default
Move semantics.
const process::action_list & actions() const
const data::data_expression & time() const
multi_action(multi_action &&) noexcept=default
multi_action(const process::action_list &actions=process::action_list(), data::data_expression time=data::undefined_real())
Constructor.
This class contains labels for probabilistic transistions, consisting of a numerator and a denumerato...
static probabilistic_data_expression one()
Constant one.
probabilistic_data_expression operator+(const probabilistic_data_expression &other) const
Standard addition operator. Note that the expression is not evaluated. For this the rewriter has to b...
probabilistic_data_expression(const data::data_expression &d)
Construct a probabilistic_data_expression from a data_expression, which must be of sort real.
bool operator==(const probabilistic_data_expression &other) const
probabilistic_data_expression(std::size_t enumerator, std::size_t denominator)
bool operator!=(const probabilistic_data_expression &other) const
bool operator>=(const probabilistic_data_expression &other) const
bool operator<(const probabilistic_data_expression &other) const
bool operator<=(const probabilistic_data_expression &other) const
bool operator>(const probabilistic_data_expression &other) const
probabilistic_data_expression operator-(const probabilistic_data_expression &other) const
Standard subtraction operator.
static data::data_specification data_specification_with_real()
static probabilistic_data_expression zero()
Constant zero.
Linear process specification.
STATE & state()
Get the state in a state probability pair.
state_probability_pair(state_probability_pair &&p)=default
state_probability_pair & operator=(state_probability_pair &&p)=default
state_probability_pair(const state_probability_pair &p)=default
Copy constructor;.
state_probability_pair & operator=(const state_probability_pair &p)=default
Standard assignment.
const PROBABILITY & probability() const
get the probability from a state proability pair.
const STATE & state() const
Get the state from a state probability pair.
PROBABILITY & probability()
Set the probability in a state probability pair.
state_probability_pair(const STATE &state, const PROBABILITY &probability)
constructor.
bool operator==(const state_probability_pair &other) const
Standard equality operator.
A class containing the values for action labels for the .lts format.
Definition lts_lts.h:144
action_label_lts & operator=(const action_label_lts &)=default
Copy assignment.
static mcrl2::lps::multi_action sort_multiactions(const mcrl2::lps::multi_action &a)
A function to sort a multi action to guarantee that multi-actions are compared properly.
Definition lts_lts.h:190
void hide_actions(const std::vector< std::string > &tau_actions)
Hide the actions with labels in tau_actions.
Definition lts_lts.h:166
action_label_lts(const action_label_lts &)=default
Copy constructor.
static const action_label_lts & tau_action()
Definition lts_lts.h:182
action_label_lts()
Default constructor.
Definition lts_lts.h:148
action_label_lts(const mcrl2::lps::multi_action &a)
Constructor.
Definition lts_lts.h:158
This class contains strings to be used as values for action labels in lts's.
static std::string sort_multiactions(const std::string &s)
Sort multiactions in a string.
void hide_actions(const std::vector< std::string > &string_vector)
action_label_string & operator=(const action_label_string &)=default
Copy assignment.
static const action_label_string & tau_action()
action_label_string(const std::string &s)
bool operator<(const action_label_string &l) const
action_label_string(const action_label_string &)=default
Copy constructor.
stores information about a block
stores information about a constellation
stores information about a single state
bool surely_has_no_transition_to(const constln_t *const SpC) const
quick check to find out whether the state has no transition to SpC
bool surely_has_transition_to(const constln_t *const SpC) const
quick check to find out whether the state has a transition to SpC
state_index number_of_states_in_block(const block_type &B) const
return the number of states in block B
void finalize_minimized_LTS()
Adapt the LTS after minimisation.
label_index label_or_divergence(const transition &t, const label_index divergent_label=-2) const
void swap_states_in_states_in_block(state_in_block_pointer *pos1, state_in_block_pointer *pos2)
swap the contents of pos1 and pos2 if they are different
LTS_TYPE & m_aut
automaton that is being reduced
block_type * select_and_remove_a_block_in_a_non_trivial_constellation()
Select a block that is not the largest block in a non-trivial constellation.
std::string ptr(const transition &t) const
std::unordered_set< state_index > set_of_states_type
std::vector< block_type * > m_blocks_with_new_bottom_states
std::unordered_set< transition_index > set_of_transitions_type
std::vector< constellation_type * > m_non_trivial_constellations
The following variable contains all non-trivial constellations.
void swap_states_in_states_in_block(state_in_block_pointer *pos1, state_in_block_pointer *pos2, state_in_block_pointer *pos3)
Move the contents of pos1 to pos2, those of pos2 to pos3 and those of pos3 to pos1
void multiple_swap_states_in_states_in_block(state_in_block_pointer *pos1, state_in_block_pointer *pos2, state_index count, const state_in_block_pointer *assign_work_to, unsigned char const max_B, enum check_complexity::counter_type const ctr=check_complexity::multiple_swap_states_in_block__swap_state_in_small_block)
Swap the range [pos1, pos1 + count) with the range [pos2, pos2 + count)
bool is_inert_during_init(const transition &t) const
state_index no_of_new_bottom_states
number of new bottom states found after constructing the initial partition
linked_list< BLC_indicators >::const_iterator next_target_constln_in_same_saC(state_in_block_pointer const src, BLC_list_const_iterator const splitter_it) const
find the next constellation after splitter_it's in the same_saC slice of the outgoing transitions
bool is_inert_during_init_if_branching(const transition &t) const
std::vector< linked_list< BLC_indicators >::iterator > m_BLC_indicators_to_be_deleted
void mark_BLC_transition(const outgoing_transitions_it out_pos)
marks the transition indicated by out_pos.
void check_incoming_tau_transitions_become_noninert(block_type *NewBotSt_block_index, state_in_block_pointer *start_bottom, state_in_block_pointer *const end_non_bottom)
makes incoming transitions from block NewBotSt_block_index non-block-inert
std::clock_t end_initial_part
time measurement after creating the initial partition (but before the first call to stabilizeB())
void move_nonbottom_states_to(const todo_state_vector &R, state_in_block_pointer *to_pos, state_index new_block_bottom_size)
Move states in a set to a specific position in m_states_in_block
linked_list< BLC_indicators >::iterator find_inert_co_transition_for_block(block_type *const index_block_B, const constellation_type *const old_constellation, const constellation_type *const new_constellation) const
find a splitter for the tau-transitions from the new constellation to the old constellation
state_index get_eq_class(const state_index si) const
Get the equivalence class of a state.
void swap_states_in_states_in_block_never_equal(state_in_block_pointer *pos1, state_in_block_pointer *pos2)
swap the contents of pos1 and pos2, assuming they are different
bool swap_in_the_doubly_linked_list_LBC_in_blocks_new_block(const transition_index ti, linked_list< BLC_indicators >::iterator new_BLC_block, linked_list< BLC_indicators >::iterator old_BLC_block)
Swap transition ti from BLC set old_BLC_block to BLC set new_BLC_block
std::size_t num_eq_classes() const
Calculate the number of equivalence classes.
bool check_data_structures(const std::string &tag, const bool initialisation=false, const bool check_temporary_complexity_counters=true) const
Checks whether data structures are consistent.
fixed_vector< transition_type > m_transitions
void order_BLC_transitions_single_BLC_set(state_in_block_pointer *const pos, BLC_list_iterator start_same_BLC, const BLC_list_iterator end_same_BLC)
create one BLC set for the block starting at pos
const bool m_branching
true iff branching (not strong) bisimulation has been requested
void display_BLC_list(const block_type *const bi) const
Prints the list of BLC sets as debug output.
void clear_state_counters(std::vector< state_in_block_pointer >::const_iterator begin, std::vector< state_in_block_pointer >::const_iterator const end, block_type *const block)
reset a range of state counters to undefined
state_index number_of_states_in_constellation(const constellation_type &C) const
return the number of states in constellation C
void swap_three_iterators_and_update_m_transitions(const BLC_list_iterator i1, const BLC_list_iterator i2, const BLC_list_iterator i3)
Move the content of i1 to i2, i2 to i3 and i3 to i1.
block_type * create_new_block(state_in_block_pointer *start_bottom_states, state_in_block_pointer *const start_non_bottom_states, state_in_block_pointer *const end_states, block_type *const old_block_index, constellation_type *const old_constellation, constellation_type *const new_constellation)
create a new block and adapt the BLC sets, and reset state counters
void order_BLC_transitions(const BLC_list_iterator start_same_BLC, const BLC_list_iterator end_same_BLC, state_in_block_pointer *min_block, state_in_block_pointer *max_block)
order m_BLC_transition entries according to source block
void check_transitions(const bool initialisation, const bool check_temporary_complexity_counters, const bool check_block_to_constellation=true) const
Checks whether the transition data structure is correct.
const bool m_preserve_divergence
true iff divergence-preserving branching bisimulation has been requested
fixed_vector< outgoing_transition_type > m_outgoing_transitions
transitions ordered per source state
void make_stable_and_move_to_start_of_BLC(block_type *const from_block, const linked_list< BLC_indicators >::iterator splitter)
Makes splitter stable and moves it to the beginning of the list.
void update_the_doubly_linked_list_LBC_new_block(block_type *const old_bi, block_type *const new_bi, const transition_index ti, constellation_type *old_constellation, constellation_type *const new_constellation)
Update the BLC list of transition ti, which now starts in block new_bi
fixed_vector< state_type_gj > m_states
information about states
fixed_vector< transition_index > m_BLC_transitions
static LTS_TYPE::labels_size_type m_aut_apply_hidden_label_map(typename LTS_TYPE::labels_size_type l)
void change_non_bottom_state_to_bottom_state(const fixed_vector< state_type_gj >::iterator si)
Moves the former non-bottom state si to the bottom states.
bool swap_in_the_doubly_linked_list_LBC_in_blocks_new_constellation(const transition_index ti, linked_list< BLC_indicators >::iterator new_BLC_block, linked_list< BLC_indicators >::iterator old_BLC_block)
Swap transition ti from BLC set old_BLC_block to BLC set new_BLC_block
block_type * four_way_splitB(block_type *const bi, linked_list< BLC_indicators >::iterator const small_splitter, linked_list< BLC_indicators >::iterator const large_splitter, constellation_type *const old_constellation, constellation_type *const new_constellation)
split a block (using main and co-splitter) into up to four subblocks
void swap_states_in_states_in_block_23_never_equal(state_in_block_pointer *pos1, state_in_block_pointer *pos2, state_in_block_pointer *pos3)
Move the contents of pos1 to pos2, those of pos2 to pos3 and those of pos3 to pos1
transition_index accumulate_entries(std::vector< transition_index > &action_counter, const std::vector< label_index > &todo_stack) const
fixed_vector< state_in_block_pointer > m_states_in_blocks
transition_index no_of_non_constellation_inert_BLC_sets
number of non-inert BLC sets in the partition
block_type * update_BLC_sets_new_block(block_type *const old_bi, block_type *const new_bi, constellation_type *const old_constellation, constellation_type *const new_constellation)
Update all BLC sets after a new block has been created.
bool update_the_doubly_linked_list_LBC_new_constellation(block_type *const index_block_B, const transition &t, const transition_index ti)
Move transition t with transition index ti to a new BLC set.
void print_data_structures(const std::string &header, const bool initialisation=false) const
Prints the partition refinement data structure as debug output.
bisim_partitioner_gj(LTS_TYPE &aut, const bool branching=false, const bool preserve_divergence=false)
constructor
bool in_same_class(state_index const s, state_index const t) const
Check whether two states are in the same equivalence class.
bool check_stability(const std::string &tag, const std::vector< std::pair< BLC_list_iterator, BLC_list_iterator > > *calM=nullptr, const std::pair< BLC_list_iterator, BLC_list_iterator > *calM_elt=nullptr, const constellation_type *const old_constellation=null_constellation, const constellation_type *const new_constellation=null_constellation) const
Checks the main invariant of the partition refinement algorithm.
implements the main algorithm for the stutter equivalence quotient
void set_truths(formula &f)
Compute and set the truth values of a formula f.
std::pair< label_type, block_index_type > observation_t
level_type gca_level(const block_index_type B1, const block_index_type B2)
Auxiliarry function that computes the level of the greatest common ancestor. In other words a lvl i s...
regular_formulas::regular_formula create_regular_formula(const mcrl2::lts::action_label_string &a) const
create_regular_formula Creates a regular formula that represents action a
bisim_partitioner_minimal_depth(LTS_TYPE &l, const std::size_t init_l2)
Creates a bisimulation partitioner for an LTS.
mcrl2::state_formulas::state_formula dist_formula_mindepth(const std::size_t s, const std::size_t t)
Creates a state formula that distinguishes state s from state t.
formula distinguish(const block_index_type b1, const block_index_type b2)
Creates a formula that distinguishes a block b1 from the block b2.
~bisim_partitioner_minimal_depth()=default
Destroys this partitioner.
std::map< std::pair< block_index_type, block_index_type >, level_type > greatest_common_ancestor
regular_formulas::regular_formula create_regular_formula(const mcrl2::lps::multi_action &a) const
create_regular_formula Creates a regular formula that represents action a
bool in_same_class(const std::size_t s, const std::size_t t)
block_index_type lift_block(const block_index_type B1, level_type goal)
mcrl2::state_formulas::state_formula conjunction(std::vector< formula > &conjunctions)
conjunction Creates a conjunction of state formulas
mcrl2::state_formulas::state_formula convert_formula(formula &f)
void split_BL(level_type lvl)
Performs the splits based on the blocks in Bsplit and the flags set in state_flags.
mcrl2::state_formulas::state_formula conjunction(std::set< mcrl2::state_formulas::state_formula > terms) const
conjunction Creates a conjunction of state formulas
regular_formulas::regular_formula create_regular_formula(const mcrl2::lts::action_label_string &a) const
create_regular_formula Creates a regular formula that represents action a
regular_formulas::regular_formula create_regular_formula(const mcrl2::lps::multi_action &a) const
create_regular_formula Creates a regular formula that represents action a
std::vector< bool > block_is_in_to_be_processed
std::map< block_index_type, block_index_type > right_child
std::vector< block_index_type > BL
bool in_same_class(const std::size_t s, const std::size_t t) const
Returns whether two states are in the same bisimulation equivalence class.
mcrl2::state_formulas::state_formula until_formula(const mcrl2::state_formulas::state_formula &phi1, const label_type &a, const mcrl2::state_formulas::state_formula &phi2)
until_formula Creates a state formula that corresponds to the until operator phi1phi2 from HMLU
std::size_t get_eq_class(const std::size_t s) const
Gives the bisimulation equivalence class number of a state.
bisim_partitioner(LTS_TYPE &l, const bool branching=false, const bool preserve_divergence=false, const bool generate_counter_examples=false)
Creates a bisimulation partitioner for an LTS.
~bisim_partitioner()=default
Destroys this partitioner.
std::map< block_index_type, std::set< block_index_type > > r_tauP
std::map< block_index_type, label_type > split_by_action
std::size_t num_eq_classes() const
Gives the number of bisimulation equivalence classes of the LTS.
void order_recursively_on_tau_reachability(const state_type s, std::map< state_type, std::vector< state_type > > &inert_transition_map, std::vector< non_bottom_state > &new_non_bottom_states, std::set< state_type > &visited)
std::vector< block_index_type > to_be_processed
std::map< block_index_type, block_index_type > split_by_block
void replace_transition_system(const bool branching, const bool preserve_divergences)
Replaces the transition relation of the current lts by the transitions of the bisimulation reduced tr...
void order_on_tau_reachability(std::vector< non_bottom_state > &non_bottom_states)
void split_the_blocks_in_BL(bool &partition_is_unstable, const label_type splitter_label, const block_index_type splitter_block)
void refine_partition_until_it_becomes_stable(const bool branching, const bool preserve_divergence)
void create_initial_partition(const bool branching, const bool preserve_divergences)
std::vector< state_type > block_index_of_a_state
std::map< block_index_type, std::set< block_index_type > > r_alpha
mcrl2::state_formulas::state_formula counter_formula_aux(const block_index_type B1, const block_index_type B2)
mcrl2::state_formulas::state_formula counter_formula(const std::size_t s, const std::size_t t)
Creates a state formula that distinguishes state s from state t.
void check_internal_consistency_of_the_partitioning_data_structure(const bool branching, const bool preserve_divergence) const
outgoing_transitions_per_state_action_t outgoing_transitions
const state_in_block_pointer * data() const
void swap_vec(std::vector< state_in_block_pointer > &other_vec)
std::vector< state_in_block_pointer >::iterator iterator
void reserve(std::vector< state_in_block_pointer >::size_type new_cap)
const state_in_block_pointer * data_end() const
const state_in_block_pointer & front() const
bool find(const state_in_block_pointer s) const
std::vector< state_in_block_pointer >::const_iterator const_iterator
class for time complexity checks
static int ilog2(state_type size)
calculate the base-2 logarithm, rounded down
static void wait(trans_type units=1)
do some work that cannot be assigned directly
static unsigned char log_n
value of floor(log2(n)) for easy access
counter_type
Type for complexity budget counters.
static void check_temporary_work()
check that not too much superfluous work has been done
static void print_grand_totals()
print grand total of work in the coroutines (to measure overhead)
static void init(state_type n)
starts counting for a new refinement run
compare_transitions_lts(const std::set< std::size_t > &hide_action_set)
Definition transition.h:72
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:76
const std::set< std::size_t > & m_hide_action_set
Definition transition.h:69
const std::set< std::size_t > & m_hide_action_set
Definition transition.h:37
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:44
compare_transitions_slt(const std::set< std::size_t > &hide_action_set)
Definition transition.h:40
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:188
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:166
compare_transitions_tl(const std::set< std::size_t > &hide_action_set)
Definition transition.h:162
const std::set< std::size_t > & m_hide_action_set
Definition transition.h:159
const std::set< std::size_t > & m_hide_action_set
Definition transition.h:98
compare_transitions_tls(const std::set< std::size_t > &hide_action_set)
Definition transition.h:101
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:105
compare_transitions_tsl(const std::set< std::size_t > &hide_action_set)
Definition transition.h:133
const std::set< std::size_t > & m_hide_action_set
Definition transition.h:130
bool operator()(const transition &t1, const transition &t2)
Definition transition.h:137
A class that can be used to store counterexample trees and.
indexed_sorted_vector_for_tau_transitions(const LTS_TYPE &aut, bool outgoing)
Definition liblts_scc.h:44
const std::vector< state_type > & get_transitions() const
Definition liblts_scc.h:96
std::vector< state_type > m_states_with_outgoing_or_incoming_tau_transition
Definition liblts_scc.h:39
std::pair< label_type, state_type > label_state_pair
indexed_sorted_vector_for_transitions(const std::vector< transition > &transitions, state_type num_states, bool outgoing)
const std::vector< CONTENT > & get_transitions() const
void swap(lts_aut_base &)
Standard swap function.
Definition lts_aut.h:48
lts_type type()
Provides the type of this lts, in casu lts_aut.
Definition lts_aut.h:42
bool operator==(const lts_aut_base &) const
Standard equality function.
Definition lts_aut.h:55
lts_type type() const
The lts_type of state_label_dot. In this case lts_dot.
Definition lts_dot.h:117
void swap(lts_dot_base &)
The standard swap function.
Definition lts_dot.h:124
void clear()
Clear the transitions system.
Definition lts_fsm.h:135
const std::vector< std::string > & state_element_values(std::size_t idx) const
Provides the vector of strings that correspond to the values of the number at position idx in a vecto...
Definition lts_fsm.h:147
std::size_t add_state_element_value(std::size_t idx, const std::string &s)
Adds a string to the state element values for the idx-th position in a state vector....
Definition lts_fsm.h:179
bool operator==(const lts_fsm_base &other) const
Definition lts_fsm.h:109
void clear_process_parameters()
Clear the state parameters for this LTS.
Definition lts_fsm.h:226
std::vector< std::vector< std::string > > m_state_element_values
state_element_values contain the values that can occur at the i-th position of a state label
Definition lts_fsm.h:100
lts_type type() const
The lts_type of this labelled transition system. In this case lts_fsm.
Definition lts_fsm.h:118
std::string state_element_value(std::size_t parameter_index, std::size_t element_index) const
Returns the element_index'th element for the parameter with index parameter_index.
Definition lts_fsm.h:194
std::pair< std::string, std::string > process_parameter(std::size_t i) const
Returns the i-th parameter of the state vectors stored in this LTS.
Definition lts_fsm.h:218
std::vector< std::pair< std::string, std::string > > m_parameters
m_parameters contain the parameters corresponding to the consecutive elements of a state vector....
Definition lts_fsm.h:105
std::vector< std::pair< std::string, std::string > > process_parameters() const
Return the parameters of the state vectors stored in this LTS.
Definition lts_fsm.h:209
void add_process_parameter(const std::string &name, const std::string &sort)
Set the state parameters for this LTS.
Definition lts_fsm.h:236
std::string state_label_to_string(const state_label_fsm &l) const
Pretty print a state value of this FSM.
Definition lts_fsm.h:157
void swap(lts_fsm_base &other)
Standard swap function.
Definition lts_fsm.h:124
a base class for lts_lts_t and probabilistic_lts_t.
Definition lts_lts.h:282
static lts_type type()
Yields the type of this lts, in this case lts_lts.
Definition lts_lts.h:311
void set_process_parameters(const data::variable_list &params)
Set the state parameters for this LTS.
Definition lts_lts.h:369
bool operator==(const lts_lts_base &other) const
Standard equality function;.
Definition lts_lts.h:294
process::action_label_list m_action_decls
Definition lts_lts.h:286
void swap(lts_lts_base &l)
Definition lts_lts.h:301
void set_action_label_declarations(const process::action_label_list &decls)
Set the action label information for this LTS.
Definition lts_lts.h:333
const data::variable & process_parameter(std::size_t i) const
Returns the i-th parameter of the state vectors stored in this LTS.
Definition lts_lts.h:356
data::data_specification m_data_spec
Definition lts_lts.h:284
const data::variable_list & process_parameters() const
Return the process parameters stored in this LTS.
Definition lts_lts.h:348
void set_data(const data::data_specification &spec)
Set the mCRL2 data specification of this LTS.
Definition lts_lts.h:341
lts_lts_base()
Default constructor.
Definition lts_lts.h:290
const process::action_label_list & action_label_declarations() const
Return action label declarations stored in this LTS.
Definition lts_lts.h:325
data::variable_list m_parameters
Definition lts_lts.h:285
This class contains an scc partitioner removing inert tau loops.
Definition liblts_scc.h:128
std::size_t get_eq_class(const std::size_t s) const
Gives the equivalence class number of a state. The equivalence class numbers range from 0 upto (and e...
Definition liblts_scc.h:339
void dfs_numbering(const state_type t, const indexed_sorted_vector_for_tau_transitions< LTS_TYPE > &src_tgt, std::vector< bool > &visited)
Definition liblts_scc.h:373
std::size_t num_eq_classes() const
Gives the number of bisimulation equivalence classes of the LTS.
Definition liblts_scc.h:333
void group_components(const state_type t, const state_type equivalence_class_index, const indexed_sorted_vector_for_tau_transitions< LTS_TYPE > &src_tgt_src, std::vector< bool > &visited)
Definition liblts_scc.h:353
scc_partitioner(LTS_TYPE &l)
Creates an scc partitioner for an LTS.
Definition liblts_scc.h:210
void replace_transition_system(const bool preserve_divergence_loops)
The lts for which this partioner is created is replaced by the lts modulo the calculated partition.
Definition liblts_scc.h:248
~scc_partitioner()=default
Destroys this partitioner.
bool in_same_class(const std::size_t s, const std::size_t t) const
Returns whether two states are in the same bisimulation equivalence class.
Definition liblts_scc.h:345
std::vector< state_type > block_index_of_a_state
Definition liblts_scc.h:194
std::vector< state_type > dfsn2state
Definition liblts_scc.h:195
A simple labelled transition format with only strings as action labels.
Definition lts_aut.h:70
void load(const std::string &filename)
Load the labelled transition system from a file.
void load(std::istream &is)
Load the labelled transition system from an input stream.
void save(const std::string &filename) const
Save the labelled transition system to file.
void swap(lts_default_base &)
Standard swap function.
Definition lts.h:53
lts_type type()
Provides the type of this lts, in casu lts_none.
Definition lts.h:47
bool operator==(const lts_default_base &) const
Standard equality function.
Definition lts.h:60
A class to contain labelled transition systems in graphviz format.
Definition lts_dot.h:137
void save(std::ostream &os) const
Save the labelled transition system to a stream.
The class lts_fsm_t contains labelled transition systems in .fsm format.
Definition lts_fsm.h:255
lts< state_label_fsm, action_label_string, detail::lts_fsm_base > super
Definition lts_fsm.h:257
void load(const std::string &filename)
Save the labelled transition system to file.
void save(const std::string &filename) const
Save the labelled transition system to file.
This class contains labelled transition systems in .lts format.
Definition lts_lts.h:385
lts_lts_t()
Creates an object containing no information.
Definition lts_lts.h:388
A class that contains a labelled transition system.
Definition lts.h:83
states_size_type num_state_labels() const
Gets the number of state labels of this LTS.
Definition lts.h:271
labels_size_type num_action_labels() const
Gets the number of action labels of this LTS.
Definition lts.h:327
states_size_type m_init_state
Definition lts.h:117
void add_transition(const transition &t)
Add a transition to the lts.
Definition lts.h:563
void set_num_action_labels(const labels_size_type n)
Sets the number of action labels of this LTS.
Definition lts.h:319
bool has_action_info() const
Checks whether this LTS has labels associated with its actions, which are numbers.
Definition lts.h:664
bool is_tau(labels_size_type action) const
Checks whether an action is a tau action.
Definition lts.h:572
std::set< labels_size_type > & hidden_label_set()
Returns the hidden label set that tells for each label what its corresponding hidden label is.
Definition lts.h:431
states_size_type num_states() const
Gets the number of states of this LTS.
Definition lts.h:245
void set_hidden_label_set(const std::set< labels_size_type > &m)
Sets the hidden label map that tells for each label what its corresponding hidden label is.
Definition lts.h:439
void clear_transitions(const std::size_t n=0)
Clear the transitions of an lts.
Definition lts.h:493
void set_num_transitions(const std::size_t n)
Sets the number of transitions of this LTS and tries to shrink the datastructure.
Definition lts.h:310
void clear_actions()
Clear the action labels of an lts.
Definition lts.h:504
void set_num_states(const states_size_type n, const bool has_state_labels=true)
Sets the number of states of this LTS.
Definition lts.h:280
void store_action_label_to_be_renamed(const ACTION_LABEL_T &a, const labels_size_type i, std::map< labels_size_type, labels_size_type > &action_rename_map)
Definition lts.h:146
void add_state_number_as_state_information()
Label each state with its state number.
Definition lts.h:521
static constexpr bool is_probabilistic_lts
An indicator that this is not a probabilistic lts.
Definition lts.h:112
bool is_probabilistic(const states_size_type state) const
Gets the label of a state.
Definition lts.h:469
ACTION_LABEL_T action_label_t
The type of action labels.
Definition lts.h:92
const std::set< labels_size_type > & hidden_label_set() const
Returns the hidden label set that tells for each label what its corresponding hidden label is.
Definition lts.h:423
std::vector< transition > m_transitions
Definition lts.h:118
std::vector< STATE_LABEL_T > & state_labels()
Provides the state labels of an LTS.
Definition lts.h:253
ACTION_LABEL_T action_label(const labels_size_type action) const
Gets the label of an action.
Definition lts.h:479
const labels_size_type tau_label_index() const
Provide the index of the label that represents tau.
Definition lts.h:385
const std::vector< ACTION_LABEL_T > & action_labels() const
The action labels in this lts.
Definition lts.h:402
bool has_state_info() const
Checks whether this LTS has state values associated with its states.
Definition lts.h:655
lts(const lts &l)
Creates a copy of the supplied LTS.
Definition lts.h:180
void set_initial_state(const states_size_type state)
Sets the initial state number of this LTS.
Definition lts.h:343
void set_action_label(const labels_size_type action, const ACTION_LABEL_T &label)
Sets the label of an action.
Definition lts.h:411
void rename_labels(const std::map< labels_size_type, labels_size_type > &action_rename_map)
Definition lts.h:127
std::set< labels_size_type > m_hidden_label_set
Definition lts.h:124
std::vector< STATE_LABEL_T >::size_type states_size_type
The sort that contains the indices of states.
Definition lts.h:100
void apply_hidden_actions(const std::vector< std::string > &tau_actions)
Rename actions in the lts by hiding the actions in the vector tau_actions.
Definition lts.h:628
labels_size_type apply_hidden_label_map(const labels_size_type action) const
If the action label is registered hidden, it returns tau, otherwise the original label.
Definition lts.h:447
std::vector< transition > & get_transitions()
Gets a reference to the vector of transitions of the current lts.
Definition lts.h:554
std::vector< ACTION_LABEL_T >::size_type labels_size_type
The sort that represents the indices of labels.
Definition lts.h:104
lts & operator=(const lts &l)
Standard assignment operator.
Definition lts.h:194
const std::vector< STATE_LABEL_T > & state_labels() const
Provides the state labels of an LTS.
Definition lts.h:261
std::vector< STATE_LABEL_T > m_state_labels
Definition lts.h:119
STATE_LABEL_T state_label(const states_size_type state) const
Gets the label of a state.
Definition lts.h:459
states_size_type add_state(const STATE_LABEL_T &label=STATE_LABEL_T())
Adds a state to this LTS.
Definition lts.h:356
STATE_LABEL_T state_label_t
The type of state labels.
Definition lts.h:88
void clear()
Clear the transitions system.
Definition lts.h:533
lts()
Creates an empty LTS.
Definition lts.h:172
bool operator==(const lts &other) const
Standard equality operator.
Definition lts.h:209
states_size_type m_nstates
Definition lts.h:116
const std::vector< transition > & get_transitions() const
Gets a const reference to the vector of transitions of the current lts.
Definition lts.h:545
LTS_BASE base_t
The type of the used lts base.
Definition lts.h:96
std::vector< transition >::size_type transitions_size_type
The sort that contains indices of transitions.
Definition lts.h:108
states_size_type initial_state() const
Gets the initial state number of this LTS.
Definition lts.h:335
void clear_state_labels()
Clear the labels of an lts.
Definition lts.h:514
void swap(lts &l)
Swap this lts with the supplied supplied LTS.
Definition lts.h:222
void record_hidden_actions(const std::vector< std::string > &tau_actions)
Records all actions with a string that occurs in tau_actions internally.
Definition lts.h:589
std::vector< ACTION_LABEL_T > m_action_labels
Definition lts.h:120
transitions_size_type num_transitions() const
Gets the number of transitions of this LTS.
Definition lts.h:302
void set_state_label(const states_size_type state, const STATE_LABEL_T &label)
Sets the label of a state.
Definition lts.h:393
labels_size_type add_action(const ACTION_LABEL_T &label)
Adds an action with a label to this LTS.
Definition lts.h:370
A simple labelled transition format with only strings as action labels.
Definition lts_aut.h:103
void load(const std::string &filename)
Load the labelled transition system from a file.
void load(std::istream &is)
Load the labelled transition system from an input stream.
void save(const std::string &filename) const
Save the labelled transition system to file.
A class to contain labelled transition systems in graphviz format.
Definition lts_dot.h:163
void save(std::ostream &os) const
Save the labelled transition system to a stream.
The class lts_fsm_t contains labelled transition systems in .fsm format.
Definition lts_fsm.h:283
probabilistic_lts< state_label_fsm, action_label_string, probabilistic_state_t, detail::lts_fsm_base > super
Definition lts_fsm.h:286
void save(const std::string &filename) const
Save the labelled transition system to file.
void load(const std::string &filename)
Save the labelled transition system to file.
This class contains probabilistic labelled transition systems in .lts format.
Definition lts_lts.h:413
probabilistic_lts_lts_t()
Creates an object containing no information.
Definition lts_lts.h:416
A class that contains a labelled transition system.
probabilistic_lts(probabilistic_lts &&other)=default
Standard move constructor.
void set_initial_probabilistic_state(const PROBABILISTIC_STATE_T &state)
Sets the probabilistic initial state number of this LTS.
const PROBABILISTIC_STATE_T & initial_probabilistic_state() const
Gets the initial state number of this LTS.
super::transitions_size_type transitions_size_type
bool operator==(const probabilistic_lts &other) const
Standard equality operator.
void swap(probabilistic_lts &other)
Swap this lts with the supplied supplied LTS.
super::states_size_type states_size_type
labels_size_type num_probabilistic_states() const
Gets the number of probabilistic states of this LTS.
static constexpr bool is_probabilistic_lts
An indicator that this is a probabilistic lts.
void clear_probabilistic_states()
Clear the probabilistic states in this probabilistic transitions system.
lts< STATE_LABEL_T, ACTION_LABEL_T, LTS_BASE > super
states_size_type add_and_reset_probabilistic_state(PROBABILISTIC_STATE_T &s)
Adds a probabilistic state to this LTS and resets the state to empty.
void set_initial_state(const states_size_type s)
void clear()
Clear the transitions system.
probabilistic_lts & operator=(probabilistic_lts &&other)=default
Standard assignment move operator.
PROBABILISTIC_STATE_T probabilistic_state_t
The type of probabilistic labels.
probabilistic_lts & operator=(const probabilistic_lts &other)=default
Standard assignment operator.
std::vector< PROBABILISTIC_STATE_T > m_probabilistic_states
probabilistic_lts(const probabilistic_lts &other)=default
Standard copy constructor.
states_size_type add_probabilistic_state(const PROBABILISTIC_STATE_T &s)
Adds a probabilistic state to this LTS.
super::state_label_t state_label_t
states_size_type initial_state() const
PROBABILISTIC_STATE_T m_init_probabilistic_state
super::labels_size_type labels_size_type
super::action_label_t action_label_t
const PROBABILISTIC_STATE_T & probabilistic_state(const states_size_type index) const
Gets the probabilistic label of an index.
probabilistic_lts()
Creates an empty LTS.
void set_probabilistic_state(const states_size_type index, const PROBABILISTIC_STATE_T &s)
Sets the probabilistic label corresponding to some index.
A class that contains a probabilistic state.
void set(const STATE &s)
Set this probabilistic state to a single state with probability one.
const_iterator begin() const
Gets an iterator over pairs of state and probability. This can only be used when the state is stored ...
void construct_internal_vector_representation()
Guarantee that this probabilistic state is internally stored as a vector, such that begin/end,...
probabilistic_state & operator=(const probabilistic_state &other)
Copy assignment constructor.
const_reverse_iterator rbegin() const
Gets a reverse iterator over pairs of state and probability. This can only be used when the state is ...
std::size_t size() const
Gets the number of probabilistic states in the vector representation of this state....
bool operator!=(const probabilistic_state &other) const
Standard equality operator.
iterator begin()
Gets an iterator over pairs of state and probability. This can only be used if the state is internall...
std::vector< state_probability_pair >::const_iterator const_iterator
STATE get() const
Get a probabilistic state if is is simple, i.e., consists of a single state.
iterator end()
Gets the end iterator over pairs of state and probability.
reverse_iterator rbegin()
Gets a reverse iterator over pairs of state and probability. This can only be used if the state is in...
std::vector< state_probability_pair > m_probabilistic_state
const_iterator end() const
Gets the end iterator over pairs of state and probability.
std::vector< state_probability_pair >::iterator iterator
void swap(probabilistic_state &other)
Swap this probabilistic state.
reverse_iterator rend()
Gets the reverse end iterator over pairs of state and probability.
std::vector< state_probability_pair >::const_reverse_iterator const_reverse_iterator
bool operator==(const probabilistic_state &other) const
Standard equality operator.
void clear()
Makes the probabilistic state empty.
probabilistic_state(const STATE_PROBABILITY_PAIR_ITERATOR begin, const STATE_PROBABILITY_PAIR_ITERATOR end)
Creates a probabilistic state on the basis of state_probability_pairs.
std::vector< state_probability_pair >::reverse_iterator reverse_iterator
probabilistic_state(const probabilistic_state &other)
Copy constructor.
void shrink_to_fit()
If a probabilistic state is ready, shrinking it to minimal size might be useful to reduce its memory ...
probabilistic_state()
Default constructor.
probabilistic_state(const STATE &s)
Constructor of a probabilistic state from a non probabilistic state.
void add(const STATE &s, const PROBABILITY &p)
Add a state with a probability to the probabilistic state.
const_reverse_iterator rend() const
Gets the reverse end iterator over pairs of state and probability.
Class for computing the signature for strong bisimulation.
Definition sigref.h:75
Class for computing the signature for branching bisimulation.
Definition sigref.h:105
Class for computing the signature for divergence preserving branching bisimulation.
Definition sigref.h:185
Signature based reductions for labelled transition systems.
Definition sigref.h:350
This class contains labels for states in dot format.
Definition lts_dot.h:37
std::string name() const
This method returns the string in the name field of a state label.
Definition lts_dot.h:64
std::string label() const
This method returns the label in the name field of a state label.
Definition lts_dot.h:78
state_label_dot()
The default constructor.
Definition lts_dot.h:46
std::string m_state_label
Definition lts_dot.h:40
bool operator==(const state_label_dot &l) const
Standard comparison operator, comparing both the string in the name field, as well as the one in the ...
Definition lts_dot.h:86
bool operator!=(const state_label_dot &l) const
Standard inequality operator. Just the negation of equality.
Definition lts_dot.h:93
Contains empty state values, used for lts's without state valued.
bool operator==(const state_label_empty &) const
static state_label_empty number_to_label(const std::size_t)
Create a state label consisting of a number as the only list element. For empty state labels this doe...
bool operator!=(const state_label_empty &other) const
state_label_empty operator+(const state_label_empty &) const
An operator to concatenate two state labels.
This class contains state labels for the fsm format.
Definition lts_fsm.h:38
state_label_fsm(const state_label_fsm &)=default
Copy constructor.
state_label_fsm & operator=(const state_label_fsm &)=default
Copy assignment.
static state_label_fsm number_to_label(const std::size_t n)
Create a state label consisting of a number as the only list element.
Definition lts_fsm.h:70
state_label_fsm()
Default constructor. The label becomes an empty vector.
Definition lts_fsm.h:42
state_label_fsm(const std::vector< std::size_t > &v)
Default constructor. The label is set to the vector v.
Definition lts_fsm.h:53
state_label_fsm operator+(const state_label_fsm &l) const
An operator to concatenate two state labels. Fsm labels cannot be concatenated. Therefore,...
Definition lts_fsm.h:59
This class contains state labels for an labelled transition system in .lts format.
Definition lts_lts.h:40
state_label_lts()
Default constructor.
Definition lts_lts.h:46
state_label_lts(const state_label_lts &)=default
Copy constructor.
state_label_lts operator+(const state_label_lts &l) const
An operator to concatenate two state labels.
Definition lts_lts.h:81
state_label_lts(const super &l)
Construct a state label out of list of balanced trees of data expressions, representing a state label...
Definition lts_lts.h:73
atermpp::term_list< lps::state > super
Definition lts_lts.h:42
state_label_lts(const lps::state &l)
Construct a state label out of a balanced tree of data expressions, representing a state label.
Definition lts_lts.h:66
state_label_lts & operator=(const state_label_lts &)=default
Copy assignment.
static state_label_lts number_to_label(const std::size_t n)
Create a state label consisting of a number as the only list element.
Definition lts_lts.h:96
state_label_lts(const CONTAINER &l)
Construct a single state label out of the elements in a container.
Definition lts_lts.h:58
A class containing triples, source label and target representing transitions.
Definition transition.h:48
void set_label(const size_type label)
Set the label of the transition.
Definition transition.h:116
transition & operator=(const transition &t)=default
Assignment.
void set_to(const size_type to)
Set the target of the transition.
Definition transition.h:123
bool operator<(const transition &t) const
Standard lexicographic ordering on transitions.
Definition transition.h:147
transition(const std::size_t f, const std::size_t l, const std::size_t t)
Constructor (there is no default constructor).
Definition transition.h:67
size_type from() const
The source of the transition.
Definition transition.h:89
transition & operator=(transition &&t)=default
Move assignment.
bool operator!=(const transition &t) const
Standard inequality on transitions.
Definition transition.h:137
size_type label() const
The label of the transition.
Definition transition.h:95
size_type to() const
The target of the transition.
Definition transition.h:102
void set_from(const size_type from)
Set the source of the transition.
Definition transition.h:109
transition(transition &&t)=default
Move constructor.
transition(const transition &t)=default
Copy constructor.
std::size_t size_type
The type of the elements in a transition.
Definition transition.h:51
bool operator==(const transition &t) const
Standard equality on transitions.
Definition transition.h:130
\brief An action label
action_label(const core::identifier_string &name, const data::sort_expression_list &sorts)
\brief Constructor Z12.
action(const action_label &label, const data::data_expression_list &arguments)
\brief Constructor Z14.
Process specification consisting of a data specification, action labels, a sequence of process equati...
\brief An untyped multi action or data application
\brief The alt operator for regular formulas
alt(const atermpp::aterm &term)
alt()
\brief Default constructor X3.
alt & operator=(alt &&) noexcept=default
const regular_formula & right() const
alt(const regular_formula &left, const regular_formula &right)
\brief Constructor Z14.
alt(const alt &) noexcept=default
Move semantics.
alt(alt &&) noexcept=default
alt & operator=(const alt &) noexcept=default
const regular_formula & left() const
regular_formula()
\brief Default constructor X3.
regular_formula(const action_formulas::action_formula &x)
\brief Constructor Z6.
regular_formula(const atermpp::aterm &term)
regular_formula(const regular_formula &) noexcept=default
Move semantics.
regular_formula(const data::data_expression &x)
\brief Constructor Z6.
regular_formula & operator=(const regular_formula &) noexcept=default
regular_formula(regular_formula &&) noexcept=default
regular_formula & operator=(regular_formula &&) noexcept=default
\brief The seq operator for regular formulas
seq(const regular_formula &left, const regular_formula &right)
\brief Constructor Z14.
const regular_formula & right() const
seq & operator=(const seq &) noexcept=default
seq(const seq &) noexcept=default
Move semantics.
const regular_formula & left() const
seq(seq &&) noexcept=default
seq()
\brief Default constructor X3.
seq & operator=(seq &&) noexcept=default
seq(const atermpp::aterm &term)
\brief The 'trans or nil' operator for regular formulas
trans_or_nil & operator=(trans_or_nil &&) noexcept=default
trans_or_nil & operator=(const trans_or_nil &) noexcept=default
trans_or_nil(const trans_or_nil &) noexcept=default
Move semantics.
trans_or_nil(const regular_formula &operand)
\brief Constructor Z14.
trans_or_nil()
\brief Default constructor X3.
trans_or_nil(trans_or_nil &&) noexcept=default
trans_or_nil(const atermpp::aterm &term)
const regular_formula & operand() const
\brief The trans operator for regular formulas
trans(const atermpp::aterm &term)
trans(trans &&) noexcept=default
const regular_formula & operand() const
trans & operator=(const trans &) noexcept=default
trans & operator=(trans &&) noexcept=default
trans()
\brief Default constructor X3.
trans(const trans &) noexcept=default
Move semantics.
trans(const regular_formula &operand)
\brief Constructor Z14.
\brief An untyped regular formula or action formula
untyped_regular_formula()
\brief Default constructor X3.
untyped_regular_formula & operator=(untyped_regular_formula &&) noexcept=default
untyped_regular_formula & operator=(const untyped_regular_formula &) noexcept=default
untyped_regular_formula(const std::string &name, const regular_formula &left, const regular_formula &right)
\brief Constructor Z2.
untyped_regular_formula(const core::identifier_string &name, const regular_formula &left, const regular_formula &right)
\brief Constructor Z14.
const core::identifier_string & name() const
untyped_regular_formula(const untyped_regular_formula &) noexcept=default
Move semantics.
untyped_regular_formula(untyped_regular_formula &&) noexcept=default
Standard exception class for reporting runtime errors.
Definition exception.h:27
\brief The and operator for state formulas
and_(and_ &&) noexcept=default
const state_formula & right() const
and_(const atermpp::aterm &term)
and_(const and_ &) noexcept=default
Move semantics.
and_(const state_formula &left, const state_formula &right)
\brief Constructor Z14.
and_ & operator=(const and_ &) noexcept=default
and_()
\brief Default constructor X3.
and_ & operator=(and_ &&) noexcept=default
const state_formula & left() const
\brief The multiply operator for state formulas with values
const_multiply_alt & operator=(const const_multiply_alt &) noexcept=default
const state_formula & left() const
const_multiply_alt(const state_formula &left, const data::data_expression &right)
\brief Constructor Z14.
const_multiply_alt(const const_multiply_alt &) noexcept=default
Move semantics.
const_multiply_alt(const_multiply_alt &&) noexcept=default
const data::data_expression & right() const
const_multiply_alt(const atermpp::aterm &term)
const_multiply_alt & operator=(const_multiply_alt &&) noexcept=default
const_multiply_alt()
\brief Default constructor X3.
\brief The multiply operator for state formulas with values
const data::data_expression & left() const
const_multiply(const const_multiply &) noexcept=default
Move semantics.
const_multiply(const data::data_expression &left, const state_formula &right)
\brief Constructor Z14.
const_multiply()
\brief Default constructor X3.
const_multiply(const_multiply &&) noexcept=default
const_multiply & operator=(const const_multiply &) noexcept=default
const_multiply & operator=(const_multiply &&) noexcept=default
const_multiply(const atermpp::aterm &term)
const state_formula & right() const
\brief The timed delay operator for state formulas
delay_timed(const atermpp::aterm &term)
delay_timed()
\brief Default constructor X3.
delay_timed & operator=(const delay_timed &) noexcept=default
const data::data_expression & time_stamp() const
delay_timed(const data::data_expression &time_stamp)
\brief Constructor Z14.
delay_timed(const delay_timed &) noexcept=default
Move semantics.
delay_timed(delay_timed &&) noexcept=default
delay_timed & operator=(delay_timed &&) noexcept=default
\brief The delay operator for state formulas
delay & operator=(delay &&) noexcept=default
delay()
\brief Default constructor X3.
delay(const delay &) noexcept=default
Move semantics.
delay(delay &&) noexcept=default
delay(const atermpp::aterm &term)
delay & operator=(const delay &) noexcept=default
\brief The existential quantification operator for state formulas
exists(const data::variable_list &variables, const state_formula &body)
\brief Constructor Z14.
const state_formula & body() const
exists(const exists &) noexcept=default
Move semantics.
exists(exists &&) noexcept=default
exists & operator=(const exists &) noexcept=default
exists & operator=(exists &&) noexcept=default
exists()
\brief Default constructor X3.
exists(const atermpp::aterm &term)
const data::variable_list & variables() const
\brief The value false for state formulas
false_(false_ &&) noexcept=default
false_ & operator=(const false_ &) noexcept=default
false_ & operator=(false_ &&) noexcept=default
false_(const atermpp::aterm &term)
false_(const false_ &) noexcept=default
Move semantics.
false_()
\brief Default constructor X3.
\brief The universal quantification operator for state formulas
const state_formula & body() const
forall(const atermpp::aterm &term)
const data::variable_list & variables() const
forall & operator=(const forall &) noexcept=default
forall & operator=(forall &&) noexcept=default
forall(const forall &) noexcept=default
Move semantics.
forall(const data::variable_list &variables, const state_formula &body)
\brief Constructor Z14.
forall(forall &&) noexcept=default
forall()
\brief Default constructor X3.
\brief The implication operator for state formulas
imp()
\brief Default constructor X3.
imp(imp &&) noexcept=default
imp(const state_formula &left, const state_formula &right)
\brief Constructor Z14.
imp & operator=(const imp &) noexcept=default
const state_formula & left() const
const state_formula & right() const
imp(const atermpp::aterm &term)
imp(const imp &) noexcept=default
Move semantics.
imp & operator=(imp &&) noexcept=default
\brief The infimum over a data type for state formulas
infimum(const infimum &) noexcept=default
Move semantics.
infimum()
\brief Default constructor X3.
infimum(const data::variable_list &variables, const state_formula &body)
\brief Constructor Z14.
infimum & operator=(infimum &&) noexcept=default
const data::variable_list & variables() const
const state_formula & body() const
infimum(const atermpp::aterm &term)
infimum(infimum &&) noexcept=default
infimum & operator=(const infimum &) noexcept=default
\brief The may operator for state formulas
const state_formula & operand() const
may()
\brief Default constructor X3.
const regular_formulas::regular_formula & formula() const
may & operator=(const may &) noexcept=default
may & operator=(may &&) noexcept=default
may(const regular_formulas::regular_formula &formula, const state_formula &operand)
\brief Constructor Z14.
may(may &&) noexcept=default
may(const atermpp::aterm &term)
may(const may &) noexcept=default
Move semantics.
\brief The minus operator for state formulas
minus & operator=(minus &&) noexcept=default
minus(minus &&) noexcept=default
minus(const minus &) noexcept=default
Move semantics.
minus(const atermpp::aterm &term)
minus(const state_formula &operand)
\brief Constructor Z14.
const state_formula & operand() const
minus & operator=(const minus &) noexcept=default
minus()
\brief Default constructor X3.
\brief The mu operator for state formulas
const core::identifier_string & name() const
const data::assignment_list & assignments() const
mu(const mu &) noexcept=default
Move semantics.
mu(const std::string &name, const data::assignment_list &assignments, const state_formula &operand)
\brief Constructor Z2.
mu(const core::identifier_string &name, const data::assignment_list &assignments, const state_formula &operand)
\brief Constructor Z14.
mu & operator=(const mu &) noexcept=default
mu(mu &&) noexcept=default
mu & operator=(mu &&) noexcept=default
mu(const atermpp::aterm &term)
mu()
\brief Default constructor X3.
const state_formula & operand() const
\brief The must operator for state formulas
must(must &&) noexcept=default
must & operator=(must &&) noexcept=default
must(const atermpp::aterm &term)
must(const regular_formulas::regular_formula &formula, const state_formula &operand)
\brief Constructor Z14.
const regular_formulas::regular_formula & formula() const
must(const must &) noexcept=default
Move semantics.
const state_formula & operand() const
must()
\brief Default constructor X3.
must & operator=(const must &) noexcept=default
\brief The not operator for state formulas
not_(not_ &&) noexcept=default
not_(const not_ &) noexcept=default
Move semantics.
not_ & operator=(const not_ &) noexcept=default
not_ & operator=(not_ &&) noexcept=default
not_()
\brief Default constructor X3.
not_(const atermpp::aterm &term)
const state_formula & operand() const
not_(const state_formula &operand)
\brief Constructor Z14.
\brief The nu operator for state formulas
nu(const atermpp::aterm &term)
nu(nu &&) noexcept=default
nu(const core::identifier_string &name, const data::assignment_list &assignments, const state_formula &operand)
\brief Constructor Z14.
nu()
\brief Default constructor X3.
nu & operator=(const nu &) noexcept=default
nu & operator=(nu &&) noexcept=default
const core::identifier_string & name() const
nu(const std::string &name, const data::assignment_list &assignments, const state_formula &operand)
\brief Constructor Z2.
const state_formula & operand() const
nu(const nu &) noexcept=default
Move semantics.
const data::assignment_list & assignments() const
\brief The or operator for state formulas
or_(or_ &&) noexcept=default
or_()
\brief Default constructor X3.
or_(const or_ &) noexcept=default
Move semantics.
or_(const state_formula &left, const state_formula &right)
\brief Constructor Z14.
or_ & operator=(const or_ &) noexcept=default
const state_formula & right() const
or_ & operator=(or_ &&) noexcept=default
or_(const atermpp::aterm &term)
const state_formula & left() const
\brief The plus operator for state formulas with values
plus & operator=(plus &&) noexcept=default
plus & operator=(const plus &) noexcept=default
plus(const plus &) noexcept=default
Move semantics.
const state_formula & left() const
plus(const atermpp::aterm &term)
plus()
\brief Default constructor X3.
const state_formula & right() const
plus(plus &&) noexcept=default
plus(const state_formula &left, const state_formula &right)
\brief Constructor Z14.
state_formula(const state_formula &) noexcept=default
Move semantics.
state_formula()
\brief Default constructor X3.
state_formula(state_formula &&) noexcept=default
bool has_time() const
Returns true if the formula is timed.
state_formula(const data::untyped_data_parameter &x)
\brief Constructor Z6.
state_formula & operator=(state_formula &&) noexcept=default
state_formula(const data::data_expression &x)
\brief Constructor Z6.
state_formula(const atermpp::aterm &term)
state_formula & operator=(const state_formula &) noexcept=default
\brief The sum over a data type for state formulas
sum(const sum &) noexcept=default
Move semantics.
sum(sum &&) noexcept=default
sum(const atermpp::aterm &term)
sum(const data::variable_list &variables, const state_formula &body)
\brief Constructor Z14.
sum & operator=(sum &&) noexcept=default
sum()
\brief Default constructor X3.
const data::variable_list & variables() const
const state_formula & body() const
sum & operator=(const sum &) noexcept=default
\brief The supremum over a data type for state formulas
supremum & operator=(supremum &&) noexcept=default
supremum(supremum &&) noexcept=default
supremum(const atermpp::aterm &term)
supremum()
\brief Default constructor X3.
supremum(const supremum &) noexcept=default
Move semantics.
supremum & operator=(const supremum &) noexcept=default
const state_formula & body() const
const data::variable_list & variables() const
supremum(const data::variable_list &variables, const state_formula &body)
\brief Constructor Z14.
\brief The value true for state formulas
true_()
\brief Default constructor X3.
true_ & operator=(const true_ &) noexcept=default
true_(true_ &&) noexcept=default
true_(const true_ &) noexcept=default
Move semantics.
true_(const atermpp::aterm &term)
true_ & operator=(true_ &&) noexcept=default
\brief The state formula variable
variable & operator=(const variable &) noexcept=default
variable(const core::identifier_string &name, const data::data_expression_list &arguments)
\brief Constructor Z14.
variable(const variable &) noexcept=default
Move semantics.
variable(const std::string &name, const data::data_expression_list &arguments)
\brief Constructor Z2.
variable()
\brief Default constructor X3.
variable & operator=(variable &&) noexcept=default
const core::identifier_string & name() const
const data::data_expression_list & arguments() const
variable(variable &&) noexcept=default
variable(const atermpp::aterm &term)
\brief The timed yaled operator for state formulas
yaled_timed(yaled_timed &&) noexcept=default
yaled_timed & operator=(const yaled_timed &) noexcept=default
yaled_timed()
\brief Default constructor X3.
yaled_timed & operator=(yaled_timed &&) noexcept=default
yaled_timed(const yaled_timed &) noexcept=default
Move semantics.
yaled_timed(const data::data_expression &time_stamp)
\brief Constructor Z14.
yaled_timed(const atermpp::aterm &term)
const data::data_expression & time_stamp() const
\brief The yaled operator for state formulas
yaled()
\brief Default constructor X3.
yaled(const atermpp::aterm &term)
yaled & operator=(const yaled &) noexcept=default
yaled(const yaled &) noexcept=default
Move semantics.
yaled(yaled &&) noexcept=default
yaled & operator=(yaled &&) noexcept=default
void div_mod(const big_natural_number &other, big_natural_number &result, big_natural_number &remainder, big_natural_number &calculation_buffer_divisor) const
bool is_number(std::size_t n) const
Returns whether this number equals a number of std::size_t.
big_natural_number(const std::size_t n)
big_natural_number operator*(const big_natural_number &other) const
operator std::size_t() const
Transforms this number to a std::size_t, provided it is sufficiently small. If not an mcrl2::runtime_...
bool operator>=(const big_natural_number &other) const
std::size_t operator[](const std::size_t n) const
Give the n-th digit where the most significant digit is positioned last.
void add(const big_natural_number &other)
std::vector< std::size_t > m_number
big_natural_number operator+(const big_natural_number &other) const
bool operator==(const big_natural_number &other) const
std::size_t divide_by(std::size_t n)
big_natural_number operator%(const big_natural_number &other) const
std::size_t size() const
Give the number of digits in this big number.
big_natural_number operator-(const big_natural_number &other) const
friend void swap(big_natural_number &x, big_natural_number &y)
Standard overload of swap.
bool operator<=(const big_natural_number &other) const
big_natural_number operator/(const big_natural_number &other) const
void clear()
Sets the number to zero.
bool operator!=(const big_natural_number &other) const
bool is_zero() const
Returns whether this number equals zero.
void multiply(const big_natural_number &other, big_natural_number &result, big_natural_number &calculation_buffer_for_multiplicand) const
void push_back(const std::size_t n)
bool operator<(const big_natural_number &other) const
void subtract(const big_natural_number &other)
bool operator>(const big_natural_number &other) const
void multiply_by(std::size_t n, std::size_t carry)
This class contains labels for probabilistic transistions, consisting of a numerator and a denominato...
probabilistic_arbitrary_precision_fraction operator*(const probabilistic_arbitrary_precision_fraction &other) const
static void greatest_common_divisor_destructive(utilities::big_natural_number &x, utilities::big_natural_number &y, utilities::big_natural_number &buffer_divide, utilities::big_natural_number &buffer_remainder, utilities::big_natural_number &buffer)
bool operator>=(const probabilistic_arbitrary_precision_fraction &other) const
static void remove_common_factors(utilities::big_natural_number &enumerator, utilities::big_natural_number &denominator)
bool operator!=(const probabilistic_arbitrary_precision_fraction &other) const
probabilistic_arbitrary_precision_fraction operator+(const probabilistic_arbitrary_precision_fraction &other) const
bool operator<(const probabilistic_arbitrary_precision_fraction &other) const
bool operator>(const probabilistic_arbitrary_precision_fraction &other) const
static probabilistic_arbitrary_precision_fraction & one()
Constant one.
static utilities::big_natural_number greatest_common_divisor(utilities::big_natural_number x, utilities::big_natural_number y)
probabilistic_arbitrary_precision_fraction operator-(const probabilistic_arbitrary_precision_fraction &other) const
static probabilistic_arbitrary_precision_fraction & zero()
Constant zero.
probabilistic_arbitrary_precision_fraction operator/(const probabilistic_arbitrary_precision_fraction &other) const
bool operator<=(const probabilistic_arbitrary_precision_fraction &other) const
probabilistic_arbitrary_precision_fraction(const utilities::big_natural_number &enumerator, const utilities::big_natural_number &denominator)
bool operator==(const probabilistic_arbitrary_precision_fraction &other) const
bool bisimulation_compare_gjkw(const LTS_TYPE &l1, const LTS_TYPE &l2, bool branching=false, bool preserve_divergence=false)
Checks whether the two initial states of two LTSs are strong or branching bisimilar.
void bisimulation_reduce_gjkw(LTS_TYPE &l, bool branching=false, bool preserve_divergence=false)
Reduce transition system l with respect to strong or (divergence preserving) branching bisimulation.
bool destructive_bisimulation_compare_gjkw(LTS_TYPE &l1, LTS_TYPE &l2, bool branching=false, bool preserve_divergence=false, bool generate_counter_examples=false)
Checks whether the two initial states of two LTSs are strong or branching bisimilar.
bool destructive_bisimulation_compare_gjkw(LTS_TYPE &l1, LTS_TYPE &l2, bool branching, bool preserve_divergence, bool generate_counter_examples, const std::string &, bool)
std::string debug_id() const
print a B_to_C slice identification for debugging
std::string debug_id() const
print a constellation identification for debugging
std::string debug_id() const
print a block identification for debugging
bisim_partitioner_gjkw(LTS_TYPE &l, bool branching=false, bool preserve_divergence=false)
check_complexity::trans_counter_t work_counter
succ_iter_t change_to_C(pred_iter_t pred_iter, ONLY_IF_DEBUG(constln_t *SpC, constln_t *NewC,) bool first_transition_of_state, bool first_transition_of_block)
transition target is moved to a new constellation
block_t * refinable_next
next block in the list of refinable blocks
static permutation_const_iter_t permutation_begin()
provide an iterator to the beginning of the permutation array
permutation_const_iter_t end() const
iterator past the last state in the block
void split_inert_to_C(block_t *B)
handle the transitions from the splitter to its own constellation
void set_inert_begin(B_to_C_iter_t new_inert_begin)
void replace_transition_system(bool branching, bool preserve_divergence)
void assert_stability(const part_state_t &part_st) const
assert that the data structure is consistent and stable
bisim_gjkw::bisim_partitioner_gjkw_initialise_helper< LTS_TYPE > init_helper
const constln_t * to_constln() const
compute the goal constellation of the transitions in this slice
static state_type nr_of_blocks
total number of blocks with unique sequence number allocated
state_type unmarked_bottom_size() const
provides the number of unmarked bottom states in the block
void replace_transition_system(const part_state_t &part_st, ONLY_IF_DEBUG(bool branching,) bool preserve_divergence)
Replaces the transition relation of the current LTS by the transitions of the bisimulation-reduced tr...
bool split_s_inert_out(state_info_ptr s ONLY_IF_DEBUG(, constln_t *OldC))
Split outgoing transitions of a state in the splitter.
void set_marked_bottom_begin(permutation_iter_t new_marked_bottom_begin)
void SetFromRed(B_to_C_desc_iter_t new_fromred)
set FromRed to an existing element in to_constln
void make_nontrivial()
makes a constellation non-trivial (i. e. inserts it into the respective list)
permutation_const_iter_t nonbottom_begin() const
iterator to the first non-bottom state in the block
pred_const_iter_t inert_pred_end() const
iterator one past the last inert incoming transition
const block_t * from_block() const
compute the source block of the transitions in this slice
bisim_gjkw::block_t * refine(bisim_gjkw::block_t *RfnB, const bisim_gjkw::constln_t *SpC, const bisim_gjkw::B_to_C_descriptor *FromRed, bool postprocessing, const bisim_gjkw::constln_t *NewC=nullptr)
refine a block into the part that can reach SpC and the part that cannot
block_t * split_off_small_block()
split off a single block from the constellation
bisim_gjkw::block_t * postprocess_new_bottom(bisim_gjkw::block_t *RedB)
Split a block with new bottom states as needed.
B_to_C_iter_t postprocess_begin
iterator to the first transition into this constellation that needs postprocessing
bool add_work_to_bottom_transns(enum check_complexity::counter_type ctr, unsigned max_value)
bool operator>(const constln_t &other) const
const constln_t * constln() const
get constellation where the state belongs
permutation_const_iter_t begin() const
iterator to the first state in the block
void make_trivial()
makes a constellation trivial (i. e. removes it from the respective list)
void swap_B_to_C(succ_iter_t const pos1, succ_iter_t const pos2)
succ_iter_t state_inert_out_end
iterator past the last inert outgoing transition
B_to_C_descriptor(B_to_C_iter_t begin_, B_to_C_iter_t end_)
bool surely_has_no_transition_to(const constln_t *SpC) const
bool needs_postprocessing() const
returns true iff the slice is marked for postprocessing
void set_begin(permutation_iter_t new_begin)
permutation_const_iter_t marked_nonbottom_begin() const
iterator to the first marked non-bottom state in the block
permutation_iter_t pos
position of the state in the permutation array
state_type get_eq_class(state_type s) const
static constln_t * nontrivial_first
first constellation in the list of non-trivial constellations
bool operator>=(const constln_t &other) const
permutation_const_iter_t end() const
constant iterator past the last state in the constellation
permutation_const_iter_t unmarked_bottom_begin() const
iterator to the first unmarked bottom state in the block
void set_inert_begin_and_end(B_to_C_iter_t new_inert_begin, B_to_C_iter_t new_inert_end)
B_to_C_const_iter_t inert_begin() const
iterator to the first inert transition of the block
void make_nonrefinable()
makes a block non-refinable (i. e. removes it from the respective list)
permutation_const_iter_t marked_bottom_begin() const
iterator to the first marked bottom state in the block
pred_const_iter_t noninert_pred_begin() const
iterator to first non-inert incoming transition
bool mark_nonbottom(state_info_ptr s)
mark a non-bottom state
permutation_const_iter_t bottom_begin() const
iterator to the first bottom state in the block
std::string debug_id_short() const
print a short state identification for debugging
permutation_iter_t int_marked_bottom_begin
iterator to the first marked bottom state of the block
void swap_out(pred_iter_t const pos1, pred_iter_t const pos2)
state_type size() const
provides the number of states in the block
void init_transitions(part_state_t &part_st, part_trans_t &part_tr, bool branching, bool preserve_divergence)
initialise the state in part_st and the transitions in part_tr
permutation_iter_t int_begin
iterator to the first state in the constellation
succ_iter_t int_current_constln
iterator to first outgoing transition to the constellation of interest
block_t * split_off_blue(permutation_iter_t blue_nonbottom_end)
refine the block (the blue subblock is smaller)
permutation_iter_t int_bottom_begin
iterator to the first bottom state of the block
permutation_const_iter_t marked_bottom_end() const
iterator past the last marked bottom state in the block
void set_slice_begin_or_before_end(succ_iter_t new_value)
state_type notblue
number of inert transitions to non-blue states
permutation_const_iter_t marked_nonbottom_end() const
iterator one past the last marked non-bottom state in the block
check_complexity::state_counter_t work_counter
const state_type sort_key
sort key to order constellation-related information
state_type marked_bottom_size() const
provides the number of marked bottom states in the block
bool is_trivial() const
returns true iff the constellation is trivial
permutation_iter_t int_marked_nonbottom_begin
iterator to the first marked non-bottom state of the block
void make_noninert(succ_iter_t const succ_iter)
permutation_iter_t int_end
iterator past the last state of the block
B_to_C_desc_list to_constln
list of B_to_C with transitions from this block
state_type get_nr_of_states() const
provides the number of states in the Kripke structure
static block_t * get_some_refinable()
provides an arbitrary refinable block
static succ_iter_t slice_end(succ_iter_t this_)
block_t * block
block where the state belongs
void create_initial_partition_gjkw(bool branching, bool preserve_divergence)
const constln_t * get_nontrivial_next() const
provides the next non-trivial constellation
void set_constln(constln_t *new_constln)
void set_marked_nonbottom_begin(permutation_iter_t new_marked_nonbottom_begin)
#define BLOCK_NO_SEQNR
static permutation_const_iter_t perm_begin
bool operator<(const constln_t &other) const
compares two constellations for ordering them
bool make_refinable()
makes a block refinable (i. e. inserts it into the respective list)
fixed_vector< state_info_entry > state_info
array with all other information about states
state_type state_size() const
provide size of state space
state_type size() const
returns number of states in the constellation
B_to_C_const_iter_t inert_end() const
iterator past the last inert transition of the block
const constln_t * constln() const
constellation where the block belongs to
constln_t * int_constln
constellation to which the block belongs
permutation_const_iter_t bottom_end() const
iterator past the last bottom state in the block
static block_t * refinable_first
first block in the list of refinable blocks
static void slice_add_work_to_transns(succ_const_iter_t this_, enum check_complexity::counter_type ctr, unsigned max_value)
void swap_in(B_to_C_iter_t const pos1, B_to_C_iter_t const pos2)
state_type int_seqnr
unique sequence number of this block
succ_const_iter_t succ_begin() const
iterator to first outgoing transition
bool operator<=(const constln_t &other) const
void new_red_block_created(block_t *OldB, block_t *NewB, bool postprocessing)
handle B_to_C slices after a new red block has been created
void set_end(permutation_iter_t new_end)
void print_trans() const
print all transitions
bool is_refinable() const
checks whether the block is refinable
block_t * split_off_red(permutation_iter_t red_nonbottom_begin)
refine the block (the red subblock is smaller)
succ_iter_t int_slice_begin_or_before_end
points to the last or the first transition to the same constellation
succ_const_iter_t inert_succ_begin() const
iterator to first inert outgoing transition
permutation_const_iter_t begin() const
constant iterator to the first state in the constellation
pred_iter_t state_in_begin
iterator to first incoming transition
permutation_iter_t int_begin
iterator to the first state of the block
pred_iter_t state_inert_in_begin
iterator to first inert incoming transition
void assign_seqnr()
assigns a unique sequence number
std::unordered_map< Key, state_type, KeyHasher > extra_kripke_states
void new_blue_block_created(block_t *OldB, block_t *NewB)
handle B_to_C slices after a new blue block has been created
permutation_iter_t begin()
iterator to the first state in the constellation
static state_info_const_ptr s_i_begin
pointer at the first entry in the state_info array
check_complexity::B_to_C_counter_t work_counter
static constln_t * get_some_nontrivial()
provides an arbitrary non-trivial constellation
permutation_const_iter_t unmarked_bottom_end() const
iterator past the last unmarked bottom state in the block
bool operator<(const block_t &other) const
compares two blocks for ordering them
void set_bottom_begin(permutation_iter_t new_bottom_begin)
void set_nonbottom_end(permutation_iter_t new_nonbottom_end)
std::string debug_id() const
print a state identification for debugging
void set_inert_pred_begin(pred_iter_t new_inert_in_begin)
void clear()
deallocates constellations and blocks
constln_t(state_type sort_key_, permutation_iter_t begin_, permutation_iter_t end_, B_to_C_iter_t postprocess_none)
constructor
void set_inert_succ_begin_and_end(succ_iter_t new_inert_out_begin, succ_iter_t new_inert_out_end)
void swap3_B_to_C(succ_iter_t const pos1, succ_iter_t const pos2, succ_iter_t const pos3)
block_t(constln_t *const constln_, permutation_iter_t const begin_, permutation_iter_t const end_)
constructor
const block_t * block(state_type s) const
find block of a state
succ_const_iter_t inert_succ_end() const
iterator past the last inert outgoing transition
permutation_iter_t end()
iterator past the last state in the constellation
succ_const_iter_t slice_begin_or_before_end() const
check_complexity::block_counter_t work_counter
bisim_partitioner_gjkw_initialise_helper(LTS_TYPE &l, bool branching, bool preserve_divergence)
constructor of the helper class
succ_iter_t state_out_begin
iterator to first outgoing transition
pred_const_iter_t inert_pred_begin() const
iterator to first inert incoming transition
std::string debug_id() const
print a transition identification for debugging
B_to_C_iter_t int_inert_begin
iterator to the first inert transition of the block
void set_unmarked_bottom_end(permutation_iter_t new_unmarked_bottom_end)
pred_const_iter_t noninert_pred_end() const
iterator past the last non-inert incoming transition
void set_inert_end(B_to_C_iter_t new_inert_end)
pred_const_iter_t pred_end() const
iterator past the last incoming transition
constln_t * nontrivial_next
next constellation in the list of non-trivial constellations
bool in_same_class(state_type s, state_type t) const
B_to_C_iter_t int_inert_end
iterator past the last inert transition of the block
static state_info_const_ptr s_i_end
pointer past the last actual entry in the state_info array
permutation_const_iter_t unmarked_nonbottom_begin() const
iterator to the first unmarked non-bottom state in the block
void set_end(permutation_iter_t new_end)
set the iterator past the last state in the constellation
permutation_const_iter_t nonbottom_end() const
iterator past the last non-bottom state in the block
succ_const_iter_t succ_end() const
iterator past the last outgoing transition
void set_unmarked_nonbottom_end(permutation_iter_t new_unmarked_nonbottom_end)
B_to_C_descriptor * FromRed(const constln_t *SpC)
read FromRed
std::string debug_id_short() const
print a short transition identification for debugging
permutation_const_iter_t unmarked_nonbottom_end() const
iterator past the last unmarked non-bottom state in the block
void print_block(const char *message, const block_t *B, permutation_const_iter_t begin, permutation_const_iter_t end) const
print a slice of the partition (typically a block)
state_type marked_size() const
provides the number of marked states in the block
B_to_C_iter_t postprocess_end
iterator past the last transition into this constellation that needs postprocessing
trans_type get_nr_of_transitions() const
provides the number of transitions in the Kripke structure
void print_part(const part_trans_t &part_tr) const
print the partition as a tree (per constellation and block)
permutation_iter_t int_end
iterator past the last state in the constellation
void set_current_constln(succ_iter_t const new_current_constln)
void set_inert_succ_begin(succ_iter_t const new_inert_out_begin)
bool mark(state_info_ptr s)
mark a state
succ_iter_t state_inert_out_begin
iterator to first inert outgoing transition
pred_const_iter_t pred_begin() const
iterator to first incoming transition
bool surely_has_transition_to(const constln_t *SpC) const
void set_begin(permutation_iter_t new_begin)
set the iterator to the first state in the constellation
#define PRINT_SG_PL(counter, sg_string, pl_string)
#define ONLY_IF_DEBUG(...)
include something in Debug mode
#define PRINT_INT_PERCENTAGE(num, denom)
#define INIT_WITHOUT_BLC_SETS
#define min_above_pivot
#define abort_if_non_bottom_size_too_large_NewBotSt(i)
#define bottom_size(coroutine)
#define linked_list
#define new_start_bottom_states(idx)
#define new_end_bottom_states(idx)
#define abort_if_size_too_large(coroutine, i)
#define non_bottom_states_NewBotSt
#define new_end_bottom_states_NewBotSt
#define abort_if_bottom_size_too_large(coroutine)
#define max_below_pivot
#define bottom_and_non_bottom_size(coroutine)
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:391
#define BRANCH_BIS_EXPERIMENT_JFG
global_function_symbol g_tree_node("@node@", 2)
global_function_symbol g_empty("@empty@", 0)
global_function_symbol g_single_tree_node("@single_node@", 1)
The main namespace for the aterm++ library.
Definition algorithm.h:21
term_balanced_tree< aterm > aterm_balanced_tree
A term_balanced_tree with elements of type aterm.
void make_term_balanced_tree(term_balanced_tree< Term > &result, ForwardTraversalIterator p, const std::size_t size, Transformer transformer)
bool is_aterm_balanced_tree(const aterm &t)
void make_exists(atermpp::aterm &t, const ARGUMENTS &... args)
std::vector< action_formula > action_formula_vector
\brief vector of action_formulas
bool is_at(const atermpp::aterm &x)
void swap(multi_action &t1, multi_action &t2)
\brief swap overload
atermpp::term_list< action_formula > action_formula_list
\brief list of action_formulas
void swap(true_ &t1, true_ &t2)
\brief swap overload
std::string pp(const action_formulas::action_formula &x)
void swap(false_ &t1, false_ &t2)
\brief swap overload
void make_not_(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const action_formulas::exists &x)
void swap(action_formula &t1, action_formula &t2)
\brief swap overload
std::string pp(const action_formulas::and_ &x)
std::set< data::variable > find_all_variables(const action_formulas::action_formula &x)
std::string pp(const action_formulas::at &x)
void swap(not_ &t1, not_ &t2)
\brief swap overload
bool is_or(const atermpp::aterm &x)
void swap(exists &t1, exists &t2)
\brief swap overload
bool is_true(const atermpp::aterm &x)
bool is_forall(const atermpp::aterm &x)
std::string pp(const action_formulas::imp &x)
void make_and_(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_false(const atermpp::aterm &x)
bool is_not(const atermpp::aterm &x)
std::string pp(const action_formulas::multi_action &x)
std::string pp(const action_formulas::or_ &x)
void make_imp(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const action_formulas::forall &x)
bool is_imp(const atermpp::aterm &x)
bool is_and(const atermpp::aterm &x)
void make_forall(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(at &t1, at &t2)
\brief swap overload
void swap(imp &t1, imp &t2)
\brief swap overload
void make_or_(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(forall &t1, forall &t2)
\brief swap overload
std::string pp(const action_formulas::not_ &x)
std::string pp(const action_formulas::false_ &x)
void make_multi_action(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(or_ &t1, or_ &t2)
\brief swap overload
bool is_multi_action(const atermpp::aterm &x)
void swap(and_ &t1, and_ &t2)
\brief swap overload
std::string pp(const action_formulas::true_ &x)
void make_at(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_exists(const atermpp::aterm &x)
bool is_action_formula(const atermpp::aterm &x)
const atermpp::function_symbol & function_symbol_StateImp()
const atermpp::function_symbol & function_symbol_StateMinus()
const atermpp::function_symbol & function_symbol_ActForall()
const atermpp::function_symbol & function_symbol_StateForall()
const atermpp::function_symbol & function_symbol_StateYaledTimed()
const atermpp::function_symbol & function_symbol_RegSeq()
const atermpp::function_symbol & function_symbol_StateNu()
const atermpp::function_symbol & function_symbol_StateConstantMultiplyAlt()
const atermpp::function_symbol & function_symbol_StateExists()
const atermpp::function_symbol & function_symbol_StateVar()
const atermpp::function_symbol & function_symbol_ActMultAct()
const atermpp::function_symbol & function_symbol_StateNot()
const atermpp::function_symbol & function_symbol_ActImp()
const atermpp::function_symbol & function_symbol_StateSum()
const atermpp::function_symbol & function_symbol_ActAt()
const atermpp::function_symbol & function_symbol_StateMu()
const atermpp::function_symbol & function_symbol_RegTransOrNil()
const atermpp::function_symbol & function_symbol_StateMay()
const atermpp::function_symbol & function_symbol_StatePlus()
const atermpp::function_symbol & function_symbol_RegTrans()
const atermpp::function_symbol & function_symbol_RegAlt()
const atermpp::function_symbol & function_symbol_ActAnd()
const atermpp::function_symbol & function_symbol_StateDelayTimed()
const atermpp::function_symbol & function_symbol_StateConstantMultiply()
const atermpp::function_symbol & function_symbol_StateOr()
const atermpp::function_symbol & function_symbol_ActOr()
const atermpp::function_symbol & function_symbol_StateAnd()
const atermpp::function_symbol & function_symbol_StateInfimum()
const atermpp::function_symbol & function_symbol_ActNot()
const atermpp::function_symbol & function_symbol_ActExists()
const atermpp::function_symbol & function_symbol_UntypedRegFrm()
const atermpp::function_symbol & function_symbol_StateSupremum()
const atermpp::function_symbol & function_symbol_StateMust()
atermpp::aterm_string identifier_string
String type of the LPS library. Identifier strings are represented internally as ATerms.
static data_specification const & default_specification()
Definition parse.h:31
Namespace for system defined sort bool_.
Definition bool.h:32
const function_symbol & false_()
Constructor for function symbol false.
Definition bool.h:109
const function_symbol & true_()
Constructor for function symbol true.
Definition bool.h:77
Namespace for system defined sort int_.
application cint(const data_expression &arg0)
Application of function symbol @cInt.
Definition int1.h:104
const basic_sort & int_()
Constructor for sort expression Int.
Definition int1.h:47
Namespace for system defined sort nat.
const basic_sort & nat()
Constructor for sort expression Nat.
Definition nat1.h:46
application cnat(const data_expression &arg0)
Application of function symbol @cNat.
Definition nat1.h:164
data_expression nat(const std::string &n)
Constructs expression of type Nat from a string.
Namespace for system defined sort pos.
const function_symbol & c1()
Constructor for function symbol @c1.
Definition pos1.h:78
data_expression pos(const std::string &n)
Constructs expression of type Pos from a string.
const basic_sort & pos()
Constructor for sort expression Pos.
Definition pos1.h:45
Namespace for system defined sort real_.
data_expression & real_one()
application creal(const data_expression &arg0, const data_expression &arg1)
Application of function symbol @cReal.
Definition real1.h:132
data_expression & real_zero()
const basic_sort & real_()
Constructor for sort expression Real.
Definition real1.h:48
application plus(const data_expression &arg0, const data_expression &arg1)
Application of function symbol +.
Definition real1.h:1115
application minus(const data_expression &arg0, const data_expression &arg1)
Application of function symbol -.
Definition real1.h:1200
Namespace for all data library functionality.
Definition data.cpp:22
bool is_data_expression(const atermpp::aterm &x)
Test for a data_expression expression.
application less(const data_expression &arg0, const data_expression &arg1)
Application of function symbol <.
Definition standard.h:258
atermpp::term_list< data_expression > data_expression_list
\brief list of data_expressions
bool is_untyped_data_parameter(const atermpp::aterm &x)
atermpp::term_list< variable > variable_list
\brief list of variables
atermpp::term_list< assignment > assignment_list
\brief list of assignments
Definition assignment.h:146
application equal_to(const data_expression &arg0, const data_expression &arg1)
Application of function symbol ==.
Definition standard.h:144
@ warning
Definition logger.h:34
@ verbose
Definition logger.h:37
bool mCRL2logEnabled(const log_level_t level)
Definition logger.h:383
multi_action complete_multi_action(process::untyped_multi_action &x, const process::action_label_list &action_decls, const data::data_specification &data_spec=data::detail::default_specification())
Definition lps.cpp:132
void remove_common_divisor(std::size_t &enumerator, std::size_t &denominator)
void complete_action_rename_specification(action_rename_specification &x, const lps::stochastic_specification &spec)
Definition lps.cpp:150
multi_action complete_multi_action(process::untyped_multi_action &x, multi_action_type_checker &typechecker, const data::data_specification &data_spec=data::detail::default_specification())
Definition lps.cpp:124
std::size_t greatest_common_divisor(std::size_t x, std::size_t y)
The main namespace for the LPS library.
Definition constelm.h:21
void complete_data_specification(stochastic_specification &spec)
Adds all sorts that appear in the process of l to the data specification of l.
void parse_lps(std::istream &, Specification &)
Definition parse.h:159
void complete_data_specification(specification &spec)
Adds all sorts that appear in the process of l to the data specification of l.
atermpp::term_balanced_tree< data::data_expression > state
Definition state.h:24
std::string pp(const probabilistic_data_expression &l)
multi_action parse_multi_action(std::stringstream &in, multi_action_type_checker &typechecker, const data::data_specification &data_spec=data::detail::default_specification())
Parses a multi_action from an input stream.
Definition parse.h:56
action_rename_specification parse_action_rename_specification(std::istream &in, const lps::stochastic_specification &spec)
Parses a process specification from an input stream.
Definition parse.h:94
multi_action parse_multi_action(std::stringstream &in, const process::action_label_list &action_decls, const data::data_specification &data_spec=data::detail::default_specification())
Parses a multi_action from an input stream.
Definition parse.h:42
void parse_lps< specification >(std::istream &from, specification &result)
Definition parse.h:166
void make_state(state &result, ForwardTraversalIterator p, const std::size_t size)
Definition state.h:37
void parse_lps< stochastic_specification >(std::istream &from, stochastic_specification &result)
Parses a stochastic linear process specification from an input stream.
Definition parse.h:183
std::string pp(const lps::state &x)
Definition state.h:49
specification parse_linear_process_specification(std::istream &spec_stream)
Parses a linear process specification from an input stream.
Definition parse.h:128
void make_state(state &result, ForwardTraversalIterator p, const std::size_t size, Transformer transformer)
Definition state.h:27
const state_info_entry * state_info_const_ptr
fixed_vector< pred_entry >::iterator pred_iter_t
fixed_vector< succ_entry >::const_iterator succ_const_iter_t
permutation_t::iterator permutation_iter_t
fixed_vector< pred_entry >::const_iterator pred_const_iter_t
fixed_vector< state_info_ptr > permutation_t
permutation_t::const_iterator permutation_const_iter_t
std::list< B_to_C_descriptor > B_to_C_desc_list
B_to_C_desc_list::const_iterator B_to_C_desc_const_iter_t
B_to_C_desc_list::iterator B_to_C_desc_iter_t
fixed_vector< B_to_C_entry >::iterator B_to_C_iter_t
fixed_vector< succ_entry >::iterator succ_iter_t
fixed_vector< B_to_C_entry >::const_iterator B_to_C_const_iter_t
static void swap_permutation(permutation_iter_t s1, permutation_iter_t s2)
swap two permutations
constexpr constellation_type * null_constellation
constexpr transition_index undefined
default counter value if the counter field of a state is not in use currently
fixed_vector< outgoing_transition_type >::const_iterator outgoing_transitions_const_it
fixed_vector< outgoing_transition_type >::iterator outgoing_transitions_it
constexpr transition_index marked_NewBotSt
counter value to indicate that a state is in the NewBotSt subset
const transition_index * BLC_list_const_iterator
constexpr transition_index marked_range
the number of counter values that can be used for one subblock
static void clear(CONTAINER &c)
static constexpr bool is_in_marked_range_of(transition_index counter, enum subblocks subblock)
checks whether a counter value is a marking for a given subblock
constexpr transition_index null_transition
constexpr transition_index marked_HitSmall
static constexpr transition_index marked(enum subblocks subblock)
base marking value for a subblock
A base class for the lts_dot labelled transition system.
bool bisimulation_compare(const LTS_TYPE &l1, const LTS_TYPE &l2, const bool branching=false, const bool preserve_divergences=false, const bool generate_counter_examples=false, const std::string &counter_example_file="", const bool structured_output=false)
Checks whether the two initial states of two lts's are strong or branching bisimilar.
void merge(LTS_TYPE &l1, const LTS_TYPE &l2)
std::string supported_lts_formats_text(lts_type default_format, const std::set< lts_type > &supported)
Gives a textual list describing supported LTS formats.
Definition liblts.cpp:153
static std::string mime_type_strings[]
Definition liblts.cpp:85
std::string supported_lts_formats_text(const std::set< lts_type > &supported)
Gives a textual list describing supported LTS formats.
Definition liblts.cpp:185
bool destructive_bisimulation_compare_minimal_depth(LTS_TYPE &l1, LTS_TYPE &l2, const std::string &counter_example_file)
std::size_t state_type
type used to store state (numbers and) counts
void bisimulation_reduce(LTS_TYPE &l, const bool branching=false, const bool preserve_divergences=false)
Reduce transition system l with respect to strong or (divergence preserving) branching bisimulation.
std::string extension_for_type(const lts_type type)
Gives the filename extension associated with an LTS format.
Definition liblts.cpp:118
void unmark_explicit_divergence_transitions(LTS_TYPE &l, const std::size_t divergent_transition_label)
bool bisimulation_compare_gj(const LTS_TYPE &l1, const LTS_TYPE &l2, const bool branching=false, const bool preserve_divergence=false)
Checks whether the two initial states of two LTSs are strong or (divergence-preserving) branching bis...
std::string string_for_type(const lts_type type)
Gives a string representation of an LTS format.
Definition liblts.cpp:113
lts_type parse_format(std::string const &s)
Determines the LTS format from a format specification string.
Definition liblts.cpp:92
bool destructive_bisimulation_compare(LTS_TYPE &l1, LTS_TYPE &l2, const bool branching=false, const bool preserve_divergences=false, const bool generate_counter_examples=false, const std::string &counter_example_file="", const bool structured_output=false)
Checks whether the two initial states of two lts's are strong or branching bisimilar.
static std::string type_desc_strings[]
Definition liblts.cpp:75
LABEL_TYPE make_divergence_label(const std::string &s)
const std::set< lts_type > & supported_lts_formats()
Gives the set of all supported LTS formats.
Definition liblts.cpp:140
bool destructive_bisimulation_compare_gj(LTS_TYPE &l1, LTS_TYPE &l2, const bool branching=false, const bool preserve_divergence=false, const bool generate_counter_examples=false, const std::string &="", bool=false)
Checks whether the two initial states of two LTSs are strong or (divergence-preserving) branching bis...
std::size_t label_type
type used to store label numbers and counts
std::size_t trans_type
type used to store transition (numbers and) counts
std::string lts_extensions_as_string(const std::set< lts_type > &supported)
Gives a list of extensions for supported LTS formats.
Definition liblts.cpp:220
static const std::string type_strings[]
Definition liblts.cpp:71
bool lts_named_cmp(const std::string N[], T a, T b)
Definition liblts.cpp:148
std::string lts_extensions_as_string(const std::string &sep, const std::set< lts_type > &supported)
Gives a list of extensions for supported LTS formats.
Definition liblts.cpp:190
std::size_t mark_explicit_divergence_transitions(LTS_TYPE &l)
lts_type guess_format(std::string const &s, const bool be_verbose)
Determines the LTS format from a filename by its extension.
Definition liblts.cpp:26
void bisimulation_reduce_gj(LTS_TYPE &l, const bool branching=false, const bool preserve_divergence=false)
nonmember functions serving as interface with the rest of mCRL2
static const std::string extension_strings[]
Definition liblts.cpp:73
void get_trans(const outgoing_transitions_per_state_t &begin, tree_set_store &tss, std::size_t d, std::vector< transition > &d_trans, LTS_TYPE &aut)
std::size_t apply_hidden_labels(const std::size_t n, const std::set< std::size_t > &hidden_action_set)
Definition transition.h:25
std::string mime_type_for_type(const lts_type type)
Gives the MIME type associated with an LTS format.
Definition liblts.cpp:123
static const std::set< lts_type > & initialise_supported_lts_formats()
Definition liblts.cpp:128
The main LTS namespace.
std::size_t label(const outgoing_transitions_per_state_action_t::const_iterator &i)
Label of an iterator exploring transitions per outgoing state and action.
std::string pp(const state_label_dot &l)
Pretty print function for a state_label_dot. Only prints the label field.
Definition lts_dot.h:101
lts_equivalence
LTS equivalence relations.
@ lts_eq_branching_bisim_sigref
@ lts_eq_divergence_preserving_weak_bisim
@ lts_eq_branching_bisim_gjkw
@ lts_eq_branching_bisim_gv
@ lts_eq_divergence_preserving_branching_bisim_sigref
@ lts_eq_divergence_preserving_branching_bisim
@ lts_eq_branching_bisim_gj
@ lts_eq_divergence_preserving_branching_bisim_gjkw
@ lts_eq_divergence_preserving_branching_bisim_gv
@ lts_eq_divergence_preserving_branching_bisim_gj
std::string pp(const state_label_lts &label)
Pretty print a state value of this LTS.
Definition lts_lts.h:108
bool is_deterministic(const LTS_TYPE &l)
Checks whether this LTS is deterministic.
lts_type
The enumerated type lts_type contains an index for every type type of labelled transition system that...
Definition lts_type.h:37
@ lts_fsm_probabilistic
Definition lts_type.h:45
@ lts_type_min
Definition lts_type.h:46
@ lts_lts_probabilistic
Definition lts_type.h:43
@ lts_aut_probabilistic
Definition lts_type.h:44
@ lts_type_max
Definition lts_type.h:47
bool destructive_compare(LTS_TYPE &l1, LTS_TYPE &l2, const lts_preorder pre, const bool generate_counter_example, const std::string &counter_example_file="", const bool structured_output=false, const lps::exploration_strategy strategy=lps::es_breadth, const bool preprocess=true)
Checks whether this LTS is smaller than another LTS according to a preorder.
transition_sort_style
Transition sort styles.
Definition transition.h:35
std::multimap< std::pair< transition::size_type, transition::size_type >, transition::size_type > outgoing_transitions_per_state_action_t
Type for exploring transitions per state and action.
std::string pp(const state_label_empty &)
outgoing_transitions_per_state_action_t transitions_per_outgoing_state_action_pair_reversed(const std::vector< transition > &trans)
Provide the transitions as a multimap accessible per from state and label, ordered backwardly.
lts_preorder
LTS preorder relations.
void group_transitions_on_label_tgt(std::vector< transition > &transitions, const std::size_t number_of_labels, const std::size_t tau_label_index, const std::size_t number_of_states)
static std::vector< std::size_t > bogus_todo_stack
void group_transitions_on_label(std::vector< transition > &transitions, std::function< std::size_t(const transition &)> get_label, const std::size_t number_of_labels, const std::size_t tau_label_index)
std::size_t to(const outgoing_pair_t &p)
Target state of a label state pair.
std::string pp(const state_label_fsm &label)
Pretty print an fsm state label.
Definition lts_fsm.h:78
outgoing_transitions_per_state_action_t transitions_per_outgoing_state_action_pair(const std::vector< transition > &trans)
Provide the transitions as a multimap accessible per from state and label.
std::size_t to(const outgoing_transitions_per_state_action_t::const_iterator &i)
To state of an iterator exploring transitions per outgoing state and action.
void sort_transitions(std::vector< transition > &transitions, const std::set< transition::size_type > &hidden_label_set, transition_sort_style ts=src_lbl_tgt)
Sorts the transitions using a sort style.
void sort_transitions(std::vector< transition > &transitions, transition_sort_style ts=src_lbl_tgt)
Sorts the transitions using a sort style.
void determinise(LTS_TYPE &l)
Determinises this LTS.
void group_transitions_on_tgt_label(std::vector< transition > &transitions, const std::size_t number_of_labels, const std::size_t tau_label_index, const std::size_t number_of_states)
std::string pp(const probabilistic_state< STATE, PROBABILITY > &l)
void reduce(LTS_TYPE &l, lts_equivalence eq)
Applies a reduction algorithm to this LTS.
detail::indexed_sorted_vector_for_transitions< outgoing_pair_t > outgoing_transitions_per_state_t
void group_transitions_on_tgt_label(LTS_TYPE &aut)
outgoing_transitions_per_state_action_t transitions_per_outgoing_state_action_pair_reversed(const std::vector< transition > &trans, const std::set< transition::size_type > &hide_label_set)
Provide the transitions as a multimap accessible per from state and label, ordered backwardly.
bool destructive_compare(LTS_TYPE &l1, LTS_TYPE &l2, const lts_equivalence eq, const bool generate_counter_examples=false, const std::string &counter_example_file=std::string(), const bool structured_output=false)
Checks whether this LTS is equivalent to another LTS.
std::string ptr(const transition t)
Sorts the transitions on labels. Action with the tau label are put first.
std::string pp(const action_label_lts &l)
Print the action label to string.
Definition lts_lts.h:202
outgoing_transitions_per_state_action_t transitions_per_outgoing_state_action_pair(const std::vector< transition > &trans, const std::set< transition::size_type > &hide_label_set)
Provide the transitions as a multimap accessible per from state and label.
void merge(LTS_TYPE &l1, const LTS_TYPE &l2)
Merge the second lts into the first lts.
bool reachability_check(lts< SL, AL, BASE > &l, bool remove_unreachable=false)
Checks whether all states in this LTS are reachable from the initial state and remove unreachable sta...
std::size_t label(const outgoing_pair_t &p)
Label of a pair of a label and target state.
std::size_t from(const outgoing_transitions_per_state_action_t::const_iterator &i)
From state of an iterator exploring transitions per outgoing state and action.
void scc_reduce(LTS_TYPE &l, const bool preserve_divergence_loops=false)
Definition liblts_scc.h:394
void group_transitions_on_label(const std::vector< transition >::iterator begin, const std::vector< transition >::iterator end, std::function< std::size_t(const transition &)> get_label, std::vector< std::pair< std::size_t, std::size_t > > &count_sum_transitions_per_action, const std::size_t tau_label_index=0, std::vector< std::size_t > &todo_stack=bogus_todo_stack)
static const std::size_t const_tau_label_index
Definition transition.h:28
bool reachability_check(probabilistic_lts< SL, AL, PROBABILISTIC_STATE, BASE > &l, bool remove_unreachable=false)
Checks whether all states in a probabilistic LTS are reachable from the initial state and remove unre...
std::pair< transition::size_type, transition::size_type > outgoing_pair_t
Type for exploring transitions per state.
bool compare(const LTS_TYPE &l1, const LTS_TYPE &l2, const lts_preorder pre, const bool generate_counter_example, const std::string &counter_example_file="", const bool structured_output=false, const lps::exploration_strategy strategy=lps::es_breadth, const bool preprocess=true)
Checks whether this LTS is smaller than another LTS according to a preorder.
std::string pp(const action_label_string &l)
bool compare(const LTS_TYPE &l1, const LTS_TYPE &l2, const lts_equivalence eq, const bool generate_counter_examples=false, const std::string &counter_example_file="", const bool structured_output=false)
Checks whether this LTS is equivalent to another LTS.
The main namespace for the Process library.
bool is_linear(const process_specification &p, bool verbose=false)
Returns true if the process specification is linear.
Definition is_linear.h:347
atermpp::term_list< action_label > action_label_list
\brief list of action_labels
std::vector< action > action_vector
\brief vector of actions
atermpp::term_list< action > action_list
\brief list of actions
bool is_untyped_multi_action(const atermpp::aterm &x)
process_specification parse_process_specification(std::istream &in)
Parses a process specification from an input stream.
Definition parse.h:43
std::string pp(const regular_formulas::regular_formula &x)
bool is_alt(const atermpp::aterm &x)
bool is_untyped_regular_formula(const atermpp::aterm &x)
void make_trans(atermpp::aterm &t, const ARGUMENTS &... args)
void make_seq(atermpp::aterm &t, const ARGUMENTS &... args)
void make_trans_or_nil(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_trans(const atermpp::aterm &x)
void swap(alt &t1, alt &t2)
\brief swap overload
std::string pp(const regular_formulas::seq &x)
void make_alt(atermpp::aterm &t, const ARGUMENTS &... args)
void make_untyped_regular_formula(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_trans_or_nil(const atermpp::aterm &x)
bool is_regular_formula(const atermpp::aterm &x)
void swap(trans &t1, trans &t2)
\brief swap overload
bool is_seq(const atermpp::aterm &x)
std::string pp(const regular_formulas::alt &x)
std::vector< regular_formula > regular_formula_vector
\brief vector of regular_formulas
void swap(untyped_regular_formula &t1, untyped_regular_formula &t2)
\brief swap overload
std::string pp(const regular_formulas::untyped_regular_formula &x)
atermpp::term_list< regular_formula > regular_formula_list
\brief list of regular_formulas
void swap(seq &t1, seq &t2)
\brief swap overload
std::string pp(const regular_formulas::trans_or_nil &x)
void swap(regular_formula &t1, regular_formula &t2)
\brief swap overload
std::string pp(const regular_formulas::trans &x)
void swap(trans_or_nil &t1, trans_or_nil &t2)
\brief swap overload
bool is_timed(const state_formula &x)
bool is_infimum(const atermpp::aterm &x)
std::string pp(const state_formulas::or_ &x)
std::string pp(const state_formulas::plus &x)
void swap(nu &t1, nu &t2)
\brief swap overload
bool is_and(const atermpp::aterm &x)
std::string pp(const state_formulas::yaled_timed &x)
std::string pp(const state_formulas::may &x)
bool is_delay_timed(const atermpp::aterm &x)
bool is_const_multiply(const atermpp::aterm &x)
std::string pp(const state_formulas::must &x)
void swap(const_multiply &t1, const_multiply &t2)
\brief swap overload
bool is_minus(const atermpp::aterm &x)
std::string pp(const state_formulas::not_ &x)
void make_imp(atermpp::aterm &t, const ARGUMENTS &... args)
atermpp::term_list< state_formula > state_formula_list
\brief list of state_formulas
bool is_exists(const atermpp::aterm &x)
std::string pp(const state_formulas::delay &x)
bool is_not(const atermpp::aterm &x)
void swap(and_ &t1, and_ &t2)
\brief swap overload
bool is_state_formula(const atermpp::aterm &x)
void make_const_multiply(atermpp::aterm &t, const ARGUMENTS &... args)
void make_exists(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(yaled_timed &t1, yaled_timed &t2)
\brief swap overload
bool is_supremum(const atermpp::aterm &x)
bool is_must(const atermpp::aterm &x)
std::set< data::variable > find_all_variables(const state_formulas::state_formula &x)
bool is_yaled(const atermpp::aterm &x)
std::string pp(const state_formulas::const_multiply &x)
void swap(infimum &t1, infimum &t2)
\brief swap overload
void make_and_(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::exists &x)
std::string pp(const state_formulas::const_multiply_alt &x)
std::set< data::variable > find_free_variables(const state_formulas::state_formula &x)
void swap(imp &t1, imp &t2)
\brief swap overload
bool is_true(const atermpp::aterm &x)
void swap(minus &t1, minus &t2)
\brief swap overload
std::string pp(const state_formulas::and_ &x)
void swap(may &t1, may &t2)
\brief swap overload
std::string pp(const state_formulas::state_formula &x)
void swap(exists &t1, exists &t2)
\brief swap overload
void swap(plus &t1, plus &t2)
\brief swap overload
std::string pp(const state_formulas::infimum &x)
void swap(mu &t1, mu &t2)
\brief swap overload
void make_plus(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::minus &x)
bool is_variable(const atermpp::aterm &x)
void make_not_(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(variable &t1, variable &t2)
\brief swap overload
void swap(supremum &t1, supremum &t2)
\brief swap overload
void make_infimum(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(true_ &t1, true_ &t2)
\brief swap overload
bool is_may(const atermpp::aterm &x)
bool is_yaled_timed(const atermpp::aterm &x)
bool is_imp(const atermpp::aterm &x)
void make_delay_timed(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::forall &x)
std::string pp(const state_formulas::imp &x)
std::string pp(const state_formulas::yaled &x)
std::vector< state_formula > state_formula_vector
\brief vector of state_formulas
void make_const_multiply_alt(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::sum &x)
void make_may(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(const_multiply_alt &t1, const_multiply_alt &t2)
\brief swap overload
bool is_sum(const atermpp::aterm &x)
void swap(or_ &t1, or_ &t2)
\brief swap overload
std::string pp(const state_formulas::delay_timed &x)
void swap(delay_timed &t1, delay_timed &t2)
\brief swap overload
state_formulas::state_formula translate_user_notation(const state_formulas::state_formula &x)
void make_must(atermpp::aterm &t, const ARGUMENTS &... args)
state_formulas::state_formula normalize_sorts(const state_formulas::state_formula &x, const data::sort_specification &sortspec)
std::string pp(const state_formulas::false_ &x)
void swap(must &t1, must &t2)
\brief swap overload
bool is_nu(const atermpp::aterm &x)
void swap(not_ &t1, not_ &t2)
\brief swap overload
bool is_delay(const atermpp::aterm &x)
std::string pp(const state_formulas::true_ &x)
std::string pp(const state_formulas::mu &x)
std::string pp(const state_formulas::supremum &x)
bool is_false(const atermpp::aterm &x)
void make_variable(atermpp::aterm &t, const ARGUMENTS &... args)
void make_nu(atermpp::aterm &t, const ARGUMENTS &... args)
void make_supremum(atermpp::aterm &t, const ARGUMENTS &... args)
void make_sum(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_plus(const atermpp::aterm &x)
void swap(state_formula &t1, state_formula &t2)
\brief swap overload
void swap(false_ &t1, false_ &t2)
\brief swap overload
void swap(sum &t1, sum &t2)
\brief swap overload
void make_forall(atermpp::aterm &t, const ARGUMENTS &... args)
bool is_mu(const atermpp::aterm &x)
void swap(yaled &t1, yaled &t2)
\brief swap overload
bool is_forall(const atermpp::aterm &x)
void swap(delay &t1, delay &t2)
\brief swap overload
void make_minus(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::nu &x)
bool is_const_multiply_alt(const atermpp::aterm &x)
bool is_or(const atermpp::aterm &x)
void make_or_(atermpp::aterm &t, const ARGUMENTS &... args)
void swap(forall &t1, forall &t2)
\brief swap overload
void make_yaled_timed(atermpp::aterm &t, const ARGUMENTS &... args)
std::string pp(const state_formulas::variable &x)
std::set< data::sort_expression > find_sort_expressions(const state_formulas::state_formula &x)
bool find_nil(const state_formulas::state_formula &x)
std::set< process::action_label > find_action_labels(const state_formulas::state_formula &x)
void make_mu(atermpp::aterm &t, const ARGUMENTS &... args)
std::set< core::identifier_string > find_identifiers(const state_formulas::state_formula &x)
void remove_common_divisor(std::size_t &p, std::size_t &q)
std::size_t hash_combine(const std::size_t h1, const std::size_t h2)
std::size_t multiply_single_number(const std::size_t n1, const std::size_t n2, std::size_t &multiplication_carry)
Definition big_numbers.h:95
std::size_t divide_single_number(const std::size_t p, const std::size_t q, std::size_t &remainder)
std::size_t add_single_number(const std::size_t n1, const std::size_t n2, std::size_t &carry)
Definition big_numbers.h:49
std::size_t greatest_common_divisor(std::size_t p, std::size_t q)
std::size_t subtract_single_number(const std::size_t n1, const std::size_t n2, std::size_t &carry)
Definition big_numbers.h:72
std::string pp(const probabilistic_arbitrary_precision_fraction &l)
std::string pp(const big_natural_number &l)
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
void swap(atermpp::term_balanced_tree< T > &t1, atermpp::term_balanced_tree< T > &t2)
Swaps two balanced trees.
#define USE_POOL_ALLOCATOR
Definition simple_list.h:66
static const atermpp::aterm StateMay
static const atermpp::aterm StateOr
static const atermpp::aterm UntypedRegFrm
static const atermpp::aterm StateFrm
static const atermpp::aterm StateYaled
static const atermpp::aterm RegAlt
static const atermpp::aterm ActNot
static const atermpp::aterm ActImp
static const atermpp::aterm ActTrue
static const atermpp::aterm StateInfimum
static const atermpp::aterm StateAnd
static const atermpp::aterm StateExists
static const atermpp::aterm RegTrans
static const atermpp::aterm ActOr
static const atermpp::aterm StateConstantMultiplyAlt
static const atermpp::aterm ActFrm
static const atermpp::aterm ActForall
static const atermpp::aterm StateYaledTimed
static const atermpp::aterm ActFalse
static const atermpp::aterm StateFalse
static const atermpp::aterm RegFrm
static const atermpp::aterm StateDelay
static const atermpp::aterm StatePlus
static const atermpp::aterm StateMinus
static const atermpp::aterm StateNu
static const atermpp::aterm ActAnd
static const atermpp::aterm StateDelayTimed
static const atermpp::aterm StateSupremum
static const atermpp::aterm StateSum
static const atermpp::aterm ActAt
static const atermpp::aterm ActExists
static const atermpp::aterm StateMu
static const atermpp::aterm RegTransOrNil
static const atermpp::aterm StateVar
static const atermpp::aterm StateImp
static const atermpp::aterm RegSeq
static const atermpp::aterm StateTrue
static const atermpp::aterm StateForall
static const atermpp::aterm StateMust
static const atermpp::aterm StateNot
static const atermpp::aterm ActMultAct
static const atermpp::aterm StateConstantMultiply
static const atermpp::function_symbol UntypedRegFrm
static const atermpp::function_symbol StateMu
static const atermpp::function_symbol ActMultAct
static const atermpp::function_symbol RegSeq
static const atermpp::function_symbol StateConstantMultiply
static const atermpp::function_symbol ActNot
static const atermpp::function_symbol StateSum
static const atermpp::function_symbol ActAnd
static const atermpp::function_symbol StateConstantMultiplyAlt
static const atermpp::function_symbol StateForall
static const atermpp::function_symbol StateOr
static const atermpp::function_symbol ActFalse
static const atermpp::function_symbol RegTransOrNil
static const atermpp::function_symbol StateYaledTimed
static const atermpp::function_symbol StateExists
static const atermpp::function_symbol ActExists
static const atermpp::function_symbol StateVar
static const atermpp::function_symbol StateTrue
static const atermpp::function_symbol StateFalse
static const atermpp::function_symbol StateImp
static const atermpp::function_symbol RegAlt
static const atermpp::function_symbol RegTrans
static const atermpp::function_symbol StateMinus
static const atermpp::function_symbol StateNot
static const atermpp::function_symbol StateInfimum
static const atermpp::function_symbol StateDelay
static const atermpp::function_symbol StateYaled
static const atermpp::function_symbol ActAt
static const atermpp::function_symbol StateMay
static const atermpp::function_symbol StateDelayTimed
static const atermpp::function_symbol ActImp
static const atermpp::function_symbol ActTrue
static const atermpp::function_symbol ActForall
static const atermpp::function_symbol StatePlus
static const atermpp::function_symbol StateMust
static const atermpp::function_symbol StateNu
static const atermpp::function_symbol StateAnd
static const atermpp::function_symbol ActOr
static const atermpp::function_symbol StateSupremum
std::vector< transition > non_inert_transitions
std::vector< non_bottom_state > non_bottom_states
non_bottom_state(const state_type s, const std::vector< state_type > &it)
std::set< std::pair< label_type, block_index_type > > outgoing_observations
bool operator!=(const BLC_indicators &other) const
std::string debug_id(const bisim_partitioner_gj< LTS_TYPE > &partitioner, const block_type *from_block=nullptr) const
print a B_to_C slice identification for debugging
BLC_indicators(BLC_list_iterator start, BLC_list_iterator end, bool is_stable)
bool operator==(const BLC_indicators &other) const
check_complexity::BLC_gj_counter_t work_counter
std::string debug_id(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a block identification for debugging
state_in_block_pointer * end_states
pointer past the last state in the block
block_type(state_in_block_pointer *start_bottom, state_in_block_pointer *start_non_bottom, state_in_block_pointer *end, constellation_type *new_c)
constructor
check_complexity::block_gj_counter_t work_counter
bool contains_new_bottom_states
a boolean that is true iff the block contains new bottom states
block_type(const block_type &other)
copy constructor. Required by MSCV.
constellation_type(state_in_block_pointer *const new_start, state_in_block_pointer *const new_end)
state_in_block_pointer * end_const_states
points past the last state in m_states_in_blocks
std::string debug_id(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a constellation identification for debugging
state_in_block_pointer * start_const_states
points to the first state in m_states_in_blocks
information about a transition stored in m_outgoing_transitions
outgoing_transition_type(const outgoing_transitions_it sssaC)
a pointer to a state, i.e. a reference to a state
bool operator!=(const state_in_block_pointer &other) const
bool operator==(const state_in_block_pointer &other) const
state_in_block_pointer(fixed_vector< state_type_gj >::iterator new_ref_state)
transition_index no_of_outgoing_block_inert_transitions
number of outgoing block-inert transitions
std::vector< transition >::iterator start_incoming_transitions
first incoming transition
check_complexity::state_gj_counter_t work_counter
state_in_block_pointer * ref_states_in_blocks
pointer to the corresponding entry in m_states_in_blocks
std::string debug_id(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a state identification for debugging
std::string debug_id_short(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a short state identification for debugging
outgoing_transitions_it start_outgoing_transitions
first outgoing transition
check_complexity::trans_gj_counter_t work_counter
std::string debug_id(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a transition identification for debugging
linked_list< BLC_indicators >::iterator transitions_per_block_to_constellation
std::string debug_id_short(const bisim_partitioner_gj< LTS_TYPE > &partitioner) const
print a short transition identification for debugging
Converts a process expression into linear process format. Use the convert member functions for this.
lps::specification convert(const process_specification &p)
Converts a process_specification into a specification. Throws non_linear_process if a non-linear sub-...
Converts a process expression into linear process format. Use the convert member functions for this.
lps::stochastic_specification convert(const process_specification &p)
Converts a process_specification into a stochastic_specification. Throws non_linear_process if a non-...
std::size_t operator()(const atermpp::aterm &t) const
Definition aterm.h:483
std::size_t operator()(const atermpp::detail::reference_aterm< T > &t) const
std::size_t operator()(const atermpp::term_balanced_tree< T > &t) const
std::size_t operator()(const mcrl2::lps::multi_action &ma) const
std::size_t operator()(const mcrl2::lps::probabilistic_data_expression &p) const
std::size_t operator()(const mcrl2::lps::state_probability_pair< STATE, PROBABILITY > &p) const
std::size_t operator()(const mcrl2::lts::action_label_lts &as) const
Definition lts_lts.h:440
std::size_t operator()(const mcrl2::lts::action_label_string &as) const
std::size_t operator()(const mcrl2::lts::probabilistic_state< STATE, PROBABILITY > &p) const
std::size_t operator()(const mcrl2::lts::transition &t) const
Definition transition.h:164
std::size_t operator()(const mcrl2::utilities::big_natural_number &n) const
std::size_t operator()(const mcrl2::utilities::probabilistic_arbitrary_precision_fraction &p) const
linked_list< BLC_indicators > to_constellation
list of descriptors of all BLC sets that contain transitions starting in the block
std::vector< state_in_block_pointer > * R
used during initialisation for a pointer to a vector of marked states
static bool if_R_is_nullptr_then_to_constellation_is_empty_list()
indicates whether the default values of the union members agree
state_in_block_pointer * first_unmarked_bottom_state
used during initialisation for the first unmarked bottom state
state_index te_in_reduced_LTS
used during finalizing for the state index in the reduced LTS
BLC_list_iterator BLC_transitions
pointer to the corresponding entry in m_BLC_transitions (used during main part of the algorithm)
transition_index transitions
transition index (used during initialisation)
void convert_to_iterator(const BLC_list_iterator other)
Convert the object from counter to iterator.