13#ifndef MCRL2_DATA_PARVALUES_H
14#define MCRL2_DATA_PARVALUES_H
16#include "mcrl2/data/enumerator.h"
17#include "mcrl2/data/optimized_boolean_operators.h"
18#include "mcrl2/utilities/views.h"
20#include <unordered_set>
21#include <unordered_map>
27template <
class VariableContainer>
35 result = result +
",";
37 result = result + pp(v) +
":" + pp(v.sort());
62 return std::tie(eqn, var) < std::tie(other.eqn, other.var);
74 if (x
.eqn == core::identifier_string(
""))
76 return out << x.var.name() <<
": " << x.var.sort();
78 return out <<
"(" << x.eqn <<
", " << x.var.name() <<
": " << x.var.sort() <<
")";
107 return std::tie(qvars, cond, updates) < std::tie(other.qvars, other.cond, other.updates);
133 template <
typename VariableContainer,
typename UpdateContainer>
136 variable_vector qvars(vars.begin(), vars.end());
137 std::vector<std::pair<parameter, data_expression>> updates(up.begin(), up.end());
138 m_edges.emplace(qvars, cond, updates);
143 assert(m_nodes.contains(v));
144 return m_nodes.at(v);
151 return m_nodes.contains(v) &&
152 (m_nodes.at(v).stable.contains(e) ||
153 m_nodes.at(v).todo.contains(e) ||
154 m_nodes.at(v).newly_found.contains(e));
160 assert(!m_nodes.contains(v));
161 m_nodes[v].newly_found.insert(e);
172 m_nodes.at(v).newly_found.insert(e);
179 auto find_it = m_nodes.find(v);
180 if (find_it == m_nodes.end())
185 auto todo_available = [](
const auto& e){
return !e.second.todo.empty(); };
186 return std::any_of(++find_it, m_nodes.end(), todo_available);
192 auto is_stable = [](
const auto& e){
return e.second.newly_found.empty(); };
193 return std::ranges::all_of(m_nodes, is_stable);
200 for (
auto& [par, values]: m_nodes)
202 values.stable.merge(values.todo);
203 assert(values.todo.empty());
204 std::swap(values.todo,values.newly_found);
211 long double size = 1;
212 for (
const auto& [_, elms]: m_nodes)
214 const auto& [a,b,c] = elms;
215 size = size * (a.size() + b.size() + c.size());
223 std::stringstream output;
224 for(
const auto& elm: m_nodes)
226 output <<
"Parameter " << elm.first <<
" (" << (elm.second.stable.size()+
227 elm.second.todo.size()+
228 elm.second.newly_found.size()) <<
")" << (!joined?
"\n Stable elements: {":
": {");
230 for(
const data::data_expression& e: elm.second.stable)
232 output << (first?
" ":
", ") << e;
237 output <<
" }\n Previous round: {";
240 for(
const data::data_expression& e: elm.second.todo)
242 output << (first?
" ":
", ") << e;
247 output <<
" }\n Current round: {";
250 for(
const data::data_expression& e: elm.second.newly_found)
252 output << (first?
" ":
", ") << e;
257 output <<
"Upperbound on the statespace is " << product_size() <<
"\n---------------------------------------------------------------------\n";
267template<
typename DataRewriter>
285 const variable_vector& qvars,
288 const bool used_new_value)
291 std::set<variable> parameters_in_condition = find_free_variables(rewritten_condition);
292 for (
const variable& qv: qvars)
294 parameters_in_condition.erase(qv);
299 if (parameters_in_condition.empty())
307 const std::set<variable> update_fv = find_free_variables(update_expr);
308 const std::set<variable> cond_fv = find_free_variables(rewritten_condition);
310 auto is_in_update = [&](
const variable& v){
return update_fv.contains(v); };
311 auto is_in_cond_or_update = [&](
const variable& v){
return update_fv.contains(v) || cond_fv.contains(v); };
312 auto is_not_in_update = [&](
const variable& v){
return !update_fv.contains(v) && cond_fv.contains(v); };
314 const variable_list qvars_in_update {qvars | std::views::filter(is_in_update)};
315 const variable_list qvars_in_cond_or_update{qvars | std::views::filter(is_in_cond_or_update)};
316 const variable_list qvars_not_in_update {qvars | std::views::filter(is_not_in_update)};
319 optimized_exists_no_empty_domain(quantified_condition, qvars_not_in_update, rewritten_condition);
321 mCRL2log(log::debug) <<
"Enumerate " << detail::ppsort(qvars_in_update) <<
" in " << quantified_condition <<
"\n";
323 const std::size_t enumeration_limit = m_qlimit;
324 enumerator_algorithm<> enumerator(m_rewriter, m_dataspec,
325 m_rewriter, m_generator,
false, enumeration_limit);
328 std::size_t count = enumerator.enumerate(
329 enumerator_element(qvars_in_cond_or_update, rewritten_condition),
331 [&](
const enumerator_element& p)
333 p.add_assignments(qvars_in_update, sigma, m_rewriter);
334 m_graph.insert(v, m_rewriter(update_expr, sigma));
335 assert(find_free_variables(m_rewriter(update_expr, sigma)).empty());
336 p.remove_assignments(qvars_in_update, sigma);
338 return update_fv.empty() && p.expression() == sort_bool::true_();
341 [&](
const data_expression& d) ->
bool
343 if (find_free_variables(d).empty() && d != sort_bool::true_() && d != sort_bool::false_())
345 mCRL2log(log::warning) <<
"The expression " << d
346 <<
" does not rewrite to true or false. It is assumed to be true.\n";
348 return d == sort_bool::false_();
350 [](
const data_expression& ) ->
bool {
return false; });
351 if (count >= enumeration_limit)
353 if (update_fv.empty())
355 m_graph.insert(v, update_expr);
359 throw mcrl2::runtime_error(
"Cannot enumerate " + detail::ppsort(qvars_in_cond_or_update) +
" in " + pp(rewritten_condition) +
". Using the flag --qlimit with a higher value may help. \n");
366 variable curr_var = *parameters_in_condition.begin();
369 for(
const data_expression& e: m_graph.at(curr_par).stable)
372 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr, used_new_value);
376 for(
const data_expression& e: m_graph.at(curr_par).todo)
379 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr,
true);
381 sigma[curr_var] = curr_var;
388 const variable_vector& qvars,
391 const bool used_new_value)
394 const data_expression update_expr_rewritten = m_rewriter(update_expr, sigma);
395 std::set<variable> relevant_parameters = find_free_variables(update_expr_rewritten);
396 bool update_is_closed = relevant_parameters.empty();
398 for (
const variable& qv: qvars)
400 relevant_parameters.erase(qv);
404 if (relevant_parameters.empty())
406 const data_expression rewritten_condition = m_rewriter(condition, sigma);
412 if (update_is_closed && m_graph.contains(v, update_expr_rewritten))
418 m_graph.insert(v, update_expr_rewritten);
422 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr_rewritten, used_new_value);
427 const variable curr_var = *relevant_parameters.begin();
429 if (
true || m_graph.has_available_next_values_from_the_previous_round(curr_par))
431 for(
const data_expression& e: m_graph.at(curr_par).stable)
434 propagate_values(sigma, v, qvars, condition, update_expr_rewritten, used_new_value);
438 for (
const data_expression& e: m_graph.at(curr_par).todo)
441 propagate_values(sigma, v, qvars, condition, update_expr_rewritten,
true);
443 sigma[curr_var] = curr_var;
454 const std::size_t qlimit,
455 const std::size_t maximal_number_of_rounds)
468 mCRL2log(log::debug) <<
"Start to explore parameter domains" << std::endl;
470 std::size_t round = 0;
471 while (round < m_maximal_number_of_rounds && !m_graph.stable())
473 mCRL2log(log::verbose) <<
"Parameter instantiation round " << round <<
" (estimated upperbound on the state space: " << m_graph.product_size() <<
").\n";
474 mCRL2log(log::debug) << m_graph.report(
true);
479 for (
const influence_graph::edge& edge: m_graph.edges())
481 mCRL2log(log::debug) <<
"Process edge (round " << round <<
") " << std::string(edge) <<
"\n==================================================================================\n";
483 for (
const auto& [par, expr]: edge.updates)
490 data::mutable_indexed_substitution<> sigma;
491 propagate_values(sigma, par, edge.qvars, edge.cond, expr,
false);
495 if (round == m_maximal_number_of_rounds)
497 mCRL2log(log::warning) <<
"The maximal number of rounds (" << round <<
") has been reached. "
498 <<
"Exploration is stopped prematurely. The domains and the upperbound of the state space can be too low.\n";
502 mCRL2log(log::verbose) << m_graph.report(
true);
503 mCRL2log(log::info) <<
"This process has at most " << m_graph.product_size() <<
" states.\n";
aterm_string(const aterm_string &t) noexcept=default
bool insert(const parameter &v, const data::data_expression &e)
const std::set< edge > & edges() const
const elements_per_domain & at(const parameter &v) const
bool contains(const parameter &v, const data::data_expression &e) const
std::string report(bool joined=true)
void new_parameter(const parameter &v, const data::data_expression &e)
std::map< parameter, elements_per_domain > m_nodes
long double product_size()
bool has_available_next_values_from_the_previous_round(const parameter &v)
void add_edge(const VariableContainer &vars, data_expression cond, const UpdateContainer &up)
Algorithm class that can be used to apply the lps_explore_domains algorithm.
parvalues_algorithm(DataRewriter &r, const data_specification &dataspec, const std::size_t qlimit, const std::size_t maximal_number_of_rounds)
Constructor for lps_explore_domains algorithm.
DataRewriter m_rewriter
Rewriter.
void propagate_values_qvars(mutable_indexed_substitution<> &sigma, const parameter &v, const variable_vector &qvars, const data_expression &condition, const data_expression &update_expr, const bool used_new_value)
const std::size_t m_qlimit
const std::size_t m_maximal_number_of_rounds
data::enumerator_identifier_generator & m_generator
const data_specification m_dataspec
void run()
Apply the algorithm to the specification passed in the constructor.
detail::influence_graph m_graph
void propagate_values(mutable_indexed_substitution<> &sigma, const parameter &v, const variable_vector &qvars, const data_expression &condition, const data_expression &update_expr, const bool used_new_value)
variable(const variable &) noexcept=default
Move semantics.
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
std::string ppsort(const VariableContainer &s)
std::ostream & operator<<(std::ostream &out, const parameter &x)
Namespace for system defined sort bool_.
const function_symbol & false_()
Constructor for function symbol false.
const function_symbol & true_()
Constructor for function symbol true.
std::unordered_set< data::data_expression > stable
Values which have already been propagated.
std::unordered_set< data::data_expression > newly_found
Values found in the current iterations (are not considered until the next iteration)
std::unordered_set< data::data_expression > todo
Values which are currently being propagated (possibly combined with old values)
data_expression cond
Condition under which the updates happen.
bool operator<(const edge &other) const
variable_vector qvars
Variables bound in a quantifier, sum or other operator that influce the update (and possibly conditio...
bool operator<(const parameter &other) const
parameter(const variable &v)
parameter(const core::identifier_string &e, const variable &v)
const core::identifier_string eqn
bool operator==(const parameter &other) const