mCRL2
Loading...
Searching...
No Matches
pbesinst_lazy.h
Go to the documentation of this file.
1// Author(s): Wieger Wesselink 2017-2019
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/pbesinst_lazy.h
10/// \brief A lazy algorithm for instantiating a PBES, ported from bes_deprecated.h.
11
12#ifndef MCRL2_PBES_PBESINST_LAZY_H
13#define MCRL2_PBES_PBESINST_LAZY_H
14
15#include <optional>
16#include <string>
17#include <thread>
18#include <mutex>
19#include <functional>
20#include <regex>
21
22#include "mcrl2/utilities/detail/container_utility.h"
23#include "mcrl2/atermpp/standard_containers/deque.h"
24#include "mcrl2/atermpp/standard_containers/indexed_set.h"
25#include "mcrl2/data/substitution_utility.h"
26#include "mcrl2/pbes/detail/bes_equation_limit.h"
27#include "mcrl2/pbes/detail/instantiate_global_variables.h"
28#include "mcrl2/pbes/pbes_equation_index.h"
29#include "mcrl2/pbes/pbes_expression.h"
30#include "mcrl2/pbes/pbessolve_options.h"
31#include "mcrl2/pbes/remove_equations.h"
32#include "mcrl2/pbes/replace_constants_by_variables.h"
33#include "mcrl2/pbes/rewriters/enumerate_quantifiers_rewriter.h"
34#include "mcrl2/pbes/rewriters/one_point_rule_rewriter.h"
35#include "mcrl2/pbes/rewriters/simplify_quantifiers_rewriter.h"
36#include "mcrl2/pbes/structure_graph.h"
37#include "mcrl2/pbes/transformation_strategy.h"
38#include "mcrl2/pbes/transformations.h"
39
40
41namespace mcrl2::pbes_system
42{
43
44// This todo set maintains elements that were removed by the reset procedure.
46{
47 protected:
49
50 // checks some invariants on the internal state
51 bool check_invariants() const
52 {
53 using utilities::detail::contains;
54 std::unordered_set<propositional_variable_instantiation> tmp(todo.begin(), todo.end());
55 return tmp.size() == todo.size();
56 }
57
58 public:
60 {
61 return todo.front();
62 }
63
65 {
66 return todo.back();
67 }
68
69 bool empty() const
70 {
71 return todo.empty();
72 }
73
74 std::size_t size() const
75 {
76 return todo.size();
77 }
78
80 {
81 return todo;
82 }
83
84 void pop_front()
85 {
86 todo.pop_front();
87 }
88
89 void pop_back()
90 {
91 todo.pop_back();
92 }
93
95 {
96 todo.push_back(x);
97 }
98
99 template <typename FwdIter, bool ThreadSafe>
100 void insert(FwdIter first,
101 FwdIter last,
102 const atermpp::indexed_set<propositional_variable_instantiation, ThreadSafe>& discovered,
103 const std::size_t thread_index)
104 {
105 using utilities::detail::contains;
106
107 for (FwdIter i = first; i != last; ++i)
108 {
109 if (!contains(discovered, *i, thread_index))
110 {
111 todo.push_back(*i);
112 }
113 }
114 }
115
116 void set_todo(atermpp::deque<propositional_variable_instantiation>& new_todo)
117 {
118 todo.swap(new_todo); // Note: std::swap(todo, new_todo) is wrong when doing multithreading as the atermpp
119 // wrapper is not moved simultaneously with the content of the deque.
120 assert(check_invariants());
121 }
122};
123
124inline
126{
127 return out << "todo = " << core::detail::print_list(todo.elements()) << std::endl;
128}
129
130/// \brief A PBES instantiation algorithm that uses a lazy strategy
132{
133 protected:
134 /// \brief Algorithm options.
136
137 /// \brief Data rewriter.
139
140 /// \brief A PBES.
142
143 /// \brief A lookup map for PBES equations.
145
146 /// \brief The propositional variable instantiations that need to be handled.
148
149 /// \brief The propositional variable instantiations that have been discovered (not necessarily handled).
150 /// \detail It appears that the index is not used. The indexed set can probably be replaced with an
151 /// atermpp::unordered_set.
153
154 /// \brief The initial value (after rewriting).
156
157 // \brief The number of iterations
159
160 // The data structures that must be separate per thread.
161 /// \brief The rewriter.
163
164 // Mutexes
166
167 // Prune round counter
169
170 volatile bool m_must_abort = false;
171
172 // \brief Returns a status message about the progress
174 {
175 if (equation_count > 0 && equation_count % 1000 == 0)
176 {
177 std::ostringstream out;
178 out << "Generated " << equation_count << " BES equations" << std::endl;
179 return out.str();
180 }
181
182 return std::nullopt;
183 }
184
185 // instantiates global variables
186 // simplifies the pbes
187 pbes preprocess(const pbes& x) const
188 {
189 pbes p = x;
190 pbes_system::detail::instantiate_global_variables(p);
191 pbes_system::one_point_rule_rewriter one_point_rule_rewriter;
192 pbes_system::simplify_quantifiers_data_rewriter<mcrl2::data::rewriter> simplify_rewriter(datar);
193 for (pbes_equation& eqn: p.equations())
194 {
195 // eqn.formula() = order_quantified_variables(one_point_rule_rewriter(simplify_rewriter(eqn.formula())), p.data());
196 pbes_expression aux = simplify_rewriter(eqn.formula());
197 pbes_expression aux1 = one_point_rule_rewriter(aux);
198 pbes_expression aux2 = order_quantified_variables(aux1,p.data());
199 eqn.formula() = aux2;
200 }
201 return p;
202 }
203
205 {
208
211 : symbol(symbol),
212 X(X)
213 {}
214
216 {
217 if (Y == X)
218 {
219 if (symbol.is_mu())
220 {
221 return false_();
222 }
223 else
224 {
225 return true_();
226 }
227 }
228 return Y;
229 }
230 };
231
233 {
235 {
236 return data::rewriter(pbesspec.data(),
237 data::used_data_equation_selector(pbesspec.data(), pbes_system::find_function_symbols(pbesspec), pbesspec.global_variables(), false),
238 m_options.rewrite_strategy);
239 }
240 else
241 {
242 return data::rewriter(pbesspec.data(), m_options.rewrite_strategy);
243 }
244 }
245
246 public:
247
248 /// \brief Constructor.
249 /// \param options The options used in the exploration algorithm.
250 /// \param p The pbes used in the exploration algorithm.
251 /// \param rewriter An optional data rewriter. If not provided, one is constructed from the pbes.
253 const pbessolve_options& options,
254 const pbes& p,
255 std::optional<data::rewriter> rewriter = std::nullopt
256 )
257 : m_options(options),
263 {}
264
265 virtual ~pbesinst_lazy_algorithm() = default;
266
267 /// \brief Reports BES equations that are produced by the algorithm.
268 /// This function is called for every BES equation X = psi with rank k that is produced. By default it does nothing.
269 virtual void on_report_equation(const std::size_t /* thread_index */,
271 const pbes_expression& /* psi */, std::size_t /* k */
272 )
273 { }
274
275 /// \brief This function is called when new elements are added to discovered.
277 { }
278
279 /// \brief This function is called right after the while loop is finished.
280 virtual void on_end_while_loop()
281 { }
282
284 {
285 if (m_options.exploration_strategy == breadth_first)
286 {
287 result = todo.front();
288 todo.pop_front();
289 }
290 else
291 {
292 result = todo.back();
293 todo.pop_back();
294 }
295 }
296
297 const fixpoint_symbol& symbol(std::size_t i) const
298 {
299 return m_pbes.equations()[i].symbol();
300 }
301
302 // Return PBES substitution that is applied to the right hand size of the
303 // equation symbol X = phi during initial exploration of the right hand side phi.
304 virtual std::function<pbes_expression(const propositional_variable_instantiation&)> phi_substitution(const std::size_t /* thread_index */,
305 const fixpoint_symbol& symbol,
307 const pbes_expression& /* phi */)
308 {
310 {
311 return true_false_substitution(symbol, X);
312 }
313
314 return pbes_system::no_substitution();
315 }
316
317 // rewrite the right hand side of the equation symbol X = psi
318 virtual void rewrite_psi(const std::size_t /* thread_index */,
319 pbes_expression& result,
320 const fixpoint_symbol& symbol,
322 const pbes_expression& psi
323 )
324 {}
325
326 virtual bool solution_found(const propositional_variable_instantiation& /* init */) const
327 {
328 return false;
329 }
330
331 virtual void run_thread(const std::size_t thread_index,
332 pbesinst_lazy_todo& todo,
333 std::atomic<std::size_t>& number_of_active_processes,
334 data::mutable_indexed_substitution<> sigma,
336 )
337 {
338 using utilities::detail::contains;
339
340 if (m_options.number_of_threads > 1)
341 {
342 mCRL2log(log::debug) << "Start thread " << thread_index << ".\n";
343 }
345
347 pbes_expression psi_e;
348 pbes_expression tmp; // temporary storate for rewritten psi_e.
349
350 while (number_of_active_processes > 0)
351 {
352 m_todo_access.lock();
353 while (!todo.elements().empty() && !m_must_abort)
354 {
355 ++m_iteration_count;
356 std::size_t local_current_prune_round = global_current_prune_round;
357 if (std::optional<std::string> message = status_message(m_iteration_count))
358 {
359 mCRL2log(log::status) << *message;
360 }
361
362 detail::check_bes_equation_limit(m_iteration_count);
363
364 next_todo(X_e);
365 m_todo_access.unlock();
366
367 std::size_t index = m_equation_index.index(X_e.name());
368 const pbes_equation& eqn = m_pbes.equations()[index];
369 const auto& phi = eqn.formula();
370 data::add_assignments(sigma, eqn.variable().parameters(), X_e.parameters());
371 R(psi_e, phi, sigma, phi_substitution(thread_index, eqn.symbol(), X_e, phi));
372 R.clear_identifier_generator();
373 data::remove_assignments(sigma, eqn.variable().parameters());
374
375 // optional step
376 m_todo_access.lock();
377 tmp = psi_e; // use tmp as input, psi_e as output for rewriting
378 rewrite_psi(thread_index, psi_e, eqn.symbol(), X_e, tmp);
379 m_todo_access.unlock();
380
381 std::set<propositional_variable_instantiation> occ = find_propositional_variable_instantiations(psi_e);
382
383 // report the generated equation
384 std::size_t k = m_equation_index.rank(X_e.name());
385 m_todo_access.lock();
386
387 // If pruning took place, the current equation may not have been relevant, and therefore we simply
388 // ignore it. If the current equation is not relevant, the newly discovered variables are also possibly
389 // not relevant, and should not be added into the todo set, as otherwise these irrelevant variables
390 // will be explored further, potentially leading to massively wasted exploration and solving effort.
391 if (local_current_prune_round == global_current_prune_round)
392 {
393 mCRL2log(log::debug) << "generated equation " << X_e << " = " << psi_e
394 << " with rank " << k << std::endl;
395 on_report_equation(thread_index, X_e, psi_e, k);
396 todo.insert(occ.begin(), occ.end(), discovered, thread_index);
397 for (const propositional_variable_instantiation& i : occ)
398 {
399 std::ignore = discovered.insert(i, thread_index);
400 }
401 on_discovered_elements(occ);
402
403 if (solution_found(init))
404 {
405 break;
406 }
407 }
408 }
409 m_todo_access.unlock();
410
411 // Check whether all processes are ready. If so the
412 // number_of_active_processes becomes 0. Otherwise, this thread becomes
413 // active again, and tries to see whether the todo buffer is not empty,
414 // to take up more work.
415 number_of_active_processes--;
416 std::this_thread::sleep_for(std::chrono::milliseconds(100));
417 if (number_of_active_processes > 0)
418 {
419 number_of_active_processes++;
420 }
421 }
422
423 if (m_options.number_of_threads > 1)
424 {
425 mCRL2log(log::debug) << "Stop thread " << thread_index << ".\n";
426 }
427 }
428
429 /// \brief Runs the algorithm. The result is obtained by calling the function \p get_result.
430 virtual void run()
431 {
432 m_iteration_count = 0;
433
434 const std::size_t number_of_threads = m_options.number_of_threads;
435 const std::size_t initialisation_thread_index = (number_of_threads==1?0:1);
436 std::atomic<std::size_t> number_of_active_processes = number_of_threads;
437 std::vector<std::thread> threads;
438
439 data::mutable_indexed_substitution<> sigma;
441 {
442 pbes_system::replace_constants_by_variables(m_pbes, datar, sigma);
443 }
444
445 init = atermpp::down_cast<propositional_variable_instantiation>(m_global_R(m_pbes.initial_state(), sigma));
446 todo.insert(init);
447 std::ignore = discovered.insert(init, initialisation_thread_index);
448
449 if (number_of_threads>1)
450 {
451 threads.reserve(number_of_threads);
452 for (std::size_t i = 1; i <= number_of_threads; ++i)
453 {
454 std::thread tr([&, i](){
455 run_thread(i,
456 todo,
457 number_of_active_processes,
458 sigma.clone(),
459 m_global_R.clone()
460 );
461 });
462 threads.push_back(std::move(tr));
463 }
464
465 for (std::size_t i = 1; i <= number_of_threads; ++i)
466 {
467 threads[i-1].join();
468 }
469 }
470 else
471 {
472 // There is only one thread. Run the process in the main thread, without cloning sigma or the rewriter.
473 const std::size_t single_thread_index=0;
474 run_thread(single_thread_index,
475 todo,
476 number_of_active_processes,
477 sigma,
478 m_global_R
479 );
480 }
482
483 mCRL2log(log::verbose) << "Generated " << m_iteration_count << " BES equations" << std::endl;
484 }
485
487 {
488 return m_equation_index;
489 }
490
492 {
493 return m_global_R;
494 }
495
497 {
498 return datar;
499 };
500};
501
502} // namespace mcrl2::pbes_system
503
504
505
506#endif // MCRL2_PBES_PBESINST_LAZY_H
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
bool is_mu() const
Returns true if the symbol is mu.
A rewriter that applies one point rule quantifier elimination to a PBES.
parameterized boolean equation system
Definition pbes.h:54
A PBES instantiation algorithm that uses a lazy strategy.
const pbes_equation_index & equation_index() const
const pbessolve_options & m_options
Algorithm options.
void next_todo(propositional_variable_instantiation &result)
const data::rewriter & data_rewriter() const
virtual void on_report_equation(const std::size_t, const propositional_variable_instantiation &, const pbes_expression &, std::size_t)
Reports BES equations that are produced by the algorithm. This function is called for every BES equat...
virtual void on_end_while_loop()
This function is called right after the while loop is finished.
virtual bool solution_found(const propositional_variable_instantiation &) const
virtual void rewrite_psi(const std::size_t, pbes_expression &result, const fixpoint_symbol &symbol, const propositional_variable_instantiation &X, const pbes_expression &psi)
pbesinst_lazy_algorithm(const pbessolve_options &options, const pbes &p, std::optional< data::rewriter > rewriter=std::nullopt)
Constructor.
data::rewriter construct_rewriter(const pbes &pbesspec)
pbesinst_lazy_todo todo
The propositional variable instantiations that need to be handled.
propositional_variable_instantiation init
The initial value (after rewriting).
virtual std::function< pbes_expression(const propositional_variable_instantiation &)> phi_substitution(const std::size_t, const fixpoint_symbol &symbol, const propositional_variable_instantiation &X, const pbes_expression &)
virtual void on_discovered_elements(const std::set< propositional_variable_instantiation > &)
This function is called when new elements are added to discovered.
virtual void run()
Runs the algorithm. The result is obtained by calling the function get_result.
pbes_equation_index m_equation_index
A lookup map for PBES equations.
enumerate_quantifiers_rewriter m_global_R
The rewriter.
const fixpoint_symbol & symbol(std::size_t i) const
virtual std::optional< std::string > status_message(std::size_t equation_count)
enumerate_quantifiers_rewriter & rewriter()
atermpp::indexed_set< propositional_variable_instantiation, true > discovered
The propositional variable instantiations that have been discovered (not necessarily handled)....
virtual void run_thread(const std::size_t thread_index, pbesinst_lazy_todo &todo, std::atomic< std::size_t > &number_of_active_processes, data::mutable_indexed_substitution<> sigma, enumerate_quantifiers_rewriter R)
atermpp::deque< propositional_variable_instantiation > todo
const propositional_variable_instantiation & back() const
void insert(const propositional_variable_instantiation &x)
const propositional_variable_instantiation & front() const
const atermpp::deque< propositional_variable_instantiation > & elements() const
void set_todo(atermpp::deque< propositional_variable_instantiation > &new_todo)
void insert(FwdIter first, FwdIter last, const atermpp::indexed_set< propositional_variable_instantiation, ThreadSafe > &discovered, const std::size_t thread_index)
\brief A propositional variable instantiation
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:392
const pbes_expression & true_()
std::ostream & operator<<(std::ostream &out, const pbesinst_lazy_todo &todo)
partial_solve_strategy
Enumeration of partial strategies for solving PBESs.
const pbes_expression & false_()
void thread_initialise()
Initialises this rewriter with thread dependent information.
pbes_expression operator()(const propositional_variable_instantiation &Y) const
true_false_substitution(const fixpoint_symbol &symbol, const propositional_variable_instantiation &X)
A rewriter that simplifies boolean expressions and quantifiers, and rewrites data expressions.