LCOV - code coverage report
Current view: top level - lts/include/mcrl2/lts/detail - liblts_pbisim_bem.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 294 298 98.7 %
Date: 2024-04-21 03:44:01 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Hector Joao Rivera Verduzco
       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 lts/detail/liblts_pbisim_bem.h
      10             : 
      11             : #ifndef _LIBLTS_PBISIM_BEM_H
      12             : #define _LIBLTS_PBISIM_BEM_H
      13             : #include "mcrl2/utilities/execution_timer.h"
      14             : #include "mcrl2/lts/detail/liblts_plts_merge.h"
      15             : 
      16             : namespace mcrl2
      17             : {
      18             : namespace lts
      19             : {
      20             : namespace detail
      21             : {
      22             : 
      23             : template < class LTS_TYPE>
      24             : class prob_bisim_partitioner_bem
      25             : {
      26             : 
      27             :   public:
      28             :     /** \brief Creates a probabilistic bisimulation partitioner for an PLTS.
      29             :      *  \details This bisimulation partitioner applies the algorithm defined in C. Baier, B. Engelen and M. Majster-Cederbaum. 
      30             :      *   Deciding Bisimilarity and Similarity for Probabilistic Processes. In Journal of Computer and System Sciences 60, 187-237 (2000)
      31             :      */
      32           7 :     prob_bisim_partitioner_bem(LTS_TYPE& l, utilities::execution_timer& timer):aut(l)
      33             :     {
      34           7 :       mCRL2log(log::verbose) << "Probabilistic bisimulation partitioner created for "
      35           0 :                   << l.num_states() << " states and " <<
      36           0 :                   l.num_transitions() << " transitions\n";
      37           7 :       timer.start("bisimulation_reduce (bem)");
      38           7 :       create_initial_partition();
      39           7 :       refine_partition_until_it_becomes_stable();
      40           7 :       timer.finish("bisimulation_reduce (bem)");
      41           7 :       postprocessing_stage();
      42           7 :     }
      43             : 
      44             :     /** \brief Gives the number of bisimulation equivalence classes of the LTS.
      45             :     *  \return The number of bisimulation equivalence classes of the LTS.
      46             :     */
      47           7 :     std::size_t num_eq_classes() const
      48             :     {
      49           7 :       return state_partition.size();
      50             :     }
      51             : 
      52             :     /** \brief Gives the bisimulation equivalence class number of a state.
      53             :      *  \param[in] s A state number.
      54             :      *  \return The number of the bisimulation equivalence class to which \e s belongs. */
      55         485 :     std::size_t get_eq_class(const std::size_t s) const
      56             :     {
      57         485 :       assert(s<block_index_of_a_state.size());
      58         485 :       return block_index_of_a_state[s]; // The block index is the state number of the block.
      59             :     }
      60             : 
      61             :     /** \brief Gives the bisimulation equivalence step class number of a probabilistic state.
      62             :     *  \param[in] s A probabilistic state number.
      63             :     *  \return The number of the step class to which s belongs. */
      64         292 :     std::size_t get_eq_step_class(const std::size_t d) const
      65             :     {
      66         292 :       assert(d < step_class_index_of_a_distribution.size());
      67         292 :       assert(step_class_index_of_a_distribution[d] < step_classes.size());
      68         292 :       assert(step_classes[step_class_index_of_a_distribution[d]].equivalent_step_class < step_classes.size());
      69         292 :       return step_classes[step_class_index_of_a_distribution[d]].equivalent_step_class;
      70             :     }
      71             : 
      72             :     /** \brief Replaces the transition relation of the current lts by the transitions
      73             :     *         of the bisimulation reduced transition system.
      74             :     * \pre The bisimulation equivalence classes have been computed. */
      75           7 :     void replace_transitions()
      76             :     {
      77           7 :       std::set < transition > resulting_transitions;
      78             : 
      79           7 :       const std::vector<transition>& trans = aut.get_transitions();
      80         299 :       for (const transition& t : trans)
      81             :       {
      82         292 :         resulting_transitions.insert(
      83         584 :           transition(
      84             :             get_eq_class(t.from()),
      85             :             t.label(),
      86             :             get_eq_step_class(t.to())));
      87             :       }
      88             :       // Remove the old transitions
      89           7 :       aut.clear_transitions();
      90             : 
      91             :       // Copy the transitions from the set into the transition system.
      92          47 :       for (const transition& t : resulting_transitions)
      93             :       {
      94          40 :         aut.add_transition(t);
      95             :       }
      96           7 :     }
      97             : 
      98             :     /** \brief Replaces the probabilistic states of the current lts by the probabilistic
      99             :     *         states of the bisimulation reduced transition system.
     100             :     * \pre The bisimulation step classes have been computed. */
     101           7 :     void replace_probabilistic_states()
     102             :     {
     103           7 :       std::vector<typename LTS_TYPE::probabilistic_state_t> new_probabilistic_states;
     104             : 
     105             :       // get the equivalent probabilistic state of each probabilistic block and add it to aut
     106          38 :       for (step_class_type& sc : equivalent_step_classes)
     107             :       {
     108          31 :         typename LTS_TYPE::probabilistic_state_t equivalent_ps = calculate_equivalent_probabilistic_state(sc);
     109          31 :         new_probabilistic_states.push_back(equivalent_ps);
     110             :       }
     111             : 
     112             :       /* Remove old probabilistic states */
     113           7 :       aut.clear_probabilistic_states();
     114             : 
     115             :       // Add new prob states to aut
     116          38 :       for (const typename LTS_TYPE::probabilistic_state_t& new_ps : new_probabilistic_states)
     117             :       {
     118          31 :         aut.add_probabilistic_state(new_ps);
     119             :       }
     120             : 
     121           7 :       typename LTS_TYPE::probabilistic_state_t old_initial_prob_state = aut.initial_probabilistic_state();
     122           7 :       typename LTS_TYPE::probabilistic_state_t new_initial_prob_state = calculate_new_probabilistic_state(old_initial_prob_state);
     123           7 :       aut.set_initial_probabilistic_state(new_initial_prob_state);
     124           7 :     }
     125             : 
     126             :     /** \brief Returns whether two states are in the same probabilistic bisimulation equivalence class.
     127             :     *  \param[in] s A state number.
     128             :     *  \param[in] t A state number.
     129             :     *  \retval true if \e s and \e t are in the same bisimulation equivalence class;
     130             :     *  \retval false otherwise. */
     131             :     bool in_same_probabilistic_class(const std::size_t s, const std::size_t t) const
     132             :     {
     133             :       return get_eq_step_class(s) == get_eq_step_class(t);
     134             :     }
     135             : 
     136             :   private:
     137             : 
     138             :   typedef std::size_t block_key_type;
     139             :   typedef std::size_t step_class_key_type;
     140             :   typedef std::size_t state_type;
     141             :   typedef std::size_t label_type;
     142             :   typedef std::size_t distribution_key_type;
     143             :   typedef typename LTS_TYPE::probabilistic_state_t::probability_t probability_fraction_type;
     144             : 
     145             :   struct distribution_type
     146             :   {
     147             :     distribution_key_type key;
     148             :     std::vector< std::list<transition*> > incoming_transitions_per_label; // Incoming transitions organized per label
     149             :   };
     150             :   
     151             :   struct block_type
     152             :   {
     153             :     block_key_type key;
     154             :     std::list<state_type> states;        // The states in the block
     155             :     bool is_in_new_blocks;
     156             :   };
     157             : 
     158             :   struct step_class_type {
     159             :     step_class_key_type key;
     160             :     label_type action;                                // action label of the pair <a,M>.
     161             :     std::list<distribution_type*> distributions;      // The distributions in the step class <a,M>.
     162             :     std::vector< bool > prev_states;                  // Previous states that can reach step_class <a,M>
     163             :     bool is_in_new_step_classes;
     164             :     std::size_t equivalent_step_class;
     165             :   };
     166             : 
     167             :   std::list<block_type*> state_partition;
     168             :   std::list<step_class_type*> step_partition;
     169             :   std::vector<distribution_type> distributions;
     170             :   std::deque<step_class_type> step_classes;
     171             :   std::deque<step_class_type> equivalent_step_classes;
     172             :   std::deque<block_type> blocks;
     173             :   std::vector<block_key_type> block_index_of_a_state;
     174             :   std::vector<step_class_key_type> step_class_index_of_a_distribution;
     175             : 
     176             :   LTS_TYPE& aut;
     177             : 
     178             :   struct tree_type
     179             :   {
     180             :     std::list<state_type> states;
     181             :     std::size_t count;
     182             :     tree_type* left;
     183             :     tree_type* right;
     184             : 
     185          63 :     void init_node()
     186             :     {
     187          63 :       count = 0;
     188          63 :       left = NULL;
     189          63 :       right = NULL;
     190          63 :     }
     191             :   };
     192             : 
     193             :   /** \brief Creates the initial partition of step classes and blocks.
     194             :    *  \detail The blocks are initially partitioned based on the actions that can perform.
     195             :    *          The step classes are partitioned based on the action that leads to the probabilistic state */
     196           7 :   void create_initial_partition (void) 
     197             :   {
     198           7 :     std::vector< std::vector< std::list<distribution_type*> > > steps; // Representation of transition in 2-d array
     199           7 :     std::vector<transition>& transitions = aut.get_transitions();
     200           7 :     std::vector< std::vector<bool> > distribution_per_step_class;
     201             :     
     202           7 :     distribution_per_step_class.resize(aut.num_action_labels());
     203          31 :     for (std::vector<bool>& i : distribution_per_step_class)
     204             :     {
     205          24 :       i.resize(aut.num_probabilistic_states());
     206             :     }
     207             : 
     208             :     //---- Preprocessing stage to transform the PLTS to the data structures suggested by Baier ---- //
     209             : 
     210             :     // Construct vector of distributions
     211           7 :     distributions.resize(aut.num_probabilistic_states());
     212           7 :     std::size_t key = 0;
     213          98 :     for (distribution_type& distribution : distributions)
     214             :     {
     215          91 :       distribution.key = key;
     216          91 :       distribution.incoming_transitions_per_label.resize(aut.num_action_labels());
     217          91 :       key++;
     218             :     }
     219             : 
     220             :     // Initialize the Steps bi-dimientional array
     221           7 :     steps.resize(aut.num_states());
     222         309 :     for (std::size_t i = 0; i < steps.size(); i++)
     223             :     {
     224         302 :       steps[i].resize(aut.num_action_labels());
     225             :     }
     226             : 
     227         299 :     for (transition& t : transitions)
     228             :     {
     229         292 :       steps[t.from()][t.label()].push_back(&distributions[t.to()]);
     230         292 :       distributions[t.to()].incoming_transitions_per_label[t.label()].push_back(&t);
     231             :     }
     232             : 
     233             :     //---- Start the initialization process (page 207. Fig. 10. Baier) ---- //
     234             : 
     235             :     // Initially there are as many step classes as lables
     236           7 :     step_classes.resize(aut.num_action_labels());
     237             : 
     238          31 :     for (step_class_type& sc : step_classes)
     239             :     {
     240          24 :       sc.prev_states.resize(aut.num_states());
     241             :     }
     242             : 
     243             :     // create tree structure to group action states based on the outgoing transitions
     244           7 :     std::size_t max_block_size = 0;
     245           7 :     tree_type* max_block = nullptr;
     246           7 :     std::deque<tree_type> tree_nodes;
     247           7 :     std::vector<tree_type*> leaves;
     248           7 :     tree_type v0;
     249           7 :     v0.init_node();
     250           7 :     tree_nodes.push_back(v0);
     251             : 
     252             :     // For all s in S do
     253         309 :     for (state_type s = 0; s < aut.num_states(); s++)
     254             :     {
     255             :       // (1) Create pointer to root of the tree
     256         302 :       tree_type* v = &tree_nodes[0];
     257             : 
     258             :       // (2) Construct tree
     259        1494 :       for (std::size_t i = 0; i < aut.num_action_labels(); i++)
     260             :       {
     261        1192 :         step_classes[i].key = i;
     262        1192 :         step_classes[i].action = i;
     263        1192 :         if (steps[s][i].size() > 0)
     264             :         {
     265         580 :           for (distribution_type* d : steps[s][i])
     266             :           {
     267         292 :             if (distribution_per_step_class[i][d->key] == false)
     268             :             {
     269         118 :               step_classes[i].distributions.push_back(d);
     270         118 :               distribution_per_step_class[i][d->key] = true;
     271             :             }
     272             :           }
     273             :           
     274         288 :           step_classes[i].prev_states[s] = true;
     275             : 
     276         288 :           if (v->left == NULL)
     277             :           {
     278             :             // create left node
     279          18 :             tree_type w;
     280          18 :             w.init_node();
     281          18 :             tree_nodes.push_back(w);
     282          18 :             v->left = &tree_nodes.back();
     283             : 
     284             :             // add new node to the leaf nodes if it is a leaf
     285          18 :             if (i == aut.num_action_labels() - 1)
     286             :             {
     287           7 :               leaves.push_back(v->left);
     288             :             }
     289             : 
     290          18 :           }
     291         288 :           v = v->left;
     292             :         }
     293             :         else
     294             :         {
     295         904 :           if (v->right == NULL)
     296             :           {
     297             :             // create left node
     298          38 :             tree_type w;
     299          38 :             w.init_node();
     300          38 :             tree_nodes.push_back(w);
     301          38 :             v->right = &tree_nodes.back();
     302             : 
     303             :             // add new node to the leaf nodes if it is a leaf
     304          38 :             if (i == aut.num_action_labels() - 1)
     305             :             {
     306          16 :               leaves.push_back(v->right);
     307             :             }
     308          38 :           }
     309         904 :           v = v->right;
     310             :         }
     311             :       }
     312             : 
     313             :       // (3) Add the state to the leaf and increment its state counter
     314         302 :       v->states.push_back(s);
     315         302 :       v->count++;
     316             : 
     317             :       // (4) Keep track of the leave containing more states
     318         302 :       if (v->count > max_block_size)
     319             :       {
     320         178 :         max_block = v;
     321         178 :         max_block_size = v->count;
     322             :       }
     323             :     }
     324             : 
     325             :     // insert all states of the leaves to blocks
     326           7 :     blocks.resize(leaves.size());
     327             : 
     328           7 :     key = 0;
     329           7 :     std::size_t larger_key = 0;
     330          30 :     for (tree_type* leaf_ptr : leaves)
     331             :     {
     332          23 :       block_type& block = blocks[key];
     333          23 :       block.key = key;
     334          23 :       block.states.splice(block.states.end(), leaf_ptr->states);
     335             : 
     336          23 :       if (leaf_ptr == max_block)
     337             :       {
     338           7 :         larger_key = key;
     339             :       }
     340             : 
     341          23 :       key++;
     342             :     }
     343             : 
     344             :     // Add all blocks to the state partition.
     345             :     // Initially only the larger block has to be at the end of the list
     346          30 :     for (block_type& b : blocks)
     347             :     {
     348          23 :       if (b.key != larger_key)
     349             :       {
     350             :         // Push the non-larger blocks to the front of the list. This are the so called new blocks
     351          16 :         b.is_in_new_blocks = true;
     352          16 :         state_partition.push_front(&b);
     353             :       }
     354             :       else
     355             :       {
     356             :         // push the larger block to the end of the list
     357           7 :         b.is_in_new_blocks = false;
     358           7 :         state_partition.push_back(&b);
     359             :       }
     360             :     }
     361             :     
     362             :     // Add all step classes to the step partition.
     363             :     // Initially all are new step classes
     364          31 :     for (step_class_type& sc : step_classes)
     365             :     {
     366          24 :       if (sc.distributions.size() > 0)
     367             :       { 
     368          18 :         step_partition.push_front(&sc);
     369          18 :         sc.is_in_new_step_classes = true;
     370             :       }
     371             :     }
     372             : 
     373           7 :     block_index_of_a_state.resize(aut.num_states());
     374          30 :     for (const block_type& b : blocks)
     375             :     {
     376         325 :       for (const state_type s : b.states)
     377             :       {
     378         302 :         block_index_of_a_state[s] = b.key;
     379             :       }
     380             :     }
     381             : 
     382           7 :   }
     383             : 
     384             :   /** \brief Calculates the probability to reach block b from distribution d.
     385             :   *   \param[in] d is a probabilistic state (distribution).
     386             :   *              b is a block of states.
     387             :   *   \return The probability to reach block b. */
     388        1189 :   probability_fraction_type probability_to_block(distribution_type& d, block_type& b)
     389             :   {
     390        1189 :     probability_fraction_type prob_to_block;
     391        1189 :     const typename LTS_TYPE::probabilistic_state_t& prob_state = aut.probabilistic_state(d.key);
     392             : 
     393             :     /* Check whether the state is in the distribution d and add up the probability*/
     394        1189 :     if (prob_state.size()>1)   // The state is stored as a vector of states/probabilities.
     395             :     {
     396        5208 :       for (const typename LTS_TYPE::probabilistic_state_t::state_probability_pair& prob_pair : prob_state)
     397             :       {
     398        4153 :         const state_type s = prob_pair.state();
     399        4153 :         if (block_index_of_a_state[s] == b.key)
     400             :         {
     401         355 :           prob_to_block = prob_to_block + prob_pair.probability();
     402             :         }
     403             :       }
     404             :     }
     405             :     else // The state consists of a number with implicit probability 1.
     406             :     {
     407         134 :       const state_type s = prob_state.get();
     408         134 :       if (block_index_of_a_state[s] == b.key)
     409             :       {
     410          16 :         prob_to_block = prob_to_block + LTS_TYPE::probabilistic_state_t::probability_t::one();
     411             :       }
     412             :     }
     413             : 
     414        1189 :     return prob_to_block;
     415           0 :   }
     416             : 
     417          77 :   typename LTS_TYPE::probabilistic_state_t calculate_new_probabilistic_state(typename LTS_TYPE::probabilistic_state_t ps)
     418             :   {
     419          77 :     typename LTS_TYPE::probabilistic_state_t new_prob_state;
     420             : 
     421             :     /* Iterate over all state probability pairs in the selected probabilistic state*/
     422          77 :     if (ps.size()>1)   // The state is stored as a vector of states/probabilities.
     423             :     {
     424          44 :       std::map<state_type, probability_fraction_type> prob_state_map;
     425         204 :       for (const typename LTS_TYPE::probabilistic_state_t::state_probability_pair& sp_pair : ps)
     426             :       {
     427             :         /* Check the resulting action state in the final State partition */
     428         160 :         state_type new_state = get_eq_class(sp_pair.state());
     429             : 
     430         160 :         if (prob_state_map.count(new_state) == 0)
     431             :         {
     432             :           /* The state is not yet in the mapping. Add the state with its probability*/
     433         112 :           prob_state_map[new_state] = sp_pair.probability();
     434             :         }
     435             :         else
     436             :         {
     437             :           /* The state is already in the mapping. Sum up probabilities */
     438          48 :           prob_state_map[new_state] = prob_state_map[new_state] + sp_pair.probability();
     439             :         }
     440             :       }
     441             :       /* Add all the state probabilities pairs in the mapping to its actual data type*/
     442             :       /* Add all the state probabilities pairs in the mapping to its actual data type*/
     443          44 :       typename std::map<state_type, probability_fraction_type>::const_iterator i = prob_state_map.begin();
     444          44 :       if (++i==prob_state_map.end()) // There is only one state with probability 1. 
     445             :       {
     446          13 :         new_prob_state.set(prob_state_map.begin()->first);
     447             :       }
     448             :       else
     449             :       {
     450             :         // This probabilistic state has more components. 
     451         130 :         for (const std::pair<const state_type, probability_fraction_type>& i: prob_state_map)
     452             :         {
     453          99 :           new_prob_state.add(i.first, i.second);
     454             :         }
     455             :       }
     456             : 
     457          44 :     }
     458             :     else // The state consists of a number with implicit probability 1.
     459             :     {
     460             :       /* Check the resulting action state in the final State partition */
     461          33 :       new_prob_state.set(get_eq_class(ps.get()));
     462             :     }
     463             : 
     464          77 :     return new_prob_state;
     465           0 :   }
     466             : 
     467          70 :   typename LTS_TYPE::probabilistic_state_t calculate_equivalent_probabilistic_state(step_class_type& sc)
     468             :   {
     469          70 :     typename LTS_TYPE::probabilistic_state_t equivalent_prob_state;
     470             : 
     471             :     /* Select the first probabilistic state of the step class */
     472          70 :     distribution_type* d = sc.distributions.front();
     473             : 
     474          70 :     typename LTS_TYPE::probabilistic_state_t old_prob_state = aut.probabilistic_state(d->key);
     475             : 
     476          70 :     equivalent_prob_state = calculate_new_probabilistic_state(old_prob_state);
     477             : 
     478         140 :     return equivalent_prob_state;
     479          70 :   }
     480             : 
     481             :   /** \brief  Two-phased partitioning algorithm described in page 204. Fig 9. Baier.
     482             :   *   \detail Refinement of state partition and step partition until no new blocks/step classes
     483             :   *           are in front of the partition lists
     484             :   */
     485           7 :   void refine_partition_until_it_becomes_stable (void) 
     486             :   {
     487           7 :     std::list<step_class_type*> step_partition_old;
     488           7 :     std::list<block_type*> state_partition_old;
     489             : 
     490             :     // Repeat until no new blocks in front of the partition lists
     491          71 :     while (state_partition.front()->is_in_new_blocks == true || 
     492          24 :            (step_partition.size()>0 && step_partition.front()->is_in_new_step_classes == true))
     493             :     { 
     494             :       // Phase 1: Splitting of step_partition via split(M,C)
     495          40 :       if (state_partition.front()->is_in_new_blocks == true)
     496             :       {
     497             :         // Choose a new block in front of the state partition and change it to the back of the list
     498          35 :         block_type* c_block = state_partition.front();
     499          35 :         state_partition.pop_front();
     500          35 :         state_partition.push_back(c_block);
     501          35 :         c_block->is_in_new_blocks = false;
     502             : 
     503             :         // swap elements of step_partition (M) to step_partition_old (M_old)
     504          35 :         step_partition_old.swap(step_partition);
     505             : 
     506             :         // vector to add the new step classes
     507          35 :         static std::vector<typename std::list<step_class_type*>::iterator> pending_new_step_classes;
     508          35 :         pending_new_step_classes.clear();
     509             : 
     510             :         // iterate over all step classes
     511          35 :         for (typename std::list<step_class_type*>::iterator sc_iter = step_partition_old.begin(); 
     512         264 :           sc_iter != step_partition_old.end(); ++sc_iter)
     513             :         {
     514         229 :           step_class_type* sc_ptr = *sc_iter;
     515             : 
     516             :           // Mapping to sort the distributions based on its probability to reach a block, instead of using
     517             :           // an ordered balanced tree as suggsted in Baier
     518         229 :           static std::map< probability_fraction_type, std::list<distribution_type*> > distributions_ordered_by_prob;
     519         229 :           distributions_ordered_by_prob.clear();
     520             : 
     521             :           // Iterate over all distributions d of the step class and add probability to block to vector
     522        1418 :           for (distribution_type* d : sc_ptr->distributions)
     523             :           {
     524        1189 :             probability_fraction_type probability = probability_to_block(*d, *c_block);
     525        1189 :             distributions_ordered_by_prob[probability].push_back(d);
     526             :           }
     527             : 
     528             :           // if there are multiple elements in the distributions_ordered_by_prob then we have to 
     529             :           // add them to the step partition as a new step class
     530         229 :           if (distributions_ordered_by_prob.size() >= 2)
     531             :           {
     532             :             step_class_type* new_step_class_ptr;
     533             : 
     534          19 :             step_classes.resize(step_classes.size() + distributions_ordered_by_prob.size() - 1);
     535             :             // iterate over mapping
     536          19 :             std::size_t new_class_count = 0;
     537          59 :             for (std::pair< probability_fraction_type, std::list<distribution_type*> > ordered_dist : distributions_ordered_by_prob)
     538             :             {
     539          40 :               std::list<distribution_type*>& distribution_list = ordered_dist.second;
     540             :               
     541          40 :               if (new_class_count == 0)
     542             :               {
     543             :                 // if it is the first element of the mapping then we do not create a new step class; instead, we 
     544             :                 // add its elements into the current step class that is being split.
     545          19 :                 sc_ptr->distributions.swap(distribution_list);
     546             : 
     547             :                 // clear the prev states of the current step class
     548        2538 :                 for (std::size_t i = 0; i < sc_ptr->prev_states.size(); i++)
     549             :                 {
     550        2519 :                   sc_ptr->prev_states[i] = false;
     551             :                 }
     552             : 
     553             :                 // recalculate prev states based on incoming transitions of each distribution
     554         215 :                 for (distribution_type* d : sc_ptr->distributions)
     555             :                 {
     556         664 :                   for (transition* t_ptr : d->incoming_transitions_per_label[sc_ptr->action])
     557             :                   {
     558         468 :                     sc_ptr->prev_states[t_ptr->from()] = true;
     559             :                   }
     560             :                 }
     561             : 
     562             :                 // add to the front of the step partition (as a new step class) if not yet added 
     563          19 :                 if (sc_ptr->is_in_new_step_classes == false)
     564             :                 {
     565             :                   // since we are iterating over this step class, we save its iterator in a pending vector,
     566             :                   // so later we can add it to the front of the step partition (outside the loop)
     567           1 :                   pending_new_step_classes.push_back(sc_iter);
     568           1 :                   sc_ptr->is_in_new_step_classes = true;
     569             :                 }
     570             :               }
     571             :               else
     572             :               {
     573          21 :                 new_step_class_ptr = &step_classes[step_classes.size() - new_class_count];
     574             : 
     575             :                 //init new step class
     576          21 :                 new_step_class_ptr->key = step_classes.size() - new_class_count;
     577          21 :                 new_step_class_ptr->action = sc_ptr->action;
     578          21 :                 new_step_class_ptr->is_in_new_step_classes = true;
     579          21 :                 new_step_class_ptr->prev_states.resize(aut.num_states());
     580          21 :                 new_step_class_ptr->distributions.swap(distribution_list);
     581             : 
     582             :                 // recalculate prev states based ont incoming transitions of each distribution
     583         101 :                 for (distribution_type* d : new_step_class_ptr->distributions)
     584             :                 {
     585         290 :                   for (transition* t_ptr : d->incoming_transitions_per_label[new_step_class_ptr->action])
     586             :                   {
     587         210 :                     new_step_class_ptr->prev_states[t_ptr->from()] = true;
     588             :                   }
     589             :                 }
     590             : 
     591             :                 // add new step class to step partition and new_step_classes
     592          21 :                 step_partition.push_front(new_step_class_ptr);
     593             :               }
     594          40 :               new_class_count++;
     595             :             }
     596             :           }
     597             :         }
     598             : 
     599             :         // Add the pending new blocks to the front of the list
     600          36 :         for (typename std::list<step_class_type*>::iterator sc_iter : pending_new_step_classes)
     601             :         {
     602           1 :           step_partition.splice(step_partition.begin(), step_partition_old, sc_iter);
     603             :         }
     604             : 
     605             :         // move remaining step classes to the end of step partition
     606          35 :         step_partition.splice(step_partition.end(), step_partition_old);
     607             :       }
     608             : 
     609             :       // Phase 2: Refinment of state_partition via Refine(X,a,M)
     610          40 :       if (step_partition.front()->is_in_new_step_classes == true)
     611             :       {
     612             :         // Choose some step class <a,M> in new_step_classes and remove it from new_step_classes
     613          40 :         step_class_type* step_class = step_partition.front();
     614          40 :         step_partition.pop_front();
     615          40 :         step_class->is_in_new_step_classes = false;
     616          40 :         step_partition.push_back(step_class);
     617             : 
     618             :         // swap elements of state_partition (X) to state_partition_old (X_old)
     619          40 :         state_partition_old.swap(state_partition);
     620             : 
     621          40 :         static std::vector<typename std::list<block_type*>::iterator > blocks_to_move_to_front;
     622          40 :         static std::vector<block_type*> new_blocks_to_move_to_front;
     623          40 :         blocks_to_move_to_front.clear();
     624          40 :         new_blocks_to_move_to_front.clear();
     625             :         // for all blocks B in X_old
     626             :         //for (block_type* b_to_split : state_partition_old)
     627         309 :         for (typename std::list<block_type*>::iterator block_iter = state_partition_old.begin(); 
     628         309 :           block_iter != state_partition_old.end(); ++block_iter)
     629             :         {
     630         269 :           block_type* b_to_split = *block_iter;
     631         269 :           block_type new_block;
     632         269 :           new_block.is_in_new_blocks = false;
     633         269 :           block_type temp_block;
     634             :           block_type* new_block_ptr;
     635         269 :           new_block.key = blocks.size();
     636             : 
     637             :           // iterate over all states in the block to split
     638        3955 :           for (state_type s : b_to_split->states)
     639             :           {
     640             :             // if block b can reach step class <a,M> starting from state s then we move it to antoher block
     641        3686 :             if (step_class->prev_states[s] == true)
     642             :             {
     643         323 :               new_block.states.push_back(s);
     644             :             }
     645             :             else
     646             :             {
     647        3363 :               temp_block.states.push_back(s);
     648             :             }
     649             :           }
     650             : 
     651             :           // if both, the new block and temp block has elements, then we add a new block into the state partition
     652         269 :           if (new_block.states.size() > 0 && temp_block.states.size() > 0)
     653             :           {
     654             : 
     655             :             // First update the block_index_of_a_state by iterating over all states of the new block
     656         182 :             for (state_type s : new_block.states)
     657             :             {
     658         163 :               block_index_of_a_state[s] = new_block.key;
     659             :             }
     660             : 
     661          19 :             blocks.push_back(new_block);
     662          19 :             new_block_ptr = &blocks.back();
     663             : 
     664             :             // return states from temp_block to b_to_split
     665          19 :             b_to_split->states.swap(temp_block.states);
     666             :             
     667             :             // if the current block is not currently a new block then add the smaller
     668             :             // block (between new block and current block) to the list of new blocks; 
     669             :             // hence, in front of state partition
     670          19 :             if (b_to_split->is_in_new_blocks == false)
     671             :             {
     672          14 :               if (new_block_ptr->states.size() < b_to_split->states.size())
     673             :               {
     674          11 :                 new_blocks_to_move_to_front.push_back(new_block_ptr);
     675          11 :                 new_block_ptr->is_in_new_blocks = true;
     676             :               }
     677             :               else
     678             :               {
     679           3 :                 b_to_split->is_in_new_blocks = true;
     680           3 :                 blocks_to_move_to_front.push_back(block_iter);
     681             : 
     682             :                 // add new block to the back of the state_partition list
     683           3 :                 state_partition.push_back(new_block_ptr);
     684             :               }
     685             :             }
     686             :             else
     687             :             {
     688             :               // the current block is already in new blocks; hence, just add the new block
     689             :               // to the front of the partition as well.
     690             : 
     691             :               //new_blocks.push_back(new_block_ptr);
     692           5 :               new_block_ptr->is_in_new_blocks = true;
     693             : 
     694             :               // add new block to the back of the state_partition list
     695           5 :               new_blocks_to_move_to_front.push_back(new_block_ptr);
     696             :             }
     697             :           }
     698             :         }
     699             : 
     700             :         // move the remaning blocks to the end of state_partition
     701          40 :         state_partition.splice(state_partition.begin(), state_partition_old);
     702             : 
     703             :         // move the blocks that should be in new blocks to the begining of the list
     704          43 :         for (typename std::list<block_type*>::iterator block_iter : blocks_to_move_to_front)
     705             :         {
     706           3 :           state_partition.splice(state_partition.begin(), state_partition, block_iter);
     707             :         }
     708             : 
     709             :         // move the blocks that are new to the front of state_partition
     710          56 :         for (block_type* block_ptr : new_blocks_to_move_to_front)
     711             :         {
     712          16 :           state_partition.push_front(block_ptr);
     713             :         }
     714             : 
     715             :       }
     716             :     }
     717           7 :   }
     718             : 
     719           7 :   void postprocessing_stage(void)
     720             :   {
     721             :     //---- Post processing to keep track of the parent block of each state ----//
     722             :     //block_index_of_a_state.resize(aut.num_states());
     723          49 :     for (const block_type& b : blocks)
     724             :     {
     725         344 :       for (const state_type s : b.states)
     726             :       {
     727         302 :         block_index_of_a_state[s] = b.key;
     728             :       }
     729             :     }
     730             : 
     731           7 :     step_class_index_of_a_distribution.resize(aut.num_probabilistic_states());
     732          52 :     for (step_class_type& sc : step_classes)
     733             :     {
     734          45 :       sc.equivalent_step_class = sc.key;
     735             : 
     736         163 :       for (const distribution_type* d : sc.distributions)
     737             :       {
     738         118 :         step_class_index_of_a_distribution[d->key] = sc.key;
     739             :       }
     740             :     }
     741             : 
     742             :     // Merge equivalent step classes in step partition. The algorithm initially separates classes by actions,
     743             :     // but it is possible that a probabilistic sate has multiple incoming actions, hence it will be repeated
     744             :     // among multiple step classes
     745           7 :     typename LTS_TYPE::probabilistic_state_t new_prob_state;
     746           7 :     std::unordered_map<typename LTS_TYPE::probabilistic_state_t, std::vector<step_class_type*> > reduced_step_partition;
     747             : 
     748             :     /* Iterate over all step classes of Step_partition*/
     749          52 :     for (step_class_type& sc : step_classes)
     750             :     {
     751          45 :       if (sc.distributions.size() > 0)
     752             :       {
     753          39 :         typename LTS_TYPE::probabilistic_state_t new_prob_state = calculate_equivalent_probabilistic_state(sc);
     754             : 
     755             :         /* Add the step class index to the new probability state*/
     756          39 :         reduced_step_partition[new_prob_state].push_back(&sc);
     757          39 :       }
     758             :     }
     759             : 
     760           7 :     step_class_key_type equivalent_class_key = 0;
     761           7 :     for (typename std::unordered_map<typename LTS_TYPE::probabilistic_state_t,
     762           7 :           std::vector<step_class_type*> >::iterator i = reduced_step_partition.begin();
     763          38 :           i != reduced_step_partition.end(); ++i)
     764             :     {
     765          31 :       std::vector<step_class_type*>& sc_vector = i->second;
     766             : 
     767          31 :       equivalent_step_classes.push_back(*sc_vector[0]);
     768          70 :       for (step_class_type* sc :sc_vector)
     769             :       {
     770          39 :         sc->equivalent_step_class = equivalent_class_key;
     771             :       }
     772          31 :       equivalent_class_key++;
     773             :     }
     774             : 
     775           7 :   }
     776             : };
     777             : 
     778             : 
     779             : /** \brief Reduce transition system l with respect to probabilistic bisimulation.
     780             :  * \param[in/out] l The transition system that is reduced.
     781             :  * \param[in/out] timer A timer that can be used to report benchmarking results.
     782             :  */
     783             : template < class LTS_TYPE>
     784             : void probabilistic_bisimulation_reduce_bem(LTS_TYPE& l, utilities::execution_timer& timer);
     785             : 
     786             : /** \brief Checks whether the two initial states of two plts's are probabilistic bisimilar.
     787             : * \details This lts and the lts l2 are not usable anymore after this call.
     788             : * \param[in/out] l1 A first probabilistic transition system.
     789             : * \param[in/out] l2 A second probabilistic transition system.
     790             : * \retval True iff the initial states of the current transition system and l2 are probabilistic bisimilar */
     791             : template < class LTS_TYPE>
     792             : bool destructive_probabilistic_bisimulation_compare_bem(LTS_TYPE& l1, LTS_TYPE& l2, utilities::execution_timer& timer);
     793             : 
     794             : 
     795             : /** \brief Checks whether the two initial states of two plts's are probabilistic bisimilar.
     796             : *  \details The current transitions system and the lts l2 are first duplicated and subsequently
     797             : *           reduced modulo bisimulation. If memory space is a concern, one could consider to
     798             : *           use destructive_bisimulation_compare.
     799             : * \param[in/out] l1 A first transition system.
     800             : * \param[in/out] l2 A second transistion system.
     801             : * \retval True iff the initial states of the current transition system and l2 are probabilistic bisimilar */
     802             : template < class LTS_TYPE>
     803             : bool probabilistic_bisimulation_compare_bem(const LTS_TYPE& l1, const LTS_TYPE& l2, utilities::execution_timer& timer);
     804             : 
     805             : 
     806             : template < class LTS_TYPE>
     807           7 : void probabilistic_bisimulation_reduce_bem(LTS_TYPE& l, utilities::execution_timer& timer)
     808             : {
     809             : 
     810             :   // Apply the probabilistic bisimulation reduction algorithm.
     811           7 :   detail::prob_bisim_partitioner_bem<LTS_TYPE> prob_bisim_part(l,timer);
     812             : 
     813             :   // Clear the state labels of the LTS l
     814           7 :   l.clear_state_labels();
     815             : 
     816             :   // Assign the reduced LTS
     817           7 :   l.set_num_states(prob_bisim_part.num_eq_classes());
     818           7 :   prob_bisim_part.replace_transitions();
     819           7 :   prob_bisim_part.replace_probabilistic_states();
     820           7 : }
     821             : 
     822             : template < class LTS_TYPE>
     823             : bool probabilistic_bisimulation_compare_bem(
     824             :   const LTS_TYPE& l1,
     825             :   const LTS_TYPE& l2,
     826             :   utilities::execution_timer& timer)
     827             : {
     828             :   LTS_TYPE l1_copy(l1);
     829             :   LTS_TYPE l2_copy(l2);
     830             :   return destructive_probabilistic_bisimulation_compare_bem(l1_copy, l2_copy, timer);
     831             : }
     832             : 
     833             : template < class LTS_TYPE>
     834             : bool destructive_probabilistic_bisimulation_compare_bem(
     835             :   LTS_TYPE& l1,
     836             :   LTS_TYPE& l2,
     837             :   utilities::execution_timer& timer)
     838             : {
     839             :   std::size_t initial_probabilistic_state_key_l1;
     840             :   std::size_t initial_probabilistic_state_key_l2;
     841             : 
     842             :   // Merge states
     843             :   mcrl2::lts::detail::plts_merge(l1, l2);
     844             :   l2.clear(); // No use for l2 anymore.
     845             : 
     846             :               // The last two probabilistic states are the initial states of l2 and l1
     847             :               // in the merged plts
     848             :   initial_probabilistic_state_key_l2 = l1.num_probabilistic_states() - 1;
     849             :   initial_probabilistic_state_key_l1 = l1.num_probabilistic_states() - 2;
     850             : 
     851             :   detail::prob_bisim_partitioner_bem<LTS_TYPE> prob_bisim_part(l1, timer);
     852             : 
     853             :   return prob_bisim_part.in_same_probabilistic_class(initial_probabilistic_state_key_l2,
     854             :     initial_probabilistic_state_key_l1);
     855             : }
     856             : 
     857             : }  // namespace detail
     858             : }  // namespace lts
     859             : }  // namespace mcrl2
     860             : 
     861             : #endif // _LIBLTS_PBISIM_BEM_H

Generated by: LCOV version 1.14