mCRL2
Loading...
Searching...
No Matches
check_complexity.h
Go to the documentation of this file.
1// Author(s): David N. Jansen, Radboud Universiteit, Nijmegen, The Netherlands
2//
3// Copyright: see the accompanying file COPYING or copy at
4// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
5//
6// Distributed under the Boost Software License, Version 1.0.
7// (See accompanying file LICENSE_1_0.txt or copy at
8// http://www.boost.org/LICENSE_1_0.txt)
9
10/// \file lts/detail/check_complexity.h
11///
12/// \brief helper class for time complexity checks during test runs
13///
14/// \details We use the class in this file to check whether the overall time
15/// complexity fits in O(m log n). Although it is difficult to test this in
16/// general because of the constant factor in the definition of the O()
17/// notation, it is often possible to give a (rather tight) upper bound on the
18/// number of iterations of most loops.
19///
20/// The principle of time measurement with this file is: the work done in
21/// every loop body is assigned to a state or a transition. Every state and
22/// every transition gets a counter for every loop body which is assigned
23/// to it. When the loop body is executed, the corresponding counter is
24/// increased. When increasing a counter, a new value is assigned, based on
25/// the logarithm of the size of the corresponding block (or constellation).
26/// If the new value is not actually larger than the old one, an error is
27/// raised. The new value never is allowed to become larger than log2(n), so
28/// we have: For every counter, its value is increased at most log2(n) times,
29/// and therefore no more than log2(n) steps (of a certain kind) can be
30/// assigned to any single state or transition.
31///
32/// Note that an ``increase'' is by at least 1, but it may be more than 1. If
33/// one always increases to the maximal allowed value, it is ensured that a
34/// very small block found early will only incur work that corresponds to its
35/// own size (and not to the size of the block from which it was split off).
36///
37/// To assign work to some unit, write
38/// `mCRL2complexity(unit, add_work(counter type, new counter value), ...);`
39/// The `unit` is a state or transition; the `counter type` is
40/// a value of `enum check_complexity::counter_type` defined
41/// below; the `new counter value` typically is
42/// `check_complexity::log_n - check_complexity::ilog2(block size)`.
43/// In the place of `...`, write parameters which might occur in
44/// `unit->debug_id(...)`.
45///
46/// For coroutines, there is an additional provision: work can temporarily be
47/// recorded in some special counters. As soon as it becomes clear which
48/// coroutine (the one handling red states or the one handling blue states, in
49/// our case) is faster, its counters are transferred to normal counters and
50/// the counters of the other coroutine are cancelled. It is checked that not
51/// too many counters are cancelled.
52///
53/// To transfer work from a temporary to a normal counter, one uses
54/// `finalise_work()`. To cancel counters, use `cancel_work()`. (The file
55/// liblts_bisim_gjkw.cpp contains wrapper functions `blue_is_smaller()` and
56/// `red_is_smaller()` that call `finalise_work()` and `cancel_work()`.)
57/// After all temporary work has been handled, call `check_temporary_work()` to
58/// compare the amount of sensible work with the amount of cancelled work.
59///
60/// If the work could be assigned to one of several counters (in particular, to
61/// any one transition out of a set of transitions), I recommend to assign it
62/// to all of them; otherwise, it may happen that a later excess of the time
63/// budget goes unnoticed because too few counters were advanced.
64/// This, however, poses some difficulties when using temporary counters: a
65/// single unit of work should be assigned to multiple counters, but added to
66/// the balance between sensible and superfluous work only once. A variant of
67/// `add_work()`, namely `add_work_notemporary()`, can be called in that case:
68/// it assigns a special value `DONT_COUNT_TEMPORARY` to a temporary counter
69/// meaning that it should be disregarded in the calculation of the balance.
70///
71/// \author David N. Jansen, Radboud Universiteit, Nijmegen, The Netherlands
72
73#ifndef MCRL2_LTS_DETAIL_CHECK_COMPLEXITY_H
74#define MCRL2_LTS_DETAIL_CHECK_COMPLEXITY_H
75
76// If the preprocessor constant `COUNT_WORK_BALANCE` is defined, the temporary
77// work is even counted in non-debug modes. No checks are executed; we only
78// keep enough information to print a grand total at the end.
79// In this mode, we at least need to call `init()`, `finalise_work()`,
80// `cancel_work()`, and `wait()`. It is not necessary to preserve
81// `add_work_notemporary()`. The function `print_grand_totals()` prints a
82// verbose message about the number of coroutine steps executed.
83//#define COUNT_WORK_BALANCE
84
85// If the preprocessor constant `TEST_WORK_COUNTER_NAMES` is defined,
86// initialising will print an overview of the defined work counter names (to
87// check whether the constants defined in this file correspond properly with
88// the strings in `check_complexity.cpp`) and terminate.
89//#define TEST_WORK_COUNTER_NAMES
90
91#include <cstring> // for std::size_t and std::memset()
92#include <array>
93#include <cassert>
94#include <cmath> // for std::log2()
95#include <climits> // for CHAR_BIT
96#include <utility> // for std::cmp_less_equal
97
98#include "mcrl2/utilities/logger.h"
99
100namespace mcrl2::lts::detail
101{
102
103/// \brief type used to store state (numbers and) counts
104/// \details defined here because this is the most basic #include header that
105/// uses it.
106///
107/// It would be better to define it as LTS_TYPE::states_size_type but that
108/// would require most classes to become templates.
109using state_type = std::size_t;
110#define STATE_TYPE_MIN (std::numeric_limits<state_type>::min())
111#define STATE_TYPE_MAX (std::numeric_limits<state_type>::max())
112
113/// \brief type used to store transition (numbers and) counts
114/// \details defined here because this is the most basic #include header that
115/// uses it.
116///
117/// It would be better to define it as LTS_TYPE::transitions_size_type but that
118/// would require most classes to become templates.
119using trans_type = std::size_t;
120#define TRANS_TYPE_MIN (std::numeric_limits<trans_type>::min())
121#define TRANS_TYPE_MAX (std::numeric_limits<trans_type>::max())
122
123/// \brief type used to store differences between transition counters
125
126/// \brief class for time complexity checks
127/// \details The class stores (as static members) the global counters needed
128/// for checking time complexity budgets.
129///
130/// It could almost be defined as a namespace because all members are static.
131/// Still, just a few members are private.
133{
134 public:
135 /// \brief calculate the base-2 logarithm, rounded down
136 /// \details The function cannot be constexpr because std::log2() may have
137 /// the side effect of setting `errno`.
138 static int ilog2(state_type size)
139 { assert(0<size);
140 #ifdef __GNUC__
141 if constexpr (sizeof(unsigned) == sizeof(size))
142 {
143 return static_cast<int>(sizeof(size) * CHAR_BIT - 1 - __builtin_clz(size));
144 }
145 else if constexpr (sizeof(unsigned long) == sizeof(size))
146 {
147 return static_cast<int>(sizeof(size) * CHAR_BIT - 1 - __builtin_clzl(size));
148 }
149 else if constexpr(sizeof(unsigned long long) == sizeof(size))
150 {
151 return static_cast<int>(sizeof(size) * CHAR_BIT - 1 - __builtin_clzll(size));
152 }
153 #endif
154 return (int) std::log2(size);
155 }
156
157#if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
158
159 /// \brief Type for complexity budget counters
160 /// \details The enumeration constants defined by this type are used to
161 /// distinguish the many counters that the time budget check uses.
162 /// There are counters assigned to blocks, states, B_to_C slices, and
163 /// transitions. Counters assigned to a block are regarded as if the work
164 /// would be registered with every state in the block. Counters assigned
165 /// to a B_to_C slice are regarded as if the work would be registered with
166 /// every transition in the slice. (These conventions are important when
167 /// splitting a block or a B_to_C slice.)
169 {
170 // block counters: every state in the block is regarded as visited. In
171 // this way, every state is ``visited'' O(log n) times.
172 // Invariant of the following block counters:
173 // 0 <= (counter value) <= ilog2(n) - ilog2(constellation size)
176
177 // If a loop runs over every state of a block exactly once, we simplify
178 // the task of updating the counters by making the corresponding loop
179 // counter a block counter:
181 // Invariant of the following block counters:
182 // 0 <= (counter value) <= ilog2(n) - ilog2(block size)
187
188 // state counters: every state is visited O(log n) times
189 // Invariant: 0 <= (counter value) <= ilog2(n) - ilog2(block size)
192
193 // The following counters are used when one refines a block: the first
194 // group is used to store the amount of work that (a posteriori) turns
195 // out to be useful. After that, there are two groups of counters to
196 // store temporary work.
199
200 // temporary state counters (blue):
204
205 // temporary state counters (red):
208
209 // new bottom state counters: every state is visited once
210 // Invariant: if s is a non-bottom state, the counter is 0;
211 // otherwise, the counter is 0 or 1.
213 // the next counter is used to count the work done on a virtual
214 // self-loop in line 4.15 (the new bottom state is regarded as red
215 // because it is in the splitter, but there is no transition to the
216 // splitter).
219
220 // B_to_C_descriptor counters: every transition in the B_to_C-slice is
221 // regarded as visited. In this way, every transition is ``visited''
222 // O(log n) times.
223 // Invariant:
224 // 0 <= (counter value) <= ilog2(n) - ilog2(target constellation size)
227
228 // If a loop runs over every transition in a B_to_C slice exactly once,
229 // we simplify the task of updating the counters by making the
230 // corresponding loop counter a B_to_C counter:
232 // the following counter is also meant for temporary work.
233 // Sometimes, after separating the new bottom states from the old ones,
234 // a constellation is reachable from the block of the new bottom
235 // states, but only from non-bottom states in this block. In that
236 // case, it cannot yet be determined which state will be a new bottom
237 // state. Then, the work is assigned temporarily to the B_to_C slice,
238 // until some new bottom state is found to which to assign it.
243
244 // transition counters: every transition is visited O(log n) times
245 // counters for transitions into the splitter NewC
246 // Invariant:
247 // 0 <= (counter value) <= ilog2(n) - ilog2(target constln size)
253
254 // counters for outgoing transitions
255 // Invariant:
256 // 0 <= (counter value) <= ilog2(n) - ilog2(source block size)
260
261 // counters for incoming transitions
262 // Invariant:
263 // 0 <= (counter value) <= ilog2(n) - ilog2(target block size)
266
267 // temporary transition counters for refine: similar to the temporary
268 // counters for states, we have a first group to store the work done
269 // for the smaller half, and temporary counters to store the work until
270 // it becomes clear which half wins.
271 // Because we have to sort the transitions into those to NewC, the
272 // outgoing and the incoming transitions, these counters are
273 // distributed above. The counters used to store the work done for
274 // the smaller half are: refine_outgoing_transition_3_6_or_23l,
275 // refine_outgoing_transition_to_marked_state_3_6l and
276 // refine_incoming_transition_3_18.
277
278 // temporary transition counters (blue):
283 // The work in the following counter is assigned to red (new
284 // bottom) states if the blue block is smaller!
286
287 // temporary transition counters (red):
291
292 // new bottom transition counters: every transition is visited once
293 // Invariant: If source is a non-bottom state, the counter is 0;
294 // otherwise, the counter is 0 or 1.
297 // For the following counters, we have an ``a priori'' and an ``a
298 // posteriori'' variant. The reason is, as explained with the
299 // B_to_C slice counters, that sometimes a constellation is
300 // reachable from the block of new bottom states but it is not yet
301 // clear which of the source states will become a new bottom state.
302 // In that case, the ``a posteriori'' counters are used. Later,
303 // the same block and the same constellation may be refined another
304 // time, but now with known new bottom states; then, the ``a
305 // priori'' counters are used.
312
313 /*-------------- counters for the bisim_jgkw algorithm --------------*/
314
315 // block counters
316 // Block counters are used to assign some work to each state in the
317 // block (and possibly, by transitivity, each incoming or outgoing
318 // transition of the block).
324
325 // state counters
326 // If every state of a block is handled by some loop, we
327 // abbreviate the counter to a block counter.
330
331 // temporary state counters (U-coroutine):
334
335 // temporary state counters (R-coroutine):
340
341 // bunch counters (only for small bunches, i. e. bunches that have been
342 // split off from a large bunch)
346
347 // block_bunch-slice counters (only for block_bunch-slices that are
348 // part of a small bunch)
359
360 // transition counters
361 // If every transition of a state is handled by some loop, we
362 // abbreviate the counter to a state counter (and possibly, by
363 // transitivity, to a block counter).
364 move_out_slice_to_new_block, // source block size
368
369 // temporary transition counters (U-coroutine):
373 // U: source block size
374
375 // temporary transition counters (R-coroutine):
379
380 // transition counters for new bottom states:
388
389 /*--------------- counters for the bisim_gj algorithm ---------------*/
390
391 // block counters
392 // Invariant:
393 // 0 <= (counter value) <= ilog2 n - ilog2(constellation size)
397 // Invariant: 0 <= (counter value) <= ilog2 n - ilog2(block size)
400
401 // state counters
402 // Invariant: 0 <= (counter value) <= ilog2 n - ilog2(block size)
410 // temporary state counters
411 // Invariant: 0 <= (counter value) <= 1
416 // bottom state counters
419 // other state counter
420 // Invariant: 0 <= (counter value) <= 1
423
424 // BLC slice counters
425 // Invariant:
426 // 0 <= (counter value) <= ilog2 n - ilog2(source constln size)
427 // Note that it should be the source constellation size,
428 // even though the BLC slice only contains transitions
429 // from a single source block.
432 // Invariant:
433 // 0 <= (counter value) <= ilog2 n - ilog2(target constln size)
438
439 // transition counters
440 // Invariant:
441 // 0 <= (counter value) <= ilog2 n - ilog2(source block size)
444 // Invariant:
445 // 0 <= (counter value) <= ilog2 n - ilog2(target block size)
447 // Invariant:
448 // 0 <= (counter value) <= ilog2 n - ilog2(target constln size)
450 // Invariant:
451 // 0 == (counter value) during the first half of initialisation
452 // 0 <= (counter value) <= ilog2 n after the quicksort part of the
453 // initialisation
455 // temporary transition counters
461 // source is in R: new bottom state
463 // counters for transitions starting in (new) bottom states
464 // Invariant: 0 <= (counter value) <= (source is a bottom state)
472 // other transition counters
473 // Invariant: 0 <= (counter value) <= 1
476 };
477
478#ifndef NDEBUG
479 /// \brief special value for temporary work without changing the balance
480 #define DONT_COUNT_TEMPORARY (std::numeric_limits<unsigned char>::max()-1)
481#endif
482
484 // three magic values to ensure that the result is actually correct...
487 complexity_error = 81956
488 };
489
490 /// \brief value of floor(log2(n)) for easy access
491 /// \details This variable has to be set by `init()` before counting work
492 /// can begin.
493 static unsigned char log_n;
494
495 private:
496#ifndef NDEBUG
497 /// \brief counter to register the work balance for coroutines
498 /// \details Sensible work will be counted positively, and cancelled work
499 /// negatively.
501 static trans_type no_of_waiting_cycles;
503#endif
504 static trans_type sensible_work_grand_total;
505 static trans_type cancelled_work_grand_total;
507
508 public:
509#ifndef NDEBUG
510 /// \brief printable names of the counter types (for error messages)
511 static const std::array<const char*, TRANS_gj_MAX - BLOCK_MIN + 1> work_names;
512#endif
513
514 /// \brief do some work that cannot be assigned directly
515 /// \details This is meant for a coroutine that has nothing to do
516 /// currently; in particular, it cannot do sensible work on a state or
517 /// transition.
518 static void wait(trans_type units = 1)
519 {
520 #ifndef NDEBUG
522 no_of_waiting_cycles += units;
523 #endif
525 }
526
528 {
529 #ifndef NDEBUG
530 assert(0 <= sensible_work);
531 assert(std::cmp_less_equal(no_of_waiting_cycles, sensible_work));
534 #endif
535 }
536
537 private:
538 static void finalise_work_units(trans_type units=1)
539 {
540 #ifndef NDEBUG
541 sensible_work += static_cast<signed_trans_type>(units);
542 #endif
544 }
545
546 static void cancel_work_units(trans_type units=1)
547 {
548 #ifndef NDEBUG
549 sensible_work -= static_cast<signed_trans_type>(units);
550 #endif
552 }
553
554 public:
555 /// \brief check that not too much superfluous work has been done
556 /// \details After having moved all temporary work counters to the normal
557 /// counters, this function can be used to ensure that not too much
558 /// temporary work is cancelled.
560 {
561 #ifndef NDEBUG
562 assert(-1 <= sensible_work);
563 sensible_work = 0;
565 #endif
566 }
567
568 /// \brief subset of counters (to be associated with a state or transition)
569 template <enum counter_type FirstCounter, enum counter_type LastCounter,
570 enum counter_type FirstTempCounter =
571 (enum counter_type) (LastCounter + 1),
572 enum counter_type FirstPostprocessCounter = FirstTempCounter>
574 {
575 static_assert(FirstCounter < FirstTempCounter);
576 static_assert(FirstTempCounter <= FirstPostprocessCounter);
577 static_assert(FirstPostprocessCounter <=
578 (enum counter_type) (LastCounter + 1));
579 public:
580 /// \brief actual space to store the counters
581 std::array<unsigned char, LastCounter - FirstCounter + 1> counters{};
582
583 /// \brief cancel temporary work
584 /// \details The function registers that all counters from `first` to
585 /// `last` (inclusive) are counting superfluous work. It adds them to
586 /// the pool of superfluous work.
587 /// \param ctr temporary counter whose work is superfluous
588 /// \returns false iff some counter was too large. In that case, also
589 /// the beginning of an error message is printed.
590 /// The function should be called through the macro
591 /// `mCRL2complexity()`, because that macro will print
592 /// the remainder of the error message as needed.
593 [[nodiscard]]
595 {
596 assert(FirstTempCounter <= ctr);
597 assert(ctr < FirstPostprocessCounter);
598 assert(0 == no_of_waiting_cycles);
599#ifndef NDEBUG
600 if ((FirstTempCounter != TRANS_MIN_TEMP &&
601 FirstTempCounter != TRANS_dnj_MIN_TEMP &&
602 FirstTempCounter != TRANS_gj_MIN_TEMP) ||
603 DONT_COUNT_TEMPORARY != counters[ctr - FirstCounter])
604#endif
605 {
606 assert(counters[ctr - FirstCounter] <= 1);
607 cancel_work_units(counters[ctr - FirstCounter]);
608 }
609 counters[ctr - FirstCounter] = 0;
610 return complexity_ok;
611 }
612
613
614 /// \brief move temporary work to its final counter
615 /// \details The function moves work from a temporary counter to a
616 /// normal counter. It also checks that the normal counter does not
617 /// get too large.
618 /// \param from temporary counter from where work is moved
619 /// \param to normal counter to which work is moved
620 /// \param max_value maximal allowed value to the normal counter. The
621 /// old value of the counter should be strictly
622 /// smaller.
623 /// \returns false iff the counter was too large. In that case, also
624 /// the beginning of an error message is printed.
625 /// The function should be called through the macro
626 /// `mCRL2complexity()`, because that macro will print
627 /// the remainder of the error message as needed.
628 [[nodiscard]]
630 enum counter_type const to, unsigned const max_value)
631 {
632#ifndef NDEBUG
633 // assert(...) -- see move_work().
634 if ((FirstTempCounter != TRANS_MIN_TEMP &&
635 FirstTempCounter != TRANS_dnj_MIN_TEMP &&
636 FirstTempCounter != TRANS_gj_MIN_TEMP) ||
637 DONT_COUNT_TEMPORARY != counters[from - FirstCounter])
638 {
639 finalise_work_units(counters[from - FirstCounter]);
640 }
641 else
642 {
643 counters[from - FirstCounter] = 1;
644 }
645#else
646 // The counter value is always != DONT_COUNT_TEMPORARY.
647 finalise_work_units(counters[from - FirstCounter]);
648#endif
649 return move_work(from, to, max_value);
650 }
651
652
653 /// \brief constructor, initializes all counters to 0
655 {
656 std::memset(counters.data(), '\0', sizeof(counters));
657 }
658
659
660 /// \brief register work with some counter
661 /// \details The function increases a work counter to a larger value.
662 /// It is also checked that the counter does not get too large.
663 /// The function is normally called through the macro
664 /// `mCRL2complexity()`.
665 /// \param ctr counter with which work is registered
666 /// \param max_value maximal allowed value of the counter. The old
667 /// value of the counter should be strictly smaller.
668 /// \returns false iff the counter was too large. In that case, also
669 /// the beginning of an error message is printed.
670 /// The function should be called through the macro
671 /// `mCRL2complexity()`, because that macro will print
672 /// the remainder of the error message as needed.
673 [[nodiscard]]
675 unsigned const max_value)
676 {
677#ifdef NDEBUG
678 if (ctr < FirstTempCounter || ctr >= FirstPostprocessCounter)
679 {
680 return complexity_ok;
681 }
682#else
683 if (FirstCounter > ctr || ctr > LastCounter)
684 {
685 mCRL2log(log::error) << "Error 20: counter \""
686 << work_names[ctr - BLOCK_MIN] << "\" is not available in ";
687 return complexity_error;
688 }
689 assert(max_value <= (ctr < FirstTempCounter ? log_n : 1U));
690 if (counters[ctr - FirstCounter] >= max_value)
691 {
692 mCRL2log(log::error) << "Error 1: counter \""
693 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
694 "maximum value (" << max_value << ") for ";
695 return complexity_error;
696 }
697#endif
698 counters[ctr - FirstCounter] = max_value;
699 return complexity_ok;
700 }
701
702 protected:
703 /// \brief move temporary work to another counter
704 /// \details The function moves work from a temporary counter to
705 /// another temporary or a normal counter. It also checks that the new
706 /// counter does not get too large.
707 /// The function is normally called through the macro
708 /// `mCRL2complexity()`.
709 /// \param from temporary counter from where work is moved
710 /// \param to (temporary or normal) counter to which work is
711 /// moved
712 /// \param max_value maximal allowed value to the normal counter. The
713 /// old value of the counter should be strictly
714 /// smaller.
715 /// \returns false iff the new counter was too large. In that case,
716 /// also the beginning of an error message is printed.
717 /// The function should be called through the macro
718 /// `mCRL2complexity()`, because that macro will print
719 /// the remainder of the error message as needed.
720 [[nodiscard]]
722 enum counter_type const to, unsigned const max_value)
723 {
724#ifndef NDEBUG
725 assert(FirstTempCounter <= from);
726 assert(from < FirstPostprocessCounter);
727 assert(FirstCounter <= to);
728 assert(to < FirstTempCounter || FirstPostprocessCounter <= to);
729 assert(to <= LastCounter);
730 assert(max_value <= (to < FirstTempCounter ? log_n : 1U));
731 if (0 == counters[from - FirstCounter])
732 {
733 return complexity_ok;
734 }
735 if (counters[to - FirstCounter] >= max_value)
736 {
737 mCRL2log(log::error) << "Error 2: counter \""
738 << work_names[to - BLOCK_MIN] << "\" exceeded "
739 "maximum value (" << max_value << ") for ";
740 return complexity_error;
741 }
742 {
743 counters[to - FirstCounter] = max_value;
744 assert(1 == counters[from - FirstCounter]);
745 }
746#else
747 (void) to; (void) max_value; // avoid unused variable warning
748#endif
749 counters[from - FirstCounter] = 0;
750 return complexity_ok;
751 }
752 };
753
754
755 // usage of the functions below: as soon as the block is refined, one
756 // has to call state_counter_t::blue_is_smaller() or state_counter_t::
757 // red_is_smaller() for every state in the old and new block. Also, one
758 // needs to call trans_counter_t::blue_is_smaller() or trans_counter_t::
759 // red_is_smaller() for every incoming and outgoing transition in the old
760 // and new block. (Inert transitions need to be handled only once.) After
761 // that, one calls check_temporary_work() to verify that not too much
762 // work was done on the larger block.
763
764 /// \brief counters for a block
765 /// \details The counters stored with a block are meant to be assigned to
766 /// each state in the block. This means that the counter values need to be
767 /// copied when the block is split.
769 {
770 public:
771 /// \brief ensures there is no orphaned temporary work counter
772 /// \details When a refinement has finished, all work registered with
773 /// temporary counters should have been moved to normal counters. This
774 /// function verifies this property.
775 /// The function additionally ensures that no work counter exceeds its
776 /// maximal allowed value, based on the size of the block or its
777 /// constellation. (The size of the constellation is the unit used for
778 /// counters related to [blocks in the] splitter constellation; the
779 /// size of the block is used for other counters.)
780 /// \param max_C ilog2(n) - ilog2(size of constellation)
781 /// \param max_B ilog2(n) - ilog2(size of block)
782 /// \returns false iff some temporary counter was nonzero. In that
783 /// case, also the beginning of an error message is
784 /// printed. The function should be called through the
785 /// macro `mCRL2complexity()`, because that macro will
786 /// print the remainder of the error message as needed.
787 [[nodiscard]]
788 result_type no_temporary_work(unsigned const max_C,
789 unsigned const max_B)
790 {
791#ifndef NDEBUG
792 assert(max_C <= max_B);
793 for (enum counter_type ctr = BLOCK_MIN;
795 ctr = (enum counter_type) (ctr + 1))
796 {
797 assert(counters[ctr - BLOCK_MIN] <= max_C);
798 counters[ctr - BLOCK_MIN] = max_C;
799 }
800 assert(max_B <= log_n);
801 for (enum counter_type ctr =
803 ctr <= BLOCK_MAX; ctr = (enum counter_type) (ctr + 1))
804 {
805 assert(counters[ctr - BLOCK_MIN] <= max_B);
806 counters[ctr - BLOCK_MIN] = max_B;
807 }
808#else
809 (void) max_C; (void) max_B; // avoid unused variable warning
810#endif
811 return complexity_ok;
812 }
813 };
814
815 /// \brief counters for a B_to_C slice
816 /// \details The counters stored with a B_to_C slice are meant to be
817 /// assigned to each transition in the slice. This means that the counter
818 /// values need to be copied when the slice is split.
821 {
822 public:
823 /// \brief ensures there is no orphaned temporary work counter
824 /// \details When a refinement has finished, all work registered with
825 /// temporary counters should have been moved to normal counters. This
826 /// function verifies this property.
827 /// The function additionally ensures that no work counter exceeds its
828 /// maximal allowed value, based on the size of the target
829 /// constellation.
830 /// \param max_targetC ilog2(n) - ilog2(size of target constellation)
831 /// \returns false iff some temporary counter was nonzero. In that
832 /// case, also the beginning of an error message is
833 /// printed. The function should be called through the
834 /// macro `mCRL2complexity()`, because that macro will
835 /// print the remainder of the error message as needed.
836 [[nodiscard]]
837 result_type no_temporary_work(unsigned const max_targetC)
838 {
839#ifndef NDEBUG
840 assert(max_targetC <= log_n);
841 for (enum counter_type ctr = B_TO_C_MIN;
842 ctr < B_TO_C_MIN_TEMP; ctr = (enum counter_type) (ctr + 1))
843 {
844 assert(counters[ctr - B_TO_C_MIN] <= max_targetC);
845 counters[ctr - B_TO_C_MIN] = max_targetC;
846 }
847 for (enum counter_type ctr = B_TO_C_MIN_TEMP;
848 ctr <= B_TO_C_MAX_TEMP; ctr = (enum counter_type) (ctr + 1))
849 {
850 if (counters[ctr - B_TO_C_MIN] > 0)
851 {
852 mCRL2log(log::error) << "Error 3: counter \""
853 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
854 "maximum value (" << 0 << ") for ";
855 return complexity_error;
856 }
857 }
858 static_assert(B_TO_C_MAX_TEMP == B_TO_C_MAX);
859#else
860 (void) max_targetC; // avoid unused variable warning
861#endif
862 return complexity_ok;
863 }
864
865 /// \brief returns the _temporary_ counter associated with line 4.4
866 /// \details The counter associated with line 4.4 is used when some
867 /// constellation is reachable from a block containing new bottom
868 /// states but it is not yet clear which states are the new bottom
869 /// states that can reach the constellation. Then, the work is
870 /// temporarily assigned to the B_to_C slice until it has become clear
871 /// to which new bottom states it can be assigned.
872 ///
873 /// We cannot use the normal mechanism of `move_work()` here because
874 /// the normal counters are transition counters, not B_to_C slice
875 /// counters.
876 ///
877 /// This function helps to decide whether the work still needs to be
878 /// moved from the temporary counter to a normal counter.
879 /// \returns the value of the _temporary_ counter associated with
880 /// line 4.4
881 unsigned char get_work_counter_4_4() const
882 {
883 return counters[for_all_constellations_C_not_in_R_from_RfnB_4_4 -
884 B_TO_C_MIN];
885 }
886
887
888 /// \brief sets the temporary counter associated with line 4.4 to zero
889 /// \details The counter associated with line 4.4 is needed when some
890 /// constellation is reachable from a block containing new bottom
891 /// states but it is not yet clear which states are the new bottom
892 /// states that can reach the constellation. Then, the work is
893 /// temporarily assigned to the B_to_C slice until it has become clear
894 /// to which new bottom states it has to be assigned.
895 ///
896 /// We cannot use the normal mechanism of `move_work()` here because
897 /// the normal counters are transition counters, not B_to_C slice
898 /// counters.
899 ///
900 /// This function resets the temporary work counter and is meant to be
901 /// called as soon as the work can be assigned to normal counters.
903 {
904 counters[for_all_constellations_C_not_in_R_from_RfnB_4_4 -
905 B_TO_C_MIN] = 0;
906 }
907 };
908
911 {
912 public:
913 /// \brief ensures there is no orphaned temporary work counter
914 /// \details When a refinement has finished, all work registered with
915 /// temporary counters should have been moved to normal counters.
916 /// Further, there should not be any work ascribed to bottom-state
917 /// counters in non-bottom states, but only to (new) bottom states.
918 /// This function verifies these properties. It also sets all counters
919 /// for bottom states to 1 so that later no more work can be assigned
920 /// to them.
921 /// The function additionally ensures that no work counter exceeds its
922 /// maximal allowed value, based on the size of the block of which the
923 /// state is a member.
924 /// \param max_B log2(n) - log2(size of the block containing this
925 /// state)
926 /// \param bottom `true` iff the state to which these counters belong
927 /// is a bottom state
928 /// \returns false iff some temporary counter or some bottom-state
929 /// counter of a non-bottom state was nonzero. In that
930 /// case, also the beginning of an error message is
931 /// printed. The function should be called through the
932 /// macro `mCRL2complexity()`, because that macro will
933 /// print the remainder of the error message as needed.
934 [[nodiscard]]
935 result_type no_temporary_work(unsigned const max_B, bool const bottom)
936 {
937#ifndef NDEBUG
938 assert(max_B <= log_n);
939 for (enum counter_type ctr = STATE_MIN;
940 ctr < STATE_MIN_TEMP; ctr = (enum counter_type) (ctr + 1))
941 {
942 assert(counters[ctr - STATE_MIN] <= max_B);
943 counters[ctr - STATE_MIN] = max_B;
944 }
945
946 // temporary state counters must be zero:
947 for (enum counter_type ctr = STATE_MIN_TEMP;
948 ctr <= STATE_MAX_TEMP; ctr = (enum counter_type) (ctr + 1))
949 {
950 if (counters[ctr - STATE_MIN] > 0)
951 {
952 mCRL2log(log::error) << "Error 4: counter \""
953 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
954 "maximum value (" << 0 << ") for ";
955 return complexity_error;
956 }
957 }
958 // bottom state counters must be 0 for non-bottom states and 1 for
959 // bottom states:
960 // assert((unsigned) bottom <= 1);
961 for(enum counter_type ctr = (enum counter_type) (STATE_MAX_TEMP+1);
962 ctr <= STATE_MAX ; ctr = (enum counter_type) (ctr + 1))
963 {
964 if (counters[ctr - STATE_MIN] > (unsigned) bottom)
965 {
966 mCRL2log(log::error) << "Error 5: counter \""
967 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
968 "maximum value (" << (unsigned) bottom << ") for ";
969 return complexity_error;
970 }
971 counters[ctr - STATE_MIN] = (unsigned) bottom;
972 }
973#else
974 (void) max_B; (void) bottom; // avoid unused variable warning
975#endif
976 return complexity_ok;
977 }
978 };
979
982 {
983 public:
984 /// \brief ensures there is no orphaned temporary work counter
985 /// \details When a refinement has finished, all work registered with
986 /// temporary counters should have been moved to normal counters.
987 /// Further, there should not be any work ascribed to bottom-state
988 /// counters in transitions from non-bottom states, but only to
989 /// transitions from (new) bottom states. This function verifies these
990 /// properties. It also sets all counters for bottom states to 1 so
991 /// that later no more work can be assigned to them.
992 /// The function additionally ensures that no work counter exceeds its
993 /// maximal allowed value, based on the size of the source block,
994 /// target block or target constellation. (The constellation size is
995 /// the relevant unit for counters that are related to transitions into
996 /// the splitter, which is the constellation `NewC`. The block size is
997 /// the unit for counters that are related to refinements. Because
998 /// some parts of a refinement look at incoming transitions of the
999 /// refined block and others at outgoing transitions, we need two block
1000 /// sizes.)
1001 /// \param max_sourceB the maximum allowed value for work counters
1002 /// based on the source state of the transition
1003 /// \param max_targetC the maximum allowed value for work counters
1004 /// based on the target constellation
1005 /// \param max_targetB the maximum allowed value for work counters
1006 /// based on the target block
1007 /// \param source_bottom `true` iff the transition to which these
1008 /// counters belong starts in a bottom state
1009 /// \returns false iff some temporary counter or some bottom-state
1010 /// counter of a transition with non-bottom source was
1011 /// nonzero. In that case, also the beginning of an
1012 /// error message is printed. The function should be
1013 /// called through the macro `mCRL2complexity()`,
1014 /// because that macro will print the remainder of the
1015 /// error message as needed.
1016 [[nodiscard]]
1017 result_type no_temporary_work(unsigned const max_sourceB,
1018 unsigned const max_targetC,
1019 unsigned const max_targetB, bool const source_bottom)
1020 {
1021#ifndef NDEBUG
1022 assert(max_targetC <= max_targetB);
1023 for (enum counter_type ctr = TRANS_MIN;
1025 ctr = (enum counter_type) (ctr + 1))
1026 {
1027 assert(counters[ctr - TRANS_MIN] <= max_targetC);
1028 counters[ctr - TRANS_MIN] = max_targetC;
1029 }
1030 assert(max_sourceB <= log_n);
1033 ctr = (enum counter_type) (ctr + 1))
1034 {
1035 assert(counters[ctr - TRANS_MIN] <= max_sourceB);
1036 counters[ctr - TRANS_MIN] = max_sourceB;
1037 }
1038 assert(max_targetB <= log_n);
1040 ctr < TRANS_MIN_TEMP; ctr = (enum counter_type) (ctr + 1))
1041 {
1042 assert(counters[ctr - TRANS_MIN] <= max_targetB);
1043 counters[ctr - TRANS_MIN] = max_targetB;
1044 }
1045 // temporary transition counters must be zero
1046 for (enum counter_type ctr = TRANS_MIN_TEMP;
1047 ctr <= TRANS_MAX_TEMP; ctr = (enum counter_type)(ctr + 1))
1048 {
1049 if (counters[ctr - TRANS_MIN] > 0)
1050 {
1051 mCRL2log(log::error) << "Error 6: counter \""
1052 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1053 "maximum value (" << 0 << ") for ";
1054 return complexity_error;
1055 }
1056 }
1057 // bottom state counters must be 0 for transitions from non-bottom
1058 // states and 1 for other transitions
1059 assert((unsigned) source_bottom <= 1);
1060 for(enum counter_type ctr = (enum counter_type) (TRANS_MAX_TEMP+1);
1061 ctr <= TRANS_MAX ; ctr = (enum counter_type) (ctr + 1))
1062 {
1063 if (counters[ctr - TRANS_MIN] > (unsigned) source_bottom)
1064 {
1065 mCRL2log(log::error) << "Error 7: counter \""
1066 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1067 "maximum value (" << (unsigned) source_bottom << ") for ";
1068 return complexity_error;
1069 }
1070 counters[ctr - TRANS_MIN] = (unsigned) source_bottom;
1071 }
1072#else
1073 (void) max_sourceB; (void) max_targetC; (void) max_targetB;
1074 (void) source_bottom; // avoid unused variable warning
1075#endif
1076 return complexity_ok;
1077 }
1078
1079
1080 /// \brief register work with some temporary counter without changing
1081 /// the balance between sensible and superfluous work
1082 /// \details The function increases a temporary work counter. It is
1083 /// also checked that the counter does not get too large. This variant
1084 /// of `add_work()` should be used if one wants to assign a single step
1085 /// of work to multiple temporary counters of transitions: for one of
1086 /// them, the normal `add_work()` is called, and for the others
1087 /// `add_work_notemporary()`.
1088 /// \param ctr counter with which work is registered
1089 /// \param max_value maximal allowed value of the counter. The old
1090 /// value of the counter should be strictly smaller.
1091 /// (Because it is a temporary counter, only `1` is
1092 /// sensible.)
1093 /// \returns false iff the counter was too large. In that case, also
1094 /// the beginning of an error message is printed.
1095 /// The function should be called through the macro
1096 /// `mCRL2complexity()`, because that macro will print
1097 /// the remainder of the error message as needed.
1098 [[nodiscard]]
1100 unsigned const max_value)
1101 {
1102#ifndef NDEBUG
1103 if (TRANS_MIN_TEMP > ctr || ctr > TRANS_MAX_TEMP)
1104 {
1105 return add_work(ctr, max_value);
1106 }
1107
1108 assert(1 == max_value);
1109 if (0 == counters[ctr - TRANS_MIN])
1110 {
1111 counters[ctr - TRANS_MIN] = DONT_COUNT_TEMPORARY;
1112 return complexity_ok;
1113 }
1114
1115 mCRL2log(log::error) << "Error 8: counter \""
1116 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1117 "maximum value (" << max_value << ") for ";
1118 return complexity_error;
1119#else
1120 (void) ctr; (void) max_value;
1121 return complexity_ok;
1122#endif
1123 }
1124 };
1125
1126 /*----------------- class specialisations for bisim_dnj -----------------*/
1127
1130 {
1131 public:
1132 /// \brief ensures there is no orphaned temporary work counter
1133 /// \details When a refinement has finished, all work registered with
1134 /// temporary counters should have been moved to normal counters. This
1135 /// function verifies this property.
1136 /// The function additionally ensures that no work counter exceeds its
1137 /// maximal allowed value, based on the size of the block or its
1138 /// constellation. (The size of the constellation is the unit used for
1139 /// counters related to [blocks in the] splitter constellation; the
1140 /// size of the block is used for other counters.)
1141 /// \param max_block ilog2(n^2) - ilog2(size of block)
1142 /// \returns false iff some temporary counter was nonzero. In that
1143 /// case, also the beginning of an error message is
1144 /// printed. The function should be called through the
1145 /// macro `mCRL2complexity()`, because that macro will
1146 /// print the remainder of the error message as needed.
1147 [[nodiscard]]
1148 result_type no_temporary_work(unsigned const max_block)
1149 {
1150#ifndef NDEBUG
1151 assert((log_n + 1U) / 2U <= max_block);
1152 if (max_block > log_n)
1153 {
1154 mCRL2log(log::error) << "Error 14: max_block == "
1155 << max_block << " exceeded log_n == "
1156 << (unsigned) log_n << " for ";
1157 return complexity_error;
1158 }
1159 assert(max_block <= log_n);
1160 for (enum counter_type ctr = BLOCK_dnj_MIN;
1162 ctr = (enum counter_type) (ctr + 1))
1163 {
1164 assert(counters[ctr - BLOCK_dnj_MIN] <= max_block);
1165 counters[ctr - BLOCK_dnj_MIN] = max_block;
1166 }
1167 for (enum counter_type ctr = create_initial_partition;
1168 ctr <= BLOCK_dnj_MAX; ctr = (enum counter_type) (ctr + 1))
1169 {
1170 assert(counters[ctr - BLOCK_dnj_MIN] <= 1);
1171 counters[ctr - BLOCK_dnj_MIN] = 1;
1172 }
1173#else
1174 (void) max_block; // avoid unused variable warning
1175#endif
1176 return complexity_ok;
1177 }
1178 };
1179
1182 {
1183 public:
1184 /// \brief ensures there is no orphaned temporary work counter
1185 /// \details When a refinement has finished, all work registered with
1186 /// temporary counters should have been moved to normal counters. This
1187 /// function verifies this property.
1188 /// The function additionally ensures that no work counter exceeds its
1189 /// maximal allowed value, based on the size of the block or its
1190 /// constellation. (The size of the constellation is the unit used for
1191 /// counters related to [blocks in the] splitter constellation; the
1192 /// size of the block is used for other counters.)
1193 /// \param max_block ilog2(n^2) - ilog2(size of block)
1194 /// \returns false iff some temporary counter was nonzero. In that
1195 /// case, also the beginning of an error message is
1196 /// printed. The function should be called through the
1197 /// macro `mCRL2complexity()`, because that macro will
1198 /// print the remainder of the error message as needed.
1199 [[nodiscard]]
1200 result_type no_temporary_work(unsigned const max_block,
1201 bool const bottom)
1202 {
1203#ifndef NDEBUG
1204 assert((log_n + 1U) / 2U <= max_block);
1205 assert(max_block <= log_n);
1206 for (enum counter_type ctr = STATE_dnj_MIN;
1207 ctr < STATE_dnj_MIN_TEMP; ctr = (enum counter_type) (ctr + 1))
1208 {
1209 assert(counters[ctr - STATE_dnj_MIN] <= max_block);
1210 counters[ctr - STATE_dnj_MIN] = max_block;
1211 }
1212 for (enum counter_type ctr = STATE_dnj_MIN_TEMP;
1213 ctr <= STATE_dnj_MAX_TEMP; ctr = (enum counter_type) (ctr + 1))
1214 {
1215 assert(counters[ctr - STATE_dnj_MIN] <= 0);
1216 }
1217 for (enum counter_type ctr =
1218 (enum counter_type) (STATE_dnj_MAX_TEMP + 1);
1219 ctr <= STATE_dnj_MAX; ctr = (enum counter_type) (ctr + 1))
1220 {
1221 assert(counters[ctr - STATE_dnj_MIN] <= (unsigned) bottom);
1222 counters[ctr - STATE_dnj_MIN] = (unsigned) bottom;
1223 }
1224#else
1225 (void) max_block; (void) bottom; // avoid unused variable warning
1226#endif
1227 return complexity_ok;
1228 }
1229 };
1230
1232 {
1233 public:
1234 /// \brief ensures there is no orphaned temporary work counter
1235 /// \details When a refinement has finished, all work registered with
1236 /// temporary counters should have been moved to normal counters. This
1237 /// function verifies this property.
1238 /// The function additionally ensures that no work counter exceeds its
1239 /// maximal allowed value, based on the size of the block or its
1240 /// constellation. (The size of the constellation is the unit used for
1241 /// counters related to [blocks in the] splitter constellation; the
1242 /// size of the block is used for other counters.)
1243 /// \param max_bunch ilog2(n^2) - ilog2(size of bunch)
1244 /// \returns false iff some temporary counter was nonzero. In that
1245 /// case, also the beginning of an error message is
1246 /// printed. The function should be called through the
1247 /// macro `mCRL2complexity()`, because that macro will
1248 /// print the remainder of the error message as needed.
1249 [[nodiscard]]
1250 result_type no_temporary_work(unsigned const max_bunch)
1251 {
1252#ifndef NDEBUG
1253 assert(max_bunch <= log_n);
1254 for (enum counter_type ctr = BUNCH_dnj_MIN;
1255 ctr <= BUNCH_dnj_MAX; ctr = (enum counter_type) (ctr + 1))
1256 {
1257 assert(counters[ctr - BUNCH_dnj_MIN] <= max_bunch);
1258 counters[ctr - BUNCH_dnj_MIN] = max_bunch;
1259 }
1260#else
1261 (void) max_bunch; // avoid unused variable warning
1262#endif
1263 return complexity_ok;
1264 }
1265 };
1266
1270 {
1271 public:
1272 /// \brief ensures there is no orphaned temporary work counter
1273 /// \details When a refinement has finished, all work registered with
1274 /// temporary counters should have been moved to normal counters. This
1275 /// function verifies this property.
1276 /// The function additionally ensures that no work counter exceeds its
1277 /// maximal allowed value, based on the size of the block or its
1278 /// constellation. (The size of the constellation is the unit used for
1279 /// counters related to [blocks in the] splitter constellation; the
1280 /// size of the block is used for other counters.)
1281 /// \param max_bunch ilog2(n^2) - ilog2(size of bunch)
1282 /// \returns false iff some temporary counter was nonzero. In that
1283 /// case, also the beginning of an error message is
1284 /// printed. The function should be called through the
1285 /// macro `mCRL2complexity()`, because that macro will
1286 /// print the remainder of the error message as needed.
1287 [[nodiscard]]
1288 result_type no_temporary_work(unsigned const max_bunch)
1289 {
1290#ifndef NDEBUG
1291 assert(max_bunch <= log_n);
1292 for (enum counter_type ctr = BLOCK_BUNCH_dnj_MIN;
1294 ctr = (enum counter_type) (ctr + 1))
1295 {
1296 if (counters[ctr - BLOCK_BUNCH_dnj_MIN] > max_bunch)
1297 {
1298 mCRL2log(log::error) << "Error 12: counter \""
1299 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1300 "maximum value (" << (unsigned) max_bunch << ") for ";
1301 return complexity_error;
1302 }
1303 assert(counters[ctr - BLOCK_BUNCH_dnj_MIN] <= max_bunch);
1304 counters[ctr - BLOCK_BUNCH_dnj_MIN] = max_bunch;
1305 }
1306 for (enum counter_type ctr = BLOCK_BUNCH_dnj_MIN_TEMP;
1307 ctr <= BLOCK_BUNCH_dnj_MAX; ctr = (enum counter_type) (ctr + 1))
1308 {
1309 if (counters[ctr - BLOCK_BUNCH_dnj_MIN] > 0)
1310 {
1311 mCRL2log(log::error) << "Error 13: counter \""
1312 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1313 "maximum value (" << (unsigned) 0 << ") for ";
1314 return complexity_error;
1315 }
1316 assert(counters[ctr - BLOCK_BUNCH_dnj_MIN] <= 0);
1317 }
1319#else
1320 (void) max_bunch; // avoid unused variable warning
1321#endif
1322 return complexity_ok;
1323 }
1324
1326 {
1328 return counters[BLOCK_BUNCH_dnj_MIN_TEMP-BLOCK_BUNCH_dnj_MIN] > 0;
1329 }
1331 {
1333 counters[BLOCK_BUNCH_dnj_MIN_TEMP - BLOCK_BUNCH_dnj_MIN] = 0;
1334 }
1335 };
1336
1339 {
1340 public:
1341 /// \brief ensures there is no orphaned temporary work counter
1342 /// \details When a refinement has finished, all work registered with
1343 /// temporary counters should have been moved to normal counters. This
1344 /// function verifies this property.
1345 /// The function additionally ensures that no work counter exceeds its
1346 /// maximal allowed value, based on the size of the block or its
1347 /// constellation. (The size of the constellation is the unit used for
1348 /// counters related to [blocks in the] splitter constellation; the
1349 /// size of the block is used for other counters.)
1350 /// \param max_source_block ilog2(n) - ilog2(size of source block)
1351 /// \param max_target_block ilog2(n) - ilog2(size of target block)
1352 /// \param bottom true iff the transition source is a bottom state
1353 /// \returns false iff some temporary counter was nonzero. In that
1354 /// case, also the beginning of an error message is
1355 /// printed. The function should be called through the
1356 /// macro `mCRL2complexity()`, because that macro will
1357 /// print the remainder of the error message as needed.
1358 [[nodiscard]]
1359 result_type no_temporary_work(unsigned const max_source_block,
1360 unsigned const max_target_block, bool const bottom)
1361 {
1362#ifndef NDEBUG
1363 assert((log_n + 1U) / 2U <= max_source_block);
1364 assert(max_source_block <= log_n);
1365 for (enum counter_type ctr = TRANS_dnj_MIN;
1367 ctr = (enum counter_type) (ctr + 1))
1368 {
1369 assert(counters[ctr - TRANS_dnj_MIN] <= max_source_block);
1370 counters[ctr - TRANS_dnj_MIN] = max_source_block;
1371 }
1372 assert((log_n + 1U) / 2U <= max_target_block);
1373 assert(max_target_block <= log_n);
1375 ctr < TRANS_dnj_MIN_TEMP;
1376 ctr = (enum counter_type) (ctr + 1))
1377 {
1378 assert(counters[ctr - TRANS_dnj_MIN] <= max_target_block);
1379 counters[ctr - TRANS_dnj_MIN] = max_target_block;
1380 }
1381 for (enum counter_type ctr = TRANS_dnj_MIN_TEMP;
1382 ctr <= TRANS_dnj_MAX_TEMP; ctr = (enum counter_type) (ctr + 1))
1383 {
1384 assert(counters[ctr - TRANS_dnj_MIN] <= 0);
1385 }
1386 for (enum counter_type ctr =
1387 (enum counter_type) (TRANS_dnj_MAX_TEMP + 1);
1388 ctr <= TRANS_dnj_MAX; ctr = (enum counter_type) (ctr + 1))
1389 {
1390 if (counters[ctr - TRANS_dnj_MIN] > (unsigned) bottom)
1391 {
1392 mCRL2log(log::error) << "Error 11: counter \""
1393 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1394 "maximum value (" << (unsigned) bottom << ") for ";
1395 return complexity_error;
1396 }
1397 counters[ctr - TRANS_dnj_MIN] = (unsigned) bottom;
1398 }
1399#else
1400 (void) max_source_block; (void) max_target_block; (void) bottom;
1401 // avoid unused variable warning
1402#endif
1403 return complexity_ok;
1404 }
1405
1406 /// \brief register work with some temporary counter without changing
1407 /// the balance between sensible and superfluous work
1408 /// \details The function increases a temporary work counter. It is
1409 /// also checked that the counter does not get too large. This variant
1410 /// of `add_work()` should be used if one wants to assign a single step
1411 /// of work to multiple temporary counters of transitions: for one of
1412 /// them, the normal `add_work()` is called, and for the others
1413 /// `add_work_notemporary()`.
1414 /// \param ctr counter with which work is registered
1415 /// \param max_value maximal allowed value of the counter. The old
1416 /// value of the counter should be strictly smaller.
1417 /// (Because it is a temporary counter, only `1` is
1418 /// sensible.)
1419 /// \returns false iff the counter was too large. In that case, also
1420 /// the beginning of an error message is printed.
1421 /// The function should be called through the macro
1422 /// `mCRL2complexity()`, because that macro will print
1423 /// the remainder of the error message as needed.
1424 [[nodiscard]]
1426 unsigned const max_value)
1427 {
1428#ifndef NDEBUG
1429 if (TRANS_dnj_MIN_TEMP > ctr || ctr > TRANS_dnj_MAX_TEMP)
1430 {
1431 return add_work(ctr, max_value);
1432 }
1433
1434 assert(1 == max_value);
1435 if (0 == counters[ctr - TRANS_dnj_MIN])
1436 {
1437 counters[ctr - TRANS_dnj_MIN] = DONT_COUNT_TEMPORARY;
1438 return complexity_ok;
1439 }
1440
1441 mCRL2log(log::error) << "Error 9: counter \""
1442 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1443 "maximum value (" << max_value << ") for ";
1444 return complexity_error;
1445#else
1446 (void) ctr; (void) max_value; // avoid unused variable warning
1447 return complexity_ok;
1448#endif
1449 }
1450 };
1451
1452 /*----------------- class specialisations for bisim_gj ------------------*/
1453
1455 {
1456 public:
1457 /// \brief ensures there is no orphaned temporary work counter
1458 /// \details When a refinement has finished, all work registered with
1459 /// temporary counters should have been moved to normal counters. This
1460 /// function verifies this property.
1461 /// The function additionally ensures that no work counter exceeds its
1462 /// maximal allowed value, based on the size of the block or its
1463 /// constellation. (The size of the constellation is the unit used for
1464 /// counters related to [blocks in the] splitter constellation; the
1465 /// size of the block is used for other counters.)
1466 /// \param max_B ilog2(n) - ilog2(size of block)
1467 /// \returns false iff some temporary counter was nonzero. In that
1468 /// case, also the beginning of an error message is
1469 /// printed. The function should be called through the
1470 /// macro `mCRL2complexity()`, because that macro will
1471 /// print the remainder of the error message as needed.
1472 [[nodiscard]]
1473 result_type no_temporary_work(unsigned const max_C,
1474 unsigned const max_B)
1475 {
1476#ifndef NDEBUG
1477 assert(max_C <= max_B);
1478 assert(max_B <= log_n);
1479 enum counter_type ctr;
1480 for (ctr = BLOCK_gj_MIN ;
1482 ctr = (enum counter_type) (ctr + 1))
1483 {
1484 assert(counters[ctr - BLOCK_gj_MIN] <= max_C);
1485 counters[ctr - BLOCK_gj_MIN] = max_C;
1486 }
1487 for (; ctr <= BLOCK_gj_MAX ; ctr = (enum counter_type) (ctr + 1))
1488 {
1489 assert(counters[ctr - BLOCK_gj_MIN] <= max_B);
1490 counters[ctr - BLOCK_gj_MIN] = max_B;
1491 }
1492#else
1493 (void) max_C; (void) max_B; // avoid unused variable warning
1494#endif
1495 return complexity_ok;
1496 }
1497 };
1498
1500 {
1501 public:
1502 /// \brief ensures there is no orphaned temporary work counter
1503 /// \details When a refinement has finished, all work registered with
1504 /// temporary counters should have been moved to normal counters. This
1505 /// function verifies this property.
1506 /// The function additionally ensures that no work counter exceeds its
1507 /// maximal allowed value, based on the size of the target
1508 /// constellation.
1509 /// \param max_sourceC ilog2(n) - ilog2(size of source constellation)
1510 /// Note that it should be the size of the source
1511 /// constellation, even though the transitions all
1512 /// start in the same block.
1513 /// \param max_targetC ilog2(n) - ilog2(size of target constellation)
1514 /// \returns false iff some temporary counter was nonzero. In that
1515 /// case, also the beginning of an error message is
1516 /// printed. The function should be called through the
1517 /// macro `mCRL2complexity()`, because that macro will
1518 /// print the remainder of the error message as needed.
1519 [[nodiscard]]
1520 result_type no_temporary_work(unsigned max_sourceC,
1521 unsigned max_targetC)
1522 {
1523#ifndef NDEBUG
1524 assert(max_sourceC <= log_n);
1525 assert(max_targetC <= log_n);
1526 enum counter_type ctr;
1527 for (ctr = BLC_gj_MIN ; ctr <
1529 ctr = (enum counter_type) (ctr + 1))
1530 {
1531 assert(counters[ctr - BLC_gj_MIN] <= max_sourceC);
1532 counters[ctr - BLC_gj_MIN] = max_sourceC;
1533 }
1534 for (; ctr <= BLC_gj_MAX ; ctr = (enum counter_type) (ctr + 1))
1535 {
1536 assert(counters[ctr - BLC_gj_MIN] <= max_targetC);
1537 counters[ctr - BLC_gj_MIN] = max_targetC;
1538 }
1539#else
1540 (void) max_sourceC; (void) max_targetC;
1541 // avoid unused variable warning
1542#endif
1543 return complexity_ok;
1544 }
1545 };
1546
1549 {
1550 public:
1551 /// \brief ensures there is no orphaned temporary work counter
1552 /// \details When a refinement has finished, all work registered with
1553 /// temporary counters should have been moved to normal counters.
1554 /// Further, there should not be any work ascribed to bottom-state
1555 /// counters in non-bottom states, but only to (new) bottom states.
1556 /// This function verifies these properties. It also sets all counters
1557 /// for bottom states to 1 so that later no more work can be assigned
1558 /// to them.
1559 /// The function additionally ensures that no work counter exceeds its
1560 /// maximal allowed value, based on the size of the block of which the
1561 /// state is a member.
1562 /// \param max_B log2(n) - log2(size of the block containing this
1563 /// state)
1564 /// \param bottom `true` iff the state to which these counters belong
1565 /// is a bottom state
1566 /// \returns false iff some temporary counter or some bottom-state
1567 /// counter of a non-bottom state was nonzero. In that
1568 /// case, also the beginning of an error message is
1569 /// printed. The function should be called through the
1570 /// macro `mCRL2complexity()`, because that macro will
1571 /// print the remainder of the error message as needed.
1572 [[nodiscard]]
1573 result_type no_temporary_work(unsigned const max_B, bool const bottom)
1574 {
1575#ifndef NDEBUG
1576 assert(max_B <= log_n);
1577 enum counter_type ctr;
1578 for (ctr = STATE_gj_MIN ;
1579 ctr < STATE_gj_MIN_TEMP ; ctr = (enum counter_type) (ctr + 1))
1580 {
1581 if (counters[ctr - STATE_gj_MIN] > max_B)
1582 {
1583 mCRL2log(log::error) << "Error 21: counter \""
1584 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1585 "maximum value (" << max_B << ") for ";
1586 return complexity_error;
1587 }
1588 assert(counters[ctr - STATE_gj_MIN] <= max_B);
1589 counters[ctr - STATE_gj_MIN] = max_B;
1590 }
1591
1592 // temporary state counters must be zero:
1593 for ( ; ctr <= STATE_gj_MAX_TEMP ; ctr=(enum counter_type) (ctr+1))
1594 {
1595 if (counters[ctr - STATE_gj_MIN] > 0)
1596 {
1597 mCRL2log(log::error) << "Error 15: counter \""
1598 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1599 "maximum value (" << 0 << ") for ";
1600 return complexity_error;
1601 }
1602 }
1603
1604 // bottom state counters must be 0 for non-bottom states and 1 for
1605 // bottom states:
1606 assert((unsigned) bottom <= 1);
1608 ctr = (enum counter_type) (ctr + 1))
1609 {
1610 if (counters[ctr - STATE_gj_MIN] > (unsigned) bottom)
1611 {
1612 mCRL2log(log::error) << "Error 16: counter \""
1613 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1614 "maximum value (" << (unsigned) bottom << ") for ";
1615 return complexity_error;
1616 }
1617 counters[ctr - STATE_gj_MIN] = (unsigned) bottom;
1618 }
1619
1620 // other counters must be at most 1 (after initialisation)
1621 for ( ; ctr <= STATE_gj_MAX ; ctr = (enum counter_type) (ctr + 1))
1622 {
1623 assert(counters[ctr - STATE_gj_MIN] <= 1);
1624 }
1625#else
1626 (void) max_B; (void) bottom; //avoid unused variable warning
1627#endif
1628 return complexity_ok;
1629 }
1630 };
1631
1634 {
1635 public:
1636 /// \brief ensures there is no orphaned temporary work counter
1637 /// \details When a refinement has finished, all work registered with
1638 /// temporary counters should have been moved to normal counters.
1639 /// Further, there should not be any work ascribed to bottom-state
1640 /// counters in transitions from non-bottom states, but only to
1641 /// transitions from (new) bottom states. This function verifies these
1642 /// properties. It also sets all counters for bottom states to 1 so
1643 /// that later no more work can be assigned to them.
1644 /// The function additionally ensures that no work counter exceeds its
1645 /// maximal allowed value, based on the size of the source block,
1646 /// target block or target constellation. (The constellation size is
1647 /// the relevant unit for counters that are related to transitions into
1648 /// the splitter, which is the constellation `NewC`. The block size is
1649 /// the unit for counters that are related to refinements. Because
1650 /// some parts of a refinement look at incoming transitions of the
1651 /// refined block and others at outgoing transitions, we need two block
1652 /// sizes.)
1653 /// \param max_sourceB the maximum allowed value for work counters
1654 /// based on the source state of the transition
1655 /// \param max_targetC the maximum allowed value for work counters
1656 /// based on the target constellation
1657 /// \param max_targetB the maximum allowed value for work counters
1658 /// based on the target block
1659 /// \param source_bottom `true` iff the transition to which these
1660 /// counters belong starts in a bottom state
1661 /// \returns false iff some temporary counter or some bottom-state
1662 /// counter of a transition with non-bottom source was
1663 /// nonzero. In that case, also the beginning of an
1664 /// error message is printed. The function should be
1665 /// called through the macro `mCRL2complexity()`,
1666 /// because that macro will print the remainder of the
1667 /// error message as needed.
1668 [[nodiscard]]
1669 result_type no_temporary_work(unsigned const max_sourceB,
1670 unsigned const max_targetC,
1671 unsigned const max_targetB, bool const source_bottom)
1672 {
1673#ifndef NDEBUG
1674 assert(max_sourceB <= log_n);
1675 assert(max_targetB <= log_n);
1676 assert(max_targetC <= max_targetB);
1677 enum counter_type ctr;
1678 for (ctr = TRANS_gj_MIN;
1680 ctr = (enum counter_type) (ctr + 1))
1681 {
1682 assert(counters[ctr - TRANS_gj_MIN] <= max_sourceB);
1683 counters[ctr - TRANS_gj_MIN] = max_sourceB;
1684 }
1685 for ( ; ctr <
1687 ctr = (enum counter_type) (ctr + 1))
1688 {
1689 assert(counters[ctr - TRANS_gj_MIN] <= max_targetB);
1690 counters[ctr - TRANS_gj_MIN] = max_targetB;
1691 }
1693 ctr = (enum counter_type) (ctr+1))
1694 {
1695 assert(counters[ctr - TRANS_gj_MIN] <= max_targetC);
1696 counters[ctr - TRANS_gj_MIN] = max_targetC;
1697 }
1698 // counter for the initialisation
1699 for ( ; ctr < TRANS_gj_MIN_TEMP; ctr = (enum counter_type) (ctr+1))
1700 {
1701 assert(counters[ctr - TRANS_gj_MIN] <= log_n);
1702 // counters[ctr - TRANS_gj_MIN] = ...;
1703 }
1704 // temporary transition counters must be zero
1705 for (; ctr <= TRANS_gj_MAX_TEMP ; ctr = (enum counter_type)(ctr+1))
1706 {
1707 if (counters[ctr - TRANS_gj_MIN] > 0)
1708 {
1709 mCRL2log(log::error) << "Error 17: counter \""
1710 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1711 "maximum value (" << 0 << ") for ";
1712 return complexity_error;
1713 }
1714 }
1715 // bottom state counters must be 0 for transitions from non-bottom
1716 // states and 1 for other transitions
1718 ctr = (enum counter_type) (ctr + 1))
1719 {
1720 if (counters[ctr - TRANS_gj_MIN] > (unsigned) source_bottom)
1721 {
1722 mCRL2log(log::error) << "Error 18: counter \""
1723 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1724 "maximum value (" << (unsigned) source_bottom << ") for ";
1725 return complexity_error;
1726 }
1727 counters[ctr - TRANS_gj_MIN] = (unsigned) source_bottom;
1728 }
1729 // other counters must be at most 1 (after initialisation)
1730 assert((unsigned) source_bottom <= 1);
1731 for ( ; ctr <= TRANS_gj_MAX ; ctr = (enum counter_type) (ctr+1))
1732 {
1733 if (counters[ctr - TRANS_gj_MIN] > 1)
1734 {
1735 mCRL2log(log::error) << "Error 19: counter \""
1736 << work_names[ctr - BLOCK_MIN]
1737 << "\" exceeded maximum value (" << 1 << ") for ";
1738 return complexity_error;
1739 }
1740 }
1741#else
1742 (void) max_sourceB; (void) max_targetC; (void) max_targetB;
1743 (void) source_bottom; // avoid unused variable warning
1744#endif
1745 return complexity_ok;
1746 }
1747
1748
1749 /// \brief register work with some temporary counter without changing
1750 /// the balance between sensible and superfluous work
1751 /// \details The function increases a temporary work counter. It is
1752 /// also checked that the counter does not get too large. This variant
1753 /// of `add_work()` should be used if one wants to assign a single step
1754 /// of work to multiple temporary counters of transitions: for one of
1755 /// them, the normal `add_work()` is called, and for the others
1756 /// `add_work_notemporary()`.
1757 /// \param ctr counter with which work is registered
1758 /// \param max_value maximal allowed value of the counter. The old
1759 /// value of the counter should be strictly smaller.
1760 /// (Because it is a temporary counter, only `1` is
1761 /// sensible.)
1762 /// \returns false iff the counter was too large. In that case, also
1763 /// the beginning of an error message is printed.
1764 /// The function should be called through the macro
1765 /// `mCRL2complexity()`, because that macro will print
1766 /// the remainder of the error message as needed.
1767 [[nodiscard]]
1769 unsigned const max_value)
1770 {
1771#ifndef NDEBUG
1772 if (TRANS_gj_MIN_TEMP > ctr || ctr > TRANS_gj_MAX_TEMP)
1773 {
1774 return add_work(ctr, max_value);
1775 }
1776
1777 assert(1 == max_value);
1778 if (0 == counters[ctr - TRANS_gj_MIN])
1779 {
1780 counters[ctr - TRANS_gj_MIN] = DONT_COUNT_TEMPORARY;
1781 return complexity_ok;
1782 }
1783
1784 mCRL2log(log::error) << "Error 8: counter \""
1785 << work_names[ctr - BLOCK_MIN] << "\" exceeded "
1786 "maximum value (" << max_value << ") for ";
1787 return complexity_error;
1788#else
1789 (void) ctr; (void) max_value; // avoid unused variable warning
1790 return complexity_ok;
1791#endif
1792 }
1793 };
1794
1795
1796 #ifdef TEST_WORK_COUNTER_NAMES
1797 /// \brief prints a message for each counter, for debugging purposes
1798 /// \details The function can be called, e. g., from
1799 /// `check_complexity::init()`. However, as it is not
1800 /// actually called, it is disabled by default.
1801 static void test_work_names();
1802 #endif
1803
1804
1805 /// \brief starts counting for a new refinement run
1806 /// \details Before any work can be counted, `init()` should be called to
1807 /// set the size of the state space to the appropriate value.
1808 /// \param n size of the state space
1809 static void init(state_type n)
1810 {
1811 #ifdef TEST_WORK_COUNTER_NAMES
1812 // as debugging measure:
1813 test_work_names();
1814 #endif
1815
1816 log_n = ilog2(n);
1817 #ifndef NDEBUG
1818 assert(0 == sensible_work); sensible_work = 0;
1821 #endif
1825 }
1826
1827
1828 /// \brief print grand total of work in the coroutines (to measure overhead)
1830 {
1831 trans_type overall_total = sensible_work_grand_total +
1833 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1834 #define percentage(steps,total)
1835 (assert((steps)<=
1836 (std::numeric_limits<trans_type>::max()-(total))/200),
1837 ((steps)*(trans_type)200+(total))/(total)/2)
1838 if (0 != overall_total)
1839 {
1840 mCRL2log(log::verbose) << "In the coroutines, "
1842 << " states and transitions were inspected. ";
1844 {
1845 mCRL2log(log::verbose) << "Additionally, there were "
1846 << no_of_waiting_cycles_grand_total << " waiting cycles ("
1848 << "% of all steps and cycles).\n";
1849 }
1850 mCRL2log(log::verbose) << "Of these, "
1851 << cancelled_work_grand_total << " steps were cancelled ("
1852 << percentage(cancelled_work_grand_total, overall_total)
1853 << "% of all steps";
1855 {
1856 mCRL2log(log::verbose) << " and cycles).\n";
1858 {
1859 mCRL2log(log::verbose) << "If we exclude the waiting "
1860 "cycles, then " << percentage(cancelled_work_grand_total,
1862 << "% of the steps have been cancelled.\n";
1863 }
1864 }
1865 else
1866 {
1867 mCRL2log(log::verbose) << ").\n";
1868 }
1872 }
1873 #undef percentage
1874 }
1875
1876
1877 /// \brief Assigns work to a counter and checks for errors
1878 /// \details Many functions that assign work to a counter actually return
1879 /// `true` if everything is ok and `false` if the counter was too large.
1880 /// In the latter case, they also print the start of an error message
1881 /// (``Counter ... too large''), but as they do not know to which larger
1882 /// unit (state, transition etc.) the counter belongs, this macro prints
1883 /// the end of the error message and aborts the program with a return code
1884 /// indicating failure.
1885 ///
1886 /// If debugging is off, the macro is translated to do nothing.
1887 /// \param unit the unit to which work is assigned
1888 /// \param call a function call that assigns work to a counter of `unit`
1889#ifndef NDEBUG
1890 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1891 #define mCRL2complexity(unit, call, info_for_debug)
1892 do
1893 {
1894 const enum check_complexity::result_type
1895 GG00OCOC0GQQ0COG00GQQQQOCOGQCO=((unit)->work_counter. call );
1896 switch (GG00OCOC0GQQ0COG00GQQQQOCOGQCO)
1897 {
1898 case check_complexity::complexity_ok: break;
1899 default:
1900 mCRL2log(log::error) << "Unexpected return value "
1901 << (int)GG00OCOC0GQQ0COG00GQQQQOCOGQCO << " for ";
1902 [[fallthrough]];
1903 case check_complexity::complexity_error:
1904 case check_complexity::complexity_print:
1905 mCRL2log(log::error)
1906 << (unit)->debug_id(info_for_debug) << '\n';
1907 if (check_complexity::complexity_print !=
1908 GG00OCOC0GQQ0COG00GQQQQOCOGQCO)
1909 exit(EXIT_FAILURE);
1910 break;
1911 }
1912 }
1913 while (0)
1914#else
1915 #define mCRL2complexity(unit, call, info_for_debug)
1916 do
1917 {
1918 if (check_complexity::complexity_ok !=
1919 ((unit)->work_counter. call ))
1920 {
1921 mCRL2log(log::error) << __FILE__ << ':' << __LINE__
1922 << " Error in mCRL2complexity()\n";
1923 exit(EXIT_FAILURE);
1924 }
1925 }
1926 while (0)
1927#endif
1928
1929#else // ifndef NDEBUG
1930
1931 #define mCRL2complexity(unit, call, info_for_debug) do {} while (0)
1932
1933#endif // ifndef NDEBUG
1934
1935};
1936
1937} // end namespace detail
1938// end namespace lts
1939// end namespace mcrl2
1940
1941#endif // MCRL2_LTS_DETAIL_CHECK_COMPLEXITY_H
#define DONT_COUNT_TEMPORARY
special value for temporary work without changing the balance
#define percentage(steps, total)
logger(const log_level_t l)
Default constructor.
Definition logger.h:163
result_type no_temporary_work(unsigned max_sourceC, unsigned max_targetC)
ensures there is no orphaned temporary work counter
void reset_work_counter_4_4()
sets the temporary counter associated with line 4.4 to zero
result_type no_temporary_work(unsigned const max_targetC)
ensures there is no orphaned temporary work counter
unsigned char get_work_counter_4_4() const
returns the temporary counter associated with line 4.4
result_type no_temporary_work(unsigned const max_bunch)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_C, unsigned const max_B)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_block)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_C, unsigned const max_B)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_bunch)
ensures there is no orphaned temporary work counter
subset of counters (to be associated with a state or transition)
result_type add_work(enum counter_type const ctr, unsigned const max_value)
register work with some counter
counter_t()
constructor, initializes all counters to 0
result_type finalise_work(enum counter_type const from, enum counter_type const to, unsigned const max_value)
move temporary work to its final counter
result_type move_work(enum counter_type const from, enum counter_type const to, unsigned const max_value)
move temporary work to another counter
result_type cancel_work(enum counter_type const ctr)
cancel temporary work
std::array< unsigned char, LastCounter - FirstCounter+1 > counters
actual space to store the counters
result_type no_temporary_work(unsigned const max_B, bool const bottom)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_block, bool const bottom)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_B, bool const bottom)
ensures there is no orphaned temporary work counter
result_type add_work_notemporary(enum counter_type const ctr, unsigned const max_value)
register work with some temporary counter without changing the balance between sensible and superfluo...
result_type no_temporary_work(unsigned const max_sourceB, unsigned const max_targetC, unsigned const max_targetB, bool const source_bottom)
ensures there is no orphaned temporary work counter
result_type no_temporary_work(unsigned const max_source_block, unsigned const max_target_block, bool const bottom)
ensures there is no orphaned temporary work counter
result_type add_work_notemporary(enum counter_type const ctr, unsigned const max_value)
register work with some temporary counter without changing the balance between sensible and superfluo...
result_type no_temporary_work(unsigned const max_sourceB, unsigned const max_targetC, unsigned const max_targetB, bool const source_bottom)
ensures there is no orphaned temporary work counter
result_type add_work_notemporary(enum counter_type const ctr, unsigned const max_value)
register work with some temporary counter without changing the balance between sensible and superfluo...
class for time complexity checks
static int ilog2(state_type size)
calculate the base-2 logarithm, rounded down
static void cancel_work_units(trans_type units=1)
static signed_trans_type sensible_work
counter to register the work balance for coroutines
static void wait(trans_type units=1)
do some work that cannot be assigned directly
static unsigned char log_n
value of floor(log2(n)) for easy access
counter_type
Type for complexity budget counters.
static trans_type sensible_work_grand_total
the number of useful steps in the course of the whole algorithm
static trans_type no_of_waiting_cycles_grand_total
the number of waiting cycles in the course of the whole algorithm
static trans_type cancelled_work_grand_total
the number of cancelled steps (in aborted coroutines) in the course of the whole algorithm
static void check_temporary_work()
check that not too much superfluous work has been done
static void finalise_work_units(trans_type units=1)
static void print_grand_totals()
print grand total of work in the coroutines (to measure overhead)
static void init(state_type n)
starts counting for a new refinement run
static const std::array< const char *, TRANS_gj_MAX - BLOCK_MIN+1 > work_names
printable names of the counter types (for error messages)
static bool cannot_wait_before_reset
indicates whether waiting cycles are allowed
static trans_type no_of_waiting_cycles
the number of waiting cycles that have been done in the current accounting period
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:393
@ verbose
Definition logger.h:36
The main LTS namespace.
std::size_t operator()(const std::vector< X > &v) const