mCRL2
Loading...
Searching...
No Matches
parvalues.h
Go to the documentation of this file.
1// Author(s): Jan Friso Groote, Thomas Neele
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 parvalues.h
10/// \brief Explore statically which parameter values may be reached
11
12
13#ifndef MCRL2_DATA_PARVALUES_H
14#define MCRL2_DATA_PARVALUES_H
15
16#include "mcrl2/data/enumerator.h"
17#include "mcrl2/data/optimized_boolean_operators.h"
18#include "mcrl2/utilities/views.h"
19
20#include <unordered_set>
21#include <unordered_map>
22#include <functional>
23
24namespace mcrl2::data::detail
25{
26
27template <class VariableContainer>
29{
30 std::string result;
31 for(const data::variable& v: s)
32 {
33 if (!result.empty())
34 {
35 result = result + ",";
36 }
37 result = result + pp(v) + ":" + pp(v.sort());
38 }
39 return result;
40}
41
43{
44 const core::identifier_string eqn;
46
47 parameter(const core::identifier_string& e, const variable& v)
48 : eqn(e), var(v)
49 {}
50
52 : eqn(""), var(v)
53 {}
54
55 bool operator==(const parameter& other) const
56 {
57 return eqn == other.eqn && var == other.var;
58 }
59
60 bool operator<(const parameter& other) const
61 {
62 return std::tie(eqn, var) < std::tie(other.eqn, other.var);
63 }
64
66 {
67 return eqn == core::identifier_string("") ? pp(var) : ("(" + pp(eqn) + ", " + pp(var) + ")");
68 }
69};
70
71inline
73{
74 if (x.eqn == core::identifier_string(""))
75 {
76 return out << x.var.name() << ": " << x.var.sort();
77 }
78 return out << "(" << x.eqn << ", " << x.var.name() << ": " << x.var.sort() << ")";
79}
80
82{
83 /// \brief Values which have already been propagated
85 /// \brief Values which are currently being propagated (possibly combined with old values)
87 /// \brief Values found in the current iterations (are not considered until the next iteration)
89};
90
92{
93
94public:
95
96 struct edge
97 {
98 /// \brief Variables bound in a quantifier, sum or other operator that influce the update (and possibly condition)
100 /// \brief Condition under which the updates happen
102 /// \brief Update expressions for parameters
104
105 bool operator<(const edge& other) const
106 {
107 return std::tie(qvars, cond, updates) < std::tie(other.qvars, other.cond, other.updates);
108 }
109
111 {
112 std::string result = "[" + ppsort(qvars) + "] " + pp(cond) + " -> ";
113 for (const auto& [from, to]: updates)
114 {
115 result += std::string(from) + " := " + pp(to) + "; ";
116 }
117 return result;
118 }
119 };
120
121private:
122
125
126public:
127
128 const std::set<edge>& edges() const
129 {
130 return m_edges;
131 }
132
133 template <typename VariableContainer, typename UpdateContainer>
134 void add_edge(const VariableContainer& vars, data_expression cond, const UpdateContainer& up)
135 {
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);
139 }
140
141 const elements_per_domain& at(const parameter& v) const
142 {
143 assert(m_nodes.contains(v));
144 return m_nodes.at(v);
145 }
146
147 // Insert a new element e in the domain of variable v.
148 // Report true if the element was not yet present.
149 bool contains(const parameter& v, const data::data_expression& e) const
150 {
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));
155 }
156
157 // Make a new parameter v with initial value e.
158 void new_parameter(const parameter& v, const data::data_expression& e)
159 {
160 assert(!m_nodes.contains(v));
161 m_nodes[v].newly_found.insert(e);
162 }
163
164 // Insert a new element e in the domain of variable v.
165 // Report true if the element was not yet present.
166 bool insert(const parameter& v, const data::data_expression& e)
167 {
168 if (contains(v,e))
169 {
170 return false;
171 }
172 m_nodes.at(v).newly_found.insert(e);
173 return true;
174 }
175
176 // Checks whether there are domains with values from the previous round related to parameters that follow v.
178 {
179 auto find_it = m_nodes.find(v);
180 if (find_it == m_nodes.end())
181 {
182 return false;
183 }
184
185 auto todo_available = [](const auto& e){ return !e.second.todo.empty(); };
186 return std::any_of(++find_it, m_nodes.end(), todo_available);
187 }
188
189 // If m_nodes[v].m_elements_added_in_current_round is not emptye for some variable v, the system is not stable.
190 bool stable() const
191 {
192 auto is_stable = [](const auto& e){ return e.second.newly_found.empty(); };
193 return std::ranges::all_of(m_nodes, is_stable);
194 }
195
196 // Add the elements of the previous round to the stable elements, move the current round to the previous round,
197 // and clear the current round for all parameters.
199 {
200 for (auto& [par, values]: m_nodes)
201 {
202 values.stable.merge(values.todo);
203 assert(values.todo.empty());
204 std::swap(values.todo,values.newly_found);
205 }
206 }
207
208 // Provide the product size of the domains for the parameters.
209 long double product_size()
210 {
211 long double size = 1;
212 for (const auto& [_, elms]: m_nodes)
213 {
214 const auto& [a,b,c] = elms;
215 size = size * (a.size() + b.size() + c.size());
216 }
217 return size;
218 }
219
220 // List the elements of the domains per variable. If joined=false they are split in stable and unstable elements;
221 std::string report(bool joined = true)
222 {
223 std::stringstream output;
224 for(const auto& elm: m_nodes)
225 {
226 output << "Parameter " << elm.first << " (" << (elm.second.stable.size()+
227 elm.second.todo.size()+
228 elm.second.newly_found.size()) << ")" << (!joined?"\n Stable elements: {":": {");
229 bool first = true;
230 for(const data::data_expression& e: elm.second.stable)
231 {
232 output << (first?" ":", ") << e;
233 first = false;
234 }
235 if (!joined)
236 {
237 output << " }\n Previous round: {";
238 first = true;
239 }
240 for(const data::data_expression& e: elm.second.todo)
241 {
242 output << (first?" ":", ") << e;
243 first = false;
244 }
245 if (!joined)
246 {
247 output << " }\n Current round: {";
248 first = true;
249 }
250 for(const data::data_expression& e: elm.second.newly_found)
251 {
252 output << (first?" ":", ") << e;
253 first = false;
254 }
255 output << " }\n";
256 }
257 output << "Upperbound on the statespace is " << product_size() << "\n---------------------------------------------------------------------\n";
258 return output.str();
259 }
260
261
262};
263
264/// \brief Algorithm class that can be used to apply the lps_explore_domains algorithm
265///
266/// All parameter values of the process parameters are enumerated.
267template<typename DataRewriter>
269{
271
272protected:
273 /// Rewriter
274 DataRewriter m_rewriter;
279
281
282
283 void propagate_values_qvars(mutable_indexed_substitution<>& sigma,
284 const parameter& v,
285 const variable_vector& qvars,
286 const data_expression& condition,
287 const data_expression& update_expr,
288 const bool used_new_value)
289 {
290 data_expression rewritten_condition = m_rewriter(condition, sigma);
291 std::set<variable> parameters_in_condition = find_free_variables(rewritten_condition);
292 for (const variable& qv: qvars)
293 {
294 parameters_in_condition.erase(qv);
295 }
296
297 // Base case: both condition and update do not contain parameters
298 // We need to enumerate the remaining sumvars/quantified variables
299 if (parameters_in_condition.empty())
300 {
301 if (!used_new_value || rewritten_condition == sort_bool::false_())
302 {
303 return;
304 }
305
306 // First split the sumvars in those that occur in rewritten_new_expression and those that do not.
307 const std::set<variable> update_fv = find_free_variables(update_expr);
308 const std::set<variable> cond_fv = find_free_variables(rewritten_condition);
309
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); };
313
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)};
317
318 data_expression quantified_condition;
319 optimized_exists_no_empty_domain(quantified_condition, qvars_not_in_update, rewritten_condition);
320
321 mCRL2log(log::debug) << "Enumerate " << detail::ppsort(qvars_in_update) << " in " << quantified_condition << "\n";
322
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);
326
327 /* Create a list to store solutions */
328 std::size_t count = enumerator.enumerate(
329 enumerator_element(qvars_in_cond_or_update, rewritten_condition),
330 sigma,
331 [&](const enumerator_element& p)
332 {
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);
337 // Stop if the condition is true and no variables occur in the new_expression.
338 return update_fv.empty() && p.expression() == sort_bool::true_();
339 },
340 // Ignore the following solution.
341 [&](const data_expression& d) -> bool
342 {
343 if (find_free_variables(d).empty() && d != sort_bool::true_() && d != sort_bool::false_())
344 {
345 mCRL2log(log::warning) << "The expression " << d
346 << " does not rewrite to true or false. It is assumed to be true.\n";
347 }
348 return d == sort_bool::false_();
349 },
350 [](const data_expression& /*d*/) -> bool { return false; });
351 if (count >= enumeration_limit)
352 {
353 if (update_fv.empty())
354 {
355 m_graph.insert(v, update_expr);
356 }
357 else
358 {
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");
360 }
361 }
362
363 return;
364 }
365
366 variable curr_var = *parameters_in_condition.begin();
367 const parameter curr_par{v.eqn, curr_var};
368 {
369 for(const data_expression& e: m_graph.at(curr_par).stable)
370 {
371 sigma[curr_var] = e;
372 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr, used_new_value);
373 }
374 }
375
376 for(const data_expression& e: m_graph.at(curr_par).todo)
377 {
378 sigma[curr_var] = e;
379 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr, true);
380 }
381 sigma[curr_var] = curr_var;
382
383 return;
384 }
385
386 void propagate_values(mutable_indexed_substitution<>& sigma,
387 const parameter& v,
388 const variable_vector& qvars,
389 const data_expression& condition,
390 const data_expression& update_expr,
391 const bool used_new_value)
392 {
393 // We consider the free variables in the new expression relevant as they determine the new value parameter v can get.
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();
397 // Split the relevant variables in those that are parameters and those that occur in the sum variables
398 for (const variable& qv: qvars)
399 {
400 relevant_parameters.erase(qv);
401 }
402
403 // Rewritten update is closed, we can return in some cases
404 if (relevant_parameters.empty())
405 {
406 const data_expression rewritten_condition = m_rewriter(condition, sigma);
407 if (rewritten_condition == sort_bool::false_())
408 {
409 return;
410 }
411
412 if (update_is_closed && m_graph.contains(v, update_expr_rewritten))
413 {
414 return;
415 }
416 if (update_is_closed && rewritten_condition == sort_bool::true_())
417 {
418 m_graph.insert(v, update_expr_rewritten);
419 return;
420 }
421
422 propagate_values_qvars(sigma, v, qvars, rewritten_condition, update_expr_rewritten, used_new_value);
423 return;
424 }
425
426 // Pick one relevant variable and iterate over its possible values
427 const variable curr_var = *relevant_parameters.begin();
428 const parameter curr_par{v.eqn, curr_var};
429 if (true || m_graph.has_available_next_values_from_the_previous_round(curr_par)) // Zoek uit of dit altijd moet....
430 {
431 for(const data_expression& e: m_graph.at(curr_par).stable)
432 {
433 sigma[curr_var] = e;
434 propagate_values(sigma, v, qvars, condition, update_expr_rewritten, used_new_value);
435 }
436 }
437
438 for (const data_expression& e: m_graph.at(curr_par).todo)
439 {
440 sigma[curr_var] = e;
441 propagate_values(sigma, v, qvars, condition, update_expr_rewritten, true);
442 }
443 sigma[curr_var] = curr_var;
444
445 return;
446 }
447
448public:
449 /// \brief Constructor for lps_explore_domains algorithm
450 /// \param spec Specification to which the algorithm should be applied
451 /// \param r a rewriter for data
452 parvalues_algorithm(DataRewriter& r,
453 const data_specification& dataspec,
454 const std::size_t qlimit,
455 const std::size_t maximal_number_of_rounds)
456
457 : m_rewriter(r),
461 m_generator(const_cast<data::enumerator_identifier_generator&>(r.identifier_generator()))
462 {}
463
464 /// \brief Apply the algorithm to the specification passed in the
465 /// constructor
466 void run()
467 {
468 mCRL2log(log::debug) << "Start to explore parameter domains" << std::endl;
469
470 std::size_t round = 0;
471 while (round < m_maximal_number_of_rounds && !m_graph.stable())
472 {
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);
475
476 round++;
477 m_graph.new_round();
478
479 for (const influence_graph::edge& edge: m_graph.edges())
480 {
481 mCRL2log(log::debug) << "Process edge (round " << round << ") " << std::string(edge) << "\n==================================================================================\n";
482
483 for (const auto& [par, expr]: edge.updates)
484 {
485 if (par.var == expr)
486 {
487 continue;
488 }
489
490 data::mutable_indexed_substitution<> sigma;
491 propagate_values(sigma, par, edge.qvars, edge.cond, expr, false);
492 }
493 }
494 }
495 if (round == m_maximal_number_of_rounds)
496 {
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";
499 }
500 m_graph.new_round();
501
502 mCRL2log(log::verbose) << m_graph.report(true);
503 mCRL2log(log::info) << "This process has at most " << m_graph.product_size() << " states.\n";
504 }
505};
506
507
508} // namespace mcrl2::data::detail
509
510#endif
aterm_string(const aterm_string &t) noexcept=default
bool insert(const parameter &v, const data::data_expression &e)
Definition parvalues.h:166
const std::set< edge > & edges() const
Definition parvalues.h:128
const elements_per_domain & at(const parameter &v) const
Definition parvalues.h:141
bool contains(const parameter &v, const data::data_expression &e) const
Definition parvalues.h:149
std::string report(bool joined=true)
Definition parvalues.h:221
void new_parameter(const parameter &v, const data::data_expression &e)
Definition parvalues.h:158
std::map< parameter, elements_per_domain > m_nodes
Definition parvalues.h:123
bool has_available_next_values_from_the_previous_round(const parameter &v)
Definition parvalues.h:177
void add_edge(const VariableContainer &vars, data_expression cond, const UpdateContainer &up)
Definition parvalues.h:134
Algorithm class that can be used to apply the lps_explore_domains algorithm.
Definition parvalues.h:269
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.
Definition parvalues.h:452
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)
Definition parvalues.h:283
data::enumerator_identifier_generator & m_generator
Definition parvalues.h:278
const data_specification m_dataspec
Definition parvalues.h:275
void run()
Apply the algorithm to the specification passed in the constructor.
Definition parvalues.h:466
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)
Definition parvalues.h:386
\brief A data variable
Definition variable.h:25
variable(const variable &) noexcept=default
Move semantics.
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:393
std::string ppsort(const VariableContainer &s)
Definition parvalues.h:28
std::ostream & operator<<(std::ostream &out, const parameter &x)
Definition parvalues.h:72
Namespace for system defined sort bool_.
Definition bool.h:29
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
std::unordered_set< data::data_expression > stable
Values which have already been propagated.
Definition parvalues.h:84
std::unordered_set< data::data_expression > newly_found
Values found in the current iterations (are not considered until the next iteration)
Definition parvalues.h:88
std::unordered_set< data::data_expression > todo
Values which are currently being propagated (possibly combined with old values)
Definition parvalues.h:86
data_expression cond
Condition under which the updates happen.
Definition parvalues.h:101
bool operator<(const edge &other) const
Definition parvalues.h:105
variable_vector qvars
Variables bound in a quantifier, sum or other operator that influce the update (and possibly conditio...
Definition parvalues.h:99
bool operator<(const parameter &other) const
Definition parvalues.h:60
parameter(const variable &v)
Definition parvalues.h:51
parameter(const core::identifier_string &e, const variable &v)
Definition parvalues.h:47
const core::identifier_string eqn
Definition parvalues.h:44
bool operator==(const parameter &other) const
Definition parvalues.h:55