10#ifndef MCRL2_PBES_PBESREACH_H
11#define MCRL2_PBES_PBESREACH_H
13#include "mcrl2/pbes/symbolic_pbessolve.h"
14#ifdef MCRL2_ENABLE_SYLVAN
16#include "mcrl2/utilities/detail/container_utility.h"
17#include "mcrl2/utilities/stopwatch.h"
18#include "mcrl2/utilities/text_utility.h"
19#include "mcrl2/data/merge_data_specifications.h"
20#include "mcrl2/data/rewriter_tool.h"
21#include "mcrl2/data/substitutions/mutable_map_substitution.h"
22#include "mcrl2/data/join.h"
23#include "mcrl2/pbes/detail/instantiate_global_variables.h"
24#include "mcrl2/pbes/detail/pbes_io.h"
25#include "mcrl2/pbes/normalize.h"
26#include "mcrl2/pbes/pbes_summand_group.h"
27#include "mcrl2/pbes/pbes.h"
28#include "mcrl2/pbes/replace_constants_by_variables.h"
29#include "mcrl2/pbes/resolve_name_clashes.h"
30#include "mcrl2/pbes/rewriters/one_point_rule_rewriter.h"
31#include "mcrl2/pbes/srf_pbes.h"
32#include "mcrl2/pbes/unify_parameters.h"
33#include "mcrl2/symbolic/print.h"
34#include "mcrl2/symbolic/symbolic_reachability.h"
36namespace mcrl2::pbes_system {
40template<
bool allow_ce>
42data::data_specification construct_propositional_variable_data_specification(
const pbes_system::detail::pre_srf_pbes<allow_ce>& pbesspec,
const std::string& sort_name)
45 std::vector<std::string> names;
46 for (
const auto& equation: pbesspec.equations())
48 names.push_back(equation.variable().name());
50 std::string text =
"sort " + sort_name +
" = struct " + utilities::string_join(names,
" | ") +
";";
51 return data::parse_data_specification(text);
54struct symbolic_reachability_options:
public symbolic::symbolic_reachability_options
56 bool compute_strategy =
false;
57 bool check_strategy =
false;
58 bool make_total =
false;
59 bool reset_parameters =
false;
60 bool aggressive =
false;
61 bool naive_counter_example_instantiation =
false;
62 std::size_t solve_strategy = 0;
63 std::size_t split_conditions = 0;
68std::ostream& operator<<(std::ostream& out,
const symbolic_reachability_options& options)
70 out <<
static_cast<
const symbolic::symbolic_reachability_options&>(options);
71 out <<
"solve_strategy = " << options.solve_strategy << std::endl;
72 out <<
"split_conditions = " << options.split_conditions << std::endl;
73 out <<
"total = " << std::boolalpha << options.make_total << std::endl;
78pbes_system::srf_pbes split_conditions(
const pbes_system::srf_pbes& pbes, std::size_t granularity)
80 mCRL2log(log::debug) <<
"splitting conditions" << std::endl;
83 data::set_identifier_generator id_generator;
84 for (
const srf_equation& equation : pbes.equations())
86 id_generator.add_identifier(equation.variable().name());
90 pbes_system::propositional_variable Xtrue = pbes.equations()[pbes.equations().size()-2].variable();
91 pbes_system::propositional_variable Xfalse = pbes.equations()[pbes.equations().size()-1].variable();
93 pbes_system::srf_pbes result = pbes;
94 std::vector<srf_equation> added_equations;
95 for (srf_equation& equation : result.equations())
97 std::vector<srf_summand> split_summands;
98 for (
const srf_summand& summand : equation.summands())
100 mCRL2log(log::debug) <<
"splitting summand " << summand << std::endl;
103 bool should_split = summand.parameters().empty() && granularity > 1;
105 if (data::sort_bool::is_or_application(summand.condition()))
108 for (
const data::data_expression& clause : data::split_or(summand.condition()))
110 split_summands.emplace_back(summand.parameters(), atermpp::down_cast<pbes_expression>(clause), summand.variable());
111 mCRL2log(log::debug) <<
"Added summand " << split_summands.back() << std::endl;
114 else if (should_split && data::sort_bool::is_and_application(summand.condition()))
117 bool simple = granularity == 3 || summand.variable().name() == Xtrue.name() || summand.variable().name() == Xfalse.name();
119 std::vector<srf_summand> split_summands_inner;
120 for (
const data::data_expression& clause : data::split_and(summand.condition()))
125 split_summands_inner.emplace_back(data::variable_list(),
126 atermpp::down_cast<pbes_expression>(data::lazy::not_(clause)),
127 !equation.is_conjunctive()
128 ? propositional_variable_instantiation(Xtrue.name(), {}) :
129 propositional_variable_instantiation(Xfalse.name(), {})
135 const propositional_variable& Y = equation.variable();
136 propositional_variable Y1(id_generator(Y.name()), Y.parameters());
138 split_summands_inner.emplace_back(data::variable_list(), true_(), propositional_variable_instantiation(Y1.name(), data::make_data_expression_list(Y1.parameters())));
139 std::vector<srf_summand> summands;
140 summands.emplace_back(data::variable_list(), atermpp::down_cast<pbes_expression>(clause), summand.variable());
141 added_equations.emplace_back(equation.symbol(), Y1, summands, !equation.is_conjunctive());
142 mCRL2log(log::debug) <<
"Added equation " << added_equations.back() << std::endl;
144 mCRL2log(log::debug) <<
"Added summand " << split_summands_inner.back() << std::endl;
149 split_summands_inner.emplace_back(data::variable_list(), true_(), summand.variable());
152 if (equation.summands().size() == 1)
155 split_summands = split_summands_inner;
156 equation.is_conjunctive() = !equation.is_conjunctive();
157 mCRL2log(log::debug) <<
"Changed equation type (conjunctive or disjunctive)" << std::endl;
162 const propositional_variable& Y = equation.variable();
163 propositional_variable Y1(id_generator(Y.name()), Y.parameters());
165 split_summands.emplace_back(data::variable_list(), true_(), propositional_variable_instantiation(Y1.name(), data::make_data_expression_list(Y1.parameters())));
166 added_equations.emplace_back(equation.symbol(), Y1, split_summands_inner, !equation.is_conjunctive());
167 mCRL2log(log::debug) <<
"Added equation " << added_equations.back() << std::endl;
173 split_summands.emplace_back(summand);
177 equation.summands() = split_summands;
181 result.equations().insert(result.equations().end()-2, added_equations.begin(), added_equations.end());
189pbes_system::srf_pbes_with_ce preprocess(pbes_system::pbes pbesspec,
const symbolic_reachability_options& options)
191 pbes_system::detail::instantiate_global_variables(pbesspec);
194 if (options.one_point_rule_rewrite)
196 pbes_system::one_point_rule_rewriter R;
197 pbes_system::replace_pbes_expressions(pbesspec, R,
false);
200 if (options.replace_constants_by_variables)
202 data::mutable_indexed_substitution<> sigma;
203 auto rewr = symbolic::construct_rewriter(pbesspec.data(), options.rewrite_strategy, pbes_system::find_function_symbols(pbesspec), options.remove_unused_rewrite_rules);
204 pbes_system::replace_constants_by_variables(pbesspec, rewr, sigma);
207 auto result = pbes2pre_srf(pbesspec,
true);
210 unify_parameters(result,
true, options.reset_parameters);
212 pbes_system::resolve_summand_variable_name_clashes(result, result.equations().front().variable().parameters());
218struct per_worker_information
220 data::mutable_indexed_substitution<> m_sigma;
221 data::rewriter m_rewr;
224class pbesreach_algorithm
226 using enumerator_element = data::enumerator_list_element_with_substitution<>;
228 template <
typename Context,
bool ActionLabel>
229 friend void symbolic::learn_successors_callback(WorkerP*, Task*, std::uint32_t* v, std::size_t n,
void* context);
232 using ldd = sylvan::ldds::ldd;
233 const symbolic_reachability_options& m_options;
234 pbes_system::srf_pbes m_pbes;
235 data::rewriter m_rewr;
236 data::mutable_indexed_substitution<> m_sigma;
237 data::enumerator_identifier_generator m_id_generator;
238 data::enumerator_algorithm<> m_enumerator;
239 data::variable_list m_process_parameters;
241 std::unordered_map<core::identifier_string, data::data_expression> m_propvar_map;
242 std::vector<symbolic::data_expression_index> m_data_index;
243 std::vector<pbes_summand_group> m_summand_groups;
244 data::data_expression_list m_initial_state;
245 std::vector<boost::dynamic_bitset<>> m_summand_patterns;
246 std::vector<boost::dynamic_bitset<>> m_group_patterns;
247 std::vector<std::size_t> m_variable_order;
252 ldd m_initial_vertex;
255 void learn_successors(std::size_t i, pbes_summand_group& R,
const ldd& X)
257 mCRL2log(log::trace) <<
"learn successors of summand group " << i <<
" for X = " << print_states(m_data_index, X, R.read) << std::endl;
259 using namespace sylvan::ldds;
260 std::pair<pbesreach_algorithm&, pbes_summand_group&> context{*
this, R};
261 sat_all_nopar(X, symbolic::learn_successors_callback<std::pair<pbesreach_algorithm&, pbes_summand_group&>,
false>, &context);
265 pbes_system::srf_pbes internal_preprocess(pbes_system::srf_pbes srf_pbes,
bool make_total)
267 if (m_options.split_conditions > 0)
269 srf_pbes = split_conditions(srf_pbes, m_options.split_conditions);
274 srf_pbes.make_total();
277 if (!has_unified_parameters(srf_pbes.to_pbes()))
279 throw mcrl2::runtime_error(
"The PBES after removing counter example information does not have unified parameters");
283 data::data_specification propvar_dataspec = construct_propositional_variable_data_specification(srf_pbes,
"PropositionalVariable");
284 srf_pbes.data() = data::merge_data_specifications(srf_pbes.data(), propvar_dataspec);
286 mCRL2log(log::trace) <<
"--- srf pbes ---\n" << srf_pbes.to_pbes() << std::endl;
290 std::string print_size(
const sylvan::ldds::ldd& L)
292 return symbolic::print_size(L, m_options.print_exact, m_options.print_nodesize);
296 pbesreach_algorithm(
const pbes_system::srf_pbes& srf_pbes,
const symbolic_reachability_options& options_)
297 : m_options(options_),
298 m_pbes(internal_preprocess(srf_pbes, options_.make_total)),
299 m_rewr(symbolic::construct_rewriter(m_pbes.data(), m_options.rewrite_strategy, pbes_system::find_function_symbols(m_pbes.to_pbes()), m_options.remove_unused_rewrite_rules)),
300 m_enumerator(m_rewr, m_pbes.data(), m_rewr, m_id_generator,
false)
302 if (!m_options.srf.empty())
304 detail::save_pbes(m_pbes.to_pbes(), m_options.srf);
307 data::basic_sort propvar_sort(
"PropositionalVariable");
308 for (
const auto& equation: m_pbes.equations())
310 m_propvar_map[equation.variable().name()] = data::function_symbol(equation.variable().name(), propvar_sort);
313 m_process_parameters = m_pbes.equations().front().variable().parameters();
314 m_process_parameters.push_front(data::variable(
"propvar", propvar_sort));
315 m_n = m_process_parameters.size();
318 std::vector<data::data_expression> initial_values;
319 for (
const data::data_expression& expression : make_state(m_pbes.initial_state(), m_propvar_map))
321 initial_values.push_back(m_rewr(expression));
324 m_initial_state = data::data_expression_list(initial_values.begin(), initial_values.end());
326 m_summand_patterns = compute_read_write_patterns(m_pbes, m_process_parameters);
327 mCRL2log(log::debug) <<
"Original read/write matrix:" << std::endl;
328 mCRL2log(log::debug) << symbolic::print_read_write_patterns(m_summand_patterns);
330 symbolic::adjust_read_write_patterns(m_summand_patterns, m_options);
332 m_variable_order = symbolic::compute_variable_order(m_options.variable_order, m_process_parameters.size(), m_summand_patterns,
true);
333 assert(m_variable_order[0] == 0);
334 mCRL2log(log::debug) <<
"variable order = " << core::detail::print_list(m_variable_order) << std::endl;
335 m_summand_patterns = symbolic::reorder_read_write_patterns(m_summand_patterns, m_variable_order);
337 m_process_parameters = symbolic::permute_copy(m_process_parameters, m_variable_order);
338 m_initial_state = symbolic::permute_copy(m_initial_state, m_variable_order);
339 mCRL2log(log::debug) <<
"process parameters = " << core::detail::print_list(m_process_parameters) << std::endl;
341 std::vector<std::set<std::size_t>> groups = symbolic::compute_summand_groups(m_options.summand_groups, m_summand_patterns);
342 for (
const auto& group: groups)
344 mCRL2log(log::debug) <<
"group " << core::detail::print_set(group) << std::endl;
346 m_group_patterns = symbolic::compute_summand_group_patterns(m_summand_patterns, groups);
347 for (std::size_t j = 0; j < m_group_patterns.size(); j++)
349 m_summand_groups.emplace_back(m_pbes, m_process_parameters, m_propvar_map, groups[j], m_group_patterns[j], m_summand_patterns, m_variable_order);
352 for (std::size_t i = 0; i < m_summand_groups.size(); i++)
354 mCRL2log(log::debug) <<
"=== summand group " << i <<
" ===\n" << m_summand_groups[i] << std::endl;
357 for (
const data::variable& param: m_process_parameters)
359 m_data_index.emplace_back(param.sort());
362 mCRL2log(log::debug) <<
"Final read/write matrix:" << std::endl;
363 mCRL2log(log::debug) << symbolic::print_read_write_patterns(m_summand_patterns);
366 virtual ~pbesreach_algorithm() =
default;
370 return symbolic::state2ldd(m_initial_state, m_data_index);
380 ldd relprod_impl(
const ldd& U,
const pbes_summand_group& group, std::size_t i)
382 if (m_options.no_relprod)
384 ldd z = symbolic::alternative_relprod(U, group);
385 mCRL2log(log::trace) <<
"relprod(" << i <<
", todo) = " << print_states(m_data_index, z) << std::endl;
390 ldd z = relprod(U, group.L, group.Ir);
391 mCRL2log(log::trace) <<
"relprod(" << i <<
", todo) = " << print_states(m_data_index, z) << std::endl;
398 std::tuple<ldd, ldd, ldd> step(
const ldd& visited,
const ldd& todo,
bool learn_transitions =
true,
bool detect_deadlocks =
false)
400 using namespace sylvan::ldds;
401 auto& R = m_summand_groups;
403 ldd todo1 = empty_set();
404 ldd potential_deadlocks = detect_deadlocks ? todo : empty_set();
406 if (!m_options.saturation)
409 todo1 = m_options.chaining ? todo : empty_set();
411 for (std::size_t i = 0; i < R.size(); i++)
413 if (learn_transitions)
415 ldd proj = project(m_options.chaining ? todo1 : todo, R[i].Ip);
416 learn_successors(i, R[i], m_options.cached ? minus(proj, R[i].Ldomain) : proj);
418 mCRL2log(log::trace) <<
"L =\n" << print_relation(m_data_index, R[i].L, R[i].read, R[i].write) << std::endl;
421 todo1 = union_(todo1, relprod_impl(m_options.chaining ? todo1 : todo, R[i], i));
423 if (detect_deadlocks)
425 potential_deadlocks = minus(potential_deadlocks, relprev(todo1, R[i].L, R[i].Ir, potential_deadlocks));
435 for (std::size_t i = 0; i < R.size(); i++)
437 if (learn_transitions)
439 ldd proj = project(todo1, R[i].Ip);
440 learn_successors(i, R[i], m_options.cached ? minus(proj, R[i].Ldomain) : proj);
442 mCRL2log(log::trace) <<
"L =\n" << print_relation(m_data_index, R[i].L, R[i].read, R[i].write) << std::endl;
449 todo1 = union_(todo1, relprod_impl(todo1, R[i], i));
451 while (todo1 != todo1_old);
453 if (detect_deadlocks)
455 potential_deadlocks = minus(potential_deadlocks, relprev(todo1, R[i].L, R[i].Ir, potential_deadlocks));
459 if (m_options.chaining)
464 for (std::size_t j = 0; j <= i; j++)
466 todo1 = union_(todo1, relprod_impl(todo1, R[j], j));
469 while (todo1 != todo1_old);
475 return std::make_tuple(union_(visited, todo), minus(todo1, visited), potential_deadlocks);
479 void run(
bool report_states =
false)
481 using namespace sylvan::ldds;
482 auto& R = m_summand_groups;
483 std::size_t iteration_count = 0;
485 mCRL2log(log::trace) <<
"initial state = " << core::detail::print_list(m_initial_state) << std::endl;
488 m_initial_vertex = initial_state();
489 m_visited = empty_set();
490 m_todo = m_initial_vertex;
491 m_deadlocks = empty_set();
493 while (m_todo != empty_set() && !solution_found() && (m_options.max_iterations == 0 || iteration_count < m_options.max_iterations))
495 stopwatch loop_start;
497 mCRL2log(log::trace) <<
"--- iteration " << iteration_count <<
" ---" << std::endl;
498 mCRL2log(log::trace) <<
"todo = " << print_states(m_data_index, m_todo) << std::endl;
499 ldd deadlocks = empty_set();
501 std::tie(m_visited, m_todo, deadlocks) = step(m_visited, m_todo,
true, m_options.detect_deadlocks);
503 if (m_options.detect_deadlocks)
505 m_deadlocks = union_(m_deadlocks, deadlocks);
508 mCRL2log(log::verbose) <<
"generated " << std::setw(12) << print_size(union_(m_visited, m_todo)) <<
" BES equations after "
509 << std::setw(3) << iteration_count <<
" iterations (time = " << std::setprecision(2)
510 << std::fixed << loop_start.seconds() <<
"s)" << std::endl;
512 if (m_options.detect_deadlocks)
514 mCRL2log(log::verbose) <<
"found " << std::setw(12) << print_size(m_deadlocks) <<
" deadlocks" << std::endl;
518 sylvan::sylvan_stats_report(stderr);
523 std::cout <<
"number of BES equations = " << print_size(m_visited) <<
" (time = " << std::setprecision(2) << std::fixed << timer.seconds() <<
"s)" << std::endl;
527 mCRL2log(log::verbose) <<
"number of BES equations = " << print_size(m_visited) <<
" (time = " << std::setprecision(2) << std::fixed << timer.seconds() <<
"s)" << std::endl;
530 mCRL2log(log::verbose) <<
"used variable order = " << core::detail::print_list(m_variable_order) << std::endl;
532 double total_time = 0.0;
533 for (std::size_t i = 0; i < R.size(); i++)
535 mCRL2log(log::verbose) <<
"group " << std::setw(4) << i <<
" contains " << std::setw(7) << print_size(R[i].L) <<
" transitions (learn time = "
536 << std::setw(5) << std::setprecision(2) << std::fixed << R[i].learn_time <<
"s with " << std::setw(9) << R[i].learn_calls
537 <<
" calls, cached " << print_size(R[i].Ldomain) <<
" values"
540 total_time += R[i].learn_time;
542 mCRL2log(log::verbose) <<
"learning transitions took " << total_time <<
"s" << std::endl;
545 for (
const auto& param : m_process_parameters)
547 auto& table = m_data_index[i];
549 mCRL2log(log::verbose) <<
"Parameter " << i <<
" (" << param <<
")" <<
" has " << table.size() <<
" values"<< std::endl;
550 for (
const auto& data : table)
552 mCRL2log(log::debug) << table.index(data) <<
": " << data << std::endl;
560 virtual void on_end_while_loop()
564 virtual bool solution_found()
const
570 virtual sylvan::ldds::ldd V()
const
577 virtual sylvan::ldds::ldd I()
const
583 virtual symbolic_solution_t partial_solution()
const
585 return symbolic_solution_t(m_options.compute_strategy);
588 std::vector<symbolic::summand_group> summand_groups()
const
590 std::vector<symbolic::summand_group> result;
592 for (
const auto& group : m_summand_groups)
594 result.push_back(group);
600 const srf_pbes& pbes()
const
605 data::rewriter rewriter()
const
610 const data::variable_list& process_parameters()
const
612 return m_process_parameters;
615 const std::unordered_map<core::identifier_string, data::data_expression>& propvar_map()
const
617 return m_propvar_map;
620 const std::vector<symbolic::data_expression_index>& data_index()
const
625 std::vector<symbolic::data_expression_index>& data_index()
630 const std::vector<boost::dynamic_bitset<>>& read_write_patterns()
const
632 return m_summand_patterns;
635 const std::vector<boost::dynamic_bitset<>>& read_write_group_patterns()
const
637 return m_group_patterns;