11#ifndef _LIBLTS_BISIM_H
12#define _LIBLTS_BISIM_H
29template <
class LTS_TYPE>
56 const bool preserve_divergence=
false,
57 const bool generate_counter_examples=
false)
63 assert(
branching || !preserve_divergence);
65 (
branching?
"ranching b":
"") <<
"isimulation partitioner created for "
66 << l.num_states() <<
" states and " <<
67 l.num_transitions() <<
" transitions\n";
69 if (generate_counter_examples)
99 std::set < transition > resulting_transitions;
103 const bool is_transition_i_hidden=
aut.is_tau(
aut.apply_hidden_label_map(i.label()));
105 !is_transition_i_hidden ||
107 (preserve_divergences && i.from()==i.to()))
109 resulting_transitions.insert(
117 (is_transition_i_hidden?
aut.tau_label_index():i.label()),
122 aut.clear_transitions();
125 for (
const transition& t: resulting_transitions)
127 aut.add_transition(t);
132 if (
aut.has_state_info())
135 std::vector<typename LTS_TYPE::state_label_t> new_labels(
num_eq_classes());
137 for(std::size_t i=
aut.num_states(); i>0; )
141 new_labels[new_index] = new_labels[new_index] +
aut.state_label(i);
147 aut.set_state_label(i,new_labels[i]);
267 std::vector< block_index_type >
BL;
278 std::map < block_index_type, std::set < block_index_type > >
r_alpha;
280 std::map < block_index_type, std::set < block_index_type > >
r_tauP;
289 const bool preserve_divergences)
294 block initial_partition;
300 bool bottom_state=
true;
301 std::vector < state_type > current_inert_transitions;
308 std::size_t initial_partition_non_inert_counter=0;
309 std::size_t current_inert_transition_counter=0;
310 const std::vector<transition> & trans=
aut.get_transitions();
311 for (std::vector<transition>::const_iterator r=trans.begin(); r!=trans.end(); ++r)
316 if (preserve_divergences && t.
from()==t.
to())
318 initial_partition_non_inert_counter++;
322 current_inert_transition_counter++;
326 current_inert_transitions.reserve(initial_partition_non_inert_counter);
330 const std::vector<transition>& trans=
aut.get_transitions();
331 for (std::vector<transition>::const_iterator r=trans.begin(); r!=trans.end(); ++r)
337 if (preserve_divergences && t.
from()==t.
to())
343 current_inert_transitions.push_back(t.
to());
347 std::vector<transition>::const_iterator next_i=r;
349 if (next_i==trans.end() || t.
from()!=next_i->from())
352 for (; last_non_stored_state_number<t.
from(); ++last_non_stored_state_number)
354 initial_partition.
bottom_states.push_back(last_non_stored_state_number);
364 current_inert_transitions.swap(initial_partition.
non_bottom_states.back().inert_transitions);
367 assert(last_non_stored_state_number==t.
from());
368 last_non_stored_state_number++;
372 for (; last_non_stored_state_number<
aut.num_states(); ++last_non_stored_state_number)
374 initial_partition.
bottom_states.push_back(last_non_stored_state_number);
384 const std::vector<transition> & trans1=
aut.get_transitions();
385 for (std::vector<transition>::const_iterator r=trans1.begin(); r!=trans1.end(); ++r)
401 blocks.back().swap(initial_partition);
418 std::size_t consistency_check_counter=1;
419 std::size_t consistency_check_barrier=1;
421 bool partition_is_unstable=
true;
427 consistency_check_counter++;
428 if (consistency_check_counter>=consistency_check_barrier)
430 consistency_check_counter=0;
431 consistency_check_barrier=consistency_check_barrier*2;
443 partition_is_unstable=
false;
458 for (std::size_t i=0; i<
blocks[splitter_index].non_inert_transitions.size(); ++i)
462 assert(
blocks[splitter_index].non_inert_transitions[i].
from()<
aut.num_states());
468 BL.push_back(marked_block_index);
472 if ((i+1)==
blocks[splitter_index].non_inert_transitions.size() ||
473 aut.apply_hidden_label_map(
blocks[splitter_index].non_inert_transitions[i].label())!=
474 aut.apply_hidden_label_map(
blocks[splitter_index].non_inert_transitions[i+1].label()))
501 bool& partition_is_unstable,
505 for (std::vector < block_index_type > :: const_iterator i1=
BL.begin();
510 std::vector < state_type > flagged_states;
511 std::vector < state_type > non_flagged_states;
512 std::vector < state_type > i1_bottom_states;
513 i1_bottom_states.swap(
blocks[*i1].bottom_states);
515 for (std::vector < state_type >::const_iterator j=i1_bottom_states.begin();
516 j!=i1_bottom_states.end(); ++j)
521 flagged_states.push_back(*j);
526 non_flagged_states.push_back(*j);
530 assert(!flagged_states.empty()||!
blocks[*i1].non_bottom_states.empty()||i1_bottom_states.empty());
533 if (!non_flagged_states.empty())
540 const std::size_t m =
static_cast<std::size_t
>(std::pow(10.0, std::floor(std::log10(
static_cast<double>((
blocks.size()+1)/2)))));
541 if ((
blocks.size()+1)/2 % m==0)
553 blocks.back().block_index=new_block1;
554 blocks.back().parent_block_index=*i1;
556 non_flagged_states.swap(
blocks.back().bottom_states);
566 blocks.back().block_index=new_block2;
567 reset_state_flags_block=new_block2;
568 blocks.back().parent_block_index=*i1;
571 flagged_states.swap(
blocks.back().bottom_states);
572 std::vector < state_type > &reference_to_flagged_states_of_block2=
blocks.back().bottom_states;
573 for (std::vector < state_type >::const_iterator j=reference_to_flagged_states_of_block2.begin();
574 j!=reference_to_flagged_states_of_block2.end(); ++j)
588 std::set<block_index_type> reachable_blocks = {};
591 for (outgoing_transitions_per_state_action_t::const_iterator i =
outgoing_transitions.lower_bound(std::pair<state_type, label_type>(source_state, splitter_label));
592 i !=
outgoing_transitions.upper_bound(std::pair<state_type, label_type>(source_state, splitter_label)); ++i)
596 reachable_blocks.insert(target_block == new_block1 || target_block == new_block2 ? *i1 : target_block);
599 r_alpha[new_block1] = reachable_blocks;
603 std::set<block_index_type> tau_reachable_blocks = {};
606 for (
label_type lab = 0; lab <
aut.num_action_labels(); ++lab)
608 if (
aut.is_tau(
aut.apply_hidden_label_map(lab)))
610 for (outgoing_transitions_per_state_action_t::const_iterator i =
outgoing_transitions.lower_bound(std::pair<state_type, label_type>(source_state, lab));
611 i !=
outgoing_transitions.upper_bound(std::pair<state_type, label_type>(source_state, lab)); ++i)
615 if (!(target_block == *i1 || target_block == new_block1 || target_block == new_block2))
617 tau_reachable_blocks.insert(target_block);
623 r_tauP[new_block1] = tau_reachable_blocks;
644 std::vector < transition > flagged_non_inert_transitions;
645 std::vector < transition > non_flagged_non_inert_transitions;
648 flagged_non_inert_transitions.reserve(
blocks[*i1].non_inert_transitions.size());
649 non_flagged_non_inert_transitions.reserve(
blocks[*i1].non_inert_transitions.size());
655 std::vector < non_bottom_state > flagged_non_bottom_states;
656 std::vector < non_bottom_state > non_flagged_non_bottom_states;
657 std::vector < non_bottom_state > i1_non_bottom_states;
658 i1_non_bottom_states.swap(
blocks[*i1].non_bottom_states);
659 for (
typename std::vector < non_bottom_state >::iterator k=i1_non_bottom_states.begin();
660 k!=i1_non_bottom_states.end(); ++k)
662 const std::vector < state_type > &inert_transitions=k->inert_transitions;
665 bool all_transitions_end_in_unflagged_block=
true;
666 for (std::vector < state_type > :: const_iterator l=inert_transitions.begin();
667 all_transitions_end_in_unflagged_block && l!=inert_transitions.end(); ++l)
672 all_transitions_end_in_unflagged_block=
false;
675 if (all_transitions_end_in_unflagged_block)
680 non_flagged_non_bottom_states.push_back(s);
687 std::vector < state_type > remaining_inert_transitions;
688 for (std::vector < state_type > :: const_iterator l=inert_transitions.begin();
689 l!=inert_transitions.end(); ++l)
695 non_flagged_non_inert_transitions.push_back(
transition(k->state,
aut.tau_label_index(),*l));
701 remaining_inert_transitions.push_back(*l);
704 if (remaining_inert_transitions.empty())
708 blocks[new_block2].bottom_states.push_back(k->state);
710 partition_is_unstable=
true;
714 flagged_non_bottom_states.push_back(
non_bottom_state(k->state,remaining_inert_transitions));
718 non_flagged_non_bottom_states.swap(
blocks[new_block1].non_bottom_states);
719 flagged_non_bottom_states.swap(
blocks[new_block2].non_bottom_states);
724 assert(*i1 <
blocks.size());
725 std::vector < transition > i1_non_inert_transitions;
726 i1_non_inert_transitions.swap(
blocks[*i1].non_inert_transitions);
727 for (std::vector < transition >::iterator k=i1_non_inert_transitions.begin();
728 k!=i1_non_inert_transitions.end(); ++k)
732 non_flagged_non_inert_transitions.push_back(*k);
737 flagged_non_inert_transitions.push_back(*k);
749 blocks[new_block1].non_inert_transitions.reserve(non_flagged_non_inert_transitions.size());
750 for(std::vector < transition > ::const_iterator i=non_flagged_non_inert_transitions.begin();
751 i!=non_flagged_non_inert_transitions.end(); i++)
753 blocks[new_block1].non_inert_transitions.push_back(*i);
756 blocks[new_block2].non_inert_transitions.reserve(flagged_non_inert_transitions.size());
757 for(std::vector < transition > ::const_iterator i=flagged_non_inert_transitions.begin();
758 i!=flagged_non_inert_transitions.end(); i++)
760 blocks[new_block2].non_inert_transitions.push_back(*i);
766 i1_bottom_states.swap(
blocks[*i1].bottom_states);
769 std::vector < state_type > &flagged_states_to_be_unflagged=
blocks[reset_state_flags_block].bottom_states;
770 for (std::vector < state_type >::const_iterator j=flagged_states_to_be_unflagged.begin();
771 j!=flagged_states_to_be_unflagged.end(); ++j)
776 std::vector < non_bottom_state > &flagged_states_to_be_unflagged1=
blocks[reset_state_flags_block].non_bottom_states;
777 for (
typename std::vector < non_bottom_state >::const_iterator j=flagged_states_to_be_unflagged1.begin();
778 j!=flagged_states_to_be_unflagged1.end(); ++j)
788 std::map <
state_type, std::vector < state_type > >& inert_transition_map,
789 std::vector < non_bottom_state >& new_non_bottom_states,
790 std::set < state_type >& visited)
792 if (inert_transition_map.count(s)>0)
794 if (visited.count(s)==0)
797 std::vector < state_type> &inert_transitions=inert_transition_map[s];
798 for (std::vector < state_type>::const_iterator j=inert_transitions.begin();
799 j!=inert_transitions.end(); j++)
804 inert_transitions.swap(new_non_bottom_states.back().inert_transitions);
811 std::set < state_type > visited;
812 std::map < state_type, std::vector < state_type > > inert_transition_map;
813 for (
typename std::vector < non_bottom_state >::iterator i=non_bottom_states.begin();
814 i!=non_bottom_states.end(); ++i)
816 i->inert_transitions.swap(inert_transition_map[i->state]);
818 std::vector < non_bottom_state > new_non_bottom_states;
820 for (
typename std::vector < non_bottom_state >::const_iterator i=non_bottom_states.begin();
821 i!=non_bottom_states.end(); ++i)
825 new_non_bottom_states.swap(non_bottom_states);
835 return utilities::detail::join<mcrl2::state_formulas::state_formula>(terms.begin(), terms.end(),
885 if (
aut.is_tau(
aut.apply_hidden_label_map(a)))
903 std::set < block_index_type > blocks_containing_B1;
905 blocks_containing_B1.insert(B1p);
906 while (
blocks[B1p].parent_block_index != B1p)
908 B1p =
blocks[B1p].parent_block_index;
909 blocks_containing_B1.insert(B1p);
914 while (blocks_containing_B1.count(DB) == 0)
916 assert(
blocks[DB].parent_block_index != DB);
917 DB =
blocks[DB].parent_block_index;
927 std::set<mcrl2::state_formulas::state_formula> Gamma2;
938 std::set<mcrl2::state_formulas::state_formula> Gamma1;
946 if (
aut.is_tau(
aut.apply_hidden_label_map(a)))
958 if (blocks_containing_B1.count(R) == 0)
977 const bool preserve_divergence)
const
980 std::size_t total_number_of_transitions=0;
983 std::set < block_index_type > block_indices;
986 for (
typename std::vector < block >::const_iterator i=
blocks.begin();
990 assert(i->block_index<
blocks.size());
991 assert(block_indices.count(i->block_index)==0);
992 block_indices.insert(i->block_index);
995 const std::vector < state_type > &i_bottom_states=i->bottom_states;
997 for (std::vector < state_type >::const_iterator j=i_bottom_states.begin();
998 j!=i_bottom_states.end(); ++j)
1000 total_number_of_states++;
1001 assert(*j<
aut.num_states());
1008 const std::vector < non_bottom_state > &i_non_bottom_states=i->non_bottom_states;
1009 std::set < state_type > visited;
1010 std::set < state_type > local_bottom_states;
1011 for (
typename std::vector < non_bottom_state >::const_iterator j=i_non_bottom_states.begin();
1012 j!=i_non_bottom_states.end(); ++j)
1014 local_bottom_states.insert(j->state);
1017 for (
typename std::vector < non_bottom_state >::const_iterator j=i_non_bottom_states.begin();
1018 j!=i_non_bottom_states.end(); ++j)
1020 total_number_of_states++;
1021 assert(j->state<
aut.num_states());
1024 const std::vector < state_type > &j_inert_transitions=j->inert_transitions;
1025 for (std::vector < state_type >::const_iterator k=j_inert_transitions.begin();
1026 k!=j_inert_transitions.end(); k++)
1028 total_number_of_transitions++;
1029 assert(*k<
aut.num_states());
1031 assert(visited.count(*k)>0 || local_bottom_states.count(*k)==0);
1033 visited.insert(j->state);
1039 const std::vector < transition > &i_non_inert_transitions=i->non_inert_transitions;
1040 std::set < label_type > observed_action_labels;
1041 for (std::vector < transition >::const_iterator j=i_non_inert_transitions.begin();
1042 j!=i_non_inert_transitions.end(); ++j)
1044 total_number_of_transitions++;
1045 assert(j->to()<
aut.num_states());
1046 assert(j->from()<
aut.num_states());
1049 std::vector < transition >::const_iterator j_next=j;
1051 if (j_next==i_non_inert_transitions.end() || (
aut.apply_hidden_label_map(j->label())!=
aut.apply_hidden_label_map(j_next->label())))
1053 assert(observed_action_labels.count(
aut.apply_hidden_label_map(j->label()))==0);
1054 observed_action_labels.insert(
aut.apply_hidden_label_map(j->label()));
1058 if (!preserve_divergence &&
branching &&
aut.is_tau(
aut.apply_hidden_label_map(j->label())))
1060 assert(j->to()!=j->from());
1069 assert(total_number_of_states==
aut.num_states());
1070 assert(total_number_of_transitions==
aut.num_transitions());
1081 for (std::vector < bool >::const_iterator i=
block_flags.begin();
1088 for (std::vector < bool >::const_iterator i=
state_flags.begin();
1096 std::vector < bool > temporary_block_is_in_to_be_processed(
blocks.size(),
false);
1098 for (std::vector< block_index_type > ::const_iterator i=
to_be_processed.begin();
1101 temporary_block_is_in_to_be_processed[*i]=
true;
1123template <
class LTS_TYPE>
1124void bisimulation_reduce(
1126 const bool branching =
false,
1127 const bool preserve_divergences =
false);
1142template <
class LTS_TYPE>
1143bool destructive_bisimulation_compare(
1146 const bool branching =
false,
1147 const bool preserve_divergences =
false,
1148 const bool generate_counter_examples =
false,
1149 const std::string& counter_example_file =
"",
1150 const bool structured_output =
false);
1167template <
class LTS_TYPE>
1168bool bisimulation_compare(
1171 const bool branching =
false,
1172 const bool preserve_divergences =
false,
1173 const bool generate_counter_examples =
false,
1174 const std::string& counter_example_file =
"",
1175 const bool structured_output =
false);
1181template <
class LTS_TYPE>
1183 const std::size_t s,
const std::size_t t)
1185 if (get_eq_class(s)==get_eq_class(t))
1187 throw mcrl2::runtime_error(
"Requesting a counter state formula for two bisimilar states. Such a state formula is not useful.");
1190 return counter_formula_aux(block_index_of_a_state[s], block_index_of_a_state[t]);
1194template <
class LTS_TYPE>
1196 const bool branching ,
1197 const bool preserve_divergences )
1213template <
class LTS_TYPE>
1217 const bool branching ,
1218 const bool preserve_divergences ,
1219 const bool generate_counter_examples ,
1220 const std::string& ,
1221 const bool structured_output )
1223 LTS_TYPE l1_copy(l1);
1224 LTS_TYPE l2_copy(l2);
1226 generate_counter_examples, structured_output);
1241template <
class LTS_TYPE>
1245 const bool branching ,
1246 const bool preserve_divergences ,
1247 const bool generate_counter_examples ,
1248 const std::string& counter_example_file ,
1251 std::size_t init_l2 = l2.initial_state() + l1.num_states();
1264 if (generate_counter_examples && !bisim_part.
in_same_class(l1.initial_state(), init_l2))
1268 std::string filename =
"Counterexample.mcf";
1269 if (!counter_example_file.empty())
1271 filename = counter_example_file;
1273 std::ofstream counter_file(filename);
1275 counter_file.close();
1279 return bisim_part.
in_same_class(l1.initial_state(),init_l2);
Read-only balanced binary tree of terms.
\brief A timed multi-action
const process::action_list & actions() const
This class contains strings to be used as values for action labels in lts's.
std::vector< bool > block_flags
state_type max_state_index
mcrl2::state_formulas::state_formula conjunction(std::set< mcrl2::state_formulas::state_formula > terms) const
conjunction Creates a conjunction of state formulas
std::size_t block_index_type
regular_formulas::regular_formula create_regular_formula(const mcrl2::lts::action_label_string &a) const
create_regular_formula Creates a regular formula that represents action a
regular_formulas::regular_formula create_regular_formula(const mcrl2::lps::multi_action &a) const
create_regular_formula Creates a regular formula that represents action a
std::vector< bool > block_is_in_to_be_processed
std::vector< bool > state_flags
std::map< block_index_type, block_index_type > right_child
std::vector< block_index_type > BL
bool in_same_class(const std::size_t s, const std::size_t t) const
Returns whether two states are in the same bisimulation equivalence class.
mcrl2::state_formulas::state_formula until_formula(const mcrl2::state_formulas::state_formula &phi1, const label_type &a, const mcrl2::state_formulas::state_formula &phi2)
until_formula Creates a state formula that corresponds to the until operator phi1phi2 from HMLU
std::size_t get_eq_class(const std::size_t s) const
Gives the bisimulation equivalence class number of a state.
bisim_partitioner(LTS_TYPE &l, const bool branching=false, const bool preserve_divergence=false, const bool generate_counter_examples=false)
Creates a bisimulation partitioner for an LTS.
~bisim_partitioner()=default
Destroys this partitioner.
std::map< block_index_type, std::set< block_index_type > > r_tauP
std::map< block_index_type, label_type > split_by_action
std::size_t num_eq_classes() const
Gives the number of bisimulation equivalence classes of the LTS.
void order_recursively_on_tau_reachability(const state_type s, std::map< state_type, std::vector< state_type > > &inert_transition_map, std::vector< non_bottom_state > &new_non_bottom_states, std::set< state_type > &visited)
std::vector< block_index_type > to_be_processed
std::map< block_index_type, block_index_type > split_by_block
void replace_transition_system(const bool branching, const bool preserve_divergences)
Replaces the transition relation of the current lts by the transitions of the bisimulation reduced tr...
std::vector< block > blocks
void order_on_tau_reachability(std::vector< non_bottom_state > &non_bottom_states)
void split_the_blocks_in_BL(bool &partition_is_unstable, const label_type splitter_label, const block_index_type splitter_block)
void refine_partition_until_it_becomes_stable(const bool branching, const bool preserve_divergence)
void create_initial_partition(const bool branching, const bool preserve_divergences)
std::vector< state_type > block_index_of_a_state
std::map< block_index_type, std::set< block_index_type > > r_alpha
mcrl2::state_formulas::state_formula counter_formula_aux(const block_index_type B1, const block_index_type B2)
mcrl2::state_formulas::state_formula counter_formula(const std::size_t s, const std::size_t t)
Creates a state formula that distinguishes state s from state t.
void refine_partion_with_respect_to_divergences(void)
void check_internal_consistency_of_the_partitioning_data_structure(const bool branching, const bool preserve_divergence) const
outgoing_transitions_per_state_action_t outgoing_transitions
This class contains an scc partitioner removing inert tau loops.
std::size_t get_eq_class(const std::size_t s) const
Gives the equivalence class number of a state. The equivalence class numbers range from 0 upto (and e...
void replace_transition_system(const bool preserve_divergence_loops)
The lts for which this partioner is created is replaced by the lts modulo the calculated partition.
A class containing triples, source label and target representing transitions.
size_type from() const
The source of the transition.
size_type label() const
The label of the transition.
size_type to() const
The target of the transition.
Standard exception class for reporting runtime errors.
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
This file contains a class that contains labelled transition systems in aut format.
This file contains a class that contains labelled transition systems in dot format.
This file contains a class that contains labelled transition systems in fsm format.
This file contains some utility functions to manipulate lts's.
bool bisimulation_compare(const LTS_TYPE &l1, const LTS_TYPE &l2, const bool branching=false, const bool preserve_divergences=false, const bool generate_counter_examples=false, const std::string &counter_example_file="", const bool structured_output=false)
Checks whether the two initial states of two lts's are strong or branching bisimilar.
void merge(LTS_TYPE &l1, const LTS_TYPE &l2)
void bisimulation_reduce(LTS_TYPE &l, const bool branching=false, const bool preserve_divergences=false)
Reduce transition system l with respect to strong or (divergence preserving) branching bisimulation.
bool destructive_bisimulation_compare(LTS_TYPE &l1, LTS_TYPE &l2, const bool branching=false, const bool preserve_divergences=false, const bool generate_counter_examples=false, const std::string &counter_example_file="", const bool structured_output=false)
Checks whether the two initial states of two lts's are strong or branching bisimilar.
std::multimap< std::pair< transition::size_type, transition::size_type >, transition::size_type > outgoing_transitions_per_state_action_t
Type for exploring transitions per state and action.
std::size_t to(const outgoing_pair_t &p)
Target state of a label state pair.
outgoing_transitions_per_state_action_t transitions_per_outgoing_state_action_pair(const std::vector< transition > &trans)
Provide the transitions as a multimap accessible per from state and label.
void sort_transitions(std::vector< transition > &transitions, const std::set< transition::size_type > &hidden_label_set, transition_sort_style ts=src_lbl_tgt)
Sorts the transitions using a sort style.
std::size_t from(const outgoing_transitions_per_state_action_t::const_iterator &i)
From state of an iterator exploring transitions per outgoing state and action.
void scc_reduce(LTS_TYPE &l, const bool preserve_divergence_loops=false)
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
std::vector< state_type > bottom_states
block_index_type parent_block_index
block_index_type block_index
std::vector< transition > non_inert_transitions
std::vector< non_bottom_state > non_bottom_states
std::vector< state_type > inert_transitions
non_bottom_state(const state_type s, const std::vector< state_type > &it)
non_bottom_state(const state_type s)