mCRL2
Loading...
Searching...
No Matches
lpsreach.h
Go to the documentation of this file.
1// Author(s): Wieger Wesselink
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//
11
12#ifndef MCRL2_LPS_LPSREACH_H
13#define MCRL2_LPS_LPSREACH_H
14
15#ifdef MCRL2_ENABLE_SYLVAN
16
19#include "mcrl2/lps/io.h"
31
32#include <sylvan_ldd.hpp>
33
34#include <chrono>
35#include <iomanip>
36#include <boost/dynamic_bitset.hpp>
37
38namespace mcrl2::lps {
39
40class lpsreach_algorithm
41{
42 using ldd = sylvan::ldds::ldd;
43 using enumerator_element = data::enumerator_list_element_with_substitution<>;
44
45 template <typename Context, bool ActionLabel>
46 friend void symbolic::learn_successors_callback(WorkerP*, Task*, std::uint32_t* v, std::size_t n, void* context);
47
48 protected:
49 const symbolic::symbolic_reachability_options& m_options;
50 data::rewriter m_rewr;
51 data::mutable_indexed_substitution<> m_sigma;
52 data::enumerator_identifier_generator m_id_generator;
53 data::enumerator_algorithm<> m_enumerator;
54 std::vector<boost::dynamic_bitset<>> m_summand_patterns;
55 std::vector<boost::dynamic_bitset<>> m_group_patterns;
56 std::vector<std::size_t> m_variable_order;
57 symbolic_lts m_lts;
58
60 template<typename Rewriter, typename Substitution>
61 lps::multi_action rewrite_action(const lps::multi_action& a, const Rewriter& rewr, const Substitution& sigma)
62 {
63 const process::action_list& actions = a.actions();
64 const data::data_expression& time = a.time();
65 return
66 lps::multi_action(
67 process::action_list(
68 actions.begin(),
69 actions.end(),
70 [&](const process::action& a)
71 {
72 const auto& args = a.arguments();
73 return process::action(a.label(), data::data_expression_list(args.begin(), args.end(), [&](const data::data_expression& x) { return rewr(x, sigma); }));
74 }
75 ),
76 a.has_time() ? rewr(time, sigma) : time
77 );
78 }
79
80 // R.L := R.L U {(x,y) in R | x in X}
81 void learn_successors(std::size_t i, symbolic::summand_group& R, const ldd& X)
82 {
83 mCRL2log(log::trace) << "learn successors of summand group " << i << " for X = " << print_states(m_lts.data_index, X, R.read) << std::endl;
84
85 using namespace sylvan::ldds;
86 std::pair<lpsreach_algorithm&, symbolic::summand_group&> context{*this, R};
87 sat_all_nopar(X, symbolic::learn_successors_callback<std::pair<lpsreach_algorithm&, lps_summand_group&>, true>, &context);
88 }
89
90 template <typename Specification>
91 Specification preprocess(const Specification& lpsspec)
92 {
93 Specification result = lpsspec;
96 resolve_summand_variable_name_clashes(result); // N.B. This is a required preprocessing step.
97 if (m_options.one_point_rule_rewrite)
98 {
100 }
101 if (m_options.replace_constants_by_variables)
102 {
103 replace_constants_by_variables(result, m_rewr, m_sigma);
104 }
105
106 return result;
107 }
108
109 std::string print_size(const sylvan::ldds::ldd& L)
110 {
111 return symbolic::print_size(L, m_options.print_exact, m_options.print_nodesize);
112 }
113
114 public:
115 lpsreach_algorithm(const lps::specification& lpsspec, const symbolic::symbolic_reachability_options& options_)
116 : m_options(options_),
117 m_rewr(symbolic::construct_rewriter(lpsspec.data(), m_options.rewrite_strategy, lps::find_function_symbols(lpsspec), m_options.remove_unused_rewrite_rules)),
118 m_enumerator(m_rewr, lpsspec.data(), m_rewr, m_id_generator, false)
119 {
121
122 lps::specification lpsspec_ = preprocess(lpsspec);
123 m_lts.process_parameters = lpsspec_.process().process_parameters();
124
125 // Rewrite the initial expressions to normal form,
126 std::vector<data::data_expression> initial_values;
127 for (const data::data_expression& expression : lpsspec_.initial_process().expressions())
128 {
129 initial_values.push_back(m_rewr(expression));
130 }
131
132 data::data_expression_list initial_state(initial_values.begin(), initial_values.end());
133
134 m_summand_patterns = compute_read_write_patterns(lpsspec_);
135 symbolic::adjust_read_write_patterns(m_summand_patterns, m_options);
136
137 m_variable_order = symbolic::compute_variable_order(m_options.variable_order, m_lts.process_parameters.size(), m_summand_patterns);
138 mCRL2log(log::debug) << "variable order = " << core::detail::print_list(m_variable_order) << std::endl;
139 m_summand_patterns = symbolic::reorder_read_write_patterns(m_summand_patterns, m_variable_order);
141
142 m_lts.process_parameters = symbolic::permute_copy(m_lts.process_parameters, m_variable_order);
143 for (const data::variable& param : m_lts.process_parameters)
144 {
145 m_lts.data_index.push_back(symbolic::data_expression_index(param.sort()));
146 }
147
148 m_lts.initial_state = symbolic::state2ldd(symbolic::permute_copy(initial_state, m_variable_order), m_lts.data_index);
149 mCRL2log(log::debug) << "process parameters = " << core::detail::print_list(m_lts.process_parameters) << std::endl;
150
151 std::vector<std::set<std::size_t>> groups = symbolic::compute_summand_groups(m_options.summand_groups, m_summand_patterns);
152 for (const auto& group: groups)
153 {
154 mCRL2log(log::debug) << "group " << core::detail::print_set(group) << std::endl;
155 }
156 m_group_patterns = symbolic::compute_summand_group_patterns(m_summand_patterns, groups);
157 for (std::size_t j = 0; j < m_group_patterns.size(); j++)
158 {
159 m_lts.summand_groups.emplace_back(lpsspec_, m_lts.process_parameters, groups[j], m_group_patterns[j], m_summand_patterns, m_variable_order);
160 }
161
162 for (std::size_t i = 0; i < m_lts.summand_groups.size(); i++)
163 {
164 mCRL2log(log::debug) << "=== summand group " << i << " ===\n" << m_lts.summand_groups[i] << std::endl;
165 }
166 }
167
169 ldd relprod_impl(const ldd& U, const lps_summand_group& group, std::size_t i)
170 {
171 if (m_options.no_relprod)
172 {
173 ldd z = symbolic::alternative_relprod(U, group);
174 mCRL2log(log::trace) << "relprod(" << i << ", todo) = " << print_states(m_lts.data_index, z) << std::endl;
175 return z;
176 }
177 else
178 {
179 ldd z = relprod(U, group.L, group.Ir);
180 mCRL2log(log::trace) << "relprod(" << i << ", todo) = " << print_states(m_lts.data_index, z) << std::endl;
181 return z;
182 }
183 }
184
187 std::tuple<ldd, ldd, ldd> step(const ldd& visited, const ldd& todo, bool learn_transitions = true, bool detect_deadlocks = false)
188 {
189 using namespace sylvan::ldds;
190 auto& R = m_lts.summand_groups;
191
192 ldd todo1 = empty_set();
193 ldd potential_deadlocks = detect_deadlocks ? todo : empty_set();
194
195 if (!m_options.saturation)
196 {
197 // regular and chaining.
198 todo1 = m_options.chaining ? todo : empty_set();
199
200 for (std::size_t i = 0; i < R.size(); i++)
201 {
202 if (learn_transitions)
203 {
204 ldd proj = project(m_options.chaining ? todo1 : todo, R[i].Ip);
205 learn_successors(i, R[i], m_options.cached ? minus(proj, R[i].Ldomain) : proj);
206
207 mCRL2log(log::trace) << "L =\n" << print_relation(m_lts.data_index, R[i].L, R[i].read, R[i].write) << std::endl;
208 }
209
210 todo1 = union_(todo1, relprod_impl(m_options.chaining ? todo1 : todo, R[i], i));
211
212 if (detect_deadlocks)
213 {
214 potential_deadlocks = minus(potential_deadlocks, relprev(todo1, R[i].L, R[i].Ir, potential_deadlocks));
215 }
216 }
217 }
218 else
219 {
220 // saturation and chaining
221 todo1 = todo;
222 ldd todo1_old; // the old todo set.
223
224 for (std::size_t i = 0; i < R.size(); i++)
225 {
226 if (learn_transitions)
227 {
228 ldd proj = project(todo1, R[i].Ip);
229 learn_successors(i, R[i], m_options.cached ? minus(proj, R[i].Ldomain) : proj);
230
231 mCRL2log(log::trace) << "L =\n" << print_relation(m_lts.data_index, R[i].L, R[i].read, R[i].write) << std::endl;
232 }
233
234 // Apply one transition relation repeatedly.
235 do
236 {
237 todo1_old = todo1;
238 todo1 = union_(todo1, relprod_impl(todo1, R[i], i));
239 }
240 while (todo1 != todo1_old);
241
242 if (detect_deadlocks)
243 {
244 potential_deadlocks = minus(potential_deadlocks, relprev(todo1, R[i].L, R[i].Ir, potential_deadlocks));
245 }
246
247 // Apply all previously learned transition relations repeatedly.
248 if (m_options.chaining)
249 {
250 do
251 {
252 todo1_old = todo1;
253 for (std::size_t j = 0; j <= i; j++)
254 {
255 todo1 = union_(todo1, relprod_impl(todo1, R[j], j));
256 }
257 }
258 while (todo1 != todo1_old);
259 }
260 }
261 }
262
263 // after all transition groups are applied the remaining potential deadlocks are actual deadlocks.
264 return std::make_tuple(union_(visited, todo), minus(todo1, visited), potential_deadlocks);
265 }
266
267 ldd run()
268 {
269 using namespace sylvan::ldds;
270 auto& R = m_lts.summand_groups;
271 std::size_t iteration_count = 0;
272
273 mCRL2log(log::trace) << "initial state = " << core::detail::print_list(m_lts.initial_state) << std::endl;
274
275 auto start = std::chrono::steady_clock::now();
276 ldd x = m_lts.initial_state;
277 std::chrono::duration<double> elapsed_seconds = std::chrono::steady_clock::now() - start;
278 ldd visited = empty_set();
279 ldd todo = x;
280 ldd deadlocks = empty_set();
281
282 while (todo != empty_set() && (m_options.max_iterations == 0 || iteration_count < m_options.max_iterations))
283 {
284 stopwatch loop_start;
285 iteration_count++;
286 mCRL2log(log::trace) << "--- iteration " << iteration_count << " ---" << std::endl;
287 mCRL2log(log::trace) << "todo = " << print_states(m_lts.data_index, todo) << std::endl;
288
289 std::tie(visited, todo, deadlocks) = step(visited, todo, true, m_options.detect_deadlocks);
290
291 mCRL2log(log::verbose) << "explored " << std::setw(12) << print_size(union_(visited, todo)) << " states after "
292 << std::setw(3) << iteration_count << " iterations (time = " << std::setprecision(2)
293 << std::fixed << loop_start.seconds() << "s)" << std::endl;
294 if (m_options.detect_deadlocks)
295 {
296 mCRL2log(log::verbose) << "found " << std::setw(12) << print_size(deadlocks) << " deadlocks" << std::endl;
297 }
298
299 sylvan::sylvan_stats_report(stderr);
300 }
301
302 elapsed_seconds = std::chrono::steady_clock::now() - start;
303 std::cout << "number of states = " << print_size(visited) << " (time = " << std::setprecision(2) << std::fixed << elapsed_seconds.count() << "s)" << std::endl;
304 mCRL2log(log::verbose) << "used variable order = " << core::detail::print_list(m_variable_order) << std::endl;
305
306 double total_time = 0.0;
307 for (std::size_t i = 0; i < R.size(); i++)
308 {
309 mCRL2log(log::verbose) << "group " << std::setw(4) << i << " contains " << std::setw(10) << print_size(R[i].L) << " transitions (learn time = "
310 << std::setw(5) << std::setprecision(2) << std::fixed << R[i].learn_time << "s with " << std::setw(9) << R[i].learn_calls
311 << " calls, cached " << print_size(R[i].Ldomain) << " values" << ")" << std::endl;
312
313 total_time += R[i].learn_time;
314 }
315 mCRL2log(log::verbose) << "learning transitions took " << total_time << "s" << std::endl;
316
317 std::size_t i = 0;
318 for (const auto& param : m_lts.process_parameters)
319 {
320 auto& table = m_lts.data_index[i];
321
322 mCRL2log(log::verbose) << "Parameter " << i << " (" << param << ")" << " has " << table.size() << " values."<< std::endl;
323 for (const auto& data : table)
324 {
325 mCRL2log(log::debug) << table.index(data) << ": " << data << std::endl;
326 }
327
328 ++i;
329 }
330
331 mCRL2log(log::verbose) << "There are " << m_lts.action_index.size() << " action labels" << std::endl;
332 for (const auto& action : m_lts.action_index)
333 {
334 mCRL2log(log::debug) << m_lts.action_index.index(action) << ": " << action << std::endl;
335 }
336
337 m_lts.states = visited;
338 return visited;
339 }
340
341 const std::vector<boost::dynamic_bitset<>>& read_write_patterns() const
342 {
343 return m_summand_patterns;
344 }
345
346 const std::vector<boost::dynamic_bitset<>>& read_write_group_patterns() const
347 {
348 return m_group_patterns;
349 }
350
351 std::vector<symbolic::data_expression_index>& data_index()
352 {
353 return m_lts.data_index;
354 }
355
356 utilities::indexed_set<lps::multi_action>& action_index()
357 {
358 return m_lts.action_index;
359 }
360
361 const symbolic_lts& get_symbolic_lts()
362 {
363 return m_lts;
364 }
365};
366
367} // namespace mcrl2::lps
368
369#endif // MCRL2_ENABLE_SYLVAN
370
371#endif // MCRL2_LPS_LPSREACH_H
size_type size() const
Returns the size of the term_list.
Definition aterm_list.h:256
Implements a simple stopwatch that starts on construction.
Definition stopwatch.h:17
double seconds()
Definition stopwatch.h:37
static RewriterCompilingJitty::substitution_type & sigma(RewriterCompilingJitty *this_rewriter)
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:391
add your file description here.
IO routines for linear process specifications.
add your file description here.
add your file description here.
std::string print_list(const Container &v, const std::string &message="", bool print_index=false, bool boundary_spaces=true)
Creates a string representation of a container.
std::string print_set(const Container &v, const std::string &message="", bool print_index=false, bool boundary_spaces=true)
Creates a string representation of a container.
function_symbol union_(const sort_expression &s, const sort_expression &s0, const sort_expression &s1)
Definition bag1.h:426
function_symbol minus(const sort_expression &s0, const sort_expression &s1)
Definition int1.h:1039
rewrite_strategy
The strategy of the rewriter.
atermpp::term_list< data_expression > data_expression_list
\brief list of data_expressions
@ verbose
Definition logger.h:37
data::mutable_map_substitution instantiate_global_variables(Specification &lpsspec)
Eliminates the global variables of an LPS, by substituting a constant value for them....
The main namespace for the LPS library.
void find_function_symbols(const T &x, OutputIterator o)
Definition find.h:138
void replace_constants_by_variables(T &x, const data::rewriter &r, data::mutable_indexed_substitution<> &sigma, typename std::enable_if<!std::is_base_of< atermpp::aterm, T >::value >::type *=nullptr)
Replace each constant data application c in x by a fresh variable v, and add extend the substitution ...
void order_summand_variables(Specification &lpsspec)
Order summand variables to make enumeration over these variables more efficient.
void resolve_summand_variable_name_clashes(Specification &spec)
Renames summand variables such that there are no name clashes between summand variables and process p...
void one_point_rule_rewrite(T &x, typename std::enable_if<!std::is_base_of< atermpp::aterm, T >::value >::type *=nullptr)
Applies the one point rule rewriter to all embedded data expressions in an object x.
const data::data_expression_list & param(const pbes_expression &t)
Returns the parameters of a propositional variable instantiation.
std::vector< std::set< std::size_t > > compute_summand_groups(const std::string &text, const std::vector< boost::dynamic_bitset<> > &patterns)
Definition ordering.h:139
std::string print_read_write_patterns(const std::vector< boost::dynamic_bitset<> > &patterns)
Definition ordering.h:425
std::vector< boost::dynamic_bitset<> > reorder_read_write_patterns(const std::vector< boost::dynamic_bitset<> > &patterns, const std::vector< std::size_t > &variable_order)
Definition ordering.h:395
std::vector< std::size_t > compute_variable_order(const std::string &text, std::size_t number_of_variables, const std::vector< boost::dynamic_bitset<> > &summand_groups, bool exclude_first_variable=false)
Definition ordering.h:374
std::vector< boost::dynamic_bitset<> > compute_summand_group_patterns(const std::vector< boost::dynamic_bitset<> > &patterns, const std::vector< std::set< std::size_t > > groups)
Definition ordering.h:531
void adjust_read_write_patterns(std::vector< boost::dynamic_bitset<> > &patterns, const Options &options)
Definition ordering.h:499
std::vector< T > as_vector(const std::set< T > &x)
add your file description here.
add your file description here.
The class specification.