mCRL2
Loading...
Searching...
No Matches
liblts_bisim_dnj.h
Go to the documentation of this file.
1// Author(s): David N. Jansen, Institute of Software, Chinese Academy of
2// Sciences, Beijing, PR China
3//
4// Copyright: see the accompanying file COPYING or copy at
5// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
6//
7// Distributed under the Boost Software License, Version 1.0.
8// (See accompanying file LICENSE_1_0.txt or copy at
9// http://www.boost.org/LICENSE_1_0.txt)
10
76
77// The file is best viewed on a screen or in a window that is 160 characters
78// wide. The left 80 columns contain the main text of the program. The right
79// 80 columns contain assertions and other code used for debugging.
80
81#ifndef LIBLTS_BISIM_DNJ_H
82#define LIBLTS_BISIM_DNJ_H
83
89
90#include <cstddef> // for std::size_t
91
92// My provisional recommendation is to always use simple lists and pool
93// allocators. Using standard allocation and standard lists is 5-15% slower
94// and uses perhaps 0.7% more memory. Using standard allocation and simple
95// lists is 10-20% slower and has no significant effect on memory use. These
96// numbers are based on a small set with not-so-large case studies, none of
97// which includes new bottom states.
98
99#define USE_SIMPLE_LIST
100
101#ifndef USE_SIMPLE_LIST
102 #include <list> // for the list of block_bunch-slices
103 #include <type_traits> // for std::is_trivially_destructible<class>
104#endif
105
106#define USE_POOL_ALLOCATOR
107
108#ifdef USE_POOL_ALLOCATOR
109 #ifndef NDEBUG
110 #include <type_traits> // for std::is_trivially_destructible<class>
111 #endif
112
113 #define ONLY_IF_POOL_ALLOCATOR(...) __VA_ARGS__
114 #ifndef USE_SIMPLE_LIST
115 #error "Using the pool allocator also requires using the simple list"
116 #endif
117#else
118 #define ONLY_IF_POOL_ALLOCATOR(...)
119#endif
120
121namespace mcrl2
122{
123namespace lts
124{
125namespace detail
126{
127 #ifndef NDEBUG
133// state_type and trans_type are defined in check_complexity.h. /// mode and to nothing otherwise.
134 #define ONLY_IF_DEBUG(...) __VA_ARGS__
135 #else
136 #define ONLY_IF_DEBUG(...)
137 #endif
141typedef std::size_t label_type;
142
143template <class LTS_TYPE> class bisim_partitioner_dnj;
144
145namespace bisim_dnj
146{
147
159template <class Iterator>
161{
164
166 Iterator begin;
167
170
171
173 void convert_to_iterator(const Iterator other)
174 {
175 new (&begin) Iterator(other);
176 }
177
178
180 ~iterator_or_counter() { begin.~Iterator(); }
181};
182
183
184class block_bunch_entry;
185class action_block_entry;
186
187
188
189
190
191/* ************************************************************************* */
192/* */
193/* M E M O R Y M A N A G E M E N T */
194/* */
195/* ************************************************************************* */
196
197
198
199
200
201#ifdef USE_POOL_ALLOCATOR
217 template <class T, size_t BLOCKSIZE = 1000>
219 { static_assert(std::is_trivially_destructible<T>::value);
220 private: static_assert(sizeof(void*) <= sizeof(T));
222 {
223 public:
224 char data[BLOCKSIZE - sizeof(pool_block_t*)];
226
227 pool_block_t(pool_block_t* const new_next_block)
228 : next_block(new_next_block)
229 { }
230 }; static_assert(sizeof(T) <= sizeof(pool_block_t::data));
231
235
238
241
242 static void*& deref_void(void* addr)
243 {
244 return *static_cast<void**>(addr);
245 }
246 public:
249 : first_block(new pool_block_t(nullptr)),
251 &first_block->data[sizeof(first_block->data)]),
252 first_free_T(nullptr)
253 { }
254
255
258 {
259 pool_block_t* block(first_block); assert(nullptr != block);
260 do
261 {
262 pool_block_t* next_block = block->next_block;
263 delete block;
264 block = next_block;
265 }
266 while(nullptr != block);
267 }
268
269
270 private:
273 template <class U, class... Args>
274 U* construct_samesize(Args&&... args)
275 { static_assert(sizeof(T) == sizeof(U));
276 void* new_el; assert(nullptr != first_block);
277 if (first_block->data + sizeof(U) <= begin_used_in_first_block)
278 {
280 new_el = static_cast<char*>(begin_used_in_first_block) -
281 sizeof(U);
282 }
283 else if (nullptr != first_free_T)
284 {
285 // free list is tested afterwards because I expect that there
286 // won't be too many elements in the free list.
287 new_el = first_free_T;
288 first_free_T = deref_void(new_el);
289 }
290 else
291 {
294 new_el = &first_block->data[sizeof(first_block->data) -
295 sizeof(U)];
296 }
297 return new(new_el) U(std::forward<Args>(args)...);
298 }
299
300
303 template <class U, class... Args>
304 U* construct_othersize(Args&&... args)
305 { static_assert(sizeof(U) != sizeof(T));
306 void* new_el; assert(nullptr != first_block);
307 if (first_block->data + sizeof(U) <= begin_used_in_first_block)
308 {
310 new_el = static_cast<char*>(begin_used_in_first_block) -
311 sizeof(U);
312 }
313 else
314 {
315 if constexpr (sizeof(T) * 2 < sizeof(U))
316 {
317 // There may be space for several T-elements
318 while (first_block->data + sizeof(T) <=
320 {
321 begin_used_in_first_block = static_cast<char*>
322 (begin_used_in_first_block) - sizeof(T);
325 }
326 }
327 else if constexpr (sizeof(T) < sizeof(U))
328 {
329 // There may be space for one T-element (but not more)
330 if (first_block->data + sizeof(T) <=
332 {
333 begin_used_in_first_block = static_cast<char*>
334 (begin_used_in_first_block) - sizeof(T);
337 }
338 } assert(first_block->data + sizeof(T) > begin_used_in_first_block);
341 new_el = &first_block->data[sizeof(first_block->data) -
342 sizeof(U)];
343 }
344 return new(new_el) U(std::forward<Args>(args)...);
345 }
346 public:
348 template <class U, class... Args>
349 U* construct(Args&&... args)
350 { static_assert(std::is_trivially_destructible<U>::value);
351 if constexpr (sizeof(U) == sizeof(T))
352 {
353 return construct_samesize<U>(std::forward<Args>(args)...);
354 }
355 else
356 { static_assert(sizeof(U) <= sizeof(first_block->data));
357 return construct_othersize<U>(std::forward<Args>(args)...);
358 }
359 }
360
361
368 template <class U>
369 void destroy(U* const old_el)
370 { static_assert(sizeof(T) == sizeof(U));
371 old_el->~U(); static_assert(std::is_trivially_destructible<U>::value);
372 #ifndef NDEBUG
373 // ensure that old_el points to an element in some block
374 static std::less<const void*> const total_order;
375 for (const pool_block_t* block(first_block);
376 assert(nullptr != block),
377 total_order(old_el, block->data) ||
378 total_order(&block->data[sizeof(block->data)], old_el + 1);
379 block = block->next_block )
380 { }
381 #endif
382 deref_void(old_el) = first_free_T;
383 first_free_T = static_cast<void*>(old_el);
384 }
385 };
386#endif
387
388#ifdef USE_SIMPLE_LIST
397 template <class T>
399 {
400 private:
403 {
404 protected:
407
410
411 empty_entry(empty_entry*const new_next, empty_entry*const new_prev)
412 : next(new_next),
413 prev(new_prev)
414 { }
415
416 friend class simple_list;
417 };
418
422
423 public:
427 class entry : public empty_entry
428 {
429 private:
431
432 friend class simple_list;
433 public:
434 template <class... Args>
435 entry(empty_entry* const new_next, empty_entry* const new_prev,
436 Args&&... args)
437 : empty_entry(new_next, new_prev),
438 data(std::forward<Args>(args)...)
439 { }
440 };
441
444 {
445 public:
446 typedef std::bidirectional_iterator_tag iterator_category;
447 typedef T value_type;
448 typedef std::ptrdiff_t difference_type;
449 typedef T* pointer;
450 typedef T& reference;
451 protected:
453
454 const_iterator(const empty_entry* const new_ptr)
455 : ptr(const_cast<empty_entry*>(new_ptr))
456 { }
457
458 friend class simple_list;
459 public:
460 const_iterator() = default;
461 const_iterator(const const_iterator& other) = default;
462 const_iterator& operator=(const const_iterator& other) = default;
463 const_iterator& operator++() { ptr = ptr->next; return *this; }
464 const_iterator& operator--() { ptr = ptr->prev; return *this; }
465 const T& operator*() const
466 {
467 return static_cast<const entry*>(ptr)->data;
468 }
469 const T* operator->() const
470 {
471 return &static_cast<const entry*>(ptr)->data;
472 }
473 bool operator==(const const_iterator& other) const
474 {
475 return ptr == other.ptr;
476 }
477 bool operator!=(const const_iterator& other) const
478 {
479 return !operator==(other);
480 }
481 };
482
485 {
486 public:
487 typedef typename const_iterator::iterator_category
493 protected:
494 iterator(empty_entry*const new_ptr) : const_iterator(new_ptr) { }
495
496 friend class simple_list;
497 public:
498 iterator() = default;
499 iterator(const iterator& other) = default;
500 iterator& operator=(const iterator& other) = default;
503 T& operator*() const
504 {
505 return static_cast<entry*>(const_iterator::ptr)->data;
506 }
507 T* operator->() const
508 {
509 return &static_cast<entry*>(const_iterator::ptr)->data;
510 }
511 };
512
518 {
519 public:
523 typedef typename iterator::pointer pointer;
526 iterator_or_null(std::nullptr_t) : iterator()
527 {
528 const_iterator::ptr = nullptr;
529 }
530 iterator_or_null(const iterator& other) : iterator(other) { }
531 bool is_null() const { return nullptr == const_iterator::ptr; }
532 T& operator*() const
533 { assert(!is_null());
534 return iterator::operator*();
535 }
536 T* operator->() const
537 { assert(!is_null());
538 return iterator::operator->();
539 }
540 bool operator==(const const_iterator& other) const
541 {
542 return const_iterator::ptr == other.ptr;
543 }
544 bool operator!=(const const_iterator& other) const
545 {
546 return !operator==(other);
547 }
548 bool operator==(const T* const other) const
549 { assert(nullptr != other);
550 // It is allowed to call this even if is_null(). Therefore, we
551 // cannot use iterator_or_null::operator->().
552 return const_iterator::operator->() == other;
553 }
554 bool operator!=(const T* const other) const
555 {
556 return !operator==(other);
557 }
558
559 void operator=(std::nullptr_t)
560 {
561 const_iterator::ptr = nullptr;
562 }
563 };
564
568 { static_assert(std::is_trivially_destructible<entry>::value);
569 }
570
571 #ifndef USE_POOL_ALLOCATOR
574 {
575 for (iterator iter = begin(); end() != iter; )
576 {
577 iterator next = std::next(iter);
578 delete static_cast<entry*>(iter.ptr);
579 iter = next;
580 }
581 }
582 #endif
583
586
589
592
595
597 const_iterator begin() const { return cbegin(); }
598
600 const_iterator end() const { return cend(); }
601
603 T& front()
604 { assert(!empty());
605 return static_cast<entry*>(sentinel.next)->data;
606 }
607
609 T& back()
610 { assert(!empty());
611 return static_cast<entry*>(sentinel.prev)->data;
612 }
613
615 bool empty() const { return sentinel.next == &sentinel; }
616
618 template<class... Args>
621 iterator pos, Args&&... args)
622 {
623 entry* const new_entry(
624 #ifdef USE_POOL_ALLOCATOR
625 pool.template construct<entry>
626 #else
627 new entry
628 #endif
629 (pos.ptr, pos.ptr->prev, std::forward<Args>(args)...));
630 pos.ptr->prev->next = new_entry;
631 pos.ptr->prev = new_entry;
632 return iterator(new_entry);
633 }
634
636 template<class... Args>
639 iterator pos, Args&&... args)
640 {
641 entry* const new_entry(
642 #ifdef USE_POOL_ALLOCATOR
643 pool.template construct<entry>
644 #else
645 new entry
646 #endif
647 (pos.ptr->next, pos.ptr, std::forward<Args>(args)...));
648 pos.ptr->next->prev = new_entry;
649 pos.ptr->next = new_entry;
650 return iterator(new_entry);
651 }
652
654 template<class... Args>
657 Args&&... args)
658 {
659 entry* const new_entry(
660 #ifdef USE_POOL_ALLOCATOR
661 pool.template construct<entry>
662 #else
663 new entry
664 #endif
665 (sentinel.next, &sentinel, std::forward<Args>(args)...));
666 sentinel.next->prev = new_entry;
667 sentinel.next = new_entry;
668 return iterator(new_entry);
669 }
670
672 template<class... Args>
675 Args&&... args)
676 {
677 entry* const new_entry(
678 #ifdef USE_POOL_ALLOCATOR
679 pool.template construct<entry>
680 #else
681 new entry
682 #endif
683 (&sentinel, sentinel.prev, std::forward<Args>(args)...));
684 sentinel.prev->next = new_entry;
685 sentinel.prev = new_entry;
686 return iterator(new_entry);
687 }
688
691 static void splice(iterator const new_pos, simple_list& /* unused */,
692 iterator const old_pos)
693 {
694 old_pos.ptr->prev->next = old_pos.ptr->next;
695 old_pos.ptr->next->prev = old_pos.ptr->prev;
696
697 old_pos.ptr->next = new_pos.ptr->prev->next;
698 old_pos.ptr->prev = new_pos.ptr->prev;
699
700 old_pos.ptr->prev->next = old_pos.ptr;
701 old_pos.ptr->next->prev = old_pos.ptr;
702 }
703
705 static void erase(
707 iterator const pos)
708 {
709 pos.ptr->prev->next = pos.ptr->next;
710 pos.ptr->next->prev = pos.ptr->prev;
711 #ifdef USE_POOL_ALLOCATOR
712 pool.destroy(static_cast<entry*>(pos.ptr));
713 #else
714 delete static_cast<entry*>(pos.ptr);
715 #endif
716 }
717 };
718#else
719 #define simple_list std::list
720#endif
721
722
723
724
725
726/* ************************************************************************* */
727/* */
728/* R E F I N A B L E P A R T I T I O N */
729/* */
730/* ************************************************************************* */
731
732
733
734
735
744class state_info_entry;
745class permutation_entry;
746
756
757class block_t;
758class bunch_t;
759
760class pred_entry;
761class succ_entry;
762
767#ifdef USE_SIMPLE_LIST
770#else
772 {
773 private:
774 const void* null;
776 public:
779 {
780 if constexpr (!std::is_trivially_destructible<
782 {
783 // We still have to internally decide whether to construct
784 // the iterator or not so the destructor knows what to do.
785 null = nullptr;
786 }
787 }
788
789
791 explicit block_bunch_slice_iter_or_null_t(nullptr_t)
792 {
793 null = nullptr;
794 }
795
796
799 const block_bunch_slice_iter_t& other)
800 {
801 new (&iter) block_bunch_slice_iter_t(other); assert(nullptr != null);
802 }
803
804
806 // not)
808 {
809 if (!is_null()) iter.~block_bunch_slice_iter_t();
810 }
811
812 block_bunch_slice_t* operator->()
813 { assert(nullptr != null);
814 return iter.operator->();
815 }
816 block_bunch_slice_t& operator*()
817 { assert(nullptr != null);
818 return iter.operator*();
819 }
820
821 void operator=(nullptr_t)
822 {
823 if (!is_null()) iter.~block_bunch_slice_iter_t();
824 null = nullptr;
825 }
826
827
828 explicit operator block_bunch_slice_iter_t() const
829 { assert(nullptr != null);
830 return iter;
831 }
832
833
834 explicit operator block_bunch_slice_const_iter_t() const
835 { assert(nullptr != null);
836 return iter;
837 }
838
839
840 void operator=(const block_bunch_slice_iter_t& other)
841 {
842 if (!is_null()) iter.~block_bunch_slice_iter_t();
843 new (&iter) block_bunch_slice_iter_t(other); assert(nullptr != null);
844 }
845
849 bool operator==(const block_bunch_slice_iter_or_null_t& other) const
850 {
851 if constexpr (sizeof(null) == sizeof(iter))
852 {
853 return &*iter == &*other.iter;
854 }
855 else
856 {
857 return (is_null() && other.is_null()) ||
858 (!is_null() && !other.is_null() && &*iter == &*other.iter);
859 }
860 }
861
862
864 bool operator!=(const block_bunch_slice_iter_or_null_t& other) const
865 {
866 return !operator==(other);
867 }
868
869
873 bool operator==(const block_bunch_slice_const_iter_t other) const
874 { // assert(nullptr != &*other); -- generates a warning
875 return (sizeof(null) == sizeof(iter) || !is_null()) &&
876 &*iter == &*other;
877 }
878
879
880 bool operator!=(const block_bunch_slice_const_iter_t other) const
881 {
882 return !operator==(other);
883 }
884
885
889 bool operator==(const block_bunch_slice_t* const other) const
890 { assert(nullptr != other);
891 return (sizeof(null) == sizeof(iter) || !is_null()) &&
892 &*iter == other;
893 }
894
895
896 bool operator!=(const block_bunch_slice_t* const other) const
897 {
898 return !operator==(other);
899 }
900
901
903 bool is_null() const { return nullptr == null; }
904 };
905#endif
906
908
909
916{
917 public:
926
935
942 union bl_t {
945 } bl;
946
949
965 #ifndef NDEBUG
967 template<class LTS_TYPE>
969 partitioner) const
970 { assert(&partitioner.part_st.state_info.front() <= this);
971 assert(this <= &partitioner.part_st.state_info.back());
972 return std::to_string(this - &partitioner.part_st.state_info.front());
973 }
974
976 template<class LTS_TYPE>
978 partitioner) const
979 {
980 return "state " + debug_id_short(partitioner);
981 }
982
984 #endif
985};
986
987
990 public:
993
994
996 permutation_entry() = default;
997
998
1002 permutation_entry(const permutation_entry&& other) noexcept
1003 {
1004 st = other.st;
1005 }
1006
1007
1013 void operator=(const permutation_entry&& other) noexcept
1014 {
1015 st = other.st;
1016 st->pos = this;
1017 }
1018};
1019
1020
1045{
1046 public:
1049
1052
1055
1058
1061
1065
1070
1071
1078 block_t(permutation_entry* const new_begin,
1079 permutation_entry* const new_end, state_type const new_seqnr)
1080 : begin(new_begin),
1081 marked_bottom_begin(new_end),
1082 nonbottom_begin(new_end),
1083 marked_nonbottom_begin(new_end),
1084 end(new_end),
1086 seqnr(new_seqnr)
1087 { assert(new_begin < new_end);
1088 }
1089
1090
1093 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1096 return end - begin;
1097 }
1098
1099
1102 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1105 return nonbottom_begin - begin;
1106 }
1107
1108
1111 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1115 }
1116
1117
1120 {
1122 }
1123
1124
1127 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1130 return marked_bottom_begin - begin;
1131 }
1132
1133
1136 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1140 }
1141
1142
1149 { assert(nonbottom_begin <= s); assert(s < end);
1150 // assert(this == s->st->bl.ock); -- does not hold during initialisation
1151 assert(begin <= marked_bottom_begin);
1154 if (marked_nonbottom_begin <= s) { return false; } assert(marked_nonbottom_begin <= end);
1156 return true;
1157 }
1158
1159
1166 { assert(begin <= s);
1167 if (s < nonbottom_begin) // assert(this == s->st->bl.ock); -- does not hold during initialisation
1168 { assert(begin <= marked_bottom_begin); assert(marked_nonbottom_begin <= end);
1170 if (marked_bottom_begin <= s) { return false; } assert(marked_bottom_begin <= nonbottom_begin);
1172 return true;
1173 }
1174 return mark_nonbottom(s);
1175 }
1176
1177
1189 ONLY_IF_DEBUG( template<class LTS_TYPE> )
1190 block_t* split_off_block(enum new_block_mode_t new_block_mode, ONLY_IF_DEBUG( const bisim_partitioner_dnj<LTS_TYPE>& partitioner, )
1193 state_type new_seqnr);
1194 #ifndef NDEBUG
1196 template<class LTS_TYPE>
1198 partitioner) const
1199 { assert(&partitioner.part_st.permutation.front() <= begin);
1200 assert(begin < end); assert(begin <= marked_bottom_begin);
1203 assert(marked_nonbottom_begin <= end);
1204 assert(end <= 1 + &partitioner.part_st.permutation.back());
1205 return "block [" +
1206 std::to_string(begin - &partitioner.part_st.permutation.front()) +
1207 "," + std::to_string(end-&partitioner.part_st.permutation.front()) +
1208 ") (#" + std::to_string(seqnr) + ")";
1209 }
1210
1212 #endif
1213};
1214
1215
1221{
1222 public:
1228
1231
1236
1243 part_state_t(state_type const num_states)
1244 : permutation(num_states),
1245 state_info(num_states),
1246 nr_of_blocks(0)
1247 { assert(0 < num_states);
1249 static_assert(std::is_trivially_destructible<block_t>::value); )
1250 state_info_entry* state_iter(&state_info.front()); assert(perm_iter <= &permutation.back());
1251 do
1252 {
1253 state_iter->pos = perm_iter;
1254 perm_iter->st = state_iter;
1255 }
1256 while (++state_iter, ++perm_iter <= &permutation.back()); assert(state_iter == 1 + &state_info.back());
1257 }
1258
1259
1260 #ifndef USE_POOL_ALLOCATOR
1267 { ONLY_IF_DEBUG( state_type deleted_blocks(0); )
1268 permutation_entry* perm_iter(1 + &permutation.back()); assert(&permutation.front() < perm_iter);
1269 do
1270 {
1271 block_t* const B(perm_iter[-1].st->bl.ock); assert(B->end == perm_iter);
1272 perm_iter = B->begin; ONLY_IF_DEBUG( ++deleted_blocks; )
1273 delete B;
1274 }
1275 while (&permutation.front() < perm_iter); assert(deleted_blocks == nr_of_blocks);
1276 }
1277 #endif
1278
1279
1282 state_type state_size() const { return permutation.size(); }
1283
1284
1288 const block_t* block(state_type const s) const
1289 {
1290 return state_info[s].bl.ock;
1291 }
1292 #ifndef NDEBUG
1293 private:
1301 template<class LTS_TYPE>
1302 void print_block(const block_t* const B,
1303 const char* const message,
1304 const permutation_entry* begin_print,
1305 const permutation_entry* const end_print,
1306 const bisim_partitioner_dnj<LTS_TYPE>& partitioner) const
1307 { assert(B->begin <= begin_print); assert(end_print <= B->end);
1308 if (end_print == begin_print) return;
1309
1310 mCRL2log(log::debug) << '\t' << message
1311 << (1 < end_print - begin_print ? "s:\n" : ":\n");
1312 assert(begin_print < end_print);
1313 do
1314 {
1315 mCRL2log(log::debug) << "\t\t"
1316 << begin_print->st->debug_id(partitioner);
1317 if (B != begin_print->st->bl.ock)
1318 {
1319 mCRL2log(log::debug) << ", inconsistent: points "
1320 "to " << begin_print->st->bl.ock->debug_id(partitioner);
1321 }
1322 if (begin_print != begin_print->st->pos)
1323 {
1325 << ", inconsistent pointer to state_info_entry";
1326 }
1327 mCRL2log(log::debug) << '\n';
1328 }
1329 while (++begin_print < end_print);
1330 }
1331 public:
1336 template<class LTS_TYPE>
1337 void print_part(const bisim_partitioner_dnj<LTS_TYPE>& partitioner) const
1338 {
1339 if (!mCRL2logEnabled(log::debug)) return;
1340 const block_t* B(permutation.front().st->bl.ock);
1341 do
1342 {
1343 mCRL2log(log::debug)<<B->debug_id(partitioner)<<":\n";
1344 print_block(B, "Bottom state",
1345 B->begin, B->marked_bottom_begin, partitioner);
1346 print_block(B, "Marked bottom state",
1347 B->marked_bottom_begin, B->nonbottom_begin, partitioner);
1348 print_block(B, "Non-bottom state",
1349 B->nonbottom_begin, B->marked_nonbottom_begin, partitioner);
1350 print_block(B, "Marked non-bottom state",
1351 B->marked_nonbottom_begin, B->end, partitioner);
1352 // go to next block
1353 }
1354 while(B->end <= &permutation.back() && (B = B->end->st->bl.ock, true));
1355 }
1356
1360 template<class LTS_TYPE>
1362 const bisim_partitioner_dnj<LTS_TYPE>& partitioner) const
1363 {
1364 const permutation_entry* perm_iter(&permutation.front());
1365 state_type true_nr_of_blocks(0);
1366 assert(perm_iter <= &permutation.back());
1367 do
1368 {
1369 const block_t* const block(perm_iter->st->bl.ock);
1370 // block is consistent:
1371 assert(block->begin == perm_iter);
1376 assert(partitioner.branching||block->nonbottom_begin==block->end);
1377 assert(0 <= block->seqnr);
1378 assert(block->seqnr < nr_of_blocks);
1379 unsigned const max_block(check_complexity::log_n -
1381 mCRL2complexity(block, no_temporary_work(max_block), partitioner);
1382
1383 // states in the block are consistent:
1384 do
1385 {
1386 const state_info_entry* const state(perm_iter->st);
1387 // assert(&part_tr.pred.front() < state->pred_inert.begin);
1388 assert(&state_info.back() == state ||
1389 state->pred_inert.begin <= state[1].pred_inert.begin);
1390 // assert(state->pred_inert.begin <= &part_tr.pred.back());
1391 // assert(state->succ_inert.begin <= &part_tr.succ.back());
1392 if (perm_iter < block->nonbottom_begin)
1393 {
1394 assert(&state_info.back() == state || state->
1395 succ_inert.begin <= state[1].succ_inert.begin);
1396 // assert(state->succ_inert.begin==&part_tr.succ.back() ||
1397 // state <
1398 // state->succ_inert.begin->block_bunch->pred->source);
1399 mCRL2complexity(state, no_temporary_work(max_block, true),
1400 partitioner);
1401 }
1402 else
1403 {
1404 // assert(state->succ_inert.begin < &part_tr.succ.back());
1405 assert(&state_info.back() == state || state->
1406 succ_inert.begin < state[1].succ_inert.begin);
1407 //assert(state ==
1408 // state->succ_inert.begin->block_bunch->pred->source);
1409 mCRL2complexity(state, no_temporary_work(max_block, false),
1410 partitioner);
1411 }
1412 assert(block == state->bl.ock);
1413 assert(perm_iter == state->pos);
1414 }
1415 while (++perm_iter < block->end);
1416 assert(perm_iter == block->end);
1417 ++true_nr_of_blocks;
1418 }
1419 while (perm_iter <= &permutation.back());
1420 assert(nr_of_blocks == true_nr_of_blocks);
1421 }
1422 #endif
1423};
1424
1426
1427
1428
1429
1430 #ifndef NDEBUG
1431/* ************************************************************************* */ static struct {
1432/* */ bool operator()(const iterator_or_counter<action_block_entry*> p1,
1433/* T R A N S I T I O N S */ const action_block_entry* const action_block) const
1434/* */ {
1435/* ************************************************************************* */ return p1.begin > action_block;
1436 }
1438 #endif
1439
1440
1474
1476
1479{
1480 public:
1483
1493
1494
1497 );
1498
1499
1501 bunch_t* bunch() const;
1502 #ifndef NDEBUG
1514 template <class LTS_TYPE>
1515 static inline void add_work_to_out_slice(
1516 const bisim_partitioner_dnj<LTS_TYPE>& partitioner,
1518 enum check_complexity::counter_type ctr, unsigned max_value);
1519 #endif
1520};
1521
1522
1526{
1527 public:
1530
1534};
1535
1536
1543{
1544 public:
1547
1550
1553 #ifndef NDEBUG
1555 template <class LTS_TYPE>
1557 partitioner) const
1558 {
1559 return "from " + source->debug_id_short(partitioner) +
1560 " to " + target->debug_id_short(partitioner);
1561 }
1562
1564 template <class LTS_TYPE>
1565 std::string debug_id(const bisim_partitioner_dnj<LTS_TYPE>& partitioner)
1566 const
1567 {
1568 // Search for the action label in partitioner.action_label
1569 label_type const label(std::lower_bound(
1570 partitioner.action_label.cbegin(), partitioner.action_label.cend(),
1572 partitioner.action_label.cbegin());
1573 assert(label < partitioner.action_label.size());
1574 assert(partitioner.action_label[label].begin <= action_block);
1575 assert(0==label||action_block<partitioner.action_label[label-1].begin);
1576 // class lts_lts_t uses a function pp() to transform the action label
1577 // to a string.
1578 return pp(partitioner.aut.action_label(label)) + "-transition " +
1579 debug_id_short(partitioner);
1580 }
1581
1583 #endif
1584};
1585
1586
1590{
1591 public:
1598
1609
1610
1613 const action_block_entry* const action_block_orig_inert_begin )
1614 )
1615 {
1616 action_block_entry* result(begin_or_before_end); assert(nullptr != result);
1617 if (this < result)
1618 { assert(this == result->begin_or_before_end);
1619 result = this; // The following assertion does not always hold: the function is called
1620 } // immediately after a block is refined, so it may be the case that the
1621 // transitions are still to be moved to different slices.
1622 // assert(succ->block_bunch->pred->target->bl.ock ==
1623 // result->succ->block_bunch->pred->target->bl.ock);
1624 assert(nullptr != succ); assert(nullptr != result->succ);
1625 assert(succ->bunch() == result->succ->bunch());
1626 assert(result == action_block_begin || nullptr == result[-1].succ ||
1627 action_block_orig_inert_begin <= result ||
1628 result[-1].succ->block_bunch->pred->target->bl.ock !=
1629 result->succ->block_bunch->pred->target->bl.ock);
1630 // assert(this has the same action as result);
1631 return result;
1632 }
1633};
1634
1635
1636class part_trans_t;
1637
1643{
1644 public:
1647
1650
1656 {
1663
1666
1667
1670 {
1671 next_nontrivial = nullptr;
1672 }
1674
1675
1677 bunch_t(action_block_entry* const new_begin,
1678 action_block_entry* const new_end)
1679 : begin(new_begin),
1680 end(new_end),
1682 { }
1683
1684
1689 bool is_trivial() const
1690 {
1692 }
1693
1694
1702 #ifndef NDEBUG
1704 template <class LTS_TYPE>
1706 partitioner) const
1707 {
1708 assert(partitioner.part_tr.action_block_begin <= begin);
1709 assert(end <= partitioner.part_tr.action_block_inert_begin);
1710 return "bunch [" + std::to_string(begin -
1711 partitioner.part_tr.action_block_begin) + "," +
1712 std::to_string(end - partitioner.part_tr.action_block_begin) + ")";
1713 }
1714
1716 template <class LTS_TYPE>
1717 std::string debug_id(const bisim_partitioner_dnj<LTS_TYPE>& partitioner)
1718 const
1719 { assert(nullptr != end[-1].succ);
1720 const action_block_entry* iter(begin); assert(iter < end);
1721 assert(nullptr != iter->succ);
1722 assert(iter == iter->succ->block_bunch->pred->action_block);
1723 std::string result(debug_id_short(partitioner));
1724 result += " containing transition";
1725 result += iter < end - 1 ? "s " : " ";
1726 result += iter->succ->block_bunch->pred->debug_id_short(partitioner);
1727 ++iter;
1728 if (end <= iter) return result;
1729 while (nullptr == iter->succ) ++iter;
1730 assert(iter < end);
1731 assert(iter == iter->succ->block_bunch->pred->action_block);
1732 result += ", ";
1733 result += iter->succ->block_bunch->pred->debug_id_short(partitioner);
1734 if (iter < end - 3)
1735 {
1736 result += ", ...";
1737 iter = end - 3;
1738 }
1739 while (++iter < end)
1740 {
1741 if (nullptr != iter->succ)
1742 { assert(iter == iter->succ->block_bunch->pred->action_block);
1743 result += ", ";
1744 result += iter->succ->block_bunch->pred->debug_id_short(
1745 partitioner);
1746 }
1747 }
1748 return result;
1749 }
1750
1756 template <class LTS_TYPE>
1758 const
1759 {
1760 // verify that the bunch only has a single action label.
1761 // Search for the action label in partitioner.action_label
1762 label_type const label(std::lower_bound(
1763 partitioner.action_label.cbegin(), partitioner.action_label.cend(),
1765 partitioner.action_label.cbegin());
1766 assert(label < partitioner.action_label.size());
1767 assert(partitioner.action_label[label].begin <= begin);
1768 assert(0 == label || begin < partitioner.action_label[label-1].begin);
1769 if (0 == label || end < partitioner.action_label[label - 1].begin)
1770 {
1771 assert(check_complexity::ilog2(end - begin) <=
1774 }
1775 return 0;
1776 }
1777
1779 #endif
1780};
1781
1782
1800{
1801 public:
1806
1809
1814
1815
1817 bool is_stable() const { return nullptr == marked_begin; }
1818
1819
1822 { assert(!is_stable()); assert(!empty());
1823 marked_begin = nullptr;
1824 }
1825
1826
1829 { assert(is_stable());
1830 marked_begin = end; assert(!is_stable());
1831 }
1832
1833
1836 bunch_t* const new_bunch, bool const new_is_stable)
1837 : end(new_end),
1838 bunch(new_bunch),
1839 marked_begin(nullptr)
1840 {
1841 if (!new_is_stable) make_unstable();
1842 }
1843
1844
1848 bool empty() const
1849 { // assert(std::less(&part_tr.block_bunch.front(), end));
1850 // assert(!std::less(part_tr.block_bunch_inert_begin, end));
1851 // assert(part_tr.block_bunch.front().slice != this);
1852 return end[-1].slice != this;
1853 }
1854
1855
1858 { assert(!empty());
1859 return end[-1].pred->source->bl.ock;
1860 }
1861 #ifndef NDEBUG
1863 template <class LTS_TYPE>
1864 std::string debug_id(const bisim_partitioner_dnj<LTS_TYPE>& partitioner)
1865 const
1866 {
1867 static struct {
1868 bool operator()(const block_bunch_entry& p1,
1869 const block_bunch_slice_t* const p2) const
1870 {
1871 return p1.slice != p2;
1872 }
1873 } const block_bunch_not_equal;
1874
1875 assert(&partitioner.part_tr.block_bunch.front() < end);
1876 assert(end <= partitioner.part_tr.block_bunch_inert_begin);
1877 std::string const index_string(std::to_string(end -
1878 &partitioner.part_tr.block_bunch.cbegin()[1]));
1879 if (empty())
1880 { //assert(!is_stable());
1881 return "empty block_bunch_slice [" + index_string + "," +
1882 index_string + ")";
1883 }
1884 const block_bunch_entry* begin(
1885 &partitioner.part_tr.block_bunch.cbegin()[1]);
1886 if (trans_type bunch_size(bunch->end - bunch->begin);
1887 (trans_type) (end - begin) > bunch_size)
1888 {
1889 begin = end - bunch_size;
1890 }
1891 begin = std::lower_bound(begin, const_cast<const block_bunch_entry*>
1893 this, block_bunch_not_equal);
1894 assert(begin->slice == this);
1895 assert(begin[-1].slice != this);
1896 return (is_stable() ? "stable block_bunch-slice ["
1897 : "unstable block_bunch_slice [") +
1898 std::to_string(begin-&partitioner.part_tr.block_bunch.cbegin()[1]) +
1899 "," + index_string + ") containing transitions from " +
1900 source_block()->debug_id(partitioner) +
1901 " in " + bunch->debug_id_short(partitioner);
1902 }
1903
1911 template <class LTS_TYPE>
1913 counter_type const ctr, unsigned const max_value,
1914 const bisim_partitioner_dnj<LTS_TYPE>& partitioner) const
1915 { assert(!empty());
1916 assert(1U == max_value);
1917 const block_t* const block(source_block());
1918 bool result(false);
1919 const block_bunch_entry* block_bunch(end);
1920 assert(partitioner.part_tr.block_bunch.front().slice != this);
1921 assert(block_bunch[-1].slice == this);
1922 do
1923 {
1924 --block_bunch;
1925 const state_info_entry* const source(block_bunch->pred->source);
1926 assert(source->bl.ock == block);
1927 if (source->pos < block->nonbottom_begin /*&&
1928 // the transition starts in a (new) bottom state
1929 block_bunch->pred->work_counter.counters[ctr -
1930 check_complexity::TRANS_dnj_MIN] != max_value*/)
1931 {
1932 mCRL2complexity(block_bunch->pred, add_work(ctr, max_value),
1933 partitioner);
1934 result = true;
1935 }
1936 }
1937 while (block_bunch[-1].slice == this);
1938 return result;
1939 }
1940
1942 #endif
1943};
1944
1945
1948 )
1949{ assert(nullptr != begin_or_before_end);
1950 succ_entry* result(begin_or_before_end); assert(result->block_bunch->pred->action_block->succ == result);
1951 if (this < result)
1952 { assert(nullptr != result->begin_or_before_end);
1953 assert(this == result->begin_or_before_end);
1954 result = this; assert(result->block_bunch->pred->action_block->succ == result);
1955 } assert(block_bunch->pred->source == result->block_bunch->pred->source);
1956 // assert(this <= result); //< holds always, based on the if() above
1957 assert(nullptr != result->begin_or_before_end);
1958 assert(this <= result->begin_or_before_end);
1959 assert(block_bunch->slice == result->block_bunch->slice);
1960 assert(&succ.cbegin()[1] == result ||
1961 result[-1].block_bunch->pred->source < block_bunch->pred->source ||
1962 result[-1].bunch() != block_bunch->slice->bunch);
1963 return result;
1964}
1965
1966
1969{
1970 return block_bunch->slice->bunch;
1971}
1972 #ifndef NDEBUG
1981 template <class LTS_TYPE>
1982 /* static */ inline void succ_entry::add_work_to_out_slice(
1983 const bisim_partitioner_dnj<LTS_TYPE>& partitioner,
1984 const succ_entry* out_slice_begin,
1985 enum check_complexity::counter_type const ctr,unsigned const max_value)
1986 {
1987 const succ_entry* const out_slice_before_end(
1989 assert(nullptr != out_slice_before_end);
1990 assert(out_slice_begin <= out_slice_before_end);
1992 add_work(ctr, max_value), partitioner);
1993 while (++out_slice_begin <= out_slice_before_end)
1994 {
1995 // treat temporary counters specially
1997 add_work_notemporary(ctr, max_value), partitioner);
1998 }
1999 }
2000 #endif
2002{
2003 public:
2009
2015
2021
2035
2038
2041 #ifndef NDEBUG
2044 #endif
2047 private:
2050
2051 public:
2052 #ifdef USE_POOL_ALLOCATOR
2053 static_assert(std::is_trivially_destructible<bunch_t>::value);
2054 static_assert(std::is_trivially_destructible<
2058 #endif
2059
2062
2068
2077 part_trans_t(trans_type num_transitions,
2078 trans_type num_actions)
2079 : succ(num_transitions + 2),
2080 block_bunch(num_transitions + 1),
2081 pred(num_transitions + 2),
2083 new action_block_entry[num_transitions + num_actions - 1]),
2084 action_block_end(action_block_begin + (num_transitions+num_actions-1)),
2087 splitter_list(),
2088 first_nontrivial(nullptr),
2090 nr_of_bunches(0),
2094 {
2095 succ.front().block_bunch = &block_bunch.front();
2096 succ.back() .block_bunch = &block_bunch.front();
2097 block_bunch.front().pred = &pred.front();
2098 block_bunch.front().slice = nullptr;
2099 pred.front().source = nullptr;
2100 pred.front().target = nullptr;
2101 pred.back() .source = nullptr;
2102 pred.back() .target = nullptr;
2103 }
2104
2105
2108 {
2109 #ifndef USE_POOL_ALLOCATOR
2110 // The destructor also deallocates the bunches, as they are not
2111 // directly referenced from anywhere. This is only necessary if we
2112 // do not use the pool allocator, as the latter will destroy the
2113 // bunches wholesale.
2114 action_block_entry* action_block_iter(action_block_begin);
2115 for (;;)
2116 {
2117 do
2118 {
2119 if (action_block_inert_begin <= action_block_iter)
2120 { assert(0 == nr_of_bunches);
2121 delete [] action_block_begin;
2122 return;
2123 }
2124 }
2125 while (nullptr == action_block_iter->succ && ( assert(nullptr == action_block_iter->begin_or_before_end),
2126 ++action_block_iter, true)); assert(nullptr != action_block_iter->begin_or_before_end);
2127 bunch_t* const bunch(action_block_iter->succ->bunch()); assert(bunch->begin == action_block_iter);
2128 action_block_iter = bunch->end;
2129 delete bunch; ONLY_IF_DEBUG( --nr_of_bunches; )
2130 }
2131 /* unreachable */ assert(0);
2132 #else
2133 delete [] action_block_begin;
2134 #endif
2135 }
2136
2137
2141 {
2142 return first_nontrivial;
2143 }
2144
2145
2148 void make_nontrivial(bunch_t* const bunch)
2149 { assert(1 < bunch->end - bunch->begin); assert(bunch->is_trivial());
2150 // The following assertions do not necessarily hold during initialisation:
2151 //assert(bunch->begin <= bunch->begin->begin_or_before_end);
2153 nullptr == first_nontrivial ? bunch : first_nontrivial; //assert(nullptr != bunch->begin->begin_or_before_end);
2154 //assert(nullptr != bunch->end[-1].begin_or_before_end);
2155 //assert(bunch->begin->begin_or_before_end <
2156 // bunch->end[-1].begin_or_before_end);
2157 //assert(nullptr != end[-1].begin_or_before_end);
2159 ++nr_of_nontrivial_bunches; //assert(end[-1].begin_or_before_end <= end);
2160 }
2161
2162
2165 void make_trivial(bunch_t* const bunch)
2166 { assert(!bunch->is_trivial()); assert(first_nontrivial == bunch);
2169 ? nullptr : bunch->next_nontrivial_and_label.next_nontrivial; assert(bunch->end - 1 == bunch->begin->begin_or_before_end);
2171 --nr_of_nontrivial_bunches; assert(bunch->begin == bunch->end[-1].begin_or_before_end);
2172 }
2173
2174
2198 action_block_entry* const action_block_iter,
2199 bunch_t* const bunch_T_a_Bprime,
2200 bool const first_transition_of_state)
2201 {
2202
2203 /* - - - - - - - - adapt part_tr.succ - - - - - - - - */
2204
2205 succ_entry* const old_succ_pos(action_block_iter->succ); assert(nullptr != old_succ_pos);
2206 assert(old_succ_pos->block_bunch->pred->action_block == action_block_iter);
2207 succ_entry* const out_slice_begin(old_succ_pos->out_slice_begin( ONLY_IF_DEBUG( succ )
2208 )); assert(out_slice_begin->block_bunch->pred->action_block->succ ==
2209 out_slice_begin);
2210 succ_entry* const new_succ_pos(out_slice_begin->begin_or_before_end); assert(nullptr != new_succ_pos);
2211 assert(out_slice_begin == new_succ_pos->begin_or_before_end);
2212 assert(new_succ_pos<old_succ_pos->block_bunch->pred->source->succ_inert.begin);
2213 /* move the transition to the end of its out-slice */ assert(new_succ_pos->block_bunch->pred->action_block->succ == new_succ_pos);
2214 if (old_succ_pos < new_succ_pos)
2215 {
2216 std::swap(old_succ_pos->block_bunch, new_succ_pos->block_bunch);
2217 old_succ_pos->block_bunch->pred->action_block->succ = old_succ_pos; assert(action_block_iter == new_succ_pos->block_bunch->pred->action_block);
2218 action_block_iter->succ = new_succ_pos;
2219 } else assert(old_succ_pos == new_succ_pos);
2220
2221 // adapt the old out-slice immediately
2222 // If the old out-slice becomes empty, then out_slice_begin ==
2223 // new_succ_pos, so the two following assignments will assign the
2224 // same variable. The second assignment is the relevant one.
2225 out_slice_begin->begin_or_before_end = new_succ_pos - 1;
2226
2227 // adapt the new out-slice, as far as is possible now:
2228 // make the begin_or_before_end pointers of the first and last
2229 // transition in the slice correct immediately. The other
2230 // begin_or_before_end pointers need to be corrected after all
2231 // transitions in the new bunch have been positioned correctly.
2232 if (first_transition_of_state)
2233 {
2234 new_succ_pos->begin_or_before_end = new_succ_pos;
2235 }
2236 else
2237 {
2238 succ_entry* const out_slice_before_end(
2239 new_succ_pos[1].begin_or_before_end); assert(nullptr != out_slice_before_end);
2240 assert(new_succ_pos < out_slice_before_end);
2241 assert(out_slice_before_end->block_bunch->pred->action_block->succ ==
2242 out_slice_before_end);
2243 assert(new_succ_pos + 1 == out_slice_before_end->begin_or_before_end);
2244 out_slice_before_end->begin_or_before_end = new_succ_pos; assert(out_slice_before_end <
2245 new_succ_pos->block_bunch->pred->source->succ_inert.begin);
2246 new_succ_pos->begin_or_before_end = out_slice_before_end; assert(bunch_T_a_Bprime == out_slice_before_end->bunch());
2247 }
2248
2249 /* - - - - - - - adapt part_tr.block_bunch - - - - - - - */ assert(new_succ_pos == action_block_iter->succ);
2250
2251 block_bunch_entry* const old_block_bunch_pos(
2252 new_succ_pos->block_bunch); assert(old_block_bunch_pos->pred->action_block == action_block_iter);
2253 block_t*const source_block = old_block_bunch_pos->pred->source->bl.ock; assert(!old_block_bunch_pos->slice.is_null());
2254 block_bunch_slice_iter_t const old_block_bunch_slice(
2255 old_block_bunch_pos->slice);
2256 block_bunch_entry* const new_block_bunch_pos(
2257 old_block_bunch_slice->end - 1); assert(nullptr != new_block_bunch_pos->pred->action_block->succ);
2258 assert(new_block_bunch_pos->pred->action_block->succ->block_bunch ==
2259 new_block_bunch_pos);
2260 // create or adapt the new block_bunch-slice
2261 block_bunch_slice_iter_t new_block_bunch_slice;
2262 if (new_block_bunch_pos + 1 >= block_bunch_inert_begin ||
2263 (new_block_bunch_slice = (block_bunch_slice_iter_t)
2264 new_block_bunch_pos[1].slice, assert(!new_block_bunch_pos[1].slice.is_null()),
2265 bunch_T_a_Bprime != new_block_bunch_slice->bunch ||
2266 source_block != new_block_bunch_slice->source_block()))
2267 { assert(first_transition_of_state);
2268 // This is the first transition in the block_bunch-slice.
2269 // The old block_bunch-slice becomes unstable, and the new
2270 // block_bunch-slice is created unstable.
2271
2272 // Note that the new block_bunch-slice should precede the old one.
2273
2274 #ifdef USE_SIMPLE_LIST
2275 new_block_bunch_slice = splitter_list.emplace_back(
2277 new_block_bunch_pos + 1, bunch_T_a_Bprime, false);
2278 #else
2279 splitter_list.emplace_back(new_block_bunch_pos + 1,
2280 bunch_T_a_Bprime, false);
2281 new_block_bunch_slice = std::prev(splitter_list.end());
2282 #endif
2283 ++nr_of_block_bunch_slices; ONLY_IF_DEBUG( new_block_bunch_slice->work_counter =
2284 old_block_bunch_slice->work_counter; )
2286 source_block->stable_block_bunch, old_block_bunch_slice);
2287 old_block_bunch_slice->make_unstable();
2288 } assert(!new_block_bunch_slice->is_stable());
2289
2290 // move the transition to the end of its block_bunch-slice
2291 if (old_block_bunch_pos < new_block_bunch_pos)
2292 {
2293 std::swap(old_block_bunch_pos->pred, new_block_bunch_pos->pred); assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2294 old_block_bunch_pos->pred->action_block->succ->block_bunch =
2295 old_block_bunch_pos; assert(new_succ_pos == new_block_bunch_pos->pred->action_block->succ);
2296 new_succ_pos->block_bunch = new_block_bunch_pos;
2297 } else assert(new_block_bunch_pos == old_block_bunch_pos);
2298 assert(new_block_bunch_pos->slice == old_block_bunch_slice);
2299 new_block_bunch_pos->slice = new_block_bunch_slice;
2300
2301 /* adapt the old block_bunch-slice */ assert(new_block_bunch_pos + 1 == old_block_bunch_slice->marked_begin);
2302 old_block_bunch_slice->end = new_block_bunch_pos;
2303 old_block_bunch_slice->marked_begin = new_block_bunch_pos; assert(nullptr != new_block_bunch_pos);
2304 if (old_block_bunch_slice->empty())
2305 { assert(!old_block_bunch_slice->is_stable());
2307 old_block_bunch_slice); assert(!new_block_bunch_slice->is_stable());
2309
2310 // Because now every bottom state has a transition in the new
2311 // bunch, and no state has a transition in the old bunch, there
2312 // is no need to refine this block. So we make this
2313 // block_bunch-slice stable again.
2314 source_block->stable_block_bunch.splice(
2315 source_block->stable_block_bunch.end(),
2316 splitter_list, new_block_bunch_slice);
2317 new_block_bunch_slice->make_stable();
2318
2319 // unmark the states
2320 // (This transition must be the last transition from
2321 // source_block in the new bunch, so unmarking the states now
2322 // will not be undone by later markings of other states.)
2323 source_block->marked_nonbottom_begin = source_block->end; assert(source_block->marked_bottom_begin == source_block->begin);
2324 source_block->marked_bottom_begin = source_block->nonbottom_begin;
2325 }
2326 }
2327
2328
2349 ONLY_IF_DEBUG( template <class LTS_TYPE> )
2351 action_block_entry* const action_block_iter, ONLY_IF_DEBUG( const bisim_partitioner_dnj<LTS_TYPE>& partitioner,
2352 bunch_t* const bunch_T_a_Bprime, )
2353 bunch_t* const large_splitter_bunch)
2354 { assert(nullptr != bunch_T_a_Bprime);
2355
2356 /* - - - - - - - - adapt part_tr.succ - - - - - - - - */
2357
2358 // We already moved the transition in part_tr.succ to the correct
2359 // place in first_move_transition_to_new_bunch(); now we have to
2360 // set begin_or_before_end.
2361 succ_entry* const new_succ_pos(action_block_iter->succ); assert(nullptr != new_succ_pos);
2362 assert(new_succ_pos->block_bunch->pred->action_block == action_block_iter);
2363 state_info_entry* const source(
2364 new_succ_pos->block_bunch->pred->source); assert(source->pos->st == source);
2365 assert(new_succ_pos < source->succ_inert.begin);
2366 assert(source == &partitioner.part_st.state_info.front() ||
2367 source[-1].succ_inert.begin <= new_succ_pos);
2368 assert(nullptr != new_succ_pos->begin_or_before_end);
2369 succ_entry* const new_begin_or_before_end(
2370 new_succ_pos->begin_or_before_end->begin_or_before_end); assert(nullptr != new_begin_or_before_end);
2371 assert(new_begin_or_before_end->block_bunch->pred->action_block->succ ==
2372 new_begin_or_before_end);
2373 if (new_begin_or_before_end < new_succ_pos)
2374 { assert(source == &partitioner.part_st.state_info.front() ||
2375 source[-1].succ_inert.begin <= new_begin_or_before_end);
2376 new_succ_pos->begin_or_before_end = new_begin_or_before_end;
2377 }
2378 else
2379 { assert(new_begin_or_before_end == new_succ_pos);
2380 // This is the first or the last transition in the out-slice.
2381 const succ_entry* const new_before_end(
2382 new_begin_or_before_end->begin_or_before_end); assert(nullptr != new_before_end);
2383 if (new_begin_or_before_end <= new_before_end)
2384 { assert(&partitioner.part_tr.succ.cbegin()[1] == new_begin_or_before_end ||
2385 /* This is the first transition in the new out-slice. */ new_begin_or_before_end[-1].block_bunch->pred->source < source ||
2386 /* If there is still a transition in the old out-slice, */ new_begin_or_before_end[-1].bunch() != bunch_T_a_Bprime);
2387 /* we prepay for it. */ assert(new_before_end + 1 == source->succ_inert.begin ||
2388 bunch_T_a_Bprime != new_before_end[1].bunch());
2389 if (source == new_succ_pos[-1].block_bunch->pred->source &&
2390 new_succ_pos[-1].bunch() == large_splitter_bunch)
2391 {
2392 // Mark one transition in the large slice
2393 block_bunch_entry* const old_block_bunch_pos(
2394 new_succ_pos[-1].block_bunch); assert(!old_block_bunch_pos->slice.is_null());
2395 assert(old_block_bunch_pos->pred->action_block->succ == new_succ_pos - 1);
2396 block_bunch_slice_iter_t const large_splitter_slice(
2397 old_block_bunch_pos->slice);
2398 if (!large_splitter_slice->is_stable())
2399 {
2400 block_bunch_entry* const new_block_bunch_pos(
2401 large_splitter_slice->marked_begin - 1); assert(nullptr != new_block_bunch_pos->pred->action_block->succ);
2402 assert(new_block_bunch_pos->pred->action_block->succ->block_bunch ==
2403 new_block_bunch_pos);
2404 if (old_block_bunch_pos < new_block_bunch_pos)
2405 {
2406 std::swap(old_block_bunch_pos->pred,
2407 new_block_bunch_pos->pred); // assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2408 old_block_bunch_pos->pred->action_block->
2409 succ->block_bunch = old_block_bunch_pos; assert(new_block_bunch_pos->pred->action_block->succ == new_succ_pos - 1);
2410 new_succ_pos[-1].block_bunch = new_block_bunch_pos;
2411 }
2412 large_splitter_slice->marked_begin=new_block_bunch_pos; assert(nullptr != new_block_bunch_pos);
2413 } else assert(1 >= source->bl.ock->size());
2414 }
2415 } else assert(source == &partitioner.part_st.state_info.front() ||
2416 source[-1].succ_inert.begin <= new_before_end);
2417 }
2418 #ifndef NDEBUG
2419 /* - - - - - - - adapt part_tr.block_bunch - - - - - - - */ const block_bunch_entry* new_block_bunch_pos(new_succ_pos->block_bunch);
2420 assert(new_block_bunch_pos->pred->action_block->succ == new_succ_pos);
2421 assert(!new_block_bunch_pos->slice.is_null());
2422 /* Nothing needs to be done. */ block_bunch_slice_const_iter_t const new_block_bunch_slice(
2423 new_block_bunch_pos->slice);
2424 assert(new_block_bunch_pos < new_block_bunch_slice->end);
2425 assert(bunch_T_a_Bprime == new_block_bunch_slice->bunch);
2426 if (new_block_bunch_pos + 1 < new_block_bunch_slice->end) return;
2427
2428 // This transition is the last in the block_bunch-slice. If there
2429 // were some task that would need to be done exactly once per
2430 // block_bunch-slice, this would be the moment.
2431 do assert(source->bl.ock == new_block_bunch_pos->pred->source->bl.ock);
2432 while ((--new_block_bunch_pos)->slice == new_block_bunch_slice);
2433 assert(new_block_bunch_pos <= &partitioner.part_tr.block_bunch.front() ||
2434 source->bl.ock != new_block_bunch_pos->pred->source->bl.ock ||
2435 bunch_T_a_Bprime != new_block_bunch_pos->slice->bunch);
2436 #endif
2437 }
2438
2439
2440 private:
2468 ONLY_IF_DEBUG( template <class LTS_TYPE> )
2470 succ_entry* out_slice_end, ONLY_IF_DEBUG( const bisim_partitioner_dnj<LTS_TYPE>& partitioner, )
2471 #ifndef USE_SIMPLE_LIST
2472 block_t* const old_block,
2473 #endif
2474 block_bunch_slice_const_iter_t const splitter_T)
2475 { assert(&succ.cbegin()[1] < out_slice_end);
2476 succ_entry* const out_slice_begin(
2477 out_slice_end[-1].begin_or_before_end); assert(nullptr != out_slice_begin);
2478 assert(out_slice_begin < out_slice_end);
2479 assert(out_slice_begin->block_bunch->pred->action_block->succ ==
2480 out_slice_begin);
2481 block_bunch_entry* old_block_bunch_pos(out_slice_end[-1].block_bunch); assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2482 assert(!old_block_bunch_pos->slice.is_null());
2483 block_bunch_slice_iter_t const old_block_bunch_slice(
2484 old_block_bunch_pos->slice); assert(old_block_bunch_pos->pred->action_block->succ->block_bunch ==
2485 old_block_bunch_pos);
2486 if (&*splitter_T == &*old_block_bunch_slice) return out_slice_begin;
2487
2488 block_bunch_entry* old_block_bunch_slice_end(
2489 old_block_bunch_slice->end);
2490 state_info_entry* const source(old_block_bunch_pos->pred->source); assert(out_slice_end <= source->succ_inert.begin);
2491 assert(&partitioner.part_st.state_info.front() == source ||
2492 source[-1].succ_inert.begin < out_slice_end);
2493 block_t* const new_block(source->bl.ock); assert(source == out_slice_begin->block_bunch->pred->source);
2494 block_bunch_slice_iter_t new_block_bunch_slice; assert(source->pos->st == source);
2495 if (old_block_bunch_slice_end >= block_bunch_inert_begin ||
2496 new_block != old_block_bunch_slice_end->pred->source->bl.ock ||
2497 (new_block_bunch_slice = (block_bunch_slice_iter_t)
2498 old_block_bunch_slice_end->slice, assert(!old_block_bunch_slice_end->slice.is_null()),
2499 old_block_bunch_slice->bunch != new_block_bunch_slice->bunch))
2500 {
2501 // the new block_bunch-slice is not suitable; create a new one and
2502 // insert it into the correct list.
2503 if (old_block_bunch_slice->is_stable())
2504 {
2505 // In most cases, but not always, the source is a bottom state.
2506 #ifdef USE_SIMPLE_LIST
2507 new_block_bunch_slice =
2510 old_block_bunch_slice->end,
2511 old_block_bunch_slice->bunch, true);
2512 #else
2514 old_block_bunch_slice->end,
2515 old_block_bunch_slice->bunch, true);
2516 new_block_bunch_slice =
2517 new_block->stable_block_bunch.begin();
2518 #endif
2519 }
2520 else
2521 {
2522 #ifdef USE_SIMPLE_LIST
2523 new_block_bunch_slice = splitter_list.emplace_after(
2525 old_block_bunch_slice,
2526 old_block_bunch_slice->end,
2527 old_block_bunch_slice->bunch, false);
2528 #else
2529 new_block_bunch_slice = splitter_list.emplace(
2530 std::next(old_block_bunch_slice),
2531 old_block_bunch_slice->end,
2532 old_block_bunch_slice->bunch, false);
2533 #endif
2534 }
2535 ++nr_of_block_bunch_slices; ONLY_IF_DEBUG( new_block_bunch_slice->work_counter =
2536 old_block_bunch_slice->work_counter; )
2537 }
2538 ONLY_IF_DEBUG( unsigned max_counter = check_complexity::log_n -
2539 check_complexity::ilog2(new_block->size()); )
2540 /* move all transitions in this out-slice to the new block_bunch */ assert(out_slice_begin < out_slice_end);
2541 do
2542 { assert(old_block_bunch_pos == out_slice_end[-1].block_bunch);
2543 --out_slice_end; assert(old_block_bunch_pos->slice == old_block_bunch_slice);
2544 assert(source == out_slice_end->block_bunch->pred->source);
2545 --old_block_bunch_slice_end; // assign work already now because the transition may be moved to several
2546 // places:
2547 old_block_bunch_slice_end->slice = new_block_bunch_slice; mCRL2complexity(old_block_bunch_pos->pred, add_work(
2548 check_complexity::move_out_slice_to_new_block, max_counter), partitioner);
2549 if (old_block_bunch_slice->is_stable() || ( assert(!new_block_bunch_slice->is_stable()),
2550 old_block_bunch_slice->marked_begin >
2551 old_block_bunch_slice_end &&
2552 (/* As the old block_bunch-slice has no marked */ assert(nullptr != old_block_bunch_slice_end),
2553 // transitions, it is enough to adapt its marked_begin
2554 // and then do a simple (two-way) swap.
2555 old_block_bunch_slice->marked_begin =
2556 old_block_bunch_slice_end, true)))
2557 {
2558 // The old block_bunch-slice is stable, or it has no
2559 // marked transitions.
2560 std::swap(old_block_bunch_pos->pred,
2561 old_block_bunch_slice_end->pred);
2562 }
2563 else
2564 {
2565 // The old block_bunch-slice is unstable and has marked
2566 // transitions.
2567 pred_entry* const old_pred = old_block_bunch_pos->pred;
2568 if (old_block_bunch_pos < old_block_bunch_slice->marked_begin)
2569 {
2570 // The transition is not marked, but there are other
2571 // marked transitions in the old block_bunch-slice.
2572 // Move the transition to the non-marked part of the
2573 // new block_bunch-slice.
2574 block_bunch_entry* const old_marked_begin =
2575 old_block_bunch_slice->marked_begin - 1; assert(old_block_bunch_pos < old_block_bunch_slice_end);
2576 old_block_bunch_slice->marked_begin = old_marked_begin;
2577
2578 old_block_bunch_pos->pred = old_marked_begin->pred;
2579 old_marked_begin->pred = old_block_bunch_slice_end->pred;
2580 old_block_bunch_slice_end->pred = old_pred; assert(nullptr != old_marked_begin->pred->action_block->succ);
2581
2582 old_marked_begin->pred->action_block->succ->
2583 block_bunch = old_marked_begin;
2584 }
2585 else
2586 {
2587 // The transition is marked. Move to the marked part
2588 // of the new block_bunch-slice.
2589 block_bunch_entry* const new_marked_begin =
2590 new_block_bunch_slice->marked_begin - 1;
2591 new_block_bunch_slice->marked_begin = new_marked_begin; assert(old_block_bunch_pos < new_marked_begin ||
2592 old_block_bunch_pos == old_block_bunch_slice_end);
2593 old_block_bunch_pos->pred=old_block_bunch_slice_end->pred; assert(old_block_bunch_slice_end <= new_marked_begin);
2594 old_block_bunch_slice_end->pred = new_marked_begin->pred;
2595 new_marked_begin->pred = old_pred; assert(out_slice_end == new_marked_begin->pred->action_block->succ);
2596
2597 out_slice_end->block_bunch = new_marked_begin;
2598 }
2599 } assert(nullptr != old_block_bunch_slice_end->pred->action_block->succ);
2600 old_block_bunch_slice_end->pred->action_block->succ->block_bunch =
2601 old_block_bunch_slice_end; assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2602 old_block_bunch_pos->pred->action_block->succ->block_bunch =
2603 old_block_bunch_pos;
2604 }
2605 while (out_slice_begin < out_slice_end &&
2606 (old_block_bunch_pos = out_slice_end[-1].block_bunch, true));
2607 old_block_bunch_slice->end = old_block_bunch_slice_end;
2608
2609 if (old_block_bunch_slice->empty())
2610 {
2611 #ifdef USE_SIMPLE_LIST
2614 old_block_bunch_slice);
2615 #else
2616 if (old_block_bunch_slice->is_stable())
2617 {
2618 // If the new block is R, then the old (U) block loses
2619 // exactly one stable block_bunch-slice, namely the one we
2620 // just stabilised for (`splitter_T`). We could perhaps
2621 // optimize this by moving that slice as a whole to the new
2622 // block -- perhaps later.
2623 //
2624 // If the new block is U, then the old (R) block loses
2625 // no stable block_bunch-slices if it contains any bottom
2626 // state. If it doesn't contain any bottom state, it will
2627 // definitely keep `splitter_T`, but nothing else can be
2628 // guaranteed.
2629 //
2630 // So old_block_bunch_slice may be deleted, in particular
2631 // if the new block is U, but not exclusively.
2632 old_block->stable_block_bunch.erase(old_block_bunch_slice);
2633 }
2634 else
2635 {
2636 splitter_list.erase(old_block_bunch_slice);
2637 }
2638 #endif
2640 }
2641 return out_slice_begin;
2642 }
2643
2644
2655 {
2656 action_block_entry* const old_action_block_pos(
2657 pred_iter->action_block); assert(nullptr != old_action_block_pos->succ);
2658 assert(old_action_block_pos->succ->block_bunch->pred == pred_iter);
2659 action_block_entry* const action_block_slice_begin(
2661 )); assert(nullptr != action_block_slice_begin->succ);
2662 assert(action_block_slice_begin->succ->block_bunch->pred->action_block ==
2663 action_block_slice_begin);
2664 action_block_entry* const new_action_block_pos(
2665 action_block_slice_begin->begin_or_before_end); assert(nullptr != new_action_block_pos);
2666 assert(action_block_slice_begin == new_action_block_pos->begin_or_before_end);
2667 assert(nullptr != new_action_block_pos->succ);
2668 assert(new_action_block_pos->succ->block_bunch->pred->action_block ==
2669 /* move the transition to the end of the action_block-slice */ new_action_block_pos);
2670 if (old_action_block_pos < new_action_block_pos)
2671 {
2672 succ_entry* const temp(new_action_block_pos->succ); assert(nullptr != temp); assert(nullptr != old_action_block_pos->succ);
2673 new_action_block_pos->succ = old_action_block_pos->succ;
2674 old_action_block_pos->succ = temp;
2675 temp->block_bunch->pred->action_block = old_action_block_pos; assert(pred_iter == new_action_block_pos->succ->block_bunch->pred);
2676 pred_iter->action_block = new_action_block_pos;
2677
2678 // adapt the old action_block-slice immediately
2679 action_block_slice_begin->begin_or_before_end =
2680 new_action_block_pos - 1;
2681 }
2682 else
2683 { assert(old_action_block_pos == new_action_block_pos);
2684 if (action_block_slice_begin < new_action_block_pos)
2685 {
2686 // The old action_block-slice is not empty, so we have to adapt
2687 // the pointer at the beginning. (If it is empty, it may
2688 // happen that `new_action_block_pos - 1` is an illegal value.)
2689 action_block_slice_begin->begin_or_before_end =
2690 new_action_block_pos - 1;
2691 }
2693 } assert(nullptr != new_action_block_pos->succ);
2694 assert(pred_iter == new_action_block_pos->succ->block_bunch->pred);
2695 // adapt the new action_block-slice, as far as is possible now
2696 // make the begin_or_before_end pointers of the first and last
2697 // transition in the slice correct immediately. The other
2698 // begin_or_before_end pointers need to be corrected after all
2699 // transitions in the new bunch have been positioned correctly.
2700 if (new_action_block_pos + 1 >= action_block_inert_begin ||
2701 nullptr == new_action_block_pos[1].succ ||
2702 new_action_block_pos[1].succ->bunch() !=
2703 new_action_block_pos->succ->bunch() ||
2704 new_action_block_pos[1].succ->block_bunch->pred->target->bl.ock !=
2705 pred_iter->target->bl.ock)
2706 {
2707 // This is the first transition that moves to this new
2708 // action_block-slice.
2709 new_action_block_pos->begin_or_before_end = new_action_block_pos;
2711 }
2712 else
2713 {
2714 action_block_entry* const action_block_slice_before_end(
2715 new_action_block_pos[1].begin_or_before_end); assert(nullptr != action_block_slice_before_end);
2716 assert(new_action_block_pos < action_block_slice_before_end);
2717 assert(nullptr != action_block_slice_before_end->succ);
2718 assert(action_block_slice_before_end->succ->block_bunch->pred->action_block ==
2719 action_block_slice_before_end);
2720 assert(new_action_block_pos + 1 ==
2721 action_block_slice_before_end->begin_or_before_end);
2722 action_block_slice_before_end->begin_or_before_end =
2723 new_action_block_pos; assert(action_block_slice_before_end->succ->block_bunch->
2724 pred->target->bl.ock == pred_iter->target->bl.ock);
2725 new_action_block_pos->begin_or_before_end =
2726 action_block_slice_before_end; assert(action_block_slice_before_end < action_block_inert_begin);
2727 }
2728 }
2729
2730
2738 pred_entry* const pred_iter)
2739 {
2740 action_block_entry* const new_action_block_pos(
2741 pred_iter->action_block); assert(nullptr != new_action_block_pos->succ);
2742 assert(new_action_block_pos->succ->block_bunch->pred == pred_iter);
2743 action_block_entry* const old_begin_or_before_end(
2744 new_action_block_pos->begin_or_before_end); assert(nullptr != old_begin_or_before_end);
2745 assert(nullptr != old_begin_or_before_end->succ);
2746 assert(old_begin_or_before_end->succ->block_bunch->pred->action_block ==
2747 old_begin_or_before_end);
2748 if (action_block_entry* const new_begin_or_before_end(
2749 old_begin_or_before_end->begin_or_before_end); assert(nullptr != new_begin_or_before_end),
2750 assert(nullptr != new_begin_or_before_end->succ),
2751 assert(new_begin_or_before_end->succ->block_bunch->pred->action_block ==
2752 new_begin_or_before_end),
2753 new_begin_or_before_end < new_action_block_pos)
2754 { assert(old_begin_or_before_end==new_begin_or_before_end->begin_or_before_end);
2755 new_action_block_pos->begin_or_before_end =
2756 new_begin_or_before_end; assert(new_action_block_pos <= old_begin_or_before_end);
2757 return;
2758 } else assert(new_begin_or_before_end == new_action_block_pos);
2759 if (old_begin_or_before_end < new_action_block_pos) return;
2760
2761 // this is the first transition in the new action_block-slice.
2762 // Check whether the bunch it belongs to has become nontrivial.
2763 bunch_t* const bunch(new_action_block_pos->succ->bunch());
2764 if (!bunch->is_trivial()) { return; } assert(old_begin_or_before_end + 1 == bunch->end);
2765 if (bunch->begin < new_action_block_pos)
2766 {
2767 make_nontrivial(bunch);
2768 }
2769 }
2770
2771
2796 bool make_noninert(pred_entry* const old_pred_pos,
2797 block_bunch_slice_iter_or_null_t* const new_noninert_block_bunch_ptr)
2798 {
2799 state_info_entry* const source(old_pred_pos->source); assert(source->pos->st == source);
2800 state_info_entry* const target(old_pred_pos->target); assert(target->pos->st == target);
2801
2802 action_block_entry* const new_action_block_pos(
2803 action_block_inert_begin++); assert(nullptr != new_action_block_pos->succ);
2804 assert(new_action_block_pos->succ->block_bunch->pred->action_block ==
2805 new_action_block_pos);
2806 succ_entry* const new_succ_pos(source->succ_inert.begin++); assert(new_succ_pos->block_bunch->pred->action_block->succ == new_succ_pos);
2807 block_bunch_entry* const new_block_bunch_pos(
2808 block_bunch_inert_begin++); assert(nullptr != new_block_bunch_pos->pred->action_block->succ);
2809 assert(new_block_bunch_pos->pred->action_block->succ->block_bunch ==
2810 new_block_bunch_pos);
2811 action_block_entry* const old_action_block_pos(
2812 old_pred_pos->action_block); assert(new_action_block_pos <= old_action_block_pos);
2813
2814 succ_entry* const old_succ_pos(old_action_block_pos->succ); assert(nullptr != old_succ_pos);
2815 block_bunch_entry* const old_block_bunch_pos(
2816 old_succ_pos->block_bunch); assert(old_pred_pos == old_block_bunch_pos->pred);
2817 pred_entry* const new_pred_pos(target->pred_inert.begin++); assert(nullptr != new_pred_pos->action_block->succ);
2818 assert(new_pred_pos->action_block->succ->block_bunch->pred == new_pred_pos);
2819
2820 /* adapt action_block */ assert(nullptr == new_action_block_pos->begin_or_before_end);
2821 if (new_action_block_pos < old_action_block_pos)
2822 { assert(nullptr == old_action_block_pos->begin_or_before_end);
2823 old_action_block_pos->succ = new_action_block_pos->succ; assert(nullptr != old_action_block_pos->succ);
2824 assert(old_action_block_pos->succ->block_bunch->pred->action_block ==
2825 new_action_block_pos);
2826 old_action_block_pos->succ->block_bunch->pred->action_block =
2827 old_action_block_pos;
2828 } else assert(new_action_block_pos == old_action_block_pos);
2829 new_action_block_pos->succ = new_succ_pos; assert(nullptr != new_succ_pos);
2830 // new_action_block_pos->begin_or_before_end = ...; -- see below
2831
2832 /* adapt succ */ assert(nullptr == new_succ_pos->begin_or_before_end);
2833 if (new_succ_pos < old_succ_pos)
2834 { assert(nullptr == old_succ_pos->begin_or_before_end);
2835 old_succ_pos->block_bunch = new_succ_pos->block_bunch; assert(old_succ_pos->block_bunch->pred->action_block->succ == new_succ_pos);
2836 old_succ_pos->block_bunch->pred->action_block->succ = old_succ_pos; assert(nullptr != old_succ_pos);
2837 } else assert(new_succ_pos == old_succ_pos);
2838 new_succ_pos->block_bunch = new_block_bunch_pos;
2839 // new_succ_pos->begin_or_before_end = ...; -- see below
2840
2841 /* adapt block_bunch */ assert(new_block_bunch_pos->slice.is_null());
2842 if (new_block_bunch_pos < old_block_bunch_pos)
2843 { assert(old_block_bunch_pos->slice.is_null());
2844 assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2845 old_block_bunch_pos->pred = new_block_bunch_pos->pred; assert(old_block_bunch_pos->pred->action_block->succ->block_bunch ==
2846 new_block_bunch_pos);
2847 assert(nullptr != old_block_bunch_pos->pred->action_block->succ);
2848 old_block_bunch_pos->pred->action_block->succ->block_bunch =
2849 old_block_bunch_pos;
2850 } else assert(new_block_bunch_pos == old_block_bunch_pos);
2851 new_block_bunch_pos->pred = new_pred_pos;
2852 // new_block_bunch_pos->slice = ...; -- see below
2853
2854 // adapt pred
2855 if (new_pred_pos < old_pred_pos)
2856 {
2857 // We need std::swap here to swap the whole content, including
2858 // work counters in case we measure the work.
2859 std::swap(*old_pred_pos, *new_pred_pos); assert(nullptr != old_pred_pos->action_block->succ);
2860 assert(old_pred_pos->action_block->succ->block_bunch->pred == new_pred_pos);
2861 old_pred_pos->action_block->succ->block_bunch->pred = old_pred_pos;
2862 } else assert(new_pred_pos == old_pred_pos);
2863 new_pred_pos->action_block = new_action_block_pos;
2864
2865 /* make the state a bottom state if necessary */ assert(source->bl.ock->nonbottom_begin <= source->pos);
2866 bool became_bottom(false); assert(succ.back().block_bunch->pred->source != source);
2867 if (source != source->succ_inert.begin->block_bunch->pred->source)
2868 {
2869 block_t* const source_block(source->bl.ock);
2870 // make the state a marked bottom state
2871 if (source->pos >= source_block->marked_nonbottom_begin)
2872 {
2873 std::swap(*source->pos,
2874 *source_block->marked_nonbottom_begin++);
2875 } assert(source->pos < source_block->marked_nonbottom_begin);
2876 std::swap(*source->pos, *source_block->nonbottom_begin++);
2878 became_bottom = true;
2879 }
2880
2881 bunch_t* new_noninert_bunch; assert(nullptr != new_action_block_pos);
2882 if (!new_noninert_block_bunch_ptr->is_null())
2883 {
2884 // There is already some new non-inert transition from this block.
2885 // So we can reuse this block_bunch and its bunch.
2886 // (However, it may be the case that the current transition goes to
2887 // another block; in the latter case, we have to create a new
2888 // action_block-slice.)
2889
2890 // extend the bunch
2891 new_noninert_bunch = (*new_noninert_block_bunch_ptr)->bunch; assert(new_action_block_pos >= new_noninert_bunch->end);
2893 for (const action_block_entry*temp_action_block_pos=new_action_block_pos ;
2894 temp_action_block_pos > new_noninert_bunch->end ; )
2895 {
2896 assert(nullptr == (--temp_action_block_pos)->succ);
2897 }
2898 )
2899 new_noninert_bunch->end = action_block_inert_begin;
2900 /* extend the block_bunch-slice */ assert((*new_noninert_block_bunch_ptr)->end == new_block_bunch_pos);
2901 (*new_noninert_block_bunch_ptr)->end = block_bunch_inert_begin;
2902 if (!(*new_noninert_block_bunch_ptr)->is_stable())
2903 { assert((*new_noninert_block_bunch_ptr)->marked_begin == new_block_bunch_pos);
2904 (*new_noninert_block_bunch_ptr)->marked_begin =
2906 }
2907 new_block_bunch_pos->slice = *new_noninert_block_bunch_ptr;
2908
2909 /* adapt the action_block-slice */ assert(new_noninert_bunch->begin < new_action_block_pos);
2910 if (nullptr != new_action_block_pos[-1].succ &&
2911 target->bl.ock == new_action_block_pos[-1].
2912 succ->block_bunch->pred->target->bl.ock)
2913 {
2914 // the action_block-slice is suitable: extend it
2915 action_block_entry* const action_block_slice_begin(
2916 new_action_block_pos[-1].begin_or_before_end); assert(nullptr != action_block_slice_begin);
2917 assert(new_action_block_pos-1==action_block_slice_begin->begin_or_before_end);
2918 assert(nullptr != action_block_slice_begin->succ);
2919 assert(action_block_slice_begin->succ->block_bunch->pred->action_block ==
2920 action_block_slice_begin);
2921 action_block_slice_begin->begin_or_before_end =
2922 new_action_block_pos;
2923 new_action_block_pos->begin_or_before_end =
2924 action_block_slice_begin;
2925 }
2926 else
2927 {
2928 // create a new action_block-slice
2929 new_action_block_pos->begin_or_before_end=new_action_block_pos;
2930 if (new_noninert_bunch->is_trivial())
2931 { // Only during initialisation, it may happen that we add new non-inert
2932 make_nontrivial(new_noninert_bunch); // transitions to a nontrivial bunch:
2933 }
2934 #ifndef NDEBUG
2935 else
2936 {
2937 // We make sure that new_noninert_bunch is the first bunch in
2938 // action_block (and because it's always the last one, it will be the
2939 // only one, so there is only one bunch, as ).
2940 for (const action_block_entry* iter = action_block_begin;
2941 iter < new_noninert_bunch->begin; ++iter)
2942 {
2943 assert(nullptr == iter->succ);
2944 assert(nullptr == iter->begin_or_before_end);
2945 }
2946 }
2947 #endif
2949 }
2950
2951 /* adapt the out-slice */ assert(source != succ.front().block_bunch->pred->source);
2952 if (source == new_succ_pos[-1].block_bunch->pred->source &&
2953 new_succ_pos[-1].bunch() == new_noninert_bunch)
2954 {
2955 // the out-slice is suitable: extend it.
2956 succ_entry* const out_slice_begin(
2957 new_succ_pos[-1].begin_or_before_end); assert(nullptr != out_slice_begin);
2958 assert(new_succ_pos - 1 == out_slice_begin->begin_or_before_end);
2959 out_slice_begin->begin_or_before_end = new_succ_pos; assert(out_slice_begin->block_bunch->pred->action_block->succ ==
2960 out_slice_begin);
2961 new_succ_pos->begin_or_before_end = out_slice_begin;
2962 return became_bottom;
2963 }
2964 }
2965 else
2966 {
2967 // create a new bunch for noninert transitions
2968 new_noninert_bunch =
2969 #ifdef USE_POOL_ALLOCATOR
2970 storage.template construct<bunch_t>
2971 #else
2972 new bunch_t
2973 #endif
2974 (new_action_block_pos, action_block_inert_begin);
2975 ++nr_of_bunches;
2976
2977 // create a new block_bunch-slice
2978 #ifdef USE_SIMPLE_LIST
2979 block_bunch_slice_iter_t new_noninert_block_bunch(
2982 block_bunch_inert_begin, new_noninert_bunch, false));
2983 #else
2985 new_noninert_bunch, false);
2986 block_bunch_slice_iter_t new_noninert_block_bunch(
2987 std::prev(splitter_list.end()));
2988 #endif
2990 new_block_bunch_pos->slice = new_noninert_block_bunch;
2991 *new_noninert_block_bunch_ptr = new_noninert_block_bunch;
2992
2993 // create a new action_block-slice
2994 new_action_block_pos->begin_or_before_end = new_action_block_pos;
2996 } assert(&succ.cbegin()[1] == new_succ_pos ||
2997 new_succ_pos[-1].block_bunch->pred->source < source ||
2998 /* create a new out-slice */ new_succ_pos[-1].bunch() != new_noninert_bunch);
2999 new_succ_pos->begin_or_before_end = new_succ_pos;
3000 return became_bottom;
3001 }
3002
3003
3004 public:
3026 ONLY_IF_DEBUG( template<class LTS_TYPE> )
3028 block_t* const new_block,
3029 block_t* const old_block, ONLY_IF_DEBUG( const bisim_partitioner_dnj<LTS_TYPE>& partitioner, )
3030 bool const add_new_noninert_to_splitter,
3031 const block_bunch_slice_iter_t splitter_T,
3032 enum new_block_mode_t const new_block_mode)
3033 { assert(splitter_T->is_stable());
3034 // We begin with a bottom state so the new block gets a sorted list of
3035 // stable block_bunch-slices.
3036 permutation_entry* s_iter(new_block->begin); assert(s_iter < new_block->end);
3037 do
3038 {
3039 state_info_entry* const s(s_iter->st); assert(new_block == s->bl.ock);
3040 assert(s->pos == s_iter);
3041 /* - - - - - - adapt part_tr.block_bunch - - - - - - */
3042 assert(s != succ.front().block_bunch->pred->source);
3043 for (succ_entry* succ_iter(s->succ_inert.begin);
3044 s == succ_iter[-1].block_bunch->pred->source; )
3045 {
3046 succ_iter = move_out_slice_to_new_block(succ_iter, ONLY_IF_DEBUG( partitioner, )
3047 #ifndef USE_SIMPLE_LIST
3048 old_block,
3049 #endif
3050 splitter_T); assert(succ_iter->block_bunch->pred->action_block->succ == succ_iter);
3051 assert(s == succ_iter->block_bunch->pred->source);
3052 // add_work_to_out_slice(succ_iter, ...) -- subsumed in the call below
3053 }
3054
3055 /*- - - - - - adapt part_tr.action_block - - - - - -*/
3056 assert(s != pred.front().target);
3057 for (pred_entry* pred_iter(s->pred_inert.begin);
3058 s == (--pred_iter)->target; )
3059 { assert(&pred.front() < pred_iter);
3060 assert(nullptr != pred_iter->action_block->succ);
3061 assert(pred_iter->action_block->succ->block_bunch->pred == pred_iter);
3062 first_move_transition_to_new_action_block(pred_iter); // mCRL2complexity(pred_iter, ...) -- subsumed in the call below
3063 } // mCRL2complexity(s, ...) -- subsumed in the call at the end
3064 }
3065 while (++s_iter < new_block->end);
3066
3067 if (new_block_is_R == new_block_mode)
3068 { assert(splitter_T->source_block() == new_block);
3069 // The `splitter_T` slice moves completely from the old to the new
3070 // block. We move it as a whole to the new block_bunch list.
3071 new_block->stable_block_bunch.splice(
3072 new_block->stable_block_bunch.begin(),
3073 old_block->stable_block_bunch, splitter_T);
3074 } else assert(splitter_T->source_block() == old_block);
3075
3076 // We cannot join the loop above with the one below because transitions
3077 // in the action_block-slices need to be handled in two phases.
3078
3079 s_iter = new_block->begin; assert(s_iter < new_block->end);
3080 do
3081 {
3082 state_info_entry* const s(s_iter->st); assert(s->pos == s_iter); assert(s != pred.front().target);
3083 for (pred_entry* pred_iter(s->pred_inert.begin);
3084 s == (--pred_iter)->target; )
3085 { assert(&pred.front() < pred_iter);
3086 assert(nullptr != pred_iter->action_block->succ);
3087 assert(pred_iter->action_block->succ->block_bunch->pred == pred_iter);
3088 second_move_transition_to_new_action_block(pred_iter); // mCRL2complexity(pred_iter, ...) -- subsumed in the call below
3089 } // mCRL2complexity(s, ...) -- subsumed in the call at the end
3090 }
3091 while (++s_iter < new_block->end);
3092 assert(0 == new_block->marked_size()); assert(0 == old_block->marked_size());
3093 /* - - - - - - find new non-inert transitions - - - - - - */ assert(1 + &block_bunch.back() - block_bunch_inert_begin ==
3095 if (block_bunch_inert_begin <= &block_bunch.back())
3096 {
3097 block_bunch_slice_iter_or_null_t new_noninert_block_bunch;
3098 if (add_new_noninert_to_splitter)
3099 {
3100 new_noninert_block_bunch = splitter_T;
3101 }
3102 else
3103 {
3104 new_noninert_block_bunch = nullptr;
3105 }
3106 if (new_block_is_U == new_block_mode)
3107 { assert(old_block == new_block->end->st->bl.ock);
3108 assert(new_block->end <= &partitioner.part_st.permutation.back());
3109 permutation_entry* target_iter(new_block->begin); assert(target_iter < new_block->end);
3110 do
3111 {
3112 state_info_entry* const s(target_iter->st); assert(s->pos == target_iter);
3113 // check all incoming inert transitions of s, whether they
3114 /* still start in new_block */ assert(s != pred.back().target);
3115 for (pred_entry* pred_iter(s->pred_inert.begin);
3116 s == pred_iter->target; ++pred_iter)
3117 { assert(pred_iter < &pred.back());
3118 assert(nullptr != pred_iter->action_block->succ);
3119 state_info_entry* const t(pred_iter->source); assert(pred_iter->action_block->succ->block_bunch->pred == pred_iter);
3120 assert(t->pos->st == t);
3121 if (new_block != t->bl.ock)
3122 { assert(old_block == t->bl.ock);
3123 if (!make_noninert(pred_iter,
3124 &new_noninert_block_bunch))
3125 // make_noninert() may modify *pred_iter
3126 {
3127 old_block->mark_nonbottom(t->pos);
3128 }
3129 } // mCRL2complexity(old value of *pred_iter, ...) -- overapproximated by the
3130 // call below
3131 } // mCRL2complexity(s, ...) -- subsumed in the call at the end
3132 }
3133 while (++target_iter < new_block->end); assert(0 < old_block->bottom_size());
3134 }
3135 else
3136 { assert(new_block_is_R == new_block_mode);
3137 /* We have to be careful because make_noninert may move */ assert(&partitioner.part_st.permutation.front() < new_block->begin);
3138 /* a state either forward (to the marked states) or */ assert(old_block == new_block->begin[-1].st->bl.ock);
3139 /* back (to the bottom states). */ assert(0 < old_block->bottom_size());
3140 for(permutation_entry* source_iter(new_block->nonbottom_begin);
3141 source_iter < new_block->marked_nonbottom_begin; )
3142 {
3143 state_info_entry* const s(source_iter->st); assert(s->pos == source_iter);
3144 // check all outgoing inert transitions of s, whether they
3145 /* still end in new_block. */ assert(succ.back().block_bunch->pred->source != s);
3146 succ_entry* succ_iter(s->succ_inert.begin); assert(succ_iter < &succ.back());
3147 bool dont_mark(true); assert(s == succ_iter->block_bunch->pred->source);
3148 do
3149 { assert(succ_iter->block_bunch->pred->action_block->succ == succ_iter);
3150 if (new_block !=
3151 succ_iter->block_bunch->pred->target->bl.ock)
3152 { assert(old_block == succ_iter->block_bunch->pred->target->bl.ock);
3153 dont_mark = make_noninert(
3154 succ_iter->block_bunch->pred,
3155 &new_noninert_block_bunch);
3156 } // mCRL2complexity(succ_iter->block_bunch->pred, ...) -- overapproximated by
3157 // the call below
3158 }
3159 while (s == (++succ_iter)->block_bunch->pred->source);
3160 if (dont_mark) ++source_iter;
3161 else
3162 { assert(s->pos == source_iter);
3163 new_block->mark_nonbottom(source_iter);
3164 } assert(new_block->nonbottom_begin <= source_iter);
3165 // mCRL2complexity(s, ...) -- overapproximated by the call at the end
3166 }
3167 }
3168 } else assert(block_bunch_inert_begin == 1 + &block_bunch.back());
3169 mCRL2complexity(new_block, add_work(check_complexity::
3171 check_complexity::ilog2(new_block->size())), partitioner);
3172 }
3173 #ifndef NDEBUG
3176 template <class LTS_TYPE>
3177 void print_trans(const bisim_partitioner_dnj<LTS_TYPE>& partitioner) const
3178 {
3179 if (!mCRL2logEnabled(log::debug)) return;
3180 // print all outgoing transitions grouped per successor and out-slice
3181 const succ_entry* succ_iter(&succ.cbegin()[1]);
3182 if (succ_iter >= &succ.back())
3183 {
3184 mCRL2log(log::debug) << "No transitions.\n";
3185 return;
3186 }
3187 const state_info_entry* source(succ_iter->block_bunch->pred->source);
3188 mCRL2log(log::debug) << source->debug_id(partitioner) << ":\n";
3189 block_bunch_slice_iter_or_null_t current_out_bunch(
3190 const_cast<part_trans_t*>(this)->splitter_list.end());
3191 do
3192 {
3193 if (source != succ_iter->block_bunch->pred->source)
3194 { assert(source < succ_iter->block_bunch->pred->source);
3195 source = succ_iter->block_bunch->pred->source;
3197 << source->debug_id(partitioner) << ":\n";
3198 current_out_bunch =
3199 const_cast<part_trans_t*>(this)->splitter_list.end();
3200 }
3201 if (succ_iter->block_bunch->slice != current_out_bunch)
3202 { assert(!current_out_bunch.is_null());
3203 if (succ_iter->block_bunch->slice.is_null())
3204 { assert(succ_iter == source->succ_inert.begin);
3205 mCRL2log(log::debug)<<"\tInert successors:\n";
3206 current_out_bunch = nullptr;
3207 }
3208 else
3209 { assert(succ_iter < source->succ_inert.begin);
3210 assert(!current_out_bunch.is_null());
3211 assert(current_out_bunch == splitter_list.end() ||
3212 current_out_bunch->bunch != succ_iter->bunch());
3213 mCRL2log(log::debug) << "\tSuccessors in "
3214 <<succ_iter->bunch()->debug_id_short(partitioner)<<":\n";
3215 current_out_bunch = succ_iter->block_bunch->slice;
3216 }
3217 }
3218 mCRL2log(log::debug) << "\t\t"
3219 << succ_iter->block_bunch->pred->debug_id(partitioner) << '\n';
3220 }
3221 while (++succ_iter < &succ.back());
3222
3223 // print all transitions grouped per bunch and action_block-slice
3224 const action_block_entry* action_block_iter(action_block_begin);
3225 do assert(action_block_iter < action_block_end);
3226 while (nullptr == action_block_iter->succ &&
3227 (assert(nullptr == action_block_iter->begin_or_before_end),
3228 ++action_block_iter, true));
3229 do
3230 {
3231 const action_block_entry* bunch_end;
3232 const action_block_entry* action_block_slice_end;
3233 assert(nullptr != action_block_iter->succ);
3234 if (action_block_iter->succ->block_bunch->slice.is_null())
3235 { assert(action_block_iter == action_block_inert_begin);
3236 mCRL2log(log::debug) <<"Inert transition slice [";
3237 action_block_slice_end = bunch_end = action_block_end;
3238 }
3239 else
3240 {
3241 const bunch_t* const bunch(action_block_iter->succ->bunch());
3242 assert(nullptr != bunch);
3244 partitioner) << ":\n\taction_block-slice [";
3245 assert(bunch->begin == action_block_iter);
3246 bunch_end = bunch->end;
3247 assert(bunch_end <= action_block_inert_begin);
3248 assert(nullptr != action_block_iter->begin_or_before_end);
3249 action_block_slice_end =
3250 action_block_iter->begin_or_before_end + 1;
3251 }
3252 assert(action_block_slice_end <= bunch_end);
3253 // for all action_block-slices in bunch
3254 for (;;)
3255 {
3256 mCRL2log(log::debug) << (action_block_iter -
3257 action_block_begin) << ","
3258 << (action_block_slice_end - action_block_begin) << "):\n";
3259 // for all transitions in the action_block-slice
3260 assert(action_block_iter < action_block_slice_end);
3261 do
3262 {
3263 assert(nullptr != action_block_iter->succ);
3264 mCRL2log(log::debug) << "\t\t"
3265 << action_block_iter->succ->block_bunch->
3266 pred->debug_id(partitioner) << '\n';
3267 }
3268 while (++action_block_iter < action_block_slice_end);
3269 // go to next action_block-slice in the same bunch
3270 while (action_block_iter < bunch_end &&
3271 nullptr == action_block_iter->succ)
3272 {
3273 assert(nullptr == action_block_iter->begin_or_before_end);
3274 ++action_block_iter;
3275 assert(action_block_iter < bunch_end);
3276 }
3277 if (action_block_iter >= bunch_end) break;
3278 assert(nullptr != action_block_iter->begin_or_before_end);
3279 action_block_slice_end =
3280 action_block_iter->begin_or_before_end + 1;
3281 mCRL2log(log::debug) << "\taction_block-slice [";
3282 }
3283 // go to next bunch
3284 assert(action_block_iter == bunch_end);
3285 while (action_block_iter < action_block_end &&
3286 nullptr == action_block_iter->succ)
3287 {
3288 assert(nullptr == action_block_iter->begin_or_before_end);
3289 ++action_block_iter;
3290 }
3291 }
3292 while (action_block_iter < action_block_end);
3293 }
3294 #endif
3295};
3296
3297
3309 ONLY_IF_DEBUG( template<class LTS_TYPE> )
3310inline block_t* block_t::split_off_block(
3311 enum new_block_mode_t const new_block_mode, ONLY_IF_DEBUG( const bisim_partitioner_dnj<LTS_TYPE>& partitioner, )
3313 my_pool<simple_list<block_bunch_slice_t>::entry>& storage, )
3314 state_type const new_seqnr)
3315{ assert(0 < marked_size()); assert(0 < unmarked_bottom_size());
3316 // create a new block
3317 block_t* new_block;
3318 state_type swapcount(std::min(marked_bottom_size(),
3319 unmarked_nonbottom_size()));
3320 if (permutation_entry* const splitpoint(marked_bottom_begin +
3321 unmarked_nonbottom_size()); assert(begin < splitpoint), assert(splitpoint < end),
3322 assert(splitpoint->st->pos == splitpoint),
3323 new_block_is_U == new_block_mode)
3324 { assert((state_type) (splitpoint - begin) <= size()/2);
3325 new_block =
3326 #ifdef USE_POOL_ALLOCATOR
3327 storage.template construct<block_t>
3328 #else
3329 new block_t
3330 #endif
3331 (begin, splitpoint, new_seqnr);
3332 new_block->nonbottom_begin = marked_bottom_begin;
3333
3334 // adapt the old block: it only keeps the R-states
3335 begin = splitpoint;
3336 nonbottom_begin = marked_nonbottom_begin;
3337 }
3338 else
3339 { assert(new_block_is_R == new_block_mode);
3340 new_block =
3341 #ifdef USE_POOL_ALLOCATOR
3342 storage.template construct<block_t>
3343 #else
3344 new block_t
3345 #endif
3346 (splitpoint, end, new_seqnr);
3347 new_block->nonbottom_begin = marked_nonbottom_begin; assert((state_type) (end - splitpoint) <= size()/2);
3348
3349 // adapt the old block: it only keeps the U-states
3350 end = splitpoint;
3351 nonbottom_begin = marked_bottom_begin;
3352 } ONLY_IF_DEBUG( new_block->work_counter = work_counter; )
3353
3354 // swap contents
3355
3356 // The structure of a block is
3357 // | unmarked | marked | unmarked | marked |
3358 // | bottom | bottom | non-bottom | non-bottom |
3359 // We have to swap the marked bottom with the unmarked non-bottom
3360 // states.
3361 //
3362 // It is not necessary to reset the untested_to_U counters; these
3363 // counters are anyway only valid for the states in the respective
3364 // slice.
3365
3366 if (0 < swapcount)
3367 {
3368 // vector swap the states:
3369 permutation_entry* pos1(marked_bottom_begin);
3370 permutation_entry* pos2(marked_nonbottom_begin); assert(pos1 < pos2);
3371 permutation_entry const temp(std::move(*pos1));
3372 for (;;)
3373 {
3374 --pos2; assert(pos1 < pos2);
3375 *pos1 = std::move(*pos2);
3376 ++pos1;
3377 if (0 >= --swapcount) { break; } assert(pos1 < pos2);
3378 *pos2 = std::move(*pos1); // mCRL2complexity(new_block_is_U == new_block_mode ? pos1[-1] : *pos2, ...)
3379 } // -- overapproximated by the call at the end
3380 *pos2 = std::move(temp); // mCRL2complexity(new_block_is_U == new_block_mode ? pos1[-1] : *pos2, ...)
3381 } // -- overapproximated by the call at the end
3382 #ifndef NDEBUG
3383 { const permutation_entry* s_iter(begin); assert(s_iter < end);
3384 do assert(s_iter->st->pos == s_iter);
3385 while (++s_iter < end); }
3386 #endif
3387 // unmark all states in both blocks
3388 marked_nonbottom_begin = end;
3389 marked_bottom_begin = nonbottom_begin;
3390 new_block->marked_bottom_begin = new_block->nonbottom_begin; assert(new_block->size() <= size());
3391
3392 /* set the block pointer of states in the new block */ assert(new_block->marked_nonbottom_begin == new_block->end);
3393 permutation_entry* s_iter(new_block->begin); assert(s_iter < new_block->end);
3394 do
3395 { assert(s_iter->st->pos == s_iter);
3396 s_iter->st->bl.ock = new_block; // mCRL2complexity (*s_iter, ...) -- subsumed in the call below
3397 }
3398 while (++s_iter < new_block->end); mCRL2complexity(new_block, add_work(check_complexity::split_off_block,
3400 partitioner);
3401 return new_block;
3402}
3403
3404
3416 part_trans_t& part_tr)
3417{ assert(begin < end); assert(nullptr != begin->succ);
3418 assert(nullptr != begin->begin_or_before_end);
3419 action_block_entry* const first_slice_end(begin->begin_or_before_end + 1); assert(nullptr != end[-1].succ); assert(nullptr!=end[-1].begin_or_before_end);
3420 action_block_entry* const last_slice_begin(end[-1].begin_or_before_end); assert(begin < first_slice_end); assert(first_slice_end <= last_slice_begin);
3421 bunch_t* bunch_T_a_Bprime;
3422 /* Line 2.6: Select some a in Act and B' in Pi_s such that */ assert(last_slice_begin < end); assert(nullptr != first_slice_end[-1].succ);
3423 /* |T--a-->B'| < 1/2 |T| */ assert(nullptr != last_slice_begin->succ);
3424 if (first_slice_end - begin > end - last_slice_begin)
3425 {
3426 // Line 2.7: Pi_t := Pi_t \ {T} union { T--a-->B', T \ T--a-->B' }
3427 bunch_T_a_Bprime =
3428 #ifdef USE_POOL_ALLOCATOR
3429 part_tr.storage.template construct<bunch_t>
3430 #else
3431 new bunch_t
3432 #endif
3433 (last_slice_begin, end); assert(nullptr != bunch_T_a_Bprime);
3434 end = last_slice_begin;
3435 while (nullptr == end[-1].succ)
3436 {
3437 --end; assert(first_slice_end <= end); assert(nullptr == end->begin_or_before_end);
3438 } assert(nullptr != end[-1].begin_or_before_end);
3439 if (first_slice_end == end) part_tr.make_trivial(this);
3440 }
3441 else
3442 {
3443 // Line 2.7: Pi_t := Pi_t \ {T} union { T--a-->B', T \ T--a-->B' }
3444 bunch_T_a_Bprime =
3445 #ifdef USE_POOL_ALLOCATOR
3446 part_tr.storage.template construct<bunch_t>
3447 #else
3448 new bunch_t
3449 #endif
3450 (begin, first_slice_end); assert(nullptr != bunch_T_a_Bprime);
3451 begin = first_slice_end;
3452 while (nullptr == begin->succ)
3453 { assert(nullptr == begin->begin_or_before_end);
3454 ++begin; assert(begin <= last_slice_begin);
3455 } assert(nullptr != begin->begin_or_before_end);
3456 if (begin == last_slice_begin) part_tr.make_trivial(this);
3457 } ONLY_IF_DEBUG( bunch_T_a_Bprime->work_counter = work_counter; )
3458 ++part_tr.nr_of_bunches;
3459 return bunch_T_a_Bprime;
3460}
3462
3463} // end namespace bisim_dnj
3464
3465
3466
3467
3468
3469/* ************************************************************************* */
3470/* */
3471/* A L G O R I T H M S */
3472/* */
3473/* ************************************************************************* */
3474
3475
3476
3477
3478
3482
3483
3484
3485/*=============================================================================
3486= main class =
3487=============================================================================*/
3488
3489
3490
3491
3492
3496template <class LTS_TYPE>
3498{
3499 private:
3504
3506 LTS_TYPE& aut;
3507 ONLY_IF_DEBUG( public: )
3509 bisim_dnj::part_state_t part_st;
3510
3514 private:
3526 ONLY_IF_DEBUG( public: )
3528 bool const branching;
3529 private:
3536 #ifndef NDEBUG
3539 #endif
3540 public:
3552 bisim_partitioner_dnj(LTS_TYPE& new_aut, bool const new_branching = false,
3553 bool const new_preserve_divergence = false)
3554 : aut(new_aut),
3555 part_st(new_aut.num_states()),
3556 part_tr(new_aut.num_transitions(), new_aut.num_action_labels()),
3557 action_label(new_aut.num_action_labels()),
3558 branching(new_branching),
3559 preserve_divergence(new_preserve_divergence)
3560 { assert(branching || !preserve_divergence);
3561
3562mCRL2log(log::verbose) << "Start initialisation.\n";
3565mCRL2log(log::verbose) << "Start refining\n";
3567 }
3568
3569
3575 {
3576 return part_st.nr_of_blocks;
3577 }
3578
3579
3588 {
3589 return part_st.block(s)->seqnr;
3590 }
3591
3592
3606 {
3607 // The labels have already been stored in
3608 // next_nontrivial_and_label.label by
3609 // refine_partition_until_it_becomes_stable().
3610
3611 // for all blocks
3613 s_iter(&part_st.permutation.front()); assert(s_iter <= &part_st.permutation.back());
3614 do
3615 {
3616 const bisim_dnj::block_t* const B(s_iter->st->bl.ock);
3617 // for all block_bunch-slices of the block
3618 for (const bisim_dnj::block_bunch_slice_t& block_bunch :
3620 { assert(block_bunch.is_stable()); assert(!block_bunch.empty());
3621 const bisim_dnj::pred_entry* const
3622 pred(block_bunch.end[-1].pred); assert(pred->source->bl.ock == B);
3623 assert(nullptr != pred->action_block->succ);
3624 /* add a transition from the source block to the goal block */ assert(pred->action_block->succ->block_bunch->pred == pred);
3625 /* with the indicated label. */ assert(pred->action_block->succ->block_bunch->slice == &block_bunch);
3626 label_type const
3627 label(block_bunch.bunch->next_nontrivial_and_label.label); assert(0 <= label); assert(label < action_label.size());
3628 aut.add_transition(transition(B->seqnr, label,
3629 pred->target->bl.ock->seqnr));
3630 }
3631 s_iter = B->end;
3632 }
3633 while (s_iter <= &part_st.permutation.back());
3634
3635 // Merge the states, by setting the state labels of each state to the
3636 // concatenation of the state labels of its equivalence class.
3637
3638 if (aut.has_state_info()) /* If there are no state labels
3639 this step can be ignored */
3640 {
3641 /* Create a vector for the new labels */
3643 new_labels(num_eq_classes());
3644
3645 state_type i(aut.num_states()); assert(0 < i);
3646 do
3647 {
3648 --i;
3649 const state_type new_index(get_eq_class(i));
3650 new_labels[new_index]=new_labels[new_index]+aut.state_label(i);
3651 }
3652 while (0 < i);
3653
3654 aut.set_num_states(num_eq_classes());
3655 i = 0; assert(i < num_eq_classes());
3656 do
3657 {
3658 aut.set_state_label(i, new_labels[i]);
3659 }
3660 while (++i < num_eq_classes());
3661 }
3662 else
3663 {
3664 aut.set_num_states(num_eq_classes());
3665 }
3666
3667 aut.set_initial_state(get_eq_class(aut.initial_state()));
3668 }
3669
3670
3675 bool in_same_class(state_type const s, state_type const t) const
3676 {
3677 return part_st.block(s) == part_st.block(t);
3678 }
3679 private:
3680
3681 /*--------------------------- main algorithm ----------------------------*/
3682
3706 {
3707 mCRL2log(log::verbose) << "An O(m log n) "
3709 ? "divergence-preserving branching "
3710 : "branching ")
3711 : "")
3712 << "bisimulation partitioner created for " << part_st.state_size()
3713 << " states and " << aut.num_transitions() << " transitions.\n";
3714
3715 if (part_st.state_size() > 2 * aut.num_transitions() + 1)
3716 {
3717 mCRL2log(log::warning) << "There are several isolated states "
3718 "without incoming or outgoing transition. It is not "
3719 "guaranteed that branching bisimulation minimisation runs in "
3720 "time O(m log n).\n";
3721 }
3722
3723sort_transitions(aut.get_transitions(), tgt_lbl_src);
3724mCRL2log(log::verbose) << "Carried out sorting\n";
3725 // create one block for all states
3727 #ifdef USE_POOL_ALLOCATOR
3728 part_tr.storage.template construct<bisim_dnj::block_t>
3729 #else
3731 #endif
3732 (&part_st.permutation.front(),
3733 1 + &part_st.permutation.back(), part_st.nr_of_blocks++));
3734
3735 // Iterate over the transitions to count how to order them in
3736 // part_trans_t
3737
3738 // counters for the non-inert outgoing and incoming transitions per
3739 // state are provided in part_st.state_info. These counters have been
3740 // initialised to zero in the constructor of part_state_t.
3741 // counters for the non-inert transition per label are stored in
3742 // action_label.
3743 assert(action_label.size() == aut.num_action_labels());
3744 // counter for the total number of inert transitions:
3745 trans_type inert_transitions(0);
3746 for (const transition& t: aut.get_transitions())
3747 {
3748 if (branching&&aut.is_tau(aut.apply_hidden_label_map(t.label()))&& ((
3749 t.from() != t.to()) || (assert(preserve_divergence), false)))
3750 {
3751 // The transition is inert.
3752 ++part_st.state_info[t.from()].succ_inert.count;
3753 ++inert_transitions;
3754
3755 // The source state should become non-bottom:
3756 if (part_st.state_info[t.from()].pos < B->nonbottom_begin)
3757 {
3758 std::swap(*part_st.state_info[t.from()].pos,
3759 *--B->nonbottom_begin);
3760 // we do not yet update the marked_bottom_begin pointer
3761 }
3762 }
3763 else
3764 {
3765 // The transition is non-inert. (It may be a self-loop).
3766 ++part_st.state_info[t.from()].untested_to_U_eqv.count;
3767 ++action_label[aut.apply_hidden_label_map(t.label())].count;
3768 }
3769 ++part_st.state_info[t.to()].pred_inert.count;
3770 }
3771 // Now we update the marked_bottom_begin pointer:
3773
3774 // set the pointers to transition slices in the state info entries
3775
3776 // We set them all to the end of the respective slice here. Then, with
3777 // every transition, the pointer will be reduced by one, so that after
3778 // placing all transitions it will point to the beginning of the slice.
3779
3780 bisim_dnj::pred_entry* next_pred_begin(&part_tr.pred.begin()[1]);
3781 bisim_dnj::succ_entry* next_succ_begin(&part_tr.succ.begin()[1]);
3782 bisim_dnj::state_info_entry* state_iter(&part_st.state_info.front()); assert(state_iter <= &part_st.state_info.back());
3783 do
3784 {
3785 state_iter->bl.ed_noninert_end = next_pred_begin;
3786 next_pred_begin += state_iter->pred_inert.count;
3787 state_iter->pred_inert.convert_to_iterator(next_pred_begin);
3788
3789 // create slice descriptors in part_tr.succ for each state with
3790 /* outgoing transitions. */ assert(nullptr != next_succ_begin);
3791 state_iter->untested_to_U_eqv.convert_to_iterator(
3792 next_succ_begin + state_iter->untested_to_U_eqv.count);
3793 if (next_succ_begin < state_iter->untested_to_U_eqv.begin)
3794 { assert(nullptr != state_iter->untested_to_U_eqv.begin);
3795 next_succ_begin->begin_or_before_end =
3796 state_iter->untested_to_U_eqv.begin - 1;
3797 for (bisim_dnj::succ_entry* const
3798 out_slice_begin(next_succ_begin);
3799 ++next_succ_begin < state_iter->untested_to_U_eqv.begin; )
3800 {
3801 next_succ_begin->begin_or_before_end = out_slice_begin; // mCRL2complexity(next_succ_begin->block_bunch->pred, ...) -- subsumed in the
3802 } // call below
3803
3804 B->mark(state_iter->pos);
3805 }
3806 state_iter->succ_inert.convert_to_iterator(next_succ_begin +
3807 state_iter->succ_inert.count);
3808 #ifndef NDEBUG
3809 while (next_succ_begin < state_iter->succ_inert.begin)
3810 { assert(nullptr == next_succ_begin->begin_or_before_end);
3811 ++next_succ_begin;
3812 }
3813 #endif
3814 next_succ_begin = state_iter->succ_inert.begin; // mCRL2complexity(*state_iter, ...) -- subsumed in the call at the end
3815 }
3816 while (++state_iter <= &part_st.state_info.back());
3817
3818 // Line 2.4: Pi_t := { { all non-inert transitions } }
3822 1 + &part_tr.block_bunch.back() - inert_transitions; assert(1 + &part_tr.block_bunch.front() <= part_tr.block_bunch_inert_begin);
3823 bisim_dnj::bunch_t* bunch(nullptr);
3824
3827 // create a single bunch containing all non-inert transitions
3828 bunch =
3829 #ifdef USE_POOL_ALLOCATOR
3830 part_tr.storage.template construct<bisim_dnj::bunch_t>
3831 #else
3833 #endif
3835 part_tr.action_block_inert_begin); assert(nullptr != bunch); assert(part_tr.splitter_list.empty());
3837
3838 // create a single block_bunch entry for all non-inert transitions
3841 part_tr.block_bunch_inert_begin, bunch, false); assert(!part_tr.splitter_list.empty());
3843 }
3844
3845 // create slice descriptors in part_tr.action_block for each label
3846
3847 // The action_block array shall have the tau transitions at the end:
3848 // first the non-inert tau transitions (during initialisation, that are
3849 // only the tau self-loops), then the tau transitions that have become
3850 // non-inert and finally the inert transitions.
3851 // Transitions with other labels are placed from beginning to end.
3852 // Every such transition block except the last one ends with a dummy
3853 /* entry. If there are transition labels without transitions, */ assert((size_t) (part_tr.action_block_end - part_tr.action_block_begin) ==
3854 /* multiple dummy entries will be placed side-by-side. */ aut.num_transitions() + action_label.size() - 1);
3856 next_action_label_begin(part_tr.action_block_begin);
3857 trans_type const n_square(part_st.state_size() * part_st.state_size()); ONLY_IF_DEBUG( trans_type max_transitions = n_square; )
3858 label_type label(action_label.size()); assert(0 < label);
3859 do
3860 {
3861 --label;
3862 if (0 < action_label[label].count)
3863 { assert(nullptr != bunch);
3865 {
3866 // This is the second action_block-slice, so the bunch is
3867 // not yet marked as nontrivial but it should be.
3868 part_tr.make_nontrivial(bunch);
3869 }
3870 if (n_square < action_label[label].count)
3871 {
3872 mCRL2log(log::warning) << "There are "
3873 << action_label[label].count << ' '
3874 << pp(aut.action_label(label)) << "-transitions. "
3875 "This is more than n^2 (= " << n_square << "). It is "
3876 "not guaranteed that branching bisimulation "
3877 "minimisation runs in time O(m log n).\n"; ONLY_IF_DEBUG( if (max_transitions < action_label[label].count)
3878 { max_transitions = action_label[label].count; } )
3879 }
3880 // initialise begin_or_before_end pointers for this
3881 // action_block-slice
3882 action_label[label].convert_to_iterator(
3883 next_action_label_begin + action_label[label].count);
3884 next_action_label_begin->begin_or_before_end =
3885 action_label[label].begin - 1; assert(nullptr != next_action_label_begin->begin_or_before_end);
3887 action_block_slice_begin(next_action_label_begin); assert(nullptr != action_block_slice_begin);
3888 while (++next_action_label_begin < action_label[label].begin)
3889 {
3890 next_action_label_begin->begin_or_before_end =
3891 action_block_slice_begin; // mCRL2complexity(next_action_label_begin->succ->block_bunch->pred, ...) --
3892 } // subsumed in the call at the end
3893 }
3894 else
3895 {
3896 action_label[label].convert_to_iterator(
3897 next_action_label_begin);
3898 if (0 != label && aut.num_transitions() < action_label.size())
3899 {
3900 mCRL2log(log::warning) << "Action label "
3901 << pp(aut.action_label(label)) << " has no "
3902 "transitions, and the number of action labels exceeds "
3903 "the number of transitions. It is not guaranteed that "
3904 "branching bisimulation minimisation runs in time "
3905 "O(m log n).\n";
3906 }
3907 }
3908 }
3909 while (0 < label && (/* insert a dummy entry */ assert(next_action_label_begin < part_tr.action_block_inert_begin),
3910 next_action_label_begin->succ = nullptr,
3911 next_action_label_begin->begin_or_before_end = nullptr,
3912 ++next_action_label_begin, true)); assert(next_action_label_begin == part_tr.action_block_inert_begin);
3913
3914 /* distribute the transitions over the data structures */ ONLY_IF_DEBUG( check_complexity::init(2 * max_transitions); )
3915
3917 next_block_bunch(1 + &part_tr.block_bunch.front());
3918 for (const transition& t: aut.get_transitions())
3919 {
3921 source(&part_st.state_info.front() + t.from());
3923 target(&part_st.state_info.front() + t.to());
3924 bisim_dnj::succ_entry* succ_pos;
3925 bisim_dnj::block_bunch_entry* block_bunch_pos;
3926 bisim_dnj::pred_entry* pred_pos;
3927 bisim_dnj::action_block_entry* action_block_pos;
3928
3929 if (branching&&aut.is_tau(aut.apply_hidden_label_map(t.label()))&& ((
3930 t.from() != t.to()) || (assert(preserve_divergence), false)))
3931 {
3932 // It is a (normal) inert transition: place near the end of the
3933 // respective pred/succ slices, just before the other inert
3934 // transitions.
3935 succ_pos = --source->succ_inert.begin; assert(nullptr == succ_pos->begin_or_before_end);
3936 block_bunch_pos = 1 + &part_tr.block_bunch.back() -
3937 inert_transitions; assert(block_bunch_pos >= part_tr.block_bunch_inert_begin);
3938 pred_pos = --target->pred_inert.begin; assert(block_bunch_pos->slice.is_null());
3939 action_block_pos = part_tr.action_block_end-inert_transitions; assert(action_block_pos >= part_tr.action_block_inert_begin);
3940 action_block_pos->begin_or_before_end = nullptr;
3941 --inert_transitions;
3942 }
3943 else
3944 {
3945 // It is a non-inert transition (possibly a self-loop): place
3946 // at the end of the respective succ slice and at the beginning
3947 // of the respective pred slice.
3948 succ_pos =
3949 --part_st.state_info[t.from()].untested_to_U_eqv.begin; assert(nullptr != succ_pos->begin_or_before_end);
3950 assert(nullptr != succ_pos->begin_or_before_end->begin_or_before_end);
3951 assert(succ_pos->begin_or_before_end <= succ_pos ||
3952 succ_pos->begin_or_before_end->begin_or_before_end == succ_pos);
3953 block_bunch_pos = next_block_bunch++; assert(block_bunch_pos < part_tr.block_bunch_inert_begin);
3954 pred_pos = target->bl.ed_noninert_end++;
3955 action_block_pos =
3956 --action_label[aut.apply_hidden_label_map(t.label())].begin; assert(nullptr != action_block_pos->begin_or_before_end);
3957 assert(nullptr != action_block_pos->begin_or_before_end->begin_or_before_end);
3958 assert(action_block_pos->begin_or_before_end <= action_block_pos ||
3959 action_block_pos->begin_or_before_end->
3960 begin_or_before_end == action_block_pos);
3961 assert(!part_tr.splitter_list.empty());
3962 block_bunch_pos->slice = part_tr.splitter_list.begin(); assert(action_block_pos < part_tr.action_block_inert_begin);
3963 } assert(target->bl.ed_noninert_end <= target->pred_inert.begin);
3964 succ_pos->block_bunch = block_bunch_pos;
3965 block_bunch_pos->pred = pred_pos;
3966 pred_pos->action_block = action_block_pos;
3967 pred_pos->source = source;
3968 pred_pos->target = target; assert(nullptr != succ_pos);
3969 action_block_pos->succ = succ_pos; // mCRL2complexity(pred_pos, ...) -- subsumed in the call at the end
3970 } assert(0 == inert_transitions);
3971 /* delete transitions already -- they are no longer needed. We will */ assert(next_block_bunch == part_tr.block_bunch_inert_begin);
3972 // add new transitions at the end of minimisation.
3973 aut.clear_transitions();
3974
3975 state_iter = &part_st.state_info.front(); assert(state_iter <= &part_st.state_info.back());
3976 do
3977 {
3978 state_iter->bl.ock = B;
3979 }
3980 while (++state_iter <= &part_st.state_info.back());
3981
3982 if (nullptr != bunch)
3983 {
3984 while (nullptr == bunch->begin->succ)
3985 { assert(nullptr == bunch->begin->begin_or_before_end);
3986 ++bunch->begin; assert(bunch->begin < bunch->end);
3987 } assert(nullptr != bunch->begin->begin_or_before_end);
3988 while (nullptr == bunch->end[-1].succ)
3989 { assert(nullptr == bunch->end[-1].begin_or_before_end);
3990 --bunch->end; assert(bunch->begin < bunch->end);
3991 } assert(nullptr != bunch->end[-1].begin_or_before_end);
3992
3993 /* Line 2.2: B_vis := { s in S | there exists a visible */ mCRL2complexity(B, add_work(check_complexity::
3994 /* transition that is reachable */ create_initial_partition, 1U), *this);
3995 // from s }
3996 // B_invis := S \ B_vis
3997 // Line 2.3: Pi_s := { B_vis, B_invis } \ { emptyset }
3998 // At this point, all states with a visible transition are
3999 // marked.
4000 if (0 < B->marked_size())
4002 part_tr.print_trans(*this); )
4005 if (1 < B->size())
4006 {
4007 B = split(B, /* splitter block_bunch */ slice,
4009 // We can ignore possible new non-inert transitions, as
4010 // every R-bottom state already has a transition in bunch.
4012 }
4013 else
4014 { assert(B->nonbottom_begin == B->end);
4015 /* A block with 1 state will not be split. However, we */ assert(B->marked_nonbottom_begin == B->end);
4016 // still have to make the splitter stable.
4018 part_tr.splitter_list, slice);
4019 slice->make_stable();
4020 } assert(!B->stable_block_bunch.empty()); assert(part_tr.splitter_list.empty());
4022 assert(1 + &part_tr.block_bunch.front() < B->stable_block_bunch.front().end);
4024 }
4025 } else assert(0 == B->marked_size());
4026 }
4027 #ifndef NDEBUG
4038 void assert_stability() const
4039 {
4041
4042 assert(part_tr.succ.size() == part_tr.block_bunch.size() + 1);
4043 assert(part_tr.pred.size() == part_tr.block_bunch.size() + 1);
4045 part_tr.block_bunch.size() + action_label.size() - 2);
4046 if (part_tr.block_bunch.empty()) return;
4047
4048 assert(part_tr.splitter_list.empty());
4049 /* for (const block_bunch_slice_t& block_bunch : part_tr.splitter_list)
4050 {
4051 assert(!block_bunch.is_stable());
4052 } */
4053
4054 trans_type true_nr_of_block_bunch_slices(0);
4055 // for all blocks
4057 perm_iter(&part_st.permutation.front());
4058 assert(perm_iter <= &part_st.permutation.back());
4059 do
4060 {
4061 const bisim_dnj::block_t* const block(perm_iter->st->bl.ock);
4062 unsigned const max_block(check_complexity::log_n -
4063 check_complexity::ilog2(block->size()));
4064 // iterators have no predefined hash, so we store pointers:
4065 std::unordered_set<const bisim_dnj::block_bunch_slice_t*>
4066 block_bunch_check_set;
4067 #ifndef USE_SIMPLE_LIST
4068 block_bunch_check_set.reserve(
4069 block->stable_block_bunch.size());
4070 #endif
4071
4072 // for all stable block_bunch-slices of the block
4073 for (const bisim_dnj::block_bunch_slice_t& block_bunch :
4074 block->stable_block_bunch)
4075 {
4076 assert(block_bunch.source_block() == block);
4077 assert(block_bunch.is_stable());
4078 block_bunch_check_set.insert(&block_bunch);
4079 mCRL2complexity(&block_bunch, no_temporary_work(
4080 block_bunch.bunch->max_work_counter(*this)), *this);
4081 ++true_nr_of_block_bunch_slices;
4082 }
4083
4084 // for all states in the block
4085 do
4086 {
4087 trans_type block_bunch_count(0);
4088 const bisim_dnj::state_info_entry* const state(perm_iter->st);
4089 assert(state!=part_tr.succ.front().block_bunch->pred->source);
4090 // for all out-slices of the state
4091 for (const bisim_dnj::succ_entry*
4092 out_slice_end(state->succ_inert.begin);
4093 state == out_slice_end[-1].block_bunch->pred->source; )
4094 { assert(!out_slice_end[-1].block_bunch->slice.is_null());
4096 block_bunch_slice(out_slice_end[-1].block_bunch->slice);
4097 const bisim_dnj::bunch_t* const bunch(
4098 block_bunch_slice->bunch);
4099 assert(block == block_bunch_slice->source_block());
4100 if (block_bunch_slice->is_stable())
4101 {
4102 assert(1 == block_bunch_check_set.count(
4103 &*block_bunch_slice));
4104 ++block_bunch_count;
4105 }
4106 else assert(0); // i. e. all block_bunch-slices should
4107 // be stable
4108 const bisim_dnj::succ_entry* const out_slice_begin(
4109 out_slice_end[-1].begin_or_before_end);
4110 assert(nullptr != out_slice_begin);
4111 assert(out_slice_begin < out_slice_end);
4112 assert(nullptr != out_slice_begin->begin_or_before_end);
4113 assert(out_slice_begin->begin_or_before_end + 1 ==
4114 out_slice_end);
4115
4116 // for all transitions in the out-slice
4117 do
4118 {
4119 --out_slice_end;
4120 assert(bunch->begin <=
4121 out_slice_end->block_bunch->pred->action_block);
4122 assert(out_slice_end->block_bunch->pred->
4123 action_block < bunch->end);
4124 assert(out_slice_end->block_bunch->slice ==
4125 block_bunch_slice);
4126 assert(nullptr != out_slice_end->begin_or_before_end);
4127 if (out_slice_end->block_bunch + 1 !=
4128 block_bunch_slice->end)
4129 {
4130 assert(out_slice_end->block_bunch + 1 <
4131 block_bunch_slice->end);
4132 assert(out_slice_end->block_bunch[1].slice ==
4133 block_bunch_slice);
4134 }
4135 mCRL2complexity(out_slice_end->block_bunch->pred,
4136 no_temporary_work(max_block,
4138 check_complexity::ilog2(out_slice_end->
4139 block_bunch->pred->target->bl.ock->size()),
4140 perm_iter < block->nonbottom_begin),*this);
4141 }
4142 while (out_slice_begin < out_slice_end &&
4143 (assert(out_slice_begin ==
4144 out_slice_end->begin_or_before_end), true));
4145 }
4146 if (perm_iter < block->nonbottom_begin)
4147 {
4148 assert(block_bunch_check_set.size() == block_bunch_count);
4149 }
4150 }
4151 while (++perm_iter < block->end);
4152 }
4153 while (perm_iter <= &part_st.permutation.back());
4155 true_nr_of_block_bunch_slices);
4158 if (branching)
4161 1 + &part_tr.block_bunch.back());
4162 assert(1 + &part_tr.block_bunch.back() -
4165
4166 // for all inert transitions
4167 for (const bisim_dnj::action_block_entry* action_block(
4169 action_block < part_tr.action_block_end;
4170 ++action_block)
4171 { assert(nullptr == action_block->begin_or_before_end);
4172 const bisim_dnj::succ_entry* const
4173 succ_iter(action_block->succ);
4174 assert(nullptr != succ_iter);
4175 assert(succ_iter->block_bunch->slice.is_null());
4176 const bisim_dnj::pred_entry* const
4177 pred_iter(succ_iter->block_bunch->pred);
4178 assert(action_block == pred_iter->action_block);
4180 succ_iter->block_bunch);
4181 assert(pred_iter->source != pred_iter->target);
4182 assert(pred_iter->source->bl.ock == pred_iter->target->bl.ock);
4183 assert(pred_iter->source->succ_inert.begin <= succ_iter);
4184 assert(pred_iter->source->succ_inert.begin == succ_iter ||
4185 succ_iter[-1].block_bunch->pred->source==pred_iter->source);
4186 assert(pred_iter->target->pred_inert.begin <= pred_iter);
4187 assert(pred_iter->target->pred_inert.begin == pred_iter ||
4188 pred_iter[-1].target == pred_iter->target);
4189 unsigned const max_block(check_complexity::log_n -
4190 check_complexity::ilog2(pred_iter->target->bl.ock->size()));
4191 mCRL2complexity(pred_iter, no_temporary_work(max_block,
4192 max_block, false), *this);
4193 }
4194 }
4195 else
4196 {
4197 assert(!preserve_divergence);
4200 1 + &part_tr.block_bunch.back());
4201 }
4203 action_slice_end(part_tr.action_block_inert_begin);
4204 trans_type true_nr_of_bunches(0);
4205 trans_type true_nr_of_nontrivial_bunches(0);
4206 trans_type true_nr_of_action_block_slices(0);
4207 // for all action labels and bunches
4208 label_type label(0);
4209 assert(label < action_label.size());
4210 const bisim_dnj::bunch_t* previous_bunch(nullptr);
4211 do
4212 {
4213 assert(part_tr.action_block_begin <= action_label[label].begin);
4214 assert(action_label[label].begin <= action_slice_end);
4215 assert(action_slice_end <= part_tr.action_block_inert_begin);
4216 // for all action_block slices
4218 action_block_slice_end(action_slice_end);
4219 action_label[label].begin < action_block_slice_end; )
4220 {
4222 action_block_slice_begin(
4223 action_block_slice_end[-1].begin_or_before_end);
4224 assert(nullptr != action_block_slice_begin);
4225 assert(action_block_slice_begin < action_block_slice_end);
4226 assert(action_block_slice_end <= action_slice_end);
4227 assert(nullptr != action_block_slice_begin->succ);
4228 const bisim_dnj::block_t* const
4229 target_block(action_block_slice_begin->
4230 succ->block_bunch->pred->target->bl.ock);
4231 const bisim_dnj::bunch_t* const
4232 bunch(action_block_slice_begin->succ->bunch());
4233 if (previous_bunch != bunch)
4234 {
4235 assert(nullptr == previous_bunch);
4236 previous_bunch = bunch;
4237 assert(bunch->end == action_block_slice_end);
4238 if (bunch->begin == action_block_slice_begin)
4239 {
4240 // Perhaps this does not always hold; sometimes, an
4241 // action_block slice disappears but the bunch cannot
4242 // be made trivial.
4243 assert(bunch->is_trivial());
4244 }
4245 else
4246 {
4247 assert(!bunch->is_trivial());
4248 ++true_nr_of_nontrivial_bunches;
4249 }
4250 mCRL2complexity(bunch, no_temporary_work(
4251 bunch->max_work_counter(*this)), *this);
4252 ++true_nr_of_bunches;
4253 }
4254 if(bunch->begin == action_block_slice_begin)
4255 {
4256 previous_bunch = nullptr;
4257 }
4258 else assert(bunch->begin < action_block_slice_begin);
4259
4260 assert(action_block_slice_begin->begin_or_before_end + 1 ==
4261 action_block_slice_end);
4262 // for all transitions in the action_block slice
4264 action_block(action_block_slice_end);
4265 do
4266 {
4267 --action_block;
4268 const bisim_dnj::succ_entry* const
4269 succ_iter(action_block->succ);
4270 assert(nullptr != succ_iter);
4271 const bisim_dnj::pred_entry* const
4272 pred_iter(succ_iter->block_bunch->pred);
4273 assert(action_block == pred_iter->action_block);
4274 assert(succ_iter->block_bunch <
4276 assert(!branching || !aut.is_tau(label) ||
4277 pred_iter->source->bl.ock!=pred_iter->target->bl.ock ||
4279 pred_iter->source == pred_iter->target));
4280 assert(succ_iter < pred_iter->source->succ_inert.begin);
4281 assert(succ_iter+1==pred_iter->source->succ_inert.begin ||
4282 succ_iter[1].block_bunch->pred->source ==
4283 pred_iter->source);
4284 assert(pred_iter < pred_iter->target->pred_inert.begin);
4285 assert(pred_iter+1==pred_iter->target->pred_inert.begin ||
4286 pred_iter[1].target == pred_iter->target);
4287 assert(target_block == pred_iter->target->bl.ock);
4288 assert(bunch == succ_iter->bunch());
4289 }
4290 while (action_block_slice_begin < action_block &&
4291 (// some properties only need to be checked for states that
4292 // are not the first one:
4293 assert(action_block->begin_or_before_end ==
4294 action_block_slice_begin), true));
4295 action_block_slice_end = action_block_slice_begin;
4296 ++true_nr_of_action_block_slices;
4297 }
4298 if (action_slice_end < part_tr.action_block_inert_begin)
4299 {
4300 // there is a dummy transition between action labels
4301 assert(nullptr == action_slice_end->succ);
4302 assert(nullptr == action_slice_end->begin_or_before_end);
4303 }
4304 }
4305 while (++label < action_label.size() &&
4306 (action_slice_end = action_label[label - 1].begin - 1, true));
4307 assert(nullptr == previous_bunch);
4308 assert(part_tr.nr_of_bunches == true_nr_of_bunches);
4310 true_nr_of_nontrivial_bunches);
4312 true_nr_of_action_block_slices);
4313 }
4314 #endif
4333 {
4334 // Line 2.5: for all non-trivial bunches bunch_T in Pi_t do
4335 clock_t next_print_time = clock();
4336 const clock_t rounded_start_time = next_print_time - CLOCKS_PER_SEC/2;
4337 // const double log_initial_nr_of_action_block_slices =
4338 // 100 / std::log(part_tr.nr_of_action_block_slices);
4339 for (;;)
4340 { // mCRL2complexity(...) -- this loop will be ascribed to (the transitions in)
4341 // the new bunch below.
4342 /*------------------ find a non-trivial bunch -------------------*/ ONLY_IF_DEBUG( part_st.print_part(*this); part_tr.print_trans(*this);
4343