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