mCRL2
Loading...
Searching...
No Matches
constelm.h
Go to the documentation of this file.
1// Author(s): Wieger Wesselink
2//
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6//
7/// \file mcrl2/pbes/constelm.h
8/// \brief The constelm algorithm.
9
10#ifndef MCRL2_PBES_CONSTELM_H
11#define MCRL2_PBES_CONSTELM_H
12
13#include <ranges>
14
15#include "mcrl2/pbes/algorithms.h"
16#include "mcrl2/pbes/pbes_rewriter_type.h"
17#include "mcrl2/pbes/print.h"
18#include "mcrl2/pbes/replace.h"
19#include "mcrl2/pbes/rewriters/enumerate_quantifiers_rewriter.h"
20#include "mcrl2/pbes/detail/pbes_remove_counterexample_info.h"
21
22
23
24namespace mcrl2::pbes_system
25{
26
27namespace detail
28{
29
30inline
31void make_constelm_substitution(const std::map<data::variable, data::data_expression>& m, data::rewriter::substitution_type& result)
32{
33 for (const auto& i : m)
34 {
35 result[i.first] = i.second;
36 }
37}
38
40{
41protected:
44
45public:
46 quantified_variable(bool is_forall, const data::variable& var)
47 : m_is_forall(is_forall)
48 , m_var(var)
49 {}
50
51 bool is_forall() const
52 {
53 return m_is_forall;
54 }
55
56 const data::variable& variable() const
57 {
58 return m_var;
59 }
60
61 bool operator==(const quantified_variable& other) const
62 {
63 return m_is_forall == other.m_is_forall && m_var == other.m_var;
64 }
65
66 bool operator!=(const quantified_variable& other) const
67 {
68 return !(*this == other);
69 }
70
71 bool operator<(const quantified_variable& other) const
72 {
73 return m_is_forall < other.m_is_forall || (m_is_forall == other.m_is_forall && m_var < other.m_var);
74 }
75
77 {
78 return m_is_forall ? pbes_expression(forall(data::variable_list({m_var}), expr)) : pbes_expression(exists(data::variable_list({m_var}), expr));
79 }
80
82 {
83 std::ostringstream out;
84 out << (is_forall() ? "forall " : "exists ") << variable() << ": " << variable().sort() << ". ";
85 return out.str();
86 }
87};
88
89/// \brief A quantified predicate variable instantiation
90struct QPVI
91{
94
95 bool operator<(const QPVI& other) const
96 {
97 return std::tie(Q, X_e) < std::tie(other.Q, other.X_e);
98 }
99};
100
102{
103 /// \brief Contains expressions that characterise when an edge is enabled.
104 /// The conjunction of these expressions is a guard for some PVI.
106 /// \brief The set of free variables that occur on the other side of the conjunctions this
107 /// PVI occurs in. Can be used to determine whether the quantifier inside rewriter
108 /// can manage to push an existential quantifier all the way to this PVI.
110 /// \brief The set of free variables that occur on the other side of the conjunctions this
111 /// PVI occurs in. Can be used to determine whether the quantifier inside rewriter
112 /// can manage to push a universal quantifier all the way to this PVI.
114};
115
117{
119
124
125 edge_traverser_stack_elem(const data::data_expression& cond_pos, const data::data_expression& cond_neg, std::set<data::variable>&& free_vars)
126 : Cpos(cond_pos), Cneg(cond_neg), FV(std::move(free_vars))
127 {}
128};
129
131{
133 using super::enter;
134 using super::leave;
135 using super::apply;
136
137 using stack_elem = edge_traverser_stack_elem;
140
143
144 template <class... Args>
145 void emplace(Args&&... args)
146 {
147 condition_fv_stack.emplace_back(std::forward<Args>(args)...);
148 }
149
150 void push(const stack_elem& x)
151 {
152 condition_fv_stack.push_back(x);
153 }
154
155 stack_elem& top()
156 {
157 return condition_fv_stack.back();
158 }
159
160 const stack_elem& top() const
161 {
162 return condition_fv_stack.back();
163 }
164
165 stack_elem pop()
166 {
167 // As the last element of the stack is removed anyway, we move the element
168 // out of the stack. This avoids (rather expensive) allocation and deallocation
169 // of a temporary stack_elem.
170 // This cost was observed during profiling (Jeroen Keiren, 27/6/2025)
171 stack_elem result = std::move(condition_fv_stack.back());
172 condition_fv_stack.pop_back();
173 return result;
174 }
175
176 // Merge conditions of stack elements ec1 and ec2 into ec.
177 // As construction and destruction of edges (in particular edge details)
178 // is expensive (observed by Jeroen Keiren, 27/6/2025), and ec1 and ec2 are
179 // not used in the calling context, we here explicitly accept them as
180 // rvalue reference; this allows us to move the edges out of ec1 and ec2.
181 // NOLINTBEGIN(cppcoreguidelines-rvalue-reference-param-not-moved) ec1 and ec2 are consumed: their edges are moved into ec below.
182 void merge_conditions(stack_elem&& ec1, bool negate1,
183 stack_elem&& ec2, bool negate2,
184 stack_elem& ec, bool is_conjunctive
185 )
186 // NOLINTEND(cppcoreguidelines-rvalue-reference-param-not-moved)
187 {
188 for (auto& i: ec1.edges)
189 {
190 auto& [Q_X_e, details] = i;
191 details.conditions.insert(negate2 ? ec2.Cneg : ec2.Cpos);
192 (is_conjunctive ? details.conjunctive_context_FV : details.disjunctive_context_FV)
193 .insert(ec2.FV.begin(), ec2.FV.end());
194 ec.edges.insert(std::move(i));
195 }
196 for (auto& i: ec2.edges)
197 {
198 auto& [Q_X_e, details] = i;
199 details.conditions.insert(negate1 ? ec1.Cneg : ec1.Cpos);
200 (is_conjunctive ? details.conjunctive_context_FV : details.disjunctive_context_FV)
201 .insert(ec1.FV.begin(), ec1.FV.end());
202 ec.edges.insert(std::move(i));
203 }
204 }
205
206 // enter functions related to maintaining the quantfier scope
207 void enter(const not_&)
208 {
209 quantified_context.clear();
210 }
211
212 void enter(const and_&)
213 {
214 quantified_context.clear();
215 }
216
217 void enter(const or_&)
218 {
219 quantified_context.clear();
220 }
221
222 void enter(const imp&)
223 {
224 quantified_context.clear();
225 }
226
227 void enter(const forall& x)
228 {
229 quantified_context.push_back(x);
230 }
231
232 void enter(const exists& x)
233 {
234 quantified_context.push_back(x);
235 }
236
237 // leave functions, mostly used to build conditions and gather free variables
238 void leave(const data::data_expression& x)
239 {
240 data::data_expression cond_not;
241 data::optimized_not(cond_not, x);
242
243 emplace(x, cond_not, data::find_free_variables(x));
244 }
245
246 void leave(const not_&)
247 {
249 }
250
251 void leave(const and_&)
252 {
253 stack_elem ec_right = pop();
254 stack_elem ec_left = pop();
255 data::data_expression cond_and;
256 data::data_expression cond_or;
257 data::optimized_and(cond_and, ec_left.Cpos, ec_right.Cpos);
258 data::optimized_or(cond_or, ec_left.Cneg, ec_right.Cneg);
259
260 emplace(cond_and, cond_or,
261 utilities::detail::set_union(ec_left.FV, ec_right.FV));
262 merge_conditions(std::move(ec_left), false, std::move(ec_right), false, top(), true);
263 }
264
265 void leave(const or_&)
266 {
267 stack_elem ec_right = pop();
268 stack_elem ec_left = pop();
269 data::data_expression cond_and;
270 data::data_expression cond_or;
271
272 data::optimized_and(cond_and, ec_left.Cneg, ec_right.Cneg);
273 data::optimized_or(cond_or, ec_left.Cpos, ec_right.Cpos);
274
275 emplace(cond_or, cond_and,
276 utilities::detail::set_union(ec_left.FV, ec_right.FV));
277 merge_conditions(std::move(ec_left), true, std::move(ec_right), true, top(), false);
278 }
279
280 void leave(const imp&)
281 {
282 stack_elem ec_right = pop();
283 stack_elem ec_left = pop();
284 data::data_expression cond_or;
285 data::data_expression cond_and;
286
287 data::optimized_or(cond_or, ec_left.Cneg, ec_right.Cpos);
288 data::optimized_and(cond_and, ec_left.Cpos, ec_right.Cneg);
289
290 emplace(cond_or, cond_and,
291 utilities::detail::set_union(ec_left.FV, ec_right.FV));;
292 merge_conditions(std::move(ec_left), false, std::move(ec_right), true, top(), false);
293 }
294
295 void leave(const forall& x)
296 {
297 // build conditions and free variable sets
298 stack_elem ec = pop();
299 for (auto& [X_e, details]: ec.edges)
300 {
301 auto& [cond_set, conj_FV, disj_FV] = details;
302
303 // Update the conditions
304 std::set<data::data_expression> new_conditions;
305 for(const data::data_expression& e: cond_set)
306 {
307 data::data_expression t;
308 data::optimized_exists(t, x.variables(), e, true);
309 new_conditions.insert(t);
310 }
311 cond_set = std::move(new_conditions);
312 data::data_expression forall;
313 data::optimized_forall(forall, x.variables(), ec.Cpos, true);
314
315 cond_set.insert(forall);
316
317 // Update FV
318 conj_FV = ec.FV;
319 }
320 data::optimized_forall(ec.Cpos, x.variables(), ec.Cpos, true);
321 data::optimized_exists(ec.Cneg, x.variables(), ec.Cneg, true);
322 std::set<data::variable> bound_vars{x.variables().begin(), x.variables().end()};
323 ec.FV = utilities::detail::set_difference(ec.FV, bound_vars);
324 push(ec);
325
326 // maintain quantifier scope
327 if(!quantified_context.empty() && quantified_context.back() == x)
328 {
329 quantified_context.pop_back();
330 }
331 }
332
333 void leave(const exists& x)
334 {
335 // build conditions and free variable sets
336 stack_elem ec = pop();
337 for (auto& [X_e, details]: ec.edges)
338 {
339 auto& [cond_set, conj_FV, disj_FV] = details;
340
341 // Update the conditions
342 std::set<data::data_expression> new_conditions;
343 for(const data::data_expression& e: cond_set)
344 {
345 data::data_expression t;
346 data::optimized_exists(t, x.variables(), e, true);
347 new_conditions.insert(t);
348 }
349 cond_set = std::move(new_conditions);
350
351 data::data_expression forall;
352 data::optimized_forall(forall, x.variables(), ec.Cneg, true);
353 cond_set.insert(forall);
354
355 // Update FV
356 disj_FV = ec.FV;
357 }
358 data::optimized_exists(ec.Cpos, x.variables(), ec.Cpos, true);
359 data::optimized_forall(ec.Cneg, x.variables(), ec.Cneg, true);
360 std::set<data::variable> bound_vars{x.variables().begin(), x.variables().end()};
361 ec.FV = utilities::detail::set_difference(ec.FV, bound_vars);
362 push(ec);
363
364 // maintain quantifier scope
365 if(!quantified_context.empty() && quantified_context.back() == x)
366 {
367 quantified_context.pop_back();
368 }
369 }
370
372 {
373 // Build list of qvars from quantifier scope
374 qvar_list qvars;
375 for(const pbes_expression& expr: quantified_context)
376 {
377 assert(is_forall(expr) || is_exists(expr));
378 data::variable_list vars(is_forall(expr) ? atermpp::down_cast<forall>(expr).variables() : atermpp::down_cast<exists>(expr).variables());
379 for(const data::variable& v: vars)
380 {
381 qvars.emplace_back(is_forall(expr), v);
382 }
383 }
384 QPVI Q_X_e{.Q = qvars, .X_e = x};
385
386 // Store the QPVI and the condition true
388 top().edges.emplace(Q_X_e,
389 edge_details{.conditions = std::set<data::data_expression>{data::sort_bool::true_()},
390 .conjunctive_context_FV = std::set<data::variable>{},
391 .disjunctive_context_FV = std::set<data::variable>{}});
392 }
393
394 const edge_map& result() const
395 {
396 assert(condition_fv_stack.size() == 1);
397 return top().edges;
398 }
399};
400
401} // namespace detail
402/// \endcond
403
404
405/// \brief Algorithm class for the constelm algorithm
406template <typename DataRewriter, typename PbesRewriter>
408{
409 protected:
410 /// \brief A map with constraints on the vertices of the graph
413
414 /// \brief Compares data expressions for equality.
415 const DataRewriter& m_data_rewriter;
416
417 /// \brief Compares data expressions for equality.
418 const PbesRewriter& m_pbes_rewriter;
419
420 class vertex;
421
422 /// \brief Represents an edge of the dependency graph. The assignments are stored
423 /// implicitly using the 'right' parameter. The condition determines under
424 /// what circumstances the influence of the edge is propagated to its target
425 /// vertex.
426 //
427 // N.B. The attribute condition "pbes_expression condition;" needs to be protected.
428 // This is achieved by deriving from pbes_expression. This is very ugly, but AFAIK
429 // this is the least destructive solution to garbage collection problems.
430 // Note that source and target are protected elsewhere.
431 class edge: public data::data_expression
432 {
433 protected:
434 /// \brief The propositional variable at the source of the edge
436
437 /// \brief The quantifiers in whose direct context the target PVI occurs
439
440 /// \brief The propositional variable instantiation that determines the target of the edge
442
445
446 public:
447 /// \brief Constructor
448 edge() = default;
449
450 /// \brief Constructor
451 /// \param src A propositional variable declaration
452 /// \param tgt A propositional variable
453 /// \param c A term
455 const propositional_variable& src,
456 const qvar_list& qvars,
458 const std::set<data::variable>& conj_context,
459 const std::set<data::variable>& disj_context,
461 )
463 , m_source(src)
464 , m_qvars(qvars)
465 , m_target(tgt)
468 {}
469
470 /// \brief Returns a string representation of the edge.
471 /// \return A string representation of the edge.
473 {
474 std::ostringstream out;
475 out << "(" << m_source.name() << ", " << m_target.name() << ") label = ";
476 for(const detail::quantified_variable& qv: m_qvars)
477 {
478 out << qv.to_string();
479 }
480 out << m_target << " condition = " << condition();
481 out << "; conjunctive context = {";
482 for (const data::variable& var: m_conj_context)
483 {
484 out << var << ": " << var.sort() << ", ";
485 }
486 out << "}; disjunctive context = {";
487 for (const data::variable& var: m_disj_context)
488 {
489 out << var << ": " << var.sort() << ", ";
490 }
491 out << "}";
492 return out.str();
493 }
494
495 /// \brief The propositional variable at the source of the edge
497 {
498 return m_source;
499 }
500
502 {
503 return m_qvars;
504 }
505
506 /// \brief The propositional variable instantiation that determines the target of the edge
508 {
509 return m_target;
510 }
511
512 /// \brief The condition of the edge
514 {
515 return *this;
516 }
517
518 /// \brief Try to guess which quantifiers of Q can end up directly
519 /// before target, when the quantifier inside rewriter is applied.
521 {
522 const qvar_list& Q = source.quantified_variables();
523 const data::variable_list& par_def = source.variable().parameters();
524 const constraint_map& constraints = source.constraints();
525
526 data::rewriter::substitution_type sigma;
527 detail::make_constelm_substitution(constraints, sigma);
528
529 qvar_list result;
530 for (const auto& it: std::ranges::reverse_view(Q))
531 {
532 bool is_forall = it.is_forall();
533 const data::variable& var = it.variable();
534 // Variable of a universal quantifier cannot occur in the disjunctive context
535 // Variable of an existential quantifier cannot occur in the conjunctive context
536 const std::set<data::variable>& context = is_forall ? m_disj_context : m_conj_context;
537 bool none_occurs_in_context = true;
538
539 auto it_pvi = m_target.parameters().begin();
540 auto it_def = par_def.begin();
541 for (; it_pvi != m_target.parameters().end(); ++it_pvi, ++it_def)
542 {
543 if (context.find(*it_def) != context.end() && data::search_free_variable(rewr(*it_pvi, sigma), var))
544 {
545 none_occurs_in_context = false;
546 break;
547 }
548 }
549
550 if (none_occurs_in_context)
551 {
552 result.push_front(it);
553 }
554 else
555 {
556 break;
557 }
558 }
559 return result;
560 }
561 };
562
563 /// \brief Represents a vertex of the dependency graph.
564 class vertex
565 {
566 protected:
567 /// \brief The propositional variable that corresponds to the vertex
569
570 /// \brief The list of quantified variables that occur in the constraints
572
573 /// \brief Maps data variables to data expressions. If a parameter is not
574 // present in this map, it means that it represents NaC ("not a constant").
576
577 /// \brief Indicates whether this vertex has been visited at least once.
578 bool m_visited = false;
579
580 /// \brief Returns true if the parameter v has been assigned a constant expression.
581 /// \param v A parameter of this->variable()
582 /// \return True if the data parameter v has been assigned a constant expression.
583 bool is_constant(const data::variable& v) const
584 {
585 auto i = m_constraints.find(v);
586 return i != m_constraints.end();
587 }
588
589 /// \brief Returns true iff all free variables in e are bound in qvars
590 bool bound_in_quantifiers(const qvar_list& qvars, const data::data_expression& e)
591 {
592 std::set<data::variable> free_vars = data::find_free_variables(e);
593 return std::all_of(free_vars.begin(), free_vars.end(), [&](const data::variable& v)
594 {
595 return std::find_if(qvars.begin(), qvars.end(), [&](const detail::quantified_variable& qvar){ return qvar.variable() == v; }) != qvars.end();
596 });
597 }
598
599 /// \brief Weaken the constraints so they satisfy
600 /// - vars(m_constraints[d]) subset vars(m_qvars); and
601 /// - vars(deleted_constraints) intersection vars(m_qvars) = {}
602 void fix_constraints(std::vector<data::data_expression> deleted_constraints)
603 {
604 while (!deleted_constraints.empty())
605 {
606 std::set<data::variable> vars_deleted;
607 for (const data::data_expression& fi: deleted_constraints)
608 {
609 data::find_free_variables(fi, std::inserter(vars_deleted, vars_deleted.end()));
610 }
611 deleted_constraints.clear();
612
613 auto del_i = std::find_if(m_qvars.rbegin(), m_qvars.rend(), [&](const detail::quantified_variable& qv)
614 {
615 return vars_deleted.find(qv.variable()) != vars_deleted.end();
616 });
617 // Remove quantified variables up to and including del_i
618 m_qvars.erase(m_qvars.begin(), del_i.base());
619
620 for (const data::variable& par: m_variable.parameters())
621 {
622 auto k = m_constraints.find(par);
623 if(k == m_constraints.end())
624 {
625 continue;
626 }
627 if(!bound_in_quantifiers(m_qvars, k->second))
628 {
629 deleted_constraints.push_back(k->second);
630 m_constraints.erase(k);
631 }
632 }
633 }
634 }
635
636 public:
637 /// \brief Constructor
638 vertex() = default;
639
640 /// \brief Constructor
641 /// \param x A propositional variable declaration
643 : m_variable(x)
644 {}
645
646 /// \brief The propositional variable that corresponds to the vertex
648 {
649 return m_variable;
650 }
651
653 {
654 return m_qvars;
655 }
656
657 /// \brief Maps data variables to data expressions. If the right hand side is a data
658 /// variable, it means that it represents NaC ("not a constant").
660 {
661 return m_constraints;
662 }
663
664 /// \brief Returns the indices of the constant parameters of this vertex.
665 /// \return The indices of the constant parameters of this vertex.
667 {
668 std::vector<std::size_t> result;
669 std::size_t index = 0;
670 for (const data::variable& parameter: m_variable.parameters())
671 {
672 if (is_constant(parameter))
673 {
674 result.push_back(index);
675 }
676 index++;
677 }
678 return result;
679 }
680
681 /// \brief Returns a string representation of the vertex.
682 /// \return A string representation of the vertex.
684 {
685 std::ostringstream out;
686 out << m_variable << " assertions = ";
687 for(const detail::quantified_variable& v: quantified_variables())
688 {
689 out << v.to_string();
690 }
691 for (const auto& constraint: m_constraints)
692 {
693 out << "{" << constraint.first << " := " << constraint.second << "} ";
694 }
695 return out.str();
696 }
697
698 /// \brief Assign new values to the parameters of this vertex, and update the constraints accordingly.
699 /// The new values have a number of constraints.
700 bool update(const qvar_list& qvars, const data::data_expression_list& e, const constraint_map& e_constraints, const DataRewriter& datar)
701 {
702 bool changed = false;
703
704 data::variable_list params = m_variable.parameters();
705 data::rewriter::substitution_type sigma;
706 detail::make_constelm_substitution(e_constraints, sigma);
707
708 if (!m_visited)
709 {
710 m_visited = true;
711 changed = true;
712
713 m_qvars = qvars;
714 // Partition expressions in e based on whether their free variables
715 // are bound in m_qvars.
716 std::vector<data::data_expression> deleted_constraints;
717 auto par = params.begin();
718 for (auto i = e.begin(); i != e.end(); ++i, ++par)
719 {
720 data::data_expression e1 = datar(*i, sigma);
721 if (bound_in_quantifiers(m_qvars, e1))
722 {
723 m_constraints[*par] = e1;
724 }
725 else
726 {
727 deleted_constraints.push_back(e1);
728 }
729 }
730 fix_constraints(deleted_constraints);
731 }
732 else
733 {
734 // Find longest common suffix of qvars
735 auto mismatch_it = std::mismatch(m_qvars.rbegin(), m_qvars.rend(), qvars.rbegin(), qvars.rend()).first;
736 changed |= mismatch_it != m_qvars.rend();
737 // Remove the outer quantifiers, up to and including mismatch_it
738 m_qvars.erase(m_qvars.begin(), mismatch_it.base());
739
740 // Find constraints for which f[i] = e[i] and for which all free
741 // variables are bound in m_qvars (which may have changed).
742 std::vector<data::data_expression> deleted_constraints;
743 auto i = e.begin();
744 for (auto par = params.begin(); i != e.end(); ++i, ++par)
745 {
746 auto k = m_constraints.find(*par);
747 if(k == m_constraints.end())
748 {
749 continue;
750 }
751 const data::data_expression& fi = k->second;
752 data::data_expression ei = datar(*i, sigma);
753 if (fi != ei || !bound_in_quantifiers(m_qvars, fi))
754 {
755 changed = true;
756 deleted_constraints.push_back(fi);
757 deleted_constraints.push_back(ei);
758 m_constraints.erase(k);
759 }
760 }
761 fix_constraints(deleted_constraints);
762 }
763 return changed;
764 }
765 };
766
767 /// \brief The storage type for vertices
769
770 /// \brief The storage type for edges
772
773 /// \brief The vertices of the dependency graph. They are stored in a map, to
774 /// support searching for a vertex.
776
777 /// \brief The edges of the dependency graph. They are stored in a map, to
778 /// easily access all out-edges corresponding to a particular vertex.
780
781 /// \brief The redundant parameters.
783
784 /// \brief Logs the vertices of the dependency graph.
786 {
787 std::ostringstream out;
788 for (const auto& v: m_vertices)
789 {
790 out << v.second.to_string() << std::endl;
791 }
792 return out.str();
793 }
794
795 /// \brief Logs the edges of the dependency graph.
797 {
798 std::ostringstream out;
799 for (const auto& [source, targets]: m_edges)
800 {
801 for (const edge& e: targets)
802 {
803 out << e.to_string() << std::endl;
804 }
805 }
806 return out.str();
807 }
808
810 {
811 std::ostringstream out;
812 out << "\n<todo list> [";
813 for (auto i = todo.begin(); i != todo.end(); ++i)
814 {
815 if (i != todo.begin())
816 {
817 out << ", ";
818 }
819 out << core::pp(i->name());
820 }
821 out << "]" << std::endl;
822 return out.str();
823 }
824
825 std::string print_edge_update(const edge& e, const vertex& u, const vertex& v)
826 {
827 std::ostringstream out;
828 out << "\n<updating edge> " << e.to_string() << std::endl;
829 out << " <source vertex > " << u.to_string() << std::endl;
830 out << " <target vertex before> " << v.to_string() << std::endl;
831 return out.str();
832 }
833
835 {
836 std::ostringstream out;
837 data::rewriter::substitution_type sigma;
838 detail::make_constelm_substitution(u.constraints(), sigma);
839 out << " <condition > " << e.condition() << sigma << " to " << value << std::endl;
840 return out.str();
841 }
842
844 {
845 std::ostringstream out;
846 data::rewriter::substitution_type sigma;
847 detail::make_constelm_substitution(u.constraints(), sigma);
848 out << "\nCould not evaluate condition " << e.condition() << sigma << " to true or false";
849 return out.str();
850 }
851
852 template <typename E>
853 std::list<E> concat(const std::list<E> a, const std::list<E> b)
854 {
855 std::list<E> result(a);
856 result.insert(result.end(), b.begin(), b.end());
857 return result;
858 }
859
860 public:
861
862 /// \brief Constructor.
863 /// \param datar A data rewriter
864 /// \param pbesr A PBES rewriter
865 pbes_constelm_algorithm(const DataRewriter& datar, const PbesRewriter& pbesr)
866 : m_data_rewriter(datar), m_pbes_rewriter(pbesr)
867 {}
868
869 /// \brief Returns the parameters that have been removed by the constelm algorithm
870 /// \return The removed parameters
872 {
875 {
878 for (const std::size_t par: red_pair.second)
879 {
881 std::advance(k, par);
883 }
884 }
885 return result;
886 }
887
888 /// \brief Runs the constelm algorithm
889 /// \param p A pbes
890 /// \param compute_conditions If true, propagation conditions are computed. Note
891 /// that the currently implementation has exponential behavior.
892 void run(pbes& p, bool compute_conditions = false, bool check_quantifiers = true)
893 {
895 m_edges.clear();
897
898 // compute the vertices and edges of the dependency graph
899 for (pbes_equation& eqn: p.equations())
900 {
903
904 // use an edge_condition_traverser to compute the edges
906 f.apply(eqn.formula());
907
909 for (const auto& [Q_X_e, details]: f.result())
910 {
911 const auto& [Q, X_e] = Q_X_e;
912 const auto& [conditions, conj_FV, disj_FV] = details;
913
914 // check options for quantifiers and conditions.
919
921 }
922 }
923
924 // initialize the todo list of vertices that need to be processed
931
932 mCRL2log(log::debug) << "\n--- initial vertices ---\n" << print_vertices();
933 mCRL2log(log::debug) << "\n--- edges ---\n" << print_edges();
934
935 // propagate constraints over the edges until the todo list is empty
936 while (!todo.empty())
937 {
940
941 // remove all occurrences of var from todo
943
944 const vertex& u = m_vertices[var.name()];
945 const std::vector<edge>& u_edges = m_edges[var.name()];
946
947 for (const edge& e: u_edges)
948 {
949 vertex& v = m_vertices[e.target().name()];
951
956
958 {
960 }
962 {
963 bool changed = v.update(
965 e.target().parameters(),
966 u.constraints(),
968 if (changed)
969 {
971 }
972 }
973 mCRL2log(log::debug) << " <target vertex after > " << v.to_string() << "\n";
974 }
975 }
976
977 mCRL2log(log::debug) << "\n--- final vertices ---\n" << print_vertices();
978
979 // compute the redundant parameters and the redundant equations
980 for (const pbes_equation& eqn: p.equations())
981 {
983 const vertex& v = m_vertices[name];
984 if (!v.constraints().empty())
985 {
987 if (!r.empty())
988 {
990 }
991 }
992 }
993
994 // Apply the constraints to the equations.
995 for (pbes_equation& eqn: p.equations())
996 {
998 const vertex& v = m_vertices[name];
999
1000 if (!v.constraints().empty())
1001 {
1005 for (auto i = v.quantified_variables().crbegin(); i != v.quantified_variables().crend(); ++i)
1006 {
1007 body = i->make_expr(body);
1008 }
1010 eqn.symbol(),
1011 eqn.variable(),
1012 body
1013 );
1014 }
1015 }
1016
1017 // remove the redundant parameters
1019
1020 // print the parameters and equation that are removed
1022 {
1023 mCRL2log(log::verbose) << "\nremoved the following constant parameters:" << std::endl;
1025 {
1026 for (const data::variable& var: i.second)
1027 {
1028 mCRL2log(log::verbose) << " (" << mcrl2::core::pp(i.first.name()) << ", " << data::pp(var) << ")" << std::endl;
1029 }
1030 }
1031 }
1032 }
1033};
1034
1035/// \brief Apply the constelm algorithm
1036/// \param p A PBES to which the algorithm is applied
1037/// \param rewrite_strategy A data rewrite strategy
1038/// \param rewriter_type A PBES rewriter type
1039/// \param compute_conditions If true, conditions for the edges of the dependency graph are used N.B. Very inefficient!
1040/// \param remove_redundant_equations If true, unreachable equations will be removed.
1041inline
1043 data::rewrite_strategy rewrite_strategy,
1044 pbes_rewriter_type rewriter_type,
1045 bool compute_conditions = false,
1046 bool remove_redundant_equations = true,
1047 bool check_quantifiers = true
1048 )
1049{
1050 const bool has_counter_example = pbes_system::detail::has_counter_example_information(p);
1051 if (has_counter_example)
1052 {
1053 mCRL2log(log::warning) << "Warning: the PBES has counter example information, which may not be preserved by constant elimination." << std::endl;
1054 }
1055 // data rewriter
1056 data::rewriter datar(p.data(), rewrite_strategy);
1057
1058 // pbes rewriter
1059 switch (rewriter_type)
1060 {
1061 case pbes_rewriter_type::simplify:
1062 {
1063 using pbes_rewriter = simplify_data_rewriter<data::rewriter>;
1064 pbes_rewriter pbesr(datar);
1065 pbes_constelm_algorithm<data::rewriter, pbes_rewriter> algorithm(datar, pbesr);
1066 algorithm.run(p, compute_conditions, check_quantifiers);
1067 if (remove_redundant_equations)
1068 {
1069 std::vector<propositional_variable> V = algorithms::remove_unreachable_variables(p);
1070 mCRL2log(log::verbose) << algorithms::print_removed_equations(V);
1071 }
1072 break;
1073 }
1074 case pbes_rewriter_type::quantifier_all:
1075 case pbes_rewriter_type::quantifier_finite:
1076 {
1077 const enumerate_quantifiers_mode enum_mode = (rewriter_type == pbes_rewriter_type::quantifier_all?
1078 expand_infinite_sorts_and_use_data_rewriter:
1079 expand_finite_sorts);
1080 enumerate_quantifiers_rewriter pbesr(datar, p.data(), enum_mode);
1082 algorithm.run(p, compute_conditions, check_quantifiers);
1083 if (remove_redundant_equations)
1084 {
1085 std::vector<propositional_variable> V = algorithms::remove_unreachable_variables(p);
1086 mCRL2log(log::verbose) << algorithms::print_removed_equations(V);
1087 }
1088 break;
1089 }
1090 default:
1091 { }
1092 }
1093}
1094
1095} // namespace mcrl2::pbes_system
1096
1097
1098
1099#endif // MCRL2_PBES_CONSTELM_H
data_expression(const data_expression &) noexcept=default
Move semantics.
Rewriter that operates on data expressions.
Definition rewriter.h:84
\brief A data variable
Definition variable.h:25
variable(const variable &) noexcept=default
Move semantics.
\brief The and operator for pbes expressions
pbes_expression make_expr(const pbes_expression &expr) const
Definition constelm.h:76
bool operator<(const quantified_variable &other) const
Definition constelm.h:71
quantified_variable(bool is_forall, const data::variable &var)
Definition constelm.h:46
bool operator==(const quantified_variable &other) const
Definition constelm.h:61
bool operator!=(const quantified_variable &other) const
Definition constelm.h:66
\brief The existential quantification operator for pbes expressions
exists(const data::variable_list &variables, const pbes_expression &body)
\brief Constructor Z14.
const data::variable_list & variables() const
\brief The universal quantification operator for pbes expressions
forall(const data::variable_list &variables, const pbes_expression &body)
\brief Constructor Z14.
const data::variable_list & variables() const
\brief The implication operator for pbes expressions
\brief The not operator for pbes expressions
\brief The or operator for pbes expressions
Represents an edge of the dependency graph. The assignments are stored implicitly using the 'right' p...
Definition constelm.h:432
edge(const propositional_variable &src, const qvar_list &qvars, const propositional_variable_instantiation &tgt, const std::set< data::variable > &conj_context, const std::set< data::variable > &disj_context, data::data_expression c=data::sort_bool::true_())
Constructor.
Definition constelm.h:454
const propositional_variable_instantiation & target() const
The propositional variable instantiation that determines the target of the edge.
Definition constelm.h:507
qvar_list quantifier_inside_approximation(const vertex &source, const DataRewriter &rewr) const
Try to guess which quantifiers of Q can end up directly before target, when the quantifier inside rew...
Definition constelm.h:520
const propositional_variable_instantiation m_target
The propositional variable instantiation that determines the target of the edge.
Definition constelm.h:441
const qvar_list & quantified_variables() const
Definition constelm.h:501
const qvar_list m_qvars
The quantifiers in whose direct context the target PVI occurs.
Definition constelm.h:438
const propositional_variable m_source
The propositional variable at the source of the edge.
Definition constelm.h:435
const std::set< data::variable > m_disj_context
Definition constelm.h:444
const data::data_expression & condition() const
The condition of the edge.
Definition constelm.h:513
const std::set< data::variable > m_conj_context
Definition constelm.h:443
const propositional_variable & source() const
The propositional variable at the source of the edge.
Definition constelm.h:496
std::string to_string() const
Returns a string representation of the edge.
Definition constelm.h:472
Represents a vertex of the dependency graph.
Definition constelm.h:565
void fix_constraints(std::vector< data::data_expression > deleted_constraints)
Weaken the constraints so they satisfy.
Definition constelm.h:602
qvar_list m_qvars
The list of quantified variables that occur in the constraints.
Definition constelm.h:571
std::string to_string() const
Returns a string representation of the vertex.
Definition constelm.h:683
bool update(const qvar_list &qvars, const data::data_expression_list &e, const constraint_map &e_constraints, const DataRewriter &datar)
Assign new values to the parameters of this vertex, and update the constraints accordingly....
Definition constelm.h:700
constraint_map m_constraints
Maps data variables to data expressions. If a parameter is not.
Definition constelm.h:575
bool m_visited
Indicates whether this vertex has been visited at least once.
Definition constelm.h:578
const constraint_map & constraints() const
Maps data variables to data expressions. If the right hand side is a data variable,...
Definition constelm.h:659
bool is_constant(const data::variable &v) const
Returns true if the parameter v has been assigned a constant expression.
Definition constelm.h:583
propositional_variable m_variable
The propositional variable that corresponds to the vertex.
Definition constelm.h:568
std::vector< std::size_t > constant_parameter_indices() const
Returns the indices of the constant parameters of this vertex.
Definition constelm.h:666
const propositional_variable & variable() const
The propositional variable that corresponds to the vertex.
Definition constelm.h:647
bool bound_in_quantifiers(const qvar_list &qvars, const data::data_expression &e)
Returns true iff all free variables in e are bound in qvars.
Definition constelm.h:590
vertex(propositional_variable x)
Constructor.
Definition constelm.h:642
Algorithm class for the constelm algorithm.
Definition constelm.h:408
const PbesRewriter & m_pbes_rewriter
Compares data expressions for equality.
Definition constelm.h:418
std::string print_todo_list(const std::deque< propositional_variable > &todo)
Definition constelm.h:809
vertex_map m_vertices
The vertices of the dependency graph. They are stored in a map, to support searching for a vertex.
Definition constelm.h:775
std::string print_edge_update(const edge &e, const vertex &u, const vertex &v)
Definition constelm.h:825
std::string print_edges()
Logs the edges of the dependency graph.
Definition constelm.h:796
pbes_constelm_algorithm(const DataRewriter &datar, const PbesRewriter &pbesr)
Constructor.
Definition constelm.h:865
std::list< E > concat(const std::list< E > a, const std::list< E > b)
Definition constelm.h:853
std::string print_evaluation_failure(const edge &e, const vertex &u)
Definition constelm.h:843
const DataRewriter & m_data_rewriter
Compares data expressions for equality.
Definition constelm.h:415
edge_map m_edges
The edges of the dependency graph. They are stored in a map, to easily access all out-edges correspon...
Definition constelm.h:779
std::string print_condition(const edge &e, const vertex &u, const pbes_expression &value)
Definition constelm.h:834
std::string print_vertices() const
Logs the vertices of the dependency graph.
Definition constelm.h:785
parameterized boolean equation system
Definition pbes.h:54
\brief A propositional variable instantiation
const data::data_expression_list & parameters() const
propositional_variable_instantiation(const propositional_variable_instantiation &) noexcept=default
Move semantics.
\brief A propositional variable declaration
propositional_variable(const propositional_variable &) noexcept=default
Move semantics.
#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
const function_symbol & true_()
Constructor for function symbol true.
Definition bool.h:74
void make_constelm_substitution(const std::map< data::variable, data::data_expression > &m, data::rewriter::substitution_type &result)
Definition constelm.h:31
bool has_counter_example_information(const pbes &pbesspec)
Guesses if a pbes has counter example information.
void constelm(pbes &p, data::rewrite_strategy rewrite_strategy, pbes_rewriter_type rewriter_type, bool compute_conditions=false, bool remove_redundant_equations=true, bool check_quantifiers=true)
Apply the constelm algorithm.
Definition constelm.h:1042
void swap(atermpp::aterm &t1, atermpp::aterm &t2) noexcept
Swaps two term_applss.
Definition aterm.h:364
A quantified predicate variable instantiation.
Definition constelm.h:91
std::list< quantified_variable > Q
Definition constelm.h:92
propositional_variable_instantiation X_e
Definition constelm.h:93
bool operator<(const QPVI &other) const
Definition constelm.h:95
void leave(const propositional_variable_instantiation &x)
Definition constelm.h:371
void merge_conditions(stack_elem &&ec1, bool negate1, stack_elem &&ec2, bool negate2, stack_elem &ec, bool is_conjunctive)
Definition constelm.h:182
std::list< pbes_expression > quantified_context
Definition constelm.h:142
void leave(const data::data_expression &x)
Definition constelm.h:238
std::set< data::variable > disjunctive_context_FV
The set of free variables that occur on the other side of the conjunctions this PVI occurs in....
Definition constelm.h:113
std::set< data::variable > conjunctive_context_FV
The set of free variables that occur on the other side of the conjunctions this PVI occurs in....
Definition constelm.h:109
std::set< data::data_expression > conditions
Contains expressions that characterise when an edge is enabled. The conjunction of these expressions ...
Definition constelm.h:105
edge_traverser_stack_elem(const data::data_expression &cond_pos, const data::data_expression &cond_neg, std::set< data::variable > &&free_vars)
Definition constelm.h:125
enumerate_quantifiers_rewriter(const data::rewriter &R, const data::data_specification &dataspec, const enumerate_quantifiers_mode enum_mode=expand_infinite_sorts_and_use_data_rewriter)
A rewriter that simplifies boolean expressions in a term, and rewrites data expressions using DataRew...