LCOV - code coverage report
Current view: top level - lts/include/mcrl2/lts - sigref.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 141 149 94.6 %
Date: 2019-06-26 00:32:26 Functions: 32 35 91.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Jeroen Keiren
       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 sigref.h
      10             : /// \brief Implementation of LTS reductions using the signature refinement
      11             : ///        approach of S. Blom and S. Orzan.
      12             : /// \author Jeroen Keiren
      13             : 
      14             : #ifndef MCRL2_LTS_SIGREF_H
      15             : #define MCRL2_LTS_SIGREF_H
      16             : 
      17             : #include <set>
      18             : #include <map>
      19             : #include <iostream>
      20             : #include "mcrl2/lts/lts.h"
      21             : #include "mcrl2/lts/lts_utilities.h"
      22             : #include "mcrl2/utilities/logger.h"
      23             : 
      24             : namespace mcrl2
      25             : {
      26             : namespace lts
      27             : {
      28             : 
      29             : /** \brief A signature is a pair of an action label and a block */
      30             : typedef std::set<std::pair<std::size_t, std::size_t> > signature_t;
      31             : 
      32             : /** \brief Base class for signature computation */
      33             : template < class LTS_T >
      34          52 : class signature
      35             : {
      36             : protected:
      37             :   /** \brief The labelled transition system for which the signature is computed */
      38             :   const LTS_T& m_lts;
      39             : 
      40             :   /** \brief Signature stored per state */
      41             :   std::vector<signature_t> m_sig;
      42             : 
      43             : public:
      44             :   /** \brief Constructor
      45             :     */
      46          52 :   signature(const LTS_T& lts_)
      47          52 :     : m_lts(lts_), m_sig(m_lts.num_states(), signature_t())
      48          52 :   {}
      49             : 
      50             :   /** \brief Compute a new signature based on \a partition.
      51             :     * \param[in] partition The current partition
      52             :     */
      53             :   virtual void compute_signature(const std::vector<std::size_t>& partition) = 0;
      54             : 
      55             :   /** \brief Compute the transitions for the quotient according to \a partition.
      56             :     * \param[in] partition The partition that is used to compute the quotient
      57             :     * \param[out] transitions A set to which the transitions of the quotient are written
      58             :     */
      59          19 :   virtual void quotient_transitions(std::set<transition>& transitions, const std::vector<std::size_t>& partition)
      60             :   {
      61         247 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
      62             :     {
      63         228 :       transitions.insert(transition(partition[i->from()], i->label(), partition[i->to()]));
      64             :     }
      65          19 :   }
      66             : 
      67             :   /** \brief Return the signature for state \a i.
      68             :     * \param[in] i The state for which to return the signature.
      69             :     * \pre i < m_lts.num_states().
      70             :     */
      71        4964 :   virtual const signature_t& get_signature(std::size_t i) const
      72             :   {
      73        4964 :     return m_sig[i];
      74             :   }
      75             : };
      76             : 
      77             : /** \brief Class for computing the signature for strong bisimulation */
      78             : template < class LTS_T >
      79          19 : class signature_bisim: public signature<LTS_T>
      80             : {
      81             : protected:
      82             :   using signature<LTS_T>::m_lts;
      83             :   using signature<LTS_T>::m_sig;
      84             : 
      85             : public:
      86             :   /** \brief Constructor */
      87          19 :   signature_bisim(const LTS_T& lts_)
      88          19 :     : signature<LTS_T>(lts_)
      89             :   {
      90          19 :     mCRL2log(log::verbose, "sigref") << "initialising signature computation for strong bisimulation" << std::endl;
      91          19 :   }
      92             : 
      93             :   /** \overload */
      94             :   virtual void
      95          58 :   compute_signature(const std::vector<std::size_t>& partition)
      96             :   {
      97             :     // compute signatures
      98          58 :     m_sig = std::vector<signature_t>(m_lts.num_states(), signature_t());
      99        1350 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
     100             :     {
     101        1292 :       m_sig[i->from()].insert(std::make_pair(m_lts.apply_hidden_label_map(i->label()), partition[i->to()]));
     102             :     }
     103          58 :   }
     104             : 
     105             : };
     106             : 
     107             : /** \brief Class for computing the signature for branching bisimulation */
     108             : template < class LTS_T >
     109          33 : class signature_branching_bisim: public signature<LTS_T>
     110             : {
     111             : protected:
     112             :   using signature<LTS_T>::m_lts;
     113             :   using signature<LTS_T>::m_sig;
     114             : 
     115             :   /** \brief Store the incoming transitions per state */
     116             :   outgoing_transitions_per_state_t m_prev_transitions;
     117             : 
     118             :   /** \brief Insert function
     119             :     * \param[in] partition The current partition
     120             :     * \param[in] t source state
     121             :     * \param[in] label_ transition label
     122             :     * \param[in] block target block
     123             :     *
     124             :     * Insert function as described in S. Blom, S. Orzan,
     125             :     * "Distributed Branching Bisimulation Reduction of State Spaces",
     126             :     * Proc. PDMC 2003.
     127             :     *
     128             :     * Inserts the pair (label_, block) in the signature of t, as well as
     129             :     * the signatures of all tau-predecessors of t within the same block.
     130             :     */
     131        1995 :   void insert(const std::vector<std::size_t>& partition, const std::size_t t, const std::size_t label_, const std::size_t block)
     132             :   {
     133        1995 :     if(m_sig[t].insert(std::make_pair(label_, block)).second)
     134             :     {
     135             :       std::pair<outgoing_transitions_per_state_t::const_iterator, outgoing_transitions_per_state_t::const_iterator> pred_range
     136        1684 :           = m_prev_transitions.equal_range(t);
     137             : 
     138        4300 :       for(outgoing_transitions_per_state_t::const_iterator i = pred_range.first; i != pred_range.second; ++i)
     139             :       {
     140        2616 :         assert(from(i) == t);
     141        2616 :         if(m_lts.is_tau(label(i)) && partition[from(i)] == partition[to(i)])
     142             :         {
     143        1077 :           insert(partition, to(i), label_, block);
     144             :         }
     145             :       }
     146             :     }
     147        1995 :   }
     148             : 
     149             : public:
     150             :   /** \brief Constructor  */
     151          33 :   signature_branching_bisim(const LTS_T& lts_)
     152             :     : signature<LTS_T>(lts_),
     153          33 :       m_prev_transitions(transitions_per_outgoing_state_reversed(lts_.get_transitions(),lts_.hidden_label_map()))
     154             :   {
     155          33 :     mCRL2log(log::verbose, "sigref") << "initialising signature computation for branching bisimulation" << std::endl;
     156          33 :   }
     157             : 
     158             :   /** \overload */
     159          46 :   virtual void compute_signature(const std::vector<std::size_t>& partition)
     160             :   {
     161             :     // compute signatures
     162          46 :     m_sig = std::vector<signature_t>(m_lts.num_states(), signature_t());
     163         933 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
     164             :     {
     165         887 :       if (!(m_lts.is_tau(m_lts.apply_hidden_label_map(i->label())) && (partition[i->from()] == partition[i->to()])))
     166             :       {
     167         533 :         insert(partition, i->from(), m_lts.apply_hidden_label_map(i->label()), partition[i->to()]);
     168             :       }
     169             :     }
     170          46 :   }
     171             : 
     172             :   /** \overload */
     173          19 :   virtual void quotient_transitions(std::set<transition>& transitions, const std::vector<std::size_t>& partition)
     174             :   {
     175         305 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
     176             :     {
     177         286 :       if(partition[i->from()] != partition[i->to()] || !m_lts.is_tau(m_lts.apply_hidden_label_map(i->label())))
     178             :       {
     179         166 :         transitions.insert(transition(partition[i->from()], m_lts.apply_hidden_label_map(i->label()), partition[i->to()]));
     180             :       }
     181             :     }
     182          19 :   }
     183             : };
     184             : 
     185             : /** \brief Class for computing the signature for divergence preserving branching bisimulation */
     186             : template < class LTS_T >
     187          14 : class signature_divergence_preserving_branching_bisim: public signature_branching_bisim<LTS_T>
     188             : {
     189             : protected:
     190             :   using signature_branching_bisim<LTS_T>::m_lts;
     191             :   using signature_branching_bisim<LTS_T>::m_sig;
     192             :   using signature_branching_bisim<LTS_T>::insert;
     193             : 
     194             :   /** \brief Record for each vertex whether it is in a tau-scc */
     195             :   std::vector<bool> m_divergent;
     196             : 
     197             :   /** \brief Iterative implementation of Tarjan's SCC algorithm.
     198             :    *
     199             :    * based on an earlier implementation by Sjoerd Cranen
     200             :    *
     201             :    * For the non-trivial tau-sccs (i.e. SCCs with more than one state, or
     202             :    * single-state SCCs with a tau-loop, we set \a m_divergent to true.
     203             :    */
     204          14 :   void compute_tau_sccs()
     205             :   {
     206          14 :     std::size_t unused = 1, lastscc = 1;
     207          28 :     std::vector<std::size_t> scc(m_lts.num_states(), 0);
     208          28 :           std::vector<std::size_t> low(m_lts.num_states(), 0);
     209          28 :           std::stack<std::size_t> stack;
     210          28 :           std::stack<std::size_t> sccstack;
     211             : 
     212             :     // Record forward transition relation sorted by state.
     213          28 :           outgoing_transitions_per_state_t m_lts_succ_transitions(transitions_per_outgoing_state(m_lts.get_transitions(),m_lts.hidden_label_map()));
     214             : 
     215         172 :           for (std::size_t i = 0; i < m_lts.num_states(); ++i)
     216             :           {
     217         158 :                   if (scc[i] == 0)
     218          52 :                           stack.push(i);
     219         790 :                   while (!stack.empty())
     220             :                   {
     221         316 :                           std::size_t vi = stack.top();
     222             : 
     223             :         // Outgoing transitions of vi.
     224             :         std::pair<outgoing_transitions_per_state_t::const_iterator, outgoing_transitions_per_state_t::const_iterator> succ_range
     225         316 :           = m_lts_succ_transitions.equal_range(vi);
     226             : 
     227         316 :                           if (low[vi] == 0 && scc[vi] == 0)
     228             :                           {
     229         158 :                                   scc[vi] = unused;
     230         158 :                                   low[vi] = unused++;
     231         158 :                                   sccstack.push(vi);
     232             : 
     233         361 :                                   for (outgoing_transitions_per_state_t::const_iterator t = succ_range.first; t != succ_range.second; ++t)
     234             :                                   {
     235         203 :             if ((low[to(t)] == 0) && (scc[to(t)] == 0) && (m_lts.is_tau(label(t))))
     236             :                                           {
     237         106 :                                                   stack.push(to(t));
     238             :                                                 }
     239             :                                   }
     240             :                           }
     241             :                           else
     242             :                           {
     243         361 :                                   for (outgoing_transitions_per_state_t::const_iterator t = succ_range.first; t != succ_range.second; ++t)
     244             :                                   {
     245         203 :                                           if ((low[to(t)] != 0) && (m_lts.is_tau(label(t))))
     246          73 :                                                   low[vi] = low[vi] < low[to(t)] ? low[vi] : low[to(t)];
     247             :                                   }
     248         158 :                                   if (low[vi] == scc[vi])
     249             :                                   {
     250         107 :                                           std::size_t tos, scc_id = lastscc++;
     251         214 :             std::vector<std::size_t> this_scc;
     252         158 :                                           do
     253             :                                           {
     254         158 :                                                   tos = sccstack.top();
     255         158 :                                                   low[tos] = 0;
     256         158 :                                                   scc[tos] = scc_id;
     257         158 :                                                   sccstack.pop();
     258         158 :               this_scc.push_back(tos);
     259             :                                           }
     260             :                                           while (tos != vi);
     261             : 
     262             :             // We only consider non-trivial sccs, hence
     263             :             // if the scc consists of a single schate, check whether it has a tau-loop
     264         107 :             if(this_scc.size() == 1)
     265             :             {
     266         218 :               for(outgoing_transitions_per_state_t::const_iterator i = succ_range.first; i != succ_range.second; ++i)
     267             :               {
     268         125 :                 if(from(i) == to(i) && m_lts.is_tau(label(i)))
     269             :                 {
     270           4 :                   m_divergent[tos] = true;
     271           4 :                   break;
     272             :                 }
     273             :               }
     274             :             }
     275             :             else
     276             :             {
     277          10 :               m_divergent[tos] = true;
     278             :             }
     279             :                                   }
     280         158 :                                   stack.pop();
     281             :                           }
     282             :                   }
     283             :           }
     284          14 :   }
     285             : 
     286             : public:
     287             :   /** \brief Constructor
     288             :     *
     289             :     * This initialises \a m_divergent to record for each vertex whether it is
     290             :     * in a tau-scc.
     291             :     */
     292          14 :   signature_divergence_preserving_branching_bisim(const LTS_T& lts_)
     293             :     : signature_branching_bisim<LTS_T>(lts_),
     294          14 :       m_divergent(lts_.num_states(), false)
     295             :   {
     296          14 :     mCRL2log(log::verbose, "sigref") << "initialising signature computation for divergence preserving branching bisimulation" << std::endl;
     297          14 :     compute_tau_sccs();
     298          14 :   }
     299             : 
     300             :   /** \overload
     301             :     *
     302             :     * Compute the signature as in branching bisimulation. In addition, add the
     303             :     * (tau, B) for edges s -tau-> t for which s,t in B and m_divergent[t]
     304             :     */
     305          34 :   virtual void compute_signature(const std::vector<std::size_t>& partition)
     306             :   {
     307             :     // compute signatures
     308          34 :     m_sig = std::vector<signature_t>(m_lts.num_states(), signature_t());
     309         689 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
     310             :     {
     311        2286 :       if(!(partition[i->from()] == partition[i->to()] && m_lts.is_tau(m_lts.apply_hidden_label_map(i->label())))
     312        1618 :          || m_divergent[i->to()])
     313             :       {
     314         385 :         insert(partition, i->from(), m_lts.apply_hidden_label_map(i->label()), partition[i->to()]);
     315             :       }
     316             :     }
     317          34 :   }
     318             : 
     319             :   /** \overload */
     320          14 :   virtual void quotient_transitions(std::set<transition>& transitions, const std::vector<std::size_t>& partition)
     321             :   {
     322         217 :     for(std::vector<transition>::const_iterator i = m_lts.get_transitions().begin(); i != m_lts.get_transitions().end(); ++i)
     323             :     {
     324         823 :       if(!(partition[i->from()] == partition[i->to()] && m_lts.is_tau(m_lts.apply_hidden_label_map(i->label())))
     325         631 :          || m_sig[i->from()].find(std::make_pair(m_lts.apply_hidden_label_map(i->label()), partition[i->to()])) != m_sig[i->from()].end())
     326             :       {
     327         173 :         transitions.insert(transition(partition[i->from()], m_lts.apply_hidden_label_map(i->label()), partition[i->to()]));
     328             :       }
     329             :     }
     330          14 :   }
     331             : };
     332             : 
     333             : 
     334             : /** \brief Signature based reductions for labelled transition systems.
     335             :   *
     336             :   * The implementation is based on the description in
     337             :   * S. Blom, S. Orzan. "Distributed Branching Bisimulation Reduction of State
     338             :   * Spaces", in Proc. PDMC 2003.
     339             :   *
     340             :   * The specific signature is a parameter of the algorithm.
     341             :   */
     342             : template < class LTS_T, typename Signature >
     343          52 : class sigref
     344             : {
     345             : 
     346             : protected:
     347             :   /** \brief Current partition; for each state (std::size_t) the block in which
     348             :              it resides is recorded. */
     349             :   std::vector<std::size_t> m_partition;
     350             : 
     351             :   /** \brief The number of blocks in the current partition */
     352             :   std::size_t m_count;
     353             : 
     354             :   /** \brief The LTS that we are reducing */
     355             :   LTS_T& m_lts;
     356             : 
     357             :   /** \brief Instance of a class performing the signature computation for the
     358             :              current equivalence */
     359             :   Signature m_signature;
     360             : 
     361             :   /** \brief Print a signature (for debugging purposes) */
     362           0 :   std::string print_sig(const signature_t& sig)
     363             :   {
     364           0 :     std::stringstream os;
     365           0 :     os << "{ ";
     366           0 :     for(signature_t::const_iterator i = sig.begin(); i != sig.end() ; ++i)
     367             :     {
     368           0 :       os << " (" << pp(m_lts.action_label(i->first)) << ", " << i->second << ") ";
     369             :     }
     370           0 :     os << " }";
     371           0 :     return os.str();
     372             :   }
     373             : 
     374             :   /** \brief Compute the partition. Repeatedly updates the signatures, and
     375             :              the partition, until the partition stabilises */
     376          52 :   void compute_partition()
     377             :   {
     378          52 :     std::size_t count_prev = m_count;
     379          52 :     std::size_t iterations = 0;
     380             : 
     381          52 :     m_lts.sort_transitions(mcrl2::lts::lbl_tgt_src);
     382             : 
     383         138 :     do
     384             :     {
     385         138 :       mCRL2log(log::verbose, "sigref") << "Iteration " << iterations
     386           0 :                                        << " currently have " << m_count << " blocks" << std::endl;
     387             : 
     388         138 :       m_signature.compute_signature(m_partition);
     389             : 
     390         138 :       count_prev = m_count;
     391             : 
     392             :       // Map signatures to block numbers
     393         276 :       std::map<signature_t, std::size_t> hashtable;
     394         138 :       m_count = 0;
     395        2209 :       for(std::size_t i = 0; i < m_lts.num_states(); ++i)
     396             :       {
     397        2071 :         if(hashtable.find(m_signature.get_signature(i)) == hashtable.end())
     398             :         {
     399         822 :           mCRL2log(log::debug, "sigref") << "Adding block for signature " << print_sig(m_signature.get_signature(i)) << std::endl;
     400         822 :           hashtable[m_signature.get_signature(i)] = m_count++;
     401             :         }
     402             :       }
     403             : 
     404             :       // Map states to block numbers
     405        2209 :       for(std::size_t i = 0; i < m_lts.num_states(); ++i)
     406             :       {
     407        2071 :         m_partition[i] = hashtable[m_signature.get_signature(i)];
     408             :       }
     409             : 
     410         138 :       ++iterations;
     411             : 
     412         138 :     } while (count_prev != m_count);
     413             : 
     414          52 :     mCRL2log(log::verbose, "sigref") << "Done after " << iterations << " iterations with " << m_count << " blocks" << std::endl;
     415          52 :   }
     416             : 
     417             :   /** \brief Perform the quotient with respect to the partition that has
     418             :              been computed */
     419          52 :   void quotient()
     420             :   {
     421             :     // Assign the reduced LTS
     422          52 :     m_lts.set_num_states(m_count);
     423          52 :     m_lts.set_initial_state(m_partition[m_lts.initial_state()]);
     424             : 
     425             :     // Compute quotient transitions
     426             :     // implemented in the signature class because it differs per equivalence.
     427         104 :     std::set<transition> transitions;
     428          52 :     m_signature.quotient_transitions(transitions, m_partition);
     429             : 
     430             :     // Set quotient transitions
     431          52 :     m_lts.clear_transitions();
     432         419 :     for(std::set<transition>::const_iterator i = transitions.begin(); i != transitions.end(); ++i)
     433             :     {
     434         367 :       m_lts.add_transition(*i);
     435             :     }
     436          52 :   }
     437             : 
     438             : public:
     439             :   /** \brief Constructor
     440             :     * \param[in] lts_ The LTS that is being reduced
     441             :     */
     442          52 :   sigref(LTS_T& lts_)
     443             :     : m_partition(std::vector<std::size_t>(lts_.num_states(), 0)),
     444             :       m_count(0),
     445             :       m_lts(lts_),
     446          52 :       m_signature(lts_)
     447          52 :   {}
     448             : 
     449             :   /** \brief Perform the reduction, modulo the equivalence for which the
     450             :     *        signature has been passed in as template parameter
     451             :     */
     452          52 :   void run()
     453             :   {
     454             :     // No need for state labels in the reduced LTS.
     455          52 :     m_lts.clear_state_labels();
     456          52 :     compute_partition();
     457          52 :     quotient();
     458          52 :   }
     459             : };
     460             : 
     461             : } // namespace lts
     462             : } // namespace mcrl2
     463             : 
     464             : #endif // MCRL2_LTS_SIGREF_H

Generated by: LCOV version 1.12