12#ifndef MCRL2_LTS_STATE_SPACE_GENERATOR_H
13#define MCRL2_LTS_STATE_SPACE_GENERATOR_H
15#include "mcrl2/lps/explorer.h"
16#include "mcrl2/lts/trace.h"
18#include <forward_list>
26 return out << atermpp::pp(s);
38 return s.states.front();
47 const std::string& filename
53 mCRL2log(log::info) <<
" and saved trace to '" << filename <<
"'";
58 mCRL2log(log::info) <<
", but its trace could not be saved to '" << filename <<
"'";
66 const std::string& filename1,
68 const std::string& filename2
75 mCRL2log(log::info) <<
" and saved traces to '" << filename1 <<
"' and '" << filename2 <<
"'";
79 mCRL2log(log::info) <<
", but its traces could not be saved to '" << filename1 <<
"' and '" << filename2 <<
"'";
84template <
typename Explorer>
95 if constexpr (Explorer::is_stochastic)
97 for (
const std::pair<lps::multi_action, lps::stochastic_state>& t: m_explorer.generate_transitions(s0))
99 for (
const lps::state& s: t.second.states)
110 for (
const std::pair<lps::multi_action, lps::state>& t: m_explorer.generate_transitions(s0))
118 throw mcrl2::runtime_error(
"no transition found in find_action");
129 std::deque<lps::state> states{ s };
130 std::deque<lps::multi_action> actions;
133 const lps::state& s1 = states.front();
134 auto i = m_backpointers.find(s1);
135 if (i == m_backpointers.end())
139 const lps::state& s0 = i->second;
140 states.push_front(s0);
141 actions.push_front(find_action(s0, s1));
145 for (std::size_t i = 0; i < actions.size(); i++)
147 tr.set_state(states[i]);
148 tr.add_action(actions[i]);
150 tr.set_state(states.back());
157 m_backpointers[s1] = s0;
162 m_backpointers.clear();
172template <
typename Explorer>
187 for (
const process::action& a: summand.multi_action().actions())
189 if (contains(trace_actions, a.label().name()))
199 return summand_matches[i];
205 std::string filename = filename_prefix +
"_act_" + std::to_string(m_trace_count++);
206 if (utilities::detail::contains(trace_multiactions, a))
208 filename +=
"_" + lps::pp(a);
210 for (
const process::action& a_i: a.actions())
212 if (utilities::detail::contains(trace_actions, a_i.label().name()))
214 filename +=
"_" + core::pp(a_i.label().name());
217 filename = filename +
".trc";
222 template <
typename Specification>
224 const Specification& lpsspec,
226 const std::set<core::identifier_string>& trace_actions_,
227 const std::set<lps::multi_action>& trace_multiactions_,
228 const std::string& filename_prefix_,
229 std::size_t max_trace_count
238 const auto& summands = lpsspec.process().action_summands();
239 summand_matches.reserve(summands.size());
240 for (
const auto& summand: summands)
242 summand_matches.push_back(match_action(summand));
249 if (!match_summand(summand_index))
255 mCRL2log(log::info) <<
"Action '" + lps::pp(a) +
"' found (state index: " + std::to_string(s0_index) +
")";
256 if (m_trace_count < m_max_trace_count)
261 std::string filename = create_filename(a);
262 save_trace(tr, filename);
266 if (m_max_trace_count > 0 && m_trace_count >= m_max_trace_count)
274template <
typename Explorer>
286 const std::string& filename_prefix_,
287 std::size_t max_trace_count
296 mCRL2log(log::info) <<
"Deadlock found (state index: " + std::to_string(s_index) +
")";
297 if (m_trace_count < m_max_trace_count)
300 std::string filename = filename_prefix +
"_dlk_" + std::to_string(m_trace_count++) +
".trc";
301 save_trace(tr, filename);
303 if (m_max_trace_count > 0 && m_trace_count >= m_max_trace_count)
311template <
typename Explorer>
324 const std::string& filename_prefix_,
325 const std::size_t number_of_threads,
326 std::size_t max_trace_count = 0
333 assert(number_of_threads>0);
338 assert(thread_index<m_transitions_vec.size());
339 m_transitions_vec[thread_index].clear();
345 assert(thread_index<m_transitions_vec.size());
346 auto i = m_transitions_vec[thread_index].find(a);
347 if (i == m_transitions_vec[thread_index].end())
349 m_transitions_vec[thread_index].insert(std::make_pair(a, s1));
351 else if (i->second != s1)
353 mCRL2log(log::info) <<
"Nondeterministic state found (state index: " + std::to_string(s0_index) +
")";
354 if (m_trace_count < m_max_trace_count)
359 std::string filename = filename_prefix +
"_nondeterministic_" + std::to_string(m_trace_count++) +
".trc";
360 save_trace(tr, filename);
364 if (m_max_trace_count > 0 && m_trace_count >= m_max_trace_count)
373template <
typename Explorer>
377 using state_type =
typename Explorer::state_type;
378 using state_index_type =
typename Explorer::state_index_type;
397 const std::set<core::identifier_string>& actions,
398 const std::string& filename_prefix_,
399 std::size_t max_trace_count
410 for (
const process::action& a: summand.multi_action.actions())
412 if (!contains(actions, a.label().name()))
422 if (is_hidden(summand))
424 m_regular_summands.push_back(summand);
430 if (is_hidden(summand))
432 m_confluent_summands.push_back(summand);
443 std::lock_guard guard(divergence_detector_mutex);
446 auto q = m_divergent_states.find(s);
447 if (q != m_divergent_states.end())
449 std::string message =
"Divergent state found (state index: " + std::to_string(s_index) +
450 "), reachable from divergent state with index " + std::to_string(q->second);
451 mCRL2log(log::info) << message <<
".\n";
452 m_divergent_states.erase(q);
456 std::unordered_set<lps::state> discovered;
457 data::data_expression_list process_parameter_undo = explorer.process_parameter_values();
461 std::unordered_set<lps::state> gray;
462 explorer.generate_state_space_dfs_recursive(
467 m_confluent_summands,
473 [&](
const lps::state& s0,
const lps::multi_action& a,
const state_type& s1) {
474 mCRL2log(log::info) <<
"Divergent state found (state index: " + std::to_string(s_index) +
")";
475 if (m_trace_count < m_max_trace_count)
477 class trace tr = global_trace_constructor.construct_trace(s);
478 class trace tr_loop = m_local_trace_constructor.construct_trace(s0);
479 for (
const lps::state& u: tr_loop.states())
481 m_divergent_states[u] = s_index;
483 tr_loop.add_action(a);
484 tr_loop.set_state(first_state(s1));
485 std::string filename = filename_prefix +
"_divergence_" + std::to_string(m_trace_count) +
".trc";
486 std::string loop_filename = filename_prefix +
"_divergence_loop" + std::to_string(m_trace_count++) +
".trc";
487 save_traces(tr, filename, tr_loop, loop_filename);
493 static_cast<lps::abortable&>(explorer).abort();
499 explorer.generate_state_space_dfs_iterative(
503 m_confluent_summands,
509 [&](
const lps::state& s0,
const lps::multi_action& a,
const state_type& s1) {
510 mCRL2log(log::info) <<
"Divergent state found (state index: " + std::to_string(s_index) +
")";
511 if (m_trace_count < m_max_trace_count)
513 class trace tr = global_trace_constructor.construct_trace(s);
514 class trace tr_loop = m_local_trace_constructor.construct_trace(s0);
515 for (
const lps::state& u: tr_loop.states())
517 m_divergent_states[u] = s_index;
519 tr_loop.add_action(a);
520 tr_loop.set_state(first_state(s1));
521 std::string filename = filename_prefix +
"_divergence_" + std::to_string(m_trace_count) +
".trc";
522 std::string loop_filename = filename_prefix +
"_divergence_loop" + std::to_string(m_trace_count++) +
".trc";
523 save_traces(tr, filename, tr_loop, loop_filename);
529 static_cast<lps::abortable&>(explorer).abort();
533 explorer.set_process_parameter_values(process_parameter_undo);
534 if (m_max_trace_count > 0 && m_trace_count >= m_max_trace_count)
566 void finish_state(std::size_t state_count, std::size_t todo_list_size, std::size_t number_of_threads)
568 time_t new_log_time = 0;
570 static std::mutex exclusive_print_mutex;
574 if (number_of_threads == 1 && count == level_up)
576 std::lock_guard guard(exclusive_print_mutex);
577 mCRL2log(log::debug) <<
"Number of states at level " << level <<
" is " << state_count - last_state_count <<
"\n";
579 level_up = count + todo_list_size;
580 last_state_count = state_count;
581 last_transition_count = transition_count;
584 if (time(&new_log_time) > last_log_time.load(std::memory_order_relaxed))
586 std::lock_guard guard(exclusive_print_mutex);
588 last_log_time = new_log_time;
589 std::size_t lvl_states = state_count - last_state_count;
590 std::size_t lvl_transitions = transition_count - last_transition_count;
591 if (number_of_threads>1)
593 mCRL2log(log::status) << std::fixed << std::setprecision(2)
594 << state_count <<
"st, " << transition_count <<
"tr"
595 <<
", explored " << 100.0 * (
static_cast<
float>(count) /
static_cast<
float>(state_count))
600 mCRL2log(log::status) << std::fixed << std::setprecision(2)
601 << state_count <<
"st, " << transition_count <<
"tr"
602 <<
", explored " << 100.0 * (
static_cast<
float>(count) /
static_cast<
float>(state_count))
603 <<
"%. Last level: " << level <<
", " << lvl_states <<
"st, "
604 << lvl_transitions <<
"tr.\n";
611 if (time(&new_log_time) > last_log_time.load(std::memory_order_relaxed))
613 std::lock_guard guard(exclusive_print_mutex);
614 last_log_time = new_log_time;
615 mCRL2log(log::status) <<
"monitor: currently explored "
616 << count <<
" state" << ((count==1)?
"":
"s")
617 <<
" and " << transition_count <<
" transition" << ((transition_count==1)?
".":
"s.")
627 mCRL2log(log::verbose) <<
"Done with state space generation (";
628 if (number_of_threads==1)
630 mCRL2log(log::verbose) << level-1 <<
" level" << ((level==2)?
"":
"s") <<
", ";
632 mCRL2log(log::verbose) << state_count <<
" state" << ((state_count == 1)?
"":
"s")
633 <<
" and " << transition_count <<
" transition" << ((transition_count==1)?
"":
"s") <<
")" << std::endl;
637 mCRL2log(log::verbose) <<
"Done with state space generation ("
638 << state_count <<
" state" << ((state_count == 1)?
"":
"s")
639 <<
" and " << transition_count <<
" transition" << ((transition_count==1)?
"":
"s") <<
")" << std::endl;
646template <
bool Stochastic,
bool Timed,
typename Specification>
649 using explorer_type =
lps::
explorer<Stochastic, Timed, Specification>;
650 using state_type =
typename explorer_type::state_type;
673 m_divergence_detector =
674 std::unique_ptr<detail::divergence_detector<explorer_type>>(
675 new detail::divergence_detector<explorer_type>(explorer,
676 options.actions_internal_for_divergencies,
677 options.trace_prefix,
678 options.max_traces));
684 return explorer.state_map().size(thread_index) >= options.max_states;
693 template <
typename LTSBuilder>
696 std::vector<aligned_bool> has_outgoing_transitions(options.number_of_threads+1);
697 const lps::state* source =
nullptr;
705 [&](
const std::size_t thread_index,
const lps::state& s, std::size_t s_index)
714 if constexpr (!Stochastic)
716 m_divergence_detector->detect_divergence(s, s_index, m_trace_constructor, options.dfs_recursive);
721 if (max_states_exceeded(thread_index))
723 static bool not_reported_yet=
true;
724 if (not_reported_yet)
726 not_reported_yet=
false;
727 mCRL2log(log::verbose) <<
"Explored the maximum number (" << options.max_states <<
") of states, terminating." << std::endl;
736 [&](
const std::size_t thread_index,
const std::size_t number_of_threads,
738 const auto& s1,
const auto& s1_index, std::size_t summand_index)
740 if constexpr (Stochastic)
742 builder.add_transition(s0_index, a, s1_index, s1.probabilities, number_of_threads);
746 builder.add_transition(s0_index, a, s1_index, number_of_threads);
748 assert(thread_index<has_outgoing_transitions.size());
749 has_outgoing_transitions[thread_index].m_bool =
true;
752 m_action_detector.detect_action(s0, s0_index, a, first_state(s1), summand_index);
756 m_nondeterminism_detector.detect_nondeterminism(s0, s0_index, a, first_state(s1), thread_index);
760 m_progress_monitor.examine_transition();
765 [&](
const std::size_t thread_index,
const lps::state& s, std::size_t )
767 if (
options.number_of_threads == 1) {
771 assert(thread_index<has_outgoing_transitions.size());
772 has_outgoing_transitions[thread_index].m_bool =
false;
775 m_nondeterminism_detector.start_state(thread_index);
780 [&](
const std::size_t thread_index,
const std::size_t number_of_threads,
781 const lps::state& s, std::size_t s_index, std::size_t todo_list_size)
783 assert(thread_index<has_outgoing_transitions.size());
784 if (options.detect_deadlock && !has_outgoing_transitions[thread_index].m_bool)
786 m_deadlock_detector.detect_deadlock(s, s_index);
790 m_progress_monitor.finish_state(explorer.state_map().size(thread_index), todo_list_size, number_of_threads);
797 if constexpr (Stochastic)
799 builder.set_initial_state(s_index, s.probabilities);
803 m_progress_monitor.finish_exploration(explorer.state_map().size(), options.number_of_threads);
804 builder.finalize(
explorer.state_map(), Timed);
808 mCRL2log(log::error) <<
"Error while exploring state space: " << e.what() <<
".\n";
811 const lps::state& s = *source;
813 std::string filename = options.trace_prefix +
"_error.trc";
814 detail::save_trace(tr, filename);
LPS summand containing a multi-action.
\brief A timed multi-action
action_detector(const Specification &lpsspec, trace_constructor< Explorer > &trace_constructor_, const std::set< core::identifier_string > &trace_actions_, const std::set< lps::multi_action > &trace_multiactions_, const std::string &filename_prefix_, std::size_t max_trace_count)
trace_constructor< Explorer > & m_trace_constructor
bool detect_action(const lps::state &s0, std::size_t s0_index, const lps::multi_action &a, const lps::state &s1, std::size_t summand_index)
bool match_action(const lps::action_summand &summand) const
const std::string & filename_prefix
std::size_t m_trace_count
std::vector< bool > summand_matches
const std::set< core::identifier_string > & trace_actions
std::string create_filename(const lps::multi_action &a)
bool match_summand(std::size_t i) const
const std::set< lps::multi_action > & trace_multiactions
std::size_t m_max_trace_count
function object to compare two constln_t pointers based on their contents
deadlock_detector(trace_constructor< Explorer > &trace_constructor_, const std::string &filename_prefix_, std::size_t max_trace_count)
std::size_t m_max_trace_count
void detect_deadlock(const lps::state &s, std::size_t s_index)
trace_constructor< Explorer > & m_trace_constructor
std::size_t m_trace_count
const std::string & filename_prefix
const std::string & filename_prefix
std::vector< lps::explorer_summand > m_confluent_summands
std::size_t m_max_trace_count
std::size_t m_trace_count
utilities::unordered_map< lps::state, std::size_t > m_divergent_states
bool detect_divergence(const lps::state &s, std::size_t s_index, trace_constructor< Explorer > &global_trace_constructor, bool dfs_recursive=false)
std::vector< lps::explorer_summand > m_regular_summands
std::mutex divergence_detector_mutex
trace_constructor< Explorer > m_local_trace_constructor
divergence_detector(Explorer &explorer_, const std::set< core::identifier_string > &actions, const std::string &filename_prefix_, std::size_t max_trace_count)
trace_constructor< Explorer > & m_trace_constructor
std::size_t m_max_trace_count
std::size_t m_trace_count
std::vector< std::map< lps::multi_action, lps::state > > m_transitions_vec
nondeterminism_detector(trace_constructor< Explorer > &trace_constructor_, const std::string &filename_prefix_, const std::size_t number_of_threads, std::size_t max_trace_count=0)
void start_state(std::size_t thread_index)
bool detect_nondeterminism(const lps::state &s0, std::size_t s0_index, const lps::multi_action &a, const lps::state &s1, std::size_t thread_index)
const std::string & filename_prefix
std::size_t last_state_count
progress_monitor(lps::exploration_strategy search_strategy_)
std::atomic< std::size_t > transition_count
std::size_t last_transition_count
lps::exploration_strategy search_strategy
void examine_transition()
void finish_exploration(std::size_t state_count, std::size_t number_of_threads)
void finish_state(std::size_t state_count, std::size_t todo_list_size, std::size_t number_of_threads)
std::atomic< time_t > last_log_time
std::atomic< std::size_t > count
lps::multi_action find_action(const lps::state &s0, const lps::state &s1)
trace_constructor(Explorer &explorer_)
void add_edge(const lps::state &s0, const lps::state &s1)
This class contains a trace consisting of a sequence of (timed) actions possibly with intermediate st...
void set_state(const lps::state &s)
Set the state at the current position.
void add_action(const mcrl2::lps::multi_action &action)
Add an action to the current trace.
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
The main namespace for the LPS library.
void save_traces(class trace &tr, const std::string &filename1, class trace &tr2, const std::string &filename2)
bool save_trace(class trace &tr, const std::string &filename)
std::ostream & operator<<(std::ostream &out, const lps::state &s)
const lps::state & first_state(const lps::state &s)
const lps::state & first_state(const lps::stochastic_state &s)
bool detect_nondeterminism
bool suppress_progress_messages
std::unique_ptr< detail::divergence_detector< explorer_type > > m_divergence_detector
const lps::explorer_options & options
bool max_states_exceeded(const std::size_t thread_index)
detail::action_detector< explorer_type > m_action_detector
state_space_generator(const Specification &lpsspec, const lps::explorer_options &options_, explorer_type &explorer_)
detail::progress_monitor m_progress_monitor
detail::trace_constructor< explorer_type > m_trace_constructor
detail::nondeterminism_detector< explorer_type > m_nondeterminism_detector
detail::deadlock_detector< explorer_type > m_deadlock_detector
bool explore(LTSBuilder &builder)