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