mCRL2
Loading...
Searching...
No Matches
pbesfixpointsolve.h
Go to the documentation of this file.
1// Author(s): Jore Booy
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/tools/pbesfixpointsolve.h
10/// \brief This file provides a tool that can simplify PBESs by
11/// substituting PBES equations for variables in the rhs,
12/// simplifying the result, and keeping it when it can
13/// eliminate PBES variables.
14
15#ifndef MCRL2_PBES_TOOLS_PBESFIXPOINTSOLVE_H
16#define MCRL2_PBES_TOOLS_PBESFIXPOINTSOLVE_H
17
18#include "mcrl2/data/detail/prover/bdd_prover.h"
19#include "mcrl2/data/merge_data_specifications.h"
20// #include "mcrl2/data/rewrite.h"
21#include "mcrl2/pbes/algorithms.h"
22#include "mcrl2/pbes/detail/iteration_builders.h"
23#include "mcrl2/pbes/io.h"
24#include "mcrl2/pbes/pbes_expression.h"
25#include "mcrl2/pbes/rewrite.h"
26#include "mcrl2/pbes/rewriter.h"
27#include "mcrl2/pbes/rewriters/pbes2data_rewriter.h"
28#include "mcrl2/smt/solver.h"
29#include "mcrl2/utilities/logger.h"
30
31#include <map>
32#include <optional>
33
34namespace mcrl2::pbes_system
35{
36
38{
39 data::rewrite_strategy rewrite_strategy = data::rewrite_strategy::jitty;
40
42 bool smt = false;
43 bool early_stopping = false;
44};
45
46inline void substitute(pbes_equation& into,
47 const pbes_equation& by,
48 detail::substitute_propositional_variables_builder<pbes_system::pbes_expression_builder>& substituter)
49{
50 substituter.set_equation(by);
51 substituter.set_name(into.variable().name());
53 substituter.apply(p, into.formula());
54 into.formula() = p;
55}
56
58 detail::replace_other_propositional_variables_with_functions_builder<pbes_system::pbes_expression_builder>&
59 replace_substituter)
60{
61 pbes_expression expr;
62 replace_substituter.set_forward(true);
63 replace_substituter.set_name(equation.variable().name());
64 replace_substituter.apply(expr, equation.formula());
65
66 data::data_expression data_expr = detail::pbes2data(expr);
67
68 replace_substituter.set_forward(false);
69 replace_substituter.apply(expr, equation.formula());
70 return data_expr;
71}
72
73enum class InvResult
74{
78};
79
80// Equation should be in full form.
82 detail::substitute_propositional_variables_builder<pbes_system::pbes_expression_builder>& substituter,
83 detail::replace_other_propositional_variables_with_functions_builder<pbes_system::pbes_expression_builder>&
84 replace_substituter,
85 data::data_specification data_spec,
86 simplify_data_rewriter<data::rewriter> pbes_rewriter,
87 std::set<data::variable>& global_variables)
88{
89 mCRL2log(log::log_level_t::debug) << "INV: " << equation.symbol() << "\n";
90 if (equation.symbol().is_mu())
91 {
93 }
94
95 // Base check: does it contain the right pvi
96 std::set<propositional_variable_instantiation> set = find_propositional_variable_instantiations(equation.formula());
97 if (set.size() == 0)
98 {
99 mCRL2log(log::log_level_t::debug) << "INV: " << equation.variable().name() << " does not contain any pvi." << "\n";
100 return InvResult::INV_TRUE;
101 }
102
103 if (std::any_of(set.begin(),
104 set.end(),
105 [=](const propositional_variable_instantiation& v) { return v.name() != equation.variable().name(); }))
106 {
107 // The formula contains another pvi than the current equation, so skip
108 mCRL2log(log::log_level_t::debug) << "INV: " << equation.variable().name() << " contains other pvi." << "\n";
110 }
111
112 // Calculate CC
113 pbes_equation eq;
115 eq.symbol() = equation.symbol();
116 eq.variable() = equation.variable();
117 substituter.set_equation(eq);
118 substituter.set_name(equation.variable().name());
120 substituter.apply(cc, equation.formula());
121 cc = pbes_rewrite(cc, pbes_rewriter);
122
123 bool global_invariant = false;
124
125 detail::substitute_propositional_variables_for_bools_builder<pbes_system::pbes_expression_builder> bool_substituter(
126 pbes_rewriter);
127
128 mcrl2::data::detail::BDD_Prover f_bdd_prover(data_spec, data::used_data_equation_selector(data_spec));
129 mCRL2log(log::log_level_t::verbose) << "INV: PVI set size " << set.size() << "\n";
130 int i = 0;
131 for (const propositional_variable_instantiation& pvi: set)
132 {
133 if (i % 5 == 0)
134 {
135 mCRL2log(log::log_level_t::verbose) << "INV: PVI " << i << "begin added now. \n";
136 }
137 i++;
138 // Calculate CC_i
139 data::mutable_indexed_substitution sigma;
140 data::data_expression_list pars = pvi.parameters();
141 for (const data::variable& v : equation.variable().parameters())
142 {
143 data::data_expression par = pars.front();
144 pars.pop_front();
145 if (par.sort() != v.sort())
146 {
147 // Parameters do not match with variables. Ignore this substitution.
148 mCRL2log(log::log_level_t::verbose) << "INV: No param match for " << v.name() << "\n";
149 return InvResult::INV_FALSE;
150 }
151 sigma[v] = par;
152 }
153 pbes_expression cc_i = pbes_rewrite(cc, pbes_rewriter, sigma);
154
155 // Calculate CC and C_i
156 bool_substituter.set_pvi(pvi);
157 bool_substituter.set_replacement(false_());
158 pbes_expression c_i;
159 bool_substituter.apply(c_i, equation.formula());
160 c_i = pbes_rewrite(c_i, pbes_rewriter);
161
162 pbes_equation cc_i_eq;
163 cc_i_eq.formula() = cc_i;
164 data::data_expression cc_i_data = pbestodata(cc_i_eq, replace_substituter);
165 pbes_equation c_i_eq;
166 c_i_eq.formula() = c_i;
167 data::data_expression c_i_data = pbestodata(c_i_eq, replace_substituter);
168
169 f_bdd_prover.set_formula(c_i_data);
170 data::detail::Answer c_i_is_contradiction = f_bdd_prover.is_contradiction();
171 if (c_i_is_contradiction == data::detail::Answer::answer_yes)
172 {
173 mCRL2log(log::log_level_t::verbose) << "Core constraint is not simple (contains pvi)." << std::endl;
174 return InvResult::INV_FALSE;
175 }
176
177 // Check CC and C_i implies CC_i
178 data::data_expression bdd_expr = data::imp(c_i_data, cc_i_data);
179 // Add exists
180 for (const data::variable& v : global_variables)
181 {
182 bdd_expr = make_exists_(data::variable_list({v}), bdd_expr);
183 }
184
185 f_bdd_prover.set_formula(bdd_expr);
186 mCRL2log(log::log_level_t::verbose) << "INV?: " << std::endl;
187
188 data::detail::Answer v_is_tautology = f_bdd_prover.is_tautology();
189 data::detail::Answer v_is_contradiction = f_bdd_prover.is_contradiction();
190 if (v_is_contradiction == data::detail::Answer::answer_yes)
191 {
192 mCRL2log(log::log_level_t::verbose) << "Contradiction for inv checker." << std::endl;
193 equation.formula() = false_();
194 return InvResult::EQ_FALSE;
195 }
196 else if (v_is_tautology == data::detail::Answer::answer_no || v_is_tautology == data::detail::Answer::answer_undefined)
197 {
198 mCRL2log(log::log_level_t::verbose) << "Found some transition that is not an invariant" << std::endl;
199 return InvResult::INV_FALSE;
200 }
201 else if (v_is_tautology == data::detail::Answer::answer_yes)
202 {
203 mCRL2log(log::log_level_t::verbose) << "This transition is true" << std::endl;
204 global_invariant = true;
205 }
206 else
207 {
208 mCRL2log(log::log_level_t::error) << "No contradiction and no tautology" << std::endl;
209 throw mcrl2::runtime_error("TODO");
210 }
211 }
212
213 mCRL2log(log::log_level_t::info) << "Is global invariant? " << global_invariant << std::endl;
214 if (global_invariant)
215 {
216 equation.formula() = cc;
217 return InvResult::INV_TRUE;
218 }
220}
221
222inline void perform_iteration(pbes_equation& equation,
223 detail::substitute_propositional_variables_builder<pbes_system::pbes_expression_builder>& substituter,
224 detail::replace_other_propositional_variables_with_functions_builder<pbes_system::pbes_expression_builder>&
225 replace_substituter,
226 data::data_specification data_spec,
229 simplify_data_rewriter<data::rewriter> pbes_rewriter)
230{
231 std::optional<smt::smt_solver> solv;
232 std::optional<mcrl2::data::detail::BDD_Prover> f_bdd_prover;
233 if (options.smt)
234 {
235 solv.emplace(data_spec);
236 }
237 else
238 {
239 f_bdd_prover.emplace(data_spec, data::used_data_equation_selector(data_spec));
240 }
241 pbes_equation eq;
242 if (equation.symbol().is_nu())
243 {
245 }
246 else
247 {
249 }
250 eq.symbol() = equation.symbol();
251 eq.variable() = equation.variable();
252 bool stable = false;
253 int i = 0;
254 while (!stable)
255 {
256 mCRL2log(log::log_level_t::info) << eq.variable().name() << ": " << i << std::endl;
257
258 // Apply substitution
259 substituter.set_equation(eq);
260 substituter.set_name(equation.variable().name());
262 substituter.apply(p, equation.formula());
263 p = pbes_rewrite(p, pbes_rewriter);
264
265 // Check if the iteration has reached a fixpoint
266 pbes_equation p_eq;
267 p_eq.formula() = pbes_rewrite(p, pbes_rewriter);
269 replace_substituter.reset_variable_list();
270 data::data_expression eq_data = pbestodata(eq, replace_substituter);
271 data::data_expression p_data = pbestodata(p_eq, replace_substituter);
272 data::data_expression formula;
273 // Due to mononicity, only one way implication needs to be tested
274 if (equation.symbol().is_nu())
275 {
276 formula = data::imp(eq_data, p_data);
277 }
278 else
279 {
280 formula = data::imp(p_data, eq_data);
281 }
282
283 if (options.smt)
284 {
285 data::variable_list var_list = p_eq.variable().parameters();
286 for (const auto& x : as_set(replace_substituter.get_variable_list()))
287 {
288 var_list.push_front(x);
289 }
290 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": SMT check for " << i << " started." << std::endl;
291 smt::answer result = solv->solve(var_list, data::not_(formula), std::chrono::seconds(0));
292 switch (result)
293 {
294 case smt::answer::UNSAT:
295 mCRL2log(log::log_level_t::info) << eq.variable().name() << ": iteration " << i << " is equal to " << i + 1 << std::endl;
296 stable = true;
297 break;
298 case smt::answer::SAT:
299 break;
300 case smt::answer::UNKNOWN:
301 break;
302 }
303 }
304 else
305 {
306 f_bdd_prover->set_formula(formula);
307 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": EQ-BDD check for " << i << " started." << std::endl;
308 data::detail::Answer v_is_tautology = f_bdd_prover->is_tautology();
309 if (v_is_tautology == data::detail::Answer::answer_yes)
310 {
311 mCRL2log(log::log_level_t::info) << eq.variable().name() << ": iteration " << i << " is equal to " << i + 1 << std::endl;
312 stable = true;
313 }
314 }
315
316 // Stop iterating if (mu) the equation is true for the initial variable or (nu) the equation is false for the
317 // initial variable
318 if (!stable && options.early_stopping && initial_state.name() == eq.variable().name())
319 {
320 pbes_equation init_eq = eq;
321 init_eq.formula() = initial_state;
322 substitute(init_eq, eq, substituter);
323 data::data_expression init_data = pbestodata(init_eq, replace_substituter);
324
325 if (options.smt)
326 {
327 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": SMT init check for " << i << " started." << std::endl;
328 data::data_expression smt_data = init_eq.symbol().is_mu() ? data::not_(init_data) : init_data;
329 smt::answer result = solv->solve({}, smt_data, std::chrono::seconds(0));
330 switch (result)
331 {
332 case smt::answer::UNSAT:
333 mCRL2log(log::log_level_t::info) << eq.variable().name() << ": iteration " << i << " is equal to " << i + 1 << std::endl;
334 stable = true;
335 break;
336 case smt::answer::SAT:
337 break;
338 case smt::answer::UNKNOWN:
339 break;
340 }
341 }
342 else
343 {
344 f_bdd_prover->set_formula(init_data);
345 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": EQ-BDD init check for " << i << " started." << std::endl;
346 data::detail::Answer v_is_tautology = f_bdd_prover->is_tautology();
347 data::detail::Answer v_is_contradiction = f_bdd_prover->is_contradiction();
348
349 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": EQ-BDD tauto? "
350 << (v_is_tautology == data::detail::Answer::answer_yes) << std::endl;
351 mCRL2log(log::log_level_t::verbose) << eq.variable().name() << ": EQ-BDD contra? "
352 << (v_is_contradiction == data::detail::Answer::answer_yes) << std::endl;
353 if (init_eq.symbol().is_mu() ? v_is_tautology == data::detail::Answer::answer_yes
354 : v_is_contradiction == data::detail::Answer::answer_yes)
355 {
356 mCRL2log(log::log_level_t::info) << eq.variable().name() << ": iteration " << i << " is equal to " << i + 1 << std::endl;
357 stable = true;
358 }
359 }
360 }
361
362 if (stable)
363 {
364 break;
365 }
366 eq.formula() = p;
367 i++;
368 }
369
370 equation.formula() = eq.formula();
371}
372
374{
376 {
377 data::rewriter data_rewriter(p.data(), options.rewrite_strategy);
378 simplify_data_rewriter<data::rewriter> pbes_rewriter(data_rewriter);
379 detail::substitute_propositional_variables_builder<pbes_system::pbes_expression_builder> substituter(pbes_rewriter);
380
381 detail::replace_other_propositional_variables_with_functions_builder<pbes_system::pbes_expression_builder>
382 replace_substituter(pbes_rewriter);
383
384 for (std::vector<pbes_equation>::reverse_iterator i = p.equations().rbegin(); i != p.equations().rend(); i++)
385 {
386 mCRL2log(log::log_level_t::verbose) << "Investigating the equation for " << i->variable().name() << "\n";
387 if (options.check_global_invariant)
388 {
389 InvResult global_inv = global_invariant_check(*i,
390 substituter,
391 replace_substituter,
392 p.data(),
393 pbes_rewriter,
394 p.global_variables());
395 if (global_inv == InvResult::INV_FALSE)
396 {
397 perform_iteration(*i, substituter, replace_substituter, p.data(), options, p.initial_state(), pbes_rewriter);
398 }
399 }
400 else
401 {
402 perform_iteration(*i, substituter, replace_substituter, p.data(), options, p.initial_state(), pbes_rewriter);
403 }
404
405 for (std::vector<pbes_equation>::reverse_iterator j = i + 1; j != p.equations().rend(); j++)
406 {
407 substitute(*j, *i, substituter);
408 }
409 }
410 }
411};
412
413} // namespace mcrl2::pbes_system
414
415
416
417#endif // MCRL2_PBES_TOOLS_PBESFIXPOINTSOLVE_H
data_expression & operator=(data_expression &&) noexcept=default
Rewriter that operates on data expressions.
Definition rewriter.h:84
Component for selecting a subset of equations that are actually used in an encompassing specification...
Definition selection.h:36
used_data_equation_selector(const data_specification &)
select all equations
Definition selection.h:181
bool is_nu() const
Returns true if the symbol is nu.
bool is_mu() const
Returns true if the symbol is mu.
fixpoint_symbol & operator=(const fixpoint_symbol &) noexcept=default
fixpoint_symbol & symbol()
Returns the fixpoint symbol of the equation.
propositional_variable & variable()
Returns the pbes variable of the equation.
pbes_expression & formula()
Returns the predicate formula on the right hand side of the equation.
pbes_expression & operator=(const pbes_expression &) noexcept=default
parameterized boolean equation system
Definition pbes.h:54
\brief A propositional variable instantiation
const core::identifier_string & name() const
propositional_variable & operator=(const propositional_variable &) noexcept=default
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:435
Answer
A prover that uses EQ-BDDs.
Definition bdd_prover.h:77
data_expression not_(const data_expression &x)
data_expression imp(const data_expression &x, const data_expression &y)
const pbes_expression & true_()
data::data_expression pbestodata(pbes_equation &equation, detail::replace_other_propositional_variables_with_functions_builder< pbes_system::pbes_expression_builder > &replace_substituter)
void perform_iteration(pbes_equation &equation, detail::substitute_propositional_variables_builder< pbes_system::pbes_expression_builder > &substituter, detail::replace_other_propositional_variables_with_functions_builder< pbes_system::pbes_expression_builder > &replace_substituter, data::data_specification data_spec, pbesfixpointsolve_options options, propositional_variable_instantiation initial_state, simplify_data_rewriter< data::rewriter > pbes_rewriter)
InvResult global_invariant_check(pbes_equation &equation, detail::substitute_propositional_variables_builder< pbes_system::pbes_expression_builder > &substituter, detail::replace_other_propositional_variables_with_functions_builder< pbes_system::pbes_expression_builder > &replace_substituter, data::data_specification data_spec, simplify_data_rewriter< data::rewriter > pbes_rewriter, std::set< data::variable > &global_variables)
const pbes_expression & false_()
void substitute(pbes_equation &into, const pbes_equation &by, substitute_propositional_variables_builder< pbes_system::pbes_expression_builder > &substituter)
void run(pbes &p, pbesfixpointsolve_options options)
A rewriter that simplifies boolean expressions in a term, and rewrites data expressions using DataRew...