mCRL2
Loading...
Searching...
No Matches
partial_order_reduction.h
Go to the documentation of this file.
1// Author(s): Wieger Wesselink
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9/// \file mcrl2/pbes/partial_order_reduction.h
10/// \brief add your file description here.
11
12#ifndef MCRL2_PBES_PARTIAL_ORDER_REDUCTION_H
13#define MCRL2_PBES_PARTIAL_ORDER_REDUCTION_H
14
15#include <iomanip>
16#include <boost/dynamic_bitset.hpp>
17#include "mcrl2/data/rewriters/one_point_rule_rewriter.h"
18#include "mcrl2/data/rewriters/quantifiers_inside_rewriter.h"
19#include "mcrl2/data/substitution_utility.h"
20#include "mcrl2/data/substitutions/maintain_variables_in_rhs.h"
21#include "mcrl2/pbes/replace_capture_avoiding_with_an_identifier_generator.h"
22#include "mcrl2/pbes/rewriters/enumerate_quantifiers_rewriter.h"
23#include "mcrl2/pbes/pbes_equation_index.h"
24#include "mcrl2/pbes/unify_parameters.h"
25#include "mcrl2/smt/solver.h"
26#include "mcrl2/utilities/skip.h"
27
28namespace mcrl2::pbes_system
29{
30
31namespace detail {
32
33inline
35{
36 return data::and_(x1, data::and_(x2, x3));
37}
38
39inline
40data::data_expression equal_to(const data::data_expression_list& x, const data::data_expression_list& y)
41{
43 auto xi = x.begin();
44 auto yi = y.begin();
45 for (; xi != x.end(); ++xi, ++yi)
46 {
47 result = data::lazy::and_(result, data::equal_to(*xi, *yi));
48 }
49 return result;
50}
51
52} // namespace detail
53
55
56inline
58{
59 bool first = true;
60 std::ostringstream buf;
61 buf << "{ ";
62 for (std::size_t k = s.find_first(); k != summand_set::npos; k = s.find_next(k))
63 {
64 buf << (first ? "" : ", ") << k;
65 first = false;
66 }
67 buf << " }";
68 return buf.str();
69}
70
72{
73 data::variable_list e;
75 data::data_expression_list g;
76 /// \brief Encodes the dependency relation belonging to this summand_class
77 /// \detail nxt[i] contains j iff X_i --this--> X_j
83 bool is_deterministic = false;
84
85 summand_class() = default;
86
87 // n is the number of PBES equations
88 summand_class(data::variable_list e_, data::data_expression f_, data::data_expression_list g_, std::size_t n)
89 : e(std::move(e_)), f(std::move(f_)), g(std::move(g_))
90 {
91 nxt.resize(n);
92 }
93
94 void set_num_summands(const std::size_t N)
95 {
96 NES.resize(N);
97 DNA.resize(N);
98 DNS.resize(N);
99 DNL.resize(N);
100 }
101
102 // returns X_i -k->
103 [[nodiscard]]
104 bool depends(std::size_t i) const
105 {
106 return !nxt[i].empty();
107 }
108
109 // returns X_i -k-> j
110 [[nodiscard]]
111 bool depends(std::size_t i, std::size_t j) const
112 {
113 using utilities::detail::contains;
114
115 return contains(nxt[i], j);
116 }
117
118 void print(std::ostream& out, const std::set<std::size_t>& s, const std::size_t N) const
119 {
120 using utilities::detail::contains;
121
122 for (std::size_t i = 0; i < N; i++)
123 {
124 out << (contains(s, i) ? '1' : '0');
125 }
126 }
127
128 void print(std::ostream& out) const
129 {
130 out << "deterministic = " << std::boolalpha << is_deterministic << std::endl;
131 out << "NES = " << print_summand_set(NES) << std::endl;
132 out << "DNA = " << print_summand_set(DNA) << std::endl;
133 out << "DNS = " << print_summand_set(DNS) << std::endl;
134 out << "DNL = " << print_summand_set(DNL) << std::endl;
135 }
136};
137
138// The part of a summand used for determining equivalence classes
140{
141 data::variable_list e;
143 data::data_expression_list g;
144
145 explicit summand_equivalence_key(data::variable_list e_, data::data_expression f_, data::data_expression_list g_)
146 : e(std::move(e_)), f(std::move(f_)), g(std::move(g_))
147 {}
148
149 explicit summand_equivalence_key(const summand_class& summand)
150 : e(summand.e), f(summand.f), g(summand.g)
151 {}
152
153 explicit summand_equivalence_key(const srf_summand& summand)
155 {}
156
157 bool operator<(const summand_equivalence_key& other) const
158 {
159 return std::tie(e, f, g) < std::tie(other.e, other.f, other.g);
160 }
161
162 bool operator==(const summand_equivalence_key& other) const
163 {
164 return std::tie(e, f, g) == std::tie(other.e, other.f, other.g);
165 }
166};
167
168} // namespace mcrl2::pbes_system
169
170namespace std {
171
172/// \brief specialization of the standard std::hash function.
173template<>
175{
177 {
178 std::size_t seed = std::hash<atermpp::aterm>()(x.f);
179 if (!x.e.empty())
180 {
181 seed = std::hash<atermpp::aterm>()(x.e) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
182 }
183 if (!x.g.empty())
184 {
185 seed = std::hash<atermpp::aterm>()(x.g) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
186 }
187 return seed;
188 }
189};
190
191} // namespace std
192
193namespace mcrl2::pbes_system
194{
195
197{
200static inline bool operator&&(tribool a, tribool b)
201{
202 return a == yes || b == yes || (a == maybe && b == maybe);
203}
204// Short-circuit version of the operator && for tribools
205// The second function will be told whether a 'yes' answer is required to satisfy
206// the expression
207static inline bool operator&&(const std::function<tribool()>& a, const std::function<tribool(bool)>& b)
208{
209 tribool a_result = a();
210 if(a_result == yes)
211 {
212 return true;
213 }
214 return a_result && b(a_result == no);
215}
216
218{
219 // if true use alternative A3 for maybe clauses in accordance conditions
221 // if true, compute which summands are deterministic
223 // if true, compute triangle accordance
225 // if true, compute left accordance
227 // if true, compute NES relation
228 bool compute_NES = true;
229 // if true, perform static analysis and reduce state space
230 bool reduction = true;
231 // if true, apply condition L
232 bool use_condition_L = true;
233
234 data::rewrite_strategy rewrite_strategy = data::rewrite_strategy::jitty;
235
236 bool use_smt_solver = false;
237 std::chrono::milliseconds smt_timeout = std::chrono::milliseconds::zero();
238};
239
241{
242 protected:
244
246 {
249 invis_pair(summand_set Twork_, summand_set Ts_)
250 : Twork(std::move(Twork_)), Ts(std::move(Ts_))
251 {}
252
253 bool operator<(const invis_pair& other) const
254 {
255 return std::tie(Twork, Ts) < std::tie(other.Twork, other.Ts);
256 }
257 };
258
260 {
261 std::set<std::size_t> Ts; // test set
262 std::set<std::size_t> Ws; // write set
263 std::set<std::size_t> Rs; // read set
264 std::set<std::size_t> Vs; // variable set
265 };
266
270 srf_pbes m_pbes;
274
275 // the parameters of the PBES equations
277
278 // maps parameters to their corresponding index
280
281 // maps summands to the index of the corresponding summand class
283
284 // X_j \in nxt_k(X_i) <=> j \in m_summand_classes[k].nxt[i]
285 // (X_i |- k -> X_j) <=> m_summand_classes[k].NES[i][j]
287
288 summand_set m_invis; // invisible summand classes
289 summand_set m_vis; // visible summand classes
290
291 // One NES for every predicate variable X_i that can be used for summand
292 // class k when !depends(i,k).
294
295 std::chrono::high_resolution_clock::duration m_static_analysis_duration{};
296 std::chrono::high_resolution_clock::duration m_exploration_duration{};
297
299
301
303 {
304 private:
308
309 data::variable_list qvars1_k;
311 data::data_expression_list updates1_k;
312
313 data::variable_list qvars1_k1;
315 data::data_expression_list updates1_k1;
316
317 data::variable_list qvars2_k;
319 data::data_expression_list updates2_k;
320
321 data::variable_list qvars2_k1;
323 data::data_expression_list updates2_k1;
324
327
329
330 // Depending on whether the weak (A3) or strong condition (A4) is used, wrap the consequent in
331 // an existential quantifier
332 data::data_expression make_exists_if_strong(const data::variable_list& vars, const data::data_expression& body)
333 {
334 return compute_weak_conditions ? body : make_exists_(vars, body);
335 }
336
338 {
339 return data::sort_bool::and_(condition1_k1, data::replace_variables_capture_avoiding(condition1_k, sigma_k1, id_gen));
340 }
341
343 {
344 data::data_expression parameters_equal = detail::equal_to(data::replace_variables_capture_avoiding(updates2_k, sigma_k1, id_gen),
345 data::replace_variables_capture_avoiding(updates2_k1, sigma_k, id_gen));
346 data::data_expression body = detail::make_and(
347 condition2_k,
348 data::replace_variables_capture_avoiding(condition2_k1, sigma_k, id_gen),
349 parameters_equal
350 );
351 return make_exists_if_strong(qvars2_k + qvars2_k1, body);
352 }
353
355 {
357 }
358
360 {
361 data::data_expression parameters_equal = detail::equal_to(data::replace_variables_capture_avoiding(updates2_k, sigma_k1, id_gen),
362 data::replace_variables_capture_avoiding(updates2_k1, sigma_k, id_gen));
363 data::data_expression body = detail::make_and(
364 data::replace_variables_capture_avoiding(condition2_k, sigma_k1, id_gen),
365 data::replace_variables_capture_avoiding(condition2_k1, sigma_k, id_gen),
366 parameters_equal
367 );
368 return make_exists_if_strong(qvars2_k + qvars2_k1, body);
369 }
370
372 {
373 data::data_expression parameters_equal = detail::equal_to(updates1_k1, data::replace_variables_capture_avoiding(updates1_k1, sigma_k, id_gen));
374 return data::sort_bool::and_(
375 data::replace_variables_capture_avoiding(condition1_k1, sigma_k, id_gen),
376 parameters_equal
377 );
378 }
379
381 {
382 data::data_expression parameters_equal = detail::equal_to(updates2_k1, data::replace_variables_capture_avoiding(updates2_k1, sigma_k, id_gen));
383 data::data_expression body = data::sort_bool::and_(
384 data::replace_variables_capture_avoiding(condition2_k1, sigma_k, id_gen),
385 parameters_equal
386 );
387 return make_exists_if_strong(qvars2_k1, body);
388 }
389
390 tribool accords_data(bool affect_set, bool needs_yes,
391 const std::function<data::data_expression()>& make_antecedent,
392 const std::function<data::data_expression()>& make_consequent)
393 {
394 // Check whether the maybe clause is satisfied by affect sets and it is sufficient to return maybe
395 if (affect_set && !needs_yes)
396 {
397 return maybe;
398 }
399
400 data::data_expression antecedent = make_antecedent();
401 data::data_expression yes_condition = make_forall_(combined_quantified_vars, data::sort_bool::not_(antecedent));
402 if (parent.is_true(yes_condition))
403 {
404 return yes;
405 }
406 if (needs_yes)
407 {
408 // we were not able to return yes, now it doesn't matter what we return
409 return no;
410 }
411
412 data::data_expression consequent = make_consequent();
413 data::data_expression condition = make_forall_(combined_quantified_vars, data::sort_bool::implies(antecedent, consequent));
414
415 return parent.is_true(condition) ? maybe : no;
416 }
417
418 public:
419 summand_relations_data(partial_order_reduction_algorithm& p, const std::size_t k, const std::size_t k1)
420 : parent(p)
421 , compute_weak_conditions(p.m_options.compute_weak_conditions)
422 {
423 const summand_class& summand_k = parent.m_summand_classes[k];
424 const summand_class& summand_k1 = parent.m_summand_classes[k1];
425
426 const data::variable_list& parameters = parent.m_pbes.equations()[0].variable().parameters();
427 for(const data::variable& v: parameters)
428 {
429 id_gen.add_identifier(v.name());
430 }
431
432 // For both summands, create a copy with fresh variables
433 // These will be used when constructing accordance and NES conditions
434 summand_equivalence_key new1_k = rename_duplicate_variables(id_gen, summand_equivalence_key(summand_k));
435 summand_equivalence_key new1_k1 = rename_duplicate_variables(id_gen, summand_equivalence_key(summand_k1));
436 qvars1_k = new1_k.e; condition1_k = new1_k.f; updates1_k = new1_k.g;
437 qvars1_k1 = new1_k1.e; condition1_k1 = new1_k1.f; updates1_k1 = new1_k1.g;
438 data::add_assignments(sigma_k, parameters, updates1_k);
439 data::add_assignments(sigma_k1, parameters, updates1_k1);
440
442 {
443 // When using the stronger condition A4, create another fresh copy
444 summand_equivalence_key new2_k = rename_duplicate_variables(id_gen, summand_equivalence_key(summand_k));
445 summand_equivalence_key new2_k1 = rename_duplicate_variables(id_gen, summand_equivalence_key(summand_k1));
446 qvars2_k = new2_k.e; condition2_k = new2_k.f; updates2_k = new2_k.g;
447 qvars2_k1 = new2_k1.e; condition2_k1 = new2_k1.f; updates2_k1 = new2_k1.g;
448 }
449 else
450 {
451 // In the weak case (A3), the two copies are identical
452 qvars2_k = qvars1_k; condition2_k = condition1_k; updates2_k = updates1_k;
453 qvars2_k1 = qvars1_k1; condition2_k1 = condition1_k1; updates2_k1 = updates1_k1;
454 }
455
456 combined_quantified_vars = parameters + qvars1_k + qvars1_k1;
457 }
458
460 {
461 data::data_expression cannot_enable = make_forall_(
462 combined_quantified_vars,
463 data::sort_bool::not_(
464 detail::make_and(
465 data::sort_bool::not_(condition1_k),
466 condition1_k1,
467 data::replace_variables_capture_avoiding(condition1_k, sigma_k1, id_gen)
468 )
469 )
470 );
471
472 // The condition is constructed in a negated way, so the approximation of the decision
473 // procedure works the right way. Note that the result of this function is negated as well.
474 return !parent.is_true(cannot_enable);
475 }
476
477 tribool left_accords_data(bool affect_set, bool needs_yes)
478 {
479 return accords_data(affect_set, needs_yes,
480 [this](){ return left_accords_antecedent(); },
481 [this](){ return left_accords_consequent(); });
482 }
483
484 tribool square_accords_data(bool affect_set, bool needs_yes)
485 {
486 return accords_data(affect_set, needs_yes,
487 [this](){ return coenabled_antecedent(); },
488 [this](){ return square_accords_consequent(); });
489 }
490
491 tribool triangle_accords_data(bool affect_set, bool needs_yes)
492 {
493 return accords_data(affect_set, needs_yes,
494 [this](){ return coenabled_antecedent(); },
495 [this](){ return triangle_accords_consequent(); });
496 }
497 };
498
500 {
501 auto i = m_summand_index.find(summand_equivalence_key(summand));
502 assert(i != m_summand_index.end());
503 return i->second;
504 }
505
507 {
508 auto i = m_parameter_positions.find(v);
509 return i->second;
510 }
511
512 const summand_set& DNA(std::size_t k) const
513 {
514 return m_summand_classes[k].DNA;
515 }
516
518 {
519 return m_summand_classes[k].DNA;
520 }
521
522 const summand_set& DNS(std::size_t k) const
523 {
524 return m_summand_classes[k].DNS;
525 }
526
528 {
529 return m_summand_classes[k].DNS;
530 }
531
532 const summand_set& DNL(std::size_t k) const
533 {
534 return m_summand_classes[k].DNL;
535 }
536
538 {
539 return m_summand_classes[k].DNL;
540 }
541
542 const summand_set& NES(std::size_t k) const
543 {
544 return m_summand_classes[k].NES;
545 }
546
548 {
549 return m_summand_classes[k].NES;
550 }
551
553 {
554 std::size_t N = m_summand_classes.size();
555
556 summand_set result(N);
557 std::size_t i = m_equation_index.index(X_e.name());
558 const data::variable_list& d = m_pbes.equations()[i].variable().parameters();
559 const data::data_expression_list& e = X_e.parameters();
560 for (std::size_t k = 0; k < N; k++)
561 {
562 if (!depends(i, k))
563 {
564 continue;
565 }
566 const summand_class& summand_k = m_summand_classes[k];
567 const data::variable_list& e_k = summand_k.e;
568 const data::data_expression& f_k = summand_k.f;
569
570 // Add assignments to d in every iteration, becuase they might have been
571 // overwritten if a variable in e_k coincides with a parameter in d.
572 add_assignments(m_sigma, d, e);
573 m_enumerator.enumerate(enumerator_element(e_k, f_k),
574 m_sigma,
575 [&](const enumerator_element&) {
576 result.set(k);
577 return true;
578 },
579 data::is_false,
580 data::is_true
581 );
582 remove_assignments(m_sigma, e_k);
583 }
584 remove_assignments(m_sigma, d);
585 return result;
586 }
587
589 {
590 return m_invis & K;
591 }
592
593 // Choose a NES according to whether k is present in X
595 {
596 if(!depends(m_equation_index.index(X_e.name()), k))
597 {
598 return m_dependency_nes[m_equation_index.index(X_e.name())];
599 }
600 else
601 {
602 return m_summand_classes[k].NES;
603 }
604 }
605
607 const summand_set& Twork,
608 const summand_set& Ts,
609 const summand_set& en_X_e
610 ) const
611 {
612 using utilities::detail::set_difference;
613 using utilities::detail::set_intersection;
614 using utilities::detail::set_union;
615
616 const summand_class& summand_k = m_summand_classes[k];
617 if (!summand_k.is_deterministic)
618 {
619 return DNL(k);
620 }
621 if (!m_options.compute_left_accordance)
622 {
623 return DNA(k);
624 }
625
626 summand_set Twork_Ts = Twork | Ts;
627
628 summand_set T1 = Twork_Ts | en_X_e;
629 summand_set T2 = Twork_Ts & en_X_e;
630
631 auto h = [&](const summand_set& A)
632 {
633 return (A - T1).count() + m_largest_equation_size * (A - T2).count();
634 };
635
636 return h(DNS(k)) <= h(DNL(k)) ? DNS(k) : DNL(k);
637 }
638
640 {
641 using utilities::detail::contains;
642 using utilities::detail::has_empty_intersection;
643 using utilities::detail::set_difference;
644 using utilities::detail::set_includes;
645 using utilities::detail::set_intersection;
646 using utilities::detail::set_union;
647
648 std::size_t N = m_summand_classes.size();
649
650 struct compare_invis_pair
651 {
652 // Prevent allocations and destructions by using temp_set
653 mutable summand_set temp_set;
654 const summand_set& m_en_X_e;
655
656 explicit compare_invis_pair(const summand_set& en)
657 : m_en_X_e(en)
658 {}
659
660 std::size_t size(const invis_pair& p) const
661 {
662 temp_set = p.Twork;
663 temp_set |= p.Ts;
664 temp_set &= m_en_X_e;
665 return temp_set.count();
666 }
667
668 bool operator()(const invis_pair& x, const invis_pair& y) const
669 {
670 std::size_t sizex = size(x);
671 std::size_t sizey = size(y);
672 return std::tie(sizex, x) < std::tie(sizey, y);
673 };
674 };
675
676 // C will always be sorted according to the size of each element.
677 std::set<invis_pair, compare_invis_pair> C{compare_invis_pair(en_X_e)};
678 auto invis_en_X_e = invis(en_X_e);
679 for (std::size_t k = invis_en_X_e.find_first(); k != summand_set::npos; k = invis_en_X_e.find_next(k))
680 {
681 // If DNL is not computed, only attempt to construct stubborn set for deterministic
682 // summands.
683 if(m_summand_classes[k].is_deterministic || m_options.compute_left_accordance)
684 {
685 invis_pair pair_k{summand_set(N), summand_set(N)};
686 pair_k.Twork.set(k);
687 C.insert(pair_k);
688 }
689 }
690 if (C.empty())
691 {
692 return en_X_e;
693 }
694
695 while (!C.empty())
696 {
697 // The smallest element is the first element in the set
698 auto p = C.extract(C.begin());
699 auto& Twork = p.value().Twork;
700 auto& Ts = p.value().Ts;
701
702 if (Twork.none())
703 {
704 summand_set T = Ts & en_X_e;
705 for (std::size_t k = T.find_first(); k != summand_set::npos; k = T.find_next(k))
706 {
707 if (DNA(k).is_subset_of(Ts))
708 {
709 return Ts;
710 }
711 }
712 std::size_t k = T.find_first(); // TODO: choose k according to D2t
713 Twork |= (DNA(k) - Ts);
714 }
715 else
716 {
717 std::size_t k = Twork.find_first();
718 Twork.reset(k);
719 Ts.set(k);
720 if (en_X_e.test(k))
721 {
722 if (m_vis.test(k))
723 {
724 // To satisfy condition P, we fully expand in case a visible
725 // transition occurs in the stubborn set.
726 // Thus, do not add this candidate set back to C.
727 // Effectively, we implement a stronger version of V:
728 // If r(s) contains an enabled visible transition,
729 // then it contains all transitions.
730 continue;
731 }
732 auto& DNS_or_DNL = DNX(k, Twork, Ts, en_X_e);
733 Twork |= (DNS_or_DNL - Ts);
734 }
735 else
736 {
737 auto& NES = choose_minimal_NES(k, X_e);
738 Twork |= (NES - Ts);
739 }
740 }
741
742 C.insert(std::move(p));
743 }
744 return en_X_e;
745 }
746
748 {
749 const auto& d = m_parameters;
750 const auto& e = X_e.parameters();
751
752 std::set<propositional_variable_instantiation> result;
753 std::size_t i = m_equation_index.index(X_e.name());
754 for (std::size_t k = K.find_first(); k != summand_set::npos; k = K.find_next(k))
755 {
756 const summand_class& summand_k = m_summand_classes[k];
757 const data::variable_list& e_k = summand_k.e;
758 const data::data_expression& f_k = summand_k.f;
759 const data::data_expression_list& g_k = summand_k.g;
760 const auto& J = summand_k.nxt[i];
761
762 // Add assignments for parameters during every iteration,
763 // because they might have been removed on the previous one if
764 // a parameter coincides with a quantified variable.
765 data::add_assignments(m_sigma, d, e);
766 data::remove_assignments(m_sigma, e_k);
767 m_enumerator.enumerate(enumerator_element(e_k, f_k),
768 m_sigma,
769 [&](const enumerator_element& p) {
770 p.add_assignments(e_k, m_sigma, m_rewr);
771 data::data_expression_list g(g_k.begin(), g_k.end(), [&](const data::data_expression& x) { return m_rewr(x, m_sigma); });
772 for (std::size_t j: J)
773 {
774 const core::identifier_string& X_j = m_pbes.equations()[j].variable().name();
775 result.insert(propositional_variable_instantiation(X_j, g));
776 }
777 return false;
778 },
779 data::is_false
780 );
781 data::remove_assignments(m_sigma, e_k);
782 }
783 return result;
784 }
785
787 {
788 std::size_t n = m_pbes.equations().size();
789 for (std::size_t i = 0; i < n; i++)
790 {
791 const srf_equation& eqn = m_pbes.equations()[i];
792 for (const srf_summand& summand: eqn.summands())
793 {
794 std::size_t j = m_equation_index.index(summand.variable().name());
795 std::size_t k = summand_index(summand);
796 m_summand_classes[k].nxt[i].insert(j);
797 }
798 }
799 }
800
801 // returns the indices of the parameters that occur freely in x
803 {
804 std::set<std::size_t> result;
805 for (const data::variable& v: find_free_variables(x))
806 {
807 result.insert(parameter_position(v));
808 }
809 return result;
810 }
811
813 {
814 if(m_solver != nullptr)
815 {
816 bool negate = false;
817 if(data::is_forall(expr))
818 {
819 negate = true;
820 const data::forall& f = atermpp::down_cast<data::forall>(expr);
822 }
823 switch(m_solver->solve(data::variable_list(), expr, m_options.smt_timeout))
824 {
825 case smt::answer::SAT: return negate ^ true;
826 case smt::answer::UNSAT: return negate ^ false;
827 case smt::answer::UNKNOWN: return false;
828 }
829 }
830 else
831 {
832 data::data_expression result = m_rewr(data::one_point_rule_rewrite((m_rewr(expr))));
833 if (result != data::sort_bool::true_() && result != data::sort_bool::false_())
834 {
835 mCRL2log(log::verbose) << "Cannot rewrite " << result << " any further" << std::endl;
836 }
837 return result == data::sort_bool::true_();
838 }
839 // This code is unreachable, but necessary to silence a compiler warning
840 return false;
841 }
842
843 /// \brief Return true iff k1 can never happen after k happens, as deduced from
844 /// predicate dependencies.
845 bool dependency_permanently_disables(const std::size_t k, const std::size_t k1) const
846 {
847 std::size_t N = m_summand_classes.size();
848 const summand_class& summand_k = m_summand_classes[k];
849 std::set<std::size_t> reachable_after_k;
850
851 // Check to which equations k can lead
852 for (std::size_t i = 0; i < m_pbes.equations().size(); i++)
853 {
854 reachable_after_k.insert(summand_k.nxt[i].begin(), summand_k.nxt[i].end());
855 }
856
857 // Explore the rest of the dependency relation
858 std::list<std::size_t> todo(reachable_after_k.begin(), reachable_after_k.end());
859 while (!todo.empty())
860 {
861 std::size_t i = todo.front();
862 todo.pop_front();
863 for (std::size_t k2 = 0; k2 < N; k2++)
864 {
865 for (const std::size_t j: m_summand_classes[k2].nxt[i])
866 {
867 if (reachable_after_k.count(j) == 0)
868 {
869 todo.push_back(j);
870 reachable_after_k.insert(j);
871 }
872 }
873 }
874 }
875
876 return !std::any_of(reachable_after_k.begin(), reachable_after_k.end(),
877 [&](const std::size_t i) { return depends(i, k1); });
878 }
879
881 {
882 using utilities::detail::set_union;
883 using utilities::detail::has_empty_intersection;
884
885 std::size_t n = m_pbes.equations().size();
886 std::size_t N = m_summand_classes.size();
887
888 for (std::size_t i = 0; i < n; i++)
889 {
890 m_dependency_nes[i].resize(N);
891 for (std::size_t k = 0; k < N; k++)
892 {
893 const std::set<std::size_t>& J = m_summand_classes[k].nxt[i];
894 if (J.size() > 1 || (J.size() == 1 && *J.begin() != i))
895 {
896 m_dependency_nes[i].set(k);
897 }
898 }
899 }
900 }
901
902 // returns X_i |--k--> X_j
903 bool depends(std::size_t i, std::size_t k, std::size_t j) const
904 {
905 return m_summand_classes[k].depends(i, j);
906 };
907
908 // TODO: precompute this function
909 // returns X_i |--k-->
910 bool depends(std::size_t i, std::size_t k) const
911 {
912 std::size_t n = m_pbes.equations().size();
913 for (std::size_t j = 0; j < n; j++)
914 {
915 if (depends(i, k, j))
916 {
917 return true;
918 }
919 }
920 return false;
921 };
922
924 {
925 std::vector<data::variable> new_variables;
926 data::maintain_variables_in_rhs< data::mutable_map_substitution<> > sigma;
927 for (const data::variable& var: summ.e)
928 {
929 core::identifier_string new_name = id_gen(var.name());
930 if (new_name != var.name())
931 {
932 sigma[var] = data::variable(new_name, var.sort());
933 }
934 new_variables.emplace_back(new_name, var.sort());
935 }
936
937 auto replace_vars = [&](const data::data_expression& e)
938 {
939 return data::replace_variables_capture_avoiding_with_an_identifier_generator(e, sigma, id_gen);
940 };
941
942 return summand_equivalence_key(
943 data::variable_list(new_variables.begin(), new_variables.end()),
944 replace_vars(summ.f),
945 data::data_expression_list(summ.g.begin(), summ.g.end(), replace_vars)
946 );
947 }
948
949 tribool left_accords_equations(std::size_t k, std::size_t k1) const
950 {
951 std::size_t n = m_pbes.equations().size();
952 tribool result = yes;
953
954 for (std::size_t i = 0; i < n; i++)
955 {
956 for (std::size_t i1 = 0; i1 < n; i1++)
957 {
958 bool X_k1_X1 = depends(i, k1, i1);
959 for (std::size_t i_prime = 0; i_prime < n; i_prime++)
960 {
961 bool X1_k_Xprime = depends(i1, k, i_prime);
962 if (X_k1_X1 && X1_k_Xprime)
963 {
964 result = maybe;
965 bool found = false;
966 for (std::size_t i2 = 0; i2 < n; i2++)
967 {
968 bool X_k_X2 = depends(i, k, i2);
969 bool X2_k1_Xprime = depends(i2, k1, i_prime);
970 if (X_k_X2 && X2_k1_Xprime)
971 {
972 found = true;
973 }
974 }
975 if (!found)
976 {
977 return no;
978 }
979 }
980 }
981 }
982 }
983 return result;
984 }
985
986 tribool square_accords_equations(std::size_t k, std::size_t k1) const
987 {
988 std::size_t n = m_pbes.equations().size();
989 tribool result = yes;
990
991 for (std::size_t i = 0; i < n; i++)
992 {
993 for (std::size_t i1 = 0; i1 < n; i1++)
994 {
995 bool X_k1_X1 = depends(i, k1, i1);
996 for (std::size_t i2 = 0; i2 < n; i2++)
997 {
998 bool X_k_X2 = depends(i, k, i2);
999 if (X_k1_X1 && X_k_X2)
1000 {
1001 result = maybe;
1002 bool found = false;
1003 for (std::size_t i_prime = 0; i_prime < n; i_prime++)
1004 {
1005 bool X1_k_Xprime = depends(i1, k, i_prime);
1006 bool X2_k1_Xprime = depends(i2, k1, i_prime);
1007 if (X1_k_Xprime && X2_k1_Xprime)
1008 {
1009 found = true;
1010 break;
1011 }
1012 }
1013 if (!found)
1014 {
1015 return no;
1016 }
1017 }
1018 }
1019 }
1020 }
1021 return result;
1022 }
1023
1024 tribool triangle_accords_equations(std::size_t k, std::size_t k1) const
1025 {
1026 std::size_t n = m_pbes.equations().size();
1027 tribool result = yes;
1028
1029 for (std::size_t i = 0; i < n; i++)
1030 {
1031 for (std::size_t i1 = 0; i1 < n; i1++)
1032 {
1033 bool X_k1_X1 = depends(i, k1, i1);
1034 for (std::size_t i2 = 0; i2 < n; i2++)
1035 {
1036 bool X_k_X2 = depends(i, k, i2);
1037 bool X2_k1_X1 = depends(i2, k1, i1);
1038 if (X_k1_X1 && X_k_X2)
1039 {
1040 result = maybe;
1041 if(!X2_k1_X1)
1042 {
1043 return no;
1044 }
1045 }
1046 }
1047 }
1048 }
1049 return result;
1050 }
1051
1052 void compute_DNA_DNL_NES(const std::vector<parameter_info>& info)
1053 {
1054 using utilities::detail::has_empty_intersection;
1055 using utilities::detail::set_includes;
1056 using utilities::detail::set_intersection;
1057 using utilities::detail::set_union;
1058
1059 std::size_t N = m_summand_classes.size();
1060
1061 auto Rs = [&](const std::size_t k) { return info[k].Rs; };
1062 auto Ts = [&](const std::size_t k) { return info[k].Ts; };
1063 auto Vs = [&](const std::size_t k) { return info[k].Vs; };
1064 auto Ws = [&](const std::size_t k) { return info[k].Ws; };
1065
1066 for (std::size_t k = 0; k < N; k++)
1067 {
1068 mCRL2log(log::verbose) << std::setw(3) << k << " = ";
1069 for (std::size_t k1 = 0; k1 < N; k1++)
1070 {
1071 if (k == k1)
1072 {
1073 mCRL2log(log::verbose) << ". ";
1074 continue;
1075 }
1076 bool DNL_DNS_affect_sets = has_empty_intersection(set_intersection(Vs(k), Vs(k1)), set_union(Ws(k), Ws(k1)));
1077 bool DNT_affect_sets = has_empty_intersection(Ws(k), Rs(k1)) && has_empty_intersection(Ws(k), Ts(k1)) && set_includes(Ws(k1), Ws(k));
1078
1079 summand_relations_data summand_data(*this, k, k1);
1080 // Use lambda lifting for short-circuiting the && operator on tribools
1081 bool left_accords = m_options.compute_left_accordance &&
1082 ([&]{ return left_accords_equations(k, k1); } &&
1083 [&](bool needs_yes) { return summand_data.left_accords_data(DNL_DNS_affect_sets, needs_yes); });
1084 // The DNS relation is symmetric
1085 bool square_accords = (k1 < k && !DNS(k1).test(k)) ||
1086 (k1 > k &&
1087 ([&]{ return square_accords_equations(k, k1); } &&
1088 [&](bool needs_yes) { return summand_data.square_accords_data(DNL_DNS_affect_sets, needs_yes); }));
1089 bool accords = square_accords ||
1090 (m_options.compute_triangle_accordance && ([&]{ return triangle_accords_equations(k, k1); } &&
1091 [&](bool needs_yes) { return summand_data.triangle_accords_data(DNT_affect_sets, needs_yes); }));
1092 bool can_enable = !m_options.compute_NES ||
1093 (!dependency_permanently_disables(k1, k) && !has_empty_intersection(Ts(k), Ws(k1)) && summand_data.can_enable());
1094
1095 if (!left_accords)
1096 {
1097 DNL(k).set(k1);
1098 }
1099 if (!square_accords)
1100 {
1101 DNS(k).set(k1);
1102 }
1103 if (!accords)
1104 {
1105 DNA(k).set(k1);
1106 mCRL2log(log::verbose) << "- ";
1107 }
1108 else
1109 {
1110 mCRL2log(log::verbose) << (DNL_DNS_affect_sets ? ": " : "+ ");
1111 }
1112 mCRL2log(log::verbose) << std::flush;
1113 if (can_enable)
1114 {
1115 NES(k).set(k1);
1116 }
1117 }
1118 mCRL2log(log::verbose) << "\n";
1119 }
1120 }
1121
1123 {
1124 if (!m_options.reduction)
1125 {
1126 return;
1127 }
1128
1129 using utilities::detail::set_union;
1130
1131 std::size_t N = m_summand_classes.size();
1132 std::vector<parameter_info> info(N);
1133 const std::vector<data::variable>& d = m_parameters;
1134
1135 auto compute_parameter_info = [&](summand_class& summand, parameter_info& info)
1136 {
1137 // compute Ts
1138 std::set<data::variable> FV = find_free_variables(summand.f);
1139 for (const data::variable& v: summand.e)
1140 {
1141 FV.erase(v);
1142 }
1143 for (const data::variable& v: FV)
1144 {
1145 info.Ts.insert(parameter_position(v));
1146 }
1147
1148 // compute Ws and Rs
1149 auto gi = summand.g.begin();
1150 auto di = d.begin();
1151 for ( ; di != d.end(); ++di, ++gi)
1152 {
1153 if (*di != *gi)
1154 {
1155 std::size_t i = di - d.begin();
1156 info.Ws.insert(i);
1157
1158 for (const data::variable& v: find_free_variables(*gi))
1159 {
1160 info.Rs.insert(parameter_position(v));
1161 }
1162 }
1163 }
1164
1165 // compute Vs
1166 info.Vs = set_union(info.Ts, set_union(info.Ws, info.Rs));
1167 };
1168
1169 for (std::size_t k = 0; k < N; k++)
1170 {
1171 compute_parameter_info(m_summand_classes[k], info[k]);
1172 }
1173
1175 compute_DNA_DNL_NES(info);
1176 }
1177
1179 {
1180 const summand_class& summand_k = m_summand_classes[k];
1181
1182 std::size_t n = m_pbes.equations().size();
1183 for (std::size_t i = 0; i < n; i++)
1184 {
1185 if (summand_k.nxt[i].size() >= 2)
1186 {
1187 return false;
1188 }
1189 }
1190 return true;
1191 }
1192
1193 bool compute_deterministic_data(std::size_t k)
1194 {
1195 const summand_class& summand_k = m_summand_classes[k];
1196
1197 const data::variable_list& parameters = m_pbes.equations()[0].variable().parameters();
1199 for(const data::variable& v: parameters)
1200 {
1201 id_gen.add_identifier(v.name());
1202 }
1203
1206 data::variable_list qvars1_k = new1_k.e; data::data_expression condition1_k = new1_k.f; data::data_expression_list updates1_k = new1_k.g;
1207 data::variable_list qvars2_k = new2_k.e; data::data_expression condition2_k = new2_k.f; data::data_expression_list updates2_k = new2_k.g;
1208
1209 data::data_expression antecedent = data::sort_bool::and_(condition1_k, condition2_k);
1211 auto it1_k = updates1_k.begin();
1212 auto it2_k = updates2_k.begin();
1213 while (it1_k != updates1_k.end())
1214 {
1215 consequent = data::lazy::and_(consequent, data::equal_to(*it1_k, *it2_k));
1216 ++it1_k; ++it2_k;
1217 }
1218 data::data_expression condition = make_forall_(parameters + qvars1_k + qvars2_k, data::sort_bool::implies(antecedent, consequent));
1219
1220 return is_true(condition);
1221 }
1222
1224 {
1225 if (!m_options.compute_determinism || !m_options.reduction)
1226 {
1227 return;
1228 }
1229 std::size_t N = m_summand_classes.size();
1230 for (std::size_t k = 0; k < N; k++)
1231 {
1232 m_summand_classes[k].is_deterministic = compute_deterministic_equations(k) && compute_deterministic_data(k);
1233 }
1234 }
1235
1237 {
1238 std::size_t n = m_pbes.equations().size();
1239
1240 for (const srf_equation& eqn: m_pbes.equations())
1241 {
1242 for (const srf_summand& summand: eqn.summands())
1243 {
1244 summand_equivalence_key key(summand);
1245 auto i = m_summand_index.find(key);
1246 if (i == m_summand_index.end())
1247 {
1248 std::size_t k = m_summand_index.size();
1249 m_summand_index[key] = k;
1250 m_summand_classes.emplace_back(summand.parameters(), summand.condition(), summand.variable().parameters(), n);
1251 }
1252 }
1253 }
1254 for(summand_class& s: m_summand_classes)
1255 {
1256 s.set_num_summands(m_summand_classes.size());
1257 }
1259 // optional steps
1262 }
1263
1265 {
1266 using utilities::detail::contains;
1267
1268 std::size_t n = m_pbes.equations().size();
1269 std::size_t N = m_summand_classes.size();
1270
1271 m_vis.resize(N);
1272 for (std::size_t i = 0; i < n; i++)
1273 {
1274 const srf_equation& eqn = m_pbes.equations()[i];
1275 const core::identifier_string& X_i = eqn.variable().name();
1276 bool op_i = eqn.is_conjunctive();
1277 std::size_t rank_i = m_equation_index.rank(X_i);
1278
1279 for (const srf_summand& summand: eqn.summands())
1280 {
1281 const core::identifier_string& X_j = summand.variable().name();
1282 std::size_t j = m_equation_index.index(X_j);
1283 std::size_t rank_j = m_equation_index.rank(X_j);
1284 bool op_j = m_pbes.equations()[j].is_conjunctive();
1285 bool is_invisible = op_i == op_j && rank_i == rank_j;
1286 if (!is_invisible)
1287 {
1288 std::size_t k = summand_index(summand);
1289 m_vis.set(k);
1290 }
1291 }
1292 }
1293
1294 // Invis is the opposite of vis
1295 m_invis = m_vis;
1296 m_invis.flip();
1297 }
1298
1300 {
1301 std::ostringstream out;
1302 for (auto i = v.begin(); i != v.end(); ++i)
1303 {
1304 if (i != v.begin())
1305 {
1306 out << ", ";
1307 }
1308 out << *i << ": " << i->sort();
1309 }
1310 return out.str();
1311 }
1312
1313 void print_summand(const srf_summand& summand, bool is_conjunctive) const
1314 {
1315 std::size_t k = summand_index(summand);
1316 mCRL2log(log::verbose) << " (" << k << ") ";
1317 if (!summand.parameters().empty())
1318 {
1319 mCRL2log(log::verbose) << (is_conjunctive ? "forall " : "exists ") << print_variables(summand.parameters()) << ". ";
1320 }
1321 mCRL2log(log::verbose) << summand.condition()
1322 << (is_conjunctive ? " => " : " && ")
1323 << summand.variable()
1324 << std::endl;
1325 }
1326
1327 void print_pbes() const
1328 {
1329 mCRL2log(log::verbose) << "srf_pbes" << std::endl;
1330 for (const srf_equation& eqn: m_pbes.equations())
1331 {
1332 mCRL2log(log::verbose) << eqn.symbol() << " " << eqn.variable() << " = " << (eqn.is_conjunctive() ? "conjunction" : "disjunction") << " of summands\n";
1333 for (const srf_summand& summand: eqn.summands())
1334 {
1335 print_summand(summand, eqn.is_conjunctive());
1336 }
1337 mCRL2log(log::verbose) << std::endl;
1338 }
1339 }
1340
1342 {
1343 using utilities::detail::contains;
1344
1345 if(mCRL2logEnabled(log::verbose))
1346 {
1347 std::size_t N = m_summand_classes.size();
1348 for (std::size_t k = 0; k < N; k++)
1349 {
1350 const summand_class& summand = m_summand_classes[k];
1351 mCRL2log(log::verbose) << "\n--- summand class " << k << " ---" << std::endl;
1352 mCRL2log(log::verbose) << "visible = " << std::boolalpha << m_vis.test(k) << "\n";
1353 summand.print(log::logger(log::verbose).get());
1354 }
1355 for (std::size_t i = 0; i < m_pbes.equations().size(); i++)
1356 {
1357 mCRL2log(log::verbose) << "dependency NES[" << std::setw(3) << i << "] " << print_summand_set(m_dependency_nes[i]) << std::endl;
1358 }
1359 }
1360 }
1361
1362 public:
1364 pbespor_options options
1365 )
1366 : m_rewr(p.data(),
1367 //TODO temporarily disabled used_data_equation_selector so the rewriter can rewrite accordance conditions
1368 // data::used_data_equation_selector(p.data(), pbes_system::find_function_symbols(p), p.global_variables()),
1371 m_pbes(pbes2srf(p)),
1376 {
1377 unify_parameters(m_pbes, false, true);
1378
1379 // initialize m_parameters and m_parameter_positions
1380 const data::variable_list& parameters = m_pbes.equations().front().variable().parameters();
1381 m_parameters = std::vector<data::variable>{parameters.begin(), parameters.end()};
1382 for (std::size_t m = 0; m < m_parameters.size(); m++)
1383 {
1384 m_parameter_positions[m_parameters[m]] = m;
1385 }
1386
1387 const std::chrono::time_point<std::chrono::high_resolution_clock> t_start =
1388 std::chrono::high_resolution_clock::now();
1391 m_static_analysis_duration = std::chrono::high_resolution_clock::now() - t_start;
1392
1393 // initialize m_largest_equation_size;
1394 for (const srf_equation& eq: m_pbes.equations())
1395 {
1396 m_largest_equation_size = std::max(m_largest_equation_size, eq.summands().size());
1397 }
1398
1400 }
1401
1403 {
1404 return m_pbes.initial_state();
1405 }
1406
1408 {
1409 return m_parameters;
1410 }
1411
1412 const fixpoint_symbol& symbol(const core::identifier_string& X) const
1413 {
1414 std::size_t i = m_equation_index.index(X);
1415 return m_pbes.equations()[i].symbol();
1416 }
1417
1418 void print() const
1419 {
1421 }
1422
1423 template <
1424 typename EmitNode = utilities::skip,
1425 typename EmitEdge = utilities::skip
1426 >
1429 EmitNode emit_node = EmitNode(),
1430 EmitEdge emit_edge = EmitEdge()
1431 )
1432 {
1433 using utilities::detail::contains;
1434 using utilities::detail::has_empty_intersection;
1435 using utilities::detail::set_difference;
1436 using utilities::detail::set_includes;
1437 using utilities::detail::set_intersection;
1438 using utilities::detail::set_union;
1439
1440 const std::chrono::time_point<std::chrono::high_resolution_clock> t_start =
1441 std::chrono::high_resolution_clock::now();
1442
1443 enum todo_state
1444 {
1445 NEW, ///< Will be partially expanded
1446 DONE_PARTIALLY, ///< Has been partially expanded
1447 STARTS_CYCLE, ///< Needs to be fully expanded, because it starts a cycle
1448 DONE ///< Has been fully expanded
1449 };
1450 using todo_pair = std::pair<propositional_variable_instantiation, todo_state>;
1451
1452 // The set seen also stores for each node an index and a boolean that expresses whether
1453 // the node is currently in the DFS stack and not fully explored, ie, its state in the
1454 // stack is DONE_PARTIALLY.
1455 std::unordered_map<propositional_variable_instantiation, std::pair<std::size_t, bool>> seen;
1456 std::deque<todo_pair> todo{todo_pair(X_init, NEW)};
1457 // Each state is given unique index, based on the order of discovery.
1458 // This means that the indices in the DFS stack are sorted from low to high.
1459 std::size_t index = 0;
1460
1461 {
1462 std::size_t rank = m_equation_index.rank(X_init.name());
1463 std::size_t i = m_equation_index.index(X_init.name());
1464 bool is_conjunctive = m_pbes.equations()[i].is_conjunctive();
1465 emit_node(X_init, is_conjunctive, rank);
1466 seen.insert(std::make_pair(X_init, std::make_pair(index, true)));
1467 index++;
1468 }
1469
1470 std::size_t iteration = 0;
1471 while (!todo.empty())
1472 {
1473 todo_pair& p = todo.back();
1474 const propositional_variable_instantiation X_e = p.first;
1475 todo_state& s = p.second;
1476 mCRL2log(log::debug) << "choose X_e = " << X_e << std::endl;
1477
1478 if (s == DONE || s == DONE_PARTIALLY)
1479 {
1480 todo.pop_back();
1481 seen[X_e].second = false;
1482 continue;
1483 }
1484
1485 std::set<propositional_variable_instantiation> next;
1486 summand_set en_X_e = en(X_e);
1487
1488 if (s == NEW)
1489 {
1490 summand_set stubborn_set_X_e = stubborn_set(X_e, en_X_e);
1491 mCRL2log(log::debug) << "stubborn_set(X_e) = " << print_summand_set(stubborn_set_X_e) << std::endl;
1492 next = succ(X_e, stubborn_set_X_e & en_X_e);
1493
1494 bool vis_expanded = m_vis.is_subset_of(stubborn_set_X_e);
1495 s = vis_expanded ? DONE : DONE_PARTIALLY;
1496 if (!vis_expanded)
1497 {
1498 seen[X_e].second = true;
1499 }
1500
1501 if (m_options.use_condition_L)
1502 {
1503 // Check if a cycle is closed
1504 // At the same time, check whether some node on the stack is fully expanded
1505 // If both are true, some node will be fully expanded
1506 std::size_t num_cycles = 0;
1507 propositional_variable_instantiation min_node;
1508 for (const propositional_variable_instantiation& Y_f: next)
1509 {
1510 auto node = seen.find(Y_f);
1511 if (node == seen.end())
1512 {
1513 continue;
1514 }
1515 std::size_t node_instack = node->second.second;
1516 if (node_instack)
1517 {
1518 num_cycles++;
1519 min_node = Y_f;
1520 if (num_cycles > 1)
1521 {
1522 break;
1523 }
1524 }
1525 }
1526 if (num_cycles == 1)
1527 {
1528 auto it = std::find_if(todo.rbegin(), todo.rend(), [&](const auto& pair){ return pair.first == min_node; });
1529 assert(it != todo.rend());
1530 auto& [Y_f, Y_f_state] = *it;
1531 if(Y_f_state == DONE_PARTIALLY)
1532 {
1533 Y_f_state = STARTS_CYCLE;
1534 seen[Y_f].second = false;
1535 }
1536 }
1537 else if (num_cycles > 1 && s == DONE_PARTIALLY)
1538 {
1539 // Generate the remainder of the successors
1540 for (auto& Y_f: succ(X_e, en_X_e - stubborn_set_X_e))
1541 {
1542 next.insert(Y_f);
1543 }
1544 s = DONE;
1545 seen[X_e].second = false;
1546 }
1547 }
1548 }
1549 else
1550 {
1551 assert(s == STARTS_CYCLE);
1552 next = succ(X_e, en_X_e);
1553 s = DONE;
1554 }
1555
1556 mCRL2log(log::debug) << "next = " << core::detail::print_set(next) << std::endl;
1557 for (const propositional_variable_instantiation& Y_f: next)
1558 {
1559 if (seen.find(Y_f) == seen.end())
1560 {
1561 std::size_t rank = m_equation_index.rank(Y_f.name());
1562 std::size_t i = m_equation_index.index(Y_f.name());
1563 bool is_conjunctive = m_pbes.equations()[i].is_conjunctive();
1564 emit_node(Y_f, is_conjunctive, rank);
1565 seen.insert(std::make_pair(Y_f, std::make_pair(index, false)));
1566 index++;
1567 todo.emplace_back(Y_f, NEW);
1568 }
1569 }
1570 for (const propositional_variable_instantiation& Y_f: next)
1571 {
1572 emit_edge(X_e, Y_f);
1573 }
1574
1575 iteration++;
1576 if(iteration == 100)
1577 {
1578 mCRL2log(log::status) << "Found " << seen.size() << " nodes. Todo set contains " << todo.size() << " nodes.\n";
1579 iteration = 0;
1580 }
1581 }
1582 mCRL2log(log::verbose) << "Finished exploration, found " << seen.size() << " nodes." << std::endl;
1583
1584 m_exploration_duration = std::chrono::high_resolution_clock::now() - t_start;
1585 mCRL2log(log::info) << "timing pbespor (wall clock time in seconds):"
1586 "\n static analysis: " << std::chrono::duration<double>(m_static_analysis_duration).count() <<
1587 "\n exploration: " << std::chrono::duration<double>(m_exploration_duration).count() << std::endl;
1588 }
1589
1590 template <
1591 typename EmitNode = utilities::skip,
1592 typename EmitEdge = utilities::skip
1593 >
1596 EmitNode emit_node = EmitNode(),
1597 EmitEdge emit_edge = EmitEdge()
1598 )
1599 {
1600 const std::chrono::time_point<std::chrono::high_resolution_clock> t_start =
1601 std::chrono::high_resolution_clock::now();
1602
1603 std::unordered_set<propositional_variable_instantiation> seen;
1604 std::deque<propositional_variable_instantiation> todo{ X_init };
1605
1606 {
1607 std::size_t rank = m_equation_index.rank(X_init.name());
1608 std::size_t i = m_equation_index.index(X_init.name());
1609 bool is_conjunctive = m_pbes.equations()[i].is_conjunctive();
1610 emit_node(X_init, is_conjunctive, rank);
1611 seen.insert(X_init);
1612 }
1613
1614 std::size_t N = m_summand_classes.size();
1615 summand_set summands_X(N);
1616
1617 std::size_t iteration = 0;
1618 while (!todo.empty())
1619 {
1620 const propositional_variable_instantiation X_e = todo.back();
1621 todo.pop_back();
1622 mCRL2log(log::debug) << "choose X_e = " << X_e << std::endl;
1623
1624 std::size_t X_index = m_equation_index.index(X_e.name());
1625 for(std::size_t i = 0; i < N; i++)
1626 {
1627 if (depends(X_index, i))
1628 {
1629 summands_X.set(i);
1630 }
1631 }
1632 mCRL2log(log::debug) << "enabled according to dependencies = " << print_summand_set(summands_X) << std::endl;
1633 std::set<propositional_variable_instantiation> next = succ(X_e, summands_X);
1634 mCRL2log(log::debug) << "next = " << core::detail::print_set(next) << std::endl;
1635 summands_X.reset();
1636
1637 for (const propositional_variable_instantiation& Y_f: next)
1638 {
1639 if (seen.find(Y_f) == seen.end())
1640 {
1641 std::size_t rank = m_equation_index.rank(Y_f.name());
1642 std::size_t i = m_equation_index.index(Y_f.name());
1643 bool is_conjunctive = m_pbes.equations()[i].is_conjunctive();
1644 emit_node(Y_f, is_conjunctive, rank);
1645 seen.insert(Y_f);
1646 todo.emplace_back(Y_f);
1647 }
1648 }
1649 for (const propositional_variable_instantiation& Y_f: next)
1650 {
1651 emit_edge(X_e, Y_f);
1652 }
1653
1654 iteration++;
1655 if(iteration == 100)
1656 {
1657 mCRL2log(log::status) << "Found " << seen.size() << " nodes. Todo set contains " << todo.size() << " nodes.\n";
1658 iteration = 0;
1659 }
1660 }
1661 mCRL2log(log::verbose) << "Finished exploration, found " << seen.size() << " nodes." << std::endl;
1662
1663 m_exploration_duration = std::chrono::high_resolution_clock::now() - t_start;
1664 mCRL2log(log::info) << "timing pbespor (wall clock time in seconds):"
1665 "\n exploration: " << std::chrono::duration<double>(m_exploration_duration).count() << std::endl;
1666 }
1667};
1668
1669} // namespace mcrl2::pbes_system
1670
1671#endif // MCRL2_PBES_PARTIAL_ORDER_REDUCTION_H
const variable_list & variables() const
Definition abstraction.h:60
const data_expression & body() const
Definition abstraction.h:65
data_expression & operator=(const data_expression &) noexcept=default
data_expression & operator=(data_expression &&) noexcept=default
data_expression(const data_expression &) noexcept=default
Move semantics.
An element for the todo list of the enumerator that collects the substitution corresponding to the ex...
Definition enumerator.h:346
universal quantification.
Definition forall.h:25
Rewriter that operates on data expressions.
Definition rewriter.h:84
Identifier generator that stores the identifiers of the context in a set. Using the operator()() and ...
data::data_expression make_exists_if_strong(const data::variable_list &vars, const data::data_expression &body)
summand_relations_data(partial_order_reduction_algorithm &p, const std::size_t k, const std::size_t k1)
tribool accords_data(bool affect_set, bool needs_yes, const std::function< data::data_expression()> &make_antecedent, const std::function< data::data_expression()> &make_consequent)
std::size_t parameter_position(const data::variable &v) const
std::size_t summand_index(const srf_summand &summand) const
std::chrono::high_resolution_clock::duration m_static_analysis_duration
partial_order_reduction_algorithm(const pbes &p, pbespor_options options)
tribool triangle_accords_equations(std::size_t k, std::size_t k1) const
const fixpoint_symbol & symbol(const core::identifier_string &X) const
summand_set stubborn_set(const propositional_variable_instantiation &X_e, const summand_set &en_X_e)
tribool square_accords_equations(std::size_t k, std::size_t k1) const
const summand_set & DNX(std::size_t k, const summand_set &Twork, const summand_set &Ts, const summand_set &en_X_e) const
void print_summand(const srf_summand &summand, bool is_conjunctive) const
std::string print_variables(const data::variable_list &v) const
std::chrono::high_resolution_clock::duration m_exploration_duration
const propositional_variable_instantiation & initial_state() const
summand_set en(const propositional_variable_instantiation &X_e)
void explore(const propositional_variable_instantiation &X_init, EmitNode emit_node=EmitNode(), EmitEdge emit_edge=EmitEdge())
static summand_equivalence_key rename_duplicate_variables(data::set_identifier_generator &id_gen, const summand_equivalence_key &summ)
std::set< std::size_t > FV(const pbes_expression &x) const
const std::vector< data::variable > & parameters() const
void compute_DNA_DNL_NES(const std::vector< parameter_info > &info)
std::set< propositional_variable_instantiation > succ(const propositional_variable_instantiation &X_e, const summand_set &K)
const summand_set & choose_minimal_NES(std::size_t k, const propositional_variable_instantiation &X_e) const
void explore_full(const propositional_variable_instantiation &X_init, EmitNode emit_node=EmitNode(), EmitEdge emit_edge=EmitEdge())
bool depends(std::size_t i, std::size_t k, std::size_t j) const
bool dependency_permanently_disables(const std::size_t k, const std::size_t k1) const
Return true iff k1 can never happen after k happens, as deduced from predicate dependencies.
tribool left_accords_equations(std::size_t k, std::size_t k1) const
parameterized boolean equation system
Definition pbes.h:54
\brief A propositional variable instantiation
const data::data_expression_list & parameters() const
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:393
Namespace for system defined sort bool_.
Definition bool.h:29
application not_(const data_expression &arg0)
Application of function symbol !.
Definition bool.h:194
application and_(const data_expression &arg0, const data_expression &arg1)
Application of function symbol &&.
Definition bool.h:257
const function_symbol & false_()
Constructor for function symbol false.
Definition bool.h:106
const function_symbol & true_()
Constructor for function symbol true.
Definition bool.h:74
data_expression make_exists_(const data::variable_list &v, const data_expression &x)
Make an existential quantification. It checks for an empty variable list, which is not allowed.
data_expression and_(const data_expression &x, const data_expression &y)
bool is_forall(const atermpp::aterm &x)
Returns true if the term t is a universal quantification.
data::data_expression equal_to(const data::data_expression_list &x, const data::data_expression_list &y)
data::data_expression make_and(const data::data_expression &x1, const data::data_expression &x2, const data::data_expression &x3)
std::string print_summand_set(const summand_set &s)
static bool operator&&(tribool a, tribool b)
static bool operator&&(const std::function< tribool()> &a, const std::function< tribool(bool)> &b)
void print(std::ostream &out, const std::set< std::size_t > &s, const std::size_t N) const
void print(std::ostream &out) const
std::vector< std::set< std::size_t > > nxt
Encodes the dependency relation belonging to this summand_class \detail nxt[i] contains j iff X_i –th...
void set_num_summands(const std::size_t N)
bool depends(std::size_t i, std::size_t j) const
summand_class(data::variable_list e_, data::data_expression f_, data::data_expression_list g_, std::size_t n)
bool operator==(const summand_equivalence_key &other) const
bool operator<(const summand_equivalence_key &other) const
summand_equivalence_key(data::variable_list e_, data::data_expression f_, data::data_expression_list g_)
std::size_t operator()(const mcrl2::pbes_system::summand_equivalence_key &x) const