LCOV - code coverage report
Current view: top level - lts/include/mcrl2/lts/detail - liblts_bisim_minimal_depth.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 240 285 84.2 %
Date: 2024-05-01 03:37:31 Functions: 18 19 94.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Jan Martens
       2             : //
       3             : // Copyright: see the accompanying file COPYING or copy at
       4             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       5             : //
       6             : // Distributed under the Boost Software License, Version 1.0.
       7             : // (See accompanying file LICENSE_1_0.txt or copy at
       8             : // http://www.boost.org/LICENSE_1_0.txt)
       9             : /// \file lts/detail/liblts_bisim_m.h
      10             : ///
      11             : /// \brief Partition refinement algorithm for guaruanteed minimal depth
      12             : /// counter-examples.
      13             : ///
      14             : /// \details
      15             : #ifndef _LIBLTS_BISIM_M
      16             : #define _LIBLTS_BISIM_M
      17             : 
      18             : #include "mcrl2/lts/detail/liblts_merge.h"
      19             : #include "mcrl2/lts/detail/liblts_scc.h"
      20             : #include "mcrl2/lts/lts_aut.h"
      21             : #include "mcrl2/lts/lts_dot.h"
      22             : #include "mcrl2/lts/lts_fsm.h"
      23             : #include "mcrl2/lts/lts_utilities.h"
      24             : #include "mcrl2/modal_formula/state_formula.h"
      25             : #include <fstream>
      26             : 
      27             : namespace mcrl2
      28             : {
      29             : namespace lts
      30             : {
      31             : namespace detail
      32             : {
      33             : template <class LTS_TYPE>
      34             : class bisim_partitioner_minimal_depth
      35             : {
      36             : public:
      37             :   /** \brief Creates a bisimulation partitioner for an LTS.
      38             :    *  \details This partitioner is specifically for creating minimal depth counter-examples for strong bisimulation.
      39             :    *           It guarantees stability w.r.t. the old partition before consideren new splitter blocks. This might cause
      40             :    * this implementation to be less efficient than other partitioners. \param l Reference to the LTS. \param init_l2
      41             :    * reference to the initial state of lts2. */
      42           1 :   bisim_partitioner_minimal_depth(LTS_TYPE &l, const std::size_t init_l2)
      43           1 :       : initial_l2(init_l2),
      44           1 :         max_state_index(0),
      45           1 :         aut(l)
      46             :   {
      47           1 :     to_be_processed.clear();
      48           1 :     block initial_block = block();
      49           5 :     for (state_type i = 0; i < aut.num_states(); i++)
      50             :     {
      51           4 :       initial_block.states.push_back(i);
      52             :     }
      53           1 :     sort_transitions(aut.get_transitions(), mcrl2::lts::lbl_tgt_src);
      54           1 :     const std::vector<transition> &trans = aut.get_transitions();
      55           3 :     for (std::vector<transition>::const_iterator r = trans.begin(); r != trans.end(); ++r)
      56             :     {
      57           2 :       initial_block.transitions.push_back(*r);
      58             :     }
      59           1 :     initial_block.block_index = 0;
      60           1 :     initial_block.state_index = 0;
      61           1 :     max_state_index = 1;
      62           1 :     initial_block.parent_block_index = 0;
      63           1 :     initial_block.level = 0;
      64           1 :     blocks.emplace_back(initial_block);
      65             : 
      66           1 :     block_index_of_a_state = std::vector<block_index_type>(aut.num_states(), 0);
      67           1 :     block_flags.push_back(false);
      68           1 :     state_flags = std::vector<bool>(aut.num_states(), false);
      69           1 :     to_be_processed.push_back(0);
      70           1 :     BL.clear();
      71             :     // finished creating initial data structures
      72             : 
      73           1 :     bool splitted = true;
      74           1 :     level_type lvl = 1;
      75           2 :     while (splitted && block_index_of_a_state[initial_l2] == block_index_of_a_state[aut.initial_state()])
      76             :     {
      77           1 :       splitted = refine_partition(lvl);
      78           1 :       lvl++;
      79           1 :       for (typename std::vector<block>::reverse_iterator i = blocks.rbegin();
      80           4 :            i != blocks.rend() && (*i).level == lvl - 1;
      81           3 :            ++i)
      82             :       {
      83           3 :         to_be_processed.push_back((*i).block_index);
      84             :       }
      85             :     }
      86             : 
      87           5 :     for (state_type i = 0; i < aut.num_states(); i++)
      88             :     {
      89           4 :       block_index_type bid = block_index_of_a_state[i];
      90           4 :       if (!block_flags[bid])
      91             :       {
      92           3 :         block_flags[bid] = true;
      93           3 :         partition.insert(bid);
      94             :       }
      95             :     }
      96           2 :     mCRL2log(mcrl2::log::info) << "Partition refinement done, partition contains: " << partition.size()
      97           1 :                                << " blocks, the history contains " << lvl - 1 << " levels." << std::endl;
      98           1 :     save_transitions();
      99           1 :   }
     100             : 
     101             :   /** \brief Destroys this partitioner. */
     102           1 :   ~bisim_partitioner_minimal_depth() = default;
     103             : 
     104             :   /** \brief Creates a state formula that distinguishes state s from state t.
     105             :    *  \details The states s and t are non bisimilar states. A distinguishign state formula phi is
     106             :    *           returned, which has the property that s \in \sem{phi} and  t \not\in\sem{phi}.
     107             :    *           Based on the preprint "Computing minimal distinguishing Hennessey-Milner formulas is NP-hard
     108             :    *           . But variants are tractable", 2023 by Jan Martens and Jan Friso Groote
     109             :    *  \param[in] s The state number for which the resulting formula should be true
     110             :    *  \param[in] t The state number for which the resulting formula should be false
     111             :    *  \return A minimal observation depth distinguishing state formula, that is often also minimum negation-depth and
     112             :    * irreducible. */
     113           1 :   mcrl2::state_formulas::state_formula dist_formula_mindepth(const std::size_t s, const std::size_t t)
     114             :   {
     115           1 :     formula f = distinguish(block_index_of_a_state[s], block_index_of_a_state[t]);
     116           1 :     mCRL2log(mcrl2::log::info) << "done with formula \n";
     117           2 :     return convert_formula(f);
     118           1 :   };
     119             : 
     120           1 :   bool in_same_class(const std::size_t s, const std::size_t t)
     121             :   {
     122           1 :     return block_index_of_a_state[s] == block_index_of_a_state[t];
     123             :   }
     124             : 
     125             : 
     126             : private:
     127             :   typedef std::size_t block_index_type;
     128             :   typedef std::size_t state_type;
     129             :   typedef std::size_t level_type;
     130             :   typedef std::size_t formula_index_type;
     131             :   typedef std::size_t label_type;
     132             :   state_type initial_l2;
     133             : 
     134             :   state_type max_state_index;
     135             :   LTS_TYPE &aut;
     136             :   std::set<block_index_type> partition;
     137             : 
     138             :   struct block
     139             :   {
     140             :     state_type state_index;              // The state number that represent the states in this block
     141             :     block_index_type block_index;        // The sequence number of this block.
     142             :     block_index_type parent_block_index; // Index of the parent block.
     143             :     level_type level;
     144             : 
     145             :     // If there is no parent block, this refers to the block itself.
     146             :     std::vector<state_type> states;
     147             :     std::vector<transition> transitions;
     148             :     std::set<std::pair<label_type, block_index_type>> outgoing_observations;
     149             : 
     150           1 :     void swap(block &b)
     151             :     {
     152           1 :       std::swap(b.state_index, state_index);
     153           1 :       std::swap(b.block_index, block_index);
     154           1 :       std::swap(b.parent_block_index, parent_block_index);
     155           1 :       std::swap(b.level, level);
     156           1 :       states.swap(b.states);
     157           1 :       transitions.swap(b.transitions);
     158           1 :     }
     159             :   };
     160             : 
     161             :   struct formula
     162             :   {
     163             :     formula_index_type index;
     164             :     label_type label;
     165             :     bool negated;
     166             :     std::vector<formula> conjunctions;
     167             :     std::set<block_index_type> truths;
     168             : 
     169             :     int depth()
     170             :     {
     171             :       int max_depth = 0;
     172             :       for (formula f : conjunctions)
     173             :       {
     174             :         max_depth = std::max(max_depth, f.depth() + 1);
     175             :       }
     176             :       return max_depth;
     177             :     }
     178             :   };
     179             : 
     180             :   std::vector<block> blocks;
     181             :   // Blocks that are split become inactive.
     182             :   std::vector<state_type> block_index_of_a_state;
     183             :   std::vector<bool> block_flags;
     184             :   std::vector<bool> state_flags;
     185             : 
     186             :   std::vector<block_index_type> to_be_processed;
     187             :   std::vector<block_index_type> BL;
     188             :   typedef std::pair<label_type, block_index_type> observation_t;
     189             :   typedef std::set<observation_t> derivatives_t;
     190             :   std::map<std::pair<block_index_type, block_index_type>, level_type> greatest_common_ancestor;
     191             : 
     192             :   /* Post processes the partition structure to save outgoing transitions per block */
     193           1 :   void save_transitions()
     194             :   {
     195           3 :     for (transition t : aut.get_transitions())
     196             :     {
     197           2 :       block_index_type sourceBlock = block_index_of_a_state[t.from()];
     198           2 :       block_index_type targetBlock = block_index_of_a_state[t.to()];
     199             : 
     200           2 :       blocks[sourceBlock].outgoing_observations.insert(std::make_pair(t.label(), targetBlock));
     201             :     }
     202           1 :   }
     203             : 
     204             :   /** \brief Compute and set the truth values of a formula f.
     205             :    *  \details Given the formula f we compute the subset of blocks in which f is true.
     206             :    *                     the truthvalues are computed based on the truth values of the different conjuncts and
     207             :    *                     the modality which consists of the label and being negated or not. */
     208           1 :   void set_truths(formula &f)
     209             :   {
     210           1 :     std::set<block_index_type> image_truths;
     211           1 :     std::set<block_index_type> pre_image_truths;
     212           1 :     std::set<block_index_type> intersection;
     213             : 
     214           1 :     image_truths = std::set(partition);
     215             : 
     216           1 :     for (formula df : f.conjunctions)
     217             :     {
     218           0 :       std::set_intersection(image_truths.begin(),
     219             :           image_truths.end(),
     220             :           df.truths.begin(),
     221             :           df.truths.end(),
     222             :           std::inserter(intersection, intersection.begin()));
     223           0 :       image_truths.swap(intersection);
     224           0 :       intersection.clear();
     225             :     }
     226             : 
     227             :     // Now compute preimage according to label
     228           4 :     for (block_index_type B : image_truths)
     229             :     {
     230           5 :       for (transition t : blocks[B].transitions)
     231             :       {
     232           2 :         if (t.label() == f.label && (pre_image_truths.find(block_index_of_a_state[t.from()]) == pre_image_truths.end()))
     233             :         {
     234           1 :           pre_image_truths.insert(block_index_of_a_state[t.from()]);
     235             :         }
     236             :       }
     237             :     }
     238             : 
     239           1 :     if (f.negated)
     240             :     {
     241           0 :       image_truths.swap(pre_image_truths);
     242           0 :       pre_image_truths.clear();
     243           0 :       std::set_difference(partition.begin(),
     244             :           partition.end(),
     245             :           image_truths.begin(),
     246             :           image_truths.end(),
     247             :           std::inserter(pre_image_truths, pre_image_truths.begin()));
     248             :     }
     249           1 :     f.truths.swap(pre_image_truths);
     250           1 :   }
     251             : 
     252             :   // Refine the partition to a certain lvl.
     253           1 :   bool refine_partition(level_type lvl)
     254             :   {
     255           1 :     if (to_be_processed.empty())
     256             :     {
     257           0 :       return false;
     258             :     }
     259             :     block_index_type splitter_index;
     260           3 :     while (!to_be_processed.empty())
     261             :     {
     262           1 :       splitter_index = to_be_processed.front();
     263             : 
     264           1 :       to_be_processed.erase(to_be_processed.begin());
     265           1 :       if (blocks[splitter_index].level == lvl)
     266             :       {
     267           0 :         return true;
     268             :       }
     269             : 
     270           1 :       std::vector<transition> transitions_to_process;
     271           1 :       transitions_to_process = blocks[splitter_index].transitions;
     272           1 :       for (std::vector<transition>::const_iterator i = transitions_to_process.begin();
     273           3 :            i != transitions_to_process.end();
     274           2 :            ++i)
     275             :       {
     276           2 :         transition t = *i;
     277           2 :         state_flags[t.from()] = true;
     278           2 :         const block_index_type marked_block_index = block_index_of_a_state[t.from()];
     279           2 :         if (block_flags[marked_block_index] == false)
     280             :         {
     281           2 :           block_flags[marked_block_index] = true;
     282           2 :           BL.push_back(marked_block_index);
     283             :         }
     284             :         // If the label of the next action is different, we must carry out the splitting.
     285           4 :         if ((i != transitions_to_process.end() && next(i) == transitions_to_process.end())
     286           4 :             || t.label() != (*next(i)).label())
     287             :         {
     288           2 :           split_BL(lvl);
     289           2 :           BL.clear();
     290             :         }
     291             :       }
     292             :     }
     293           1 :     return true;
     294             :   }
     295             : 
     296             :   /** \brief Performs the splits based on the blocks in Bsplit and the flags set in state_flags. */
     297           2 :   void split_BL(level_type lvl)
     298             :   {
     299           4 :     for (block_index_type Bsplit : BL)
     300             :     {
     301           2 :       block_flags[Bsplit] = false;
     302           2 :       std::vector<state_type> flagged_states;
     303           2 :       std::vector<state_type> non_flagged_states;
     304           2 :       std::vector<state_type> Bsplit_states;
     305           2 :       Bsplit_states.swap(blocks[Bsplit].states);
     306           9 :       for (state_type s : Bsplit_states)
     307             :       {
     308           7 :         if (state_flags[s])
     309             :         {
     310             :           // state is flagged.
     311           2 :           flagged_states.push_back(s);
     312             :         }
     313             :         else
     314             :         {
     315             :           // state is not flagged. It will be moved to a new block.
     316           5 :           non_flagged_states.push_back(s);
     317           5 :           block_index_of_a_state[s] = blocks.size();
     318             :         }
     319             :       }
     320           2 :       block_index_type Bparent = Bsplit;
     321             :       // Set the correct parent
     322           3 :       while (blocks[Bparent].level == lvl)
     323             :       {
     324           1 :         Bparent = blocks[Bparent].parent_block_index;
     325             :       }
     326             : 
     327           2 :       block_index_type reset_state_flags_block = Bsplit;
     328           2 :       if (!non_flagged_states.empty())
     329             :       {
     330             :         // There are flagged and non flagged states. So, the block must be split.
     331             :         // Move the unflagged states to the new block.
     332           2 :         if (mCRL2logEnabled(log::debug))
     333             :         {
     334           0 :           const std::size_t m = static_cast<std::size_t>(
     335           0 :               std::pow(10.0, std::floor(std::log10(static_cast<double>((blocks.size() + 1) / 2)))));
     336           0 :           if ((blocks.size() + 1) / 2 % m == 0)
     337             :           {
     338           0 :             mCRL2log(log::debug) << "Bisimulation partitioner: create block " << (blocks.size() + 1) / 2 << std::endl;
     339             :           }
     340             :         }
     341             :         // Create a first new block.
     342           2 :         blocks.push_back(block());
     343           2 :         block_index_type new_block1 = blocks.size() - 1;
     344           2 :         blocks.back().state_index = max_state_index;
     345           2 :         max_state_index++;
     346           2 :         blocks.back().block_index = new_block1;
     347           2 :         blocks.back().level = lvl;
     348             : 
     349           2 :         blocks.back().parent_block_index = Bparent;
     350           2 :         non_flagged_states.swap(blocks.back().states);
     351             : 
     352             :         // The flag fields of the new blocks is set to false;
     353           2 :         block_flags.push_back(false);
     354             :         // distribute the transitions
     355             :         // Finally the non-inert transitions are distributed over both blocks in the obvious way.
     356             :         // Note that this must be done after all states are properly put into a new block.
     357           2 :         std::vector<transition> old_transitions;
     358           2 :         std::vector<transition> flagged_transitions;
     359           2 :         std::vector<transition> non_flagged_transitions;
     360             : 
     361           2 :         old_transitions = blocks[Bsplit].transitions;
     362           6 :         for (std::vector<transition>::iterator k = old_transitions.begin(); k != old_transitions.end(); ++k)
     363             :         {
     364           4 :           if (state_flags[(*k).to()])
     365             :           {
     366           0 :             flagged_transitions.push_back(*k);
     367             :           }
     368             :           else
     369             :           {
     370           4 :             non_flagged_transitions.push_back(*k);
     371             :           }
     372             :         }
     373             : 
     374             :         // Create a second new block.
     375           2 :         block BlockLeft = block();
     376             : 
     377             :         // block_is_active.push_back(true);
     378           2 :         BlockLeft.state_index = blocks[Bsplit].state_index;
     379           2 :         BlockLeft.level = lvl;
     380           2 :         BlockLeft.parent_block_index = Bparent;
     381           2 :         BlockLeft.transitions.swap(flagged_transitions);
     382           2 :         BlockLeft.states.swap(flagged_states);
     383           2 :         if (blocks[Bsplit].level == lvl)
     384             :         {
     385           1 :           BlockLeft.block_index = Bsplit;
     386           1 :           BlockLeft.state_index = blocks[Bsplit].state_index;
     387           1 :           blocks[Bsplit].swap(BlockLeft);
     388             :         }
     389             :         else
     390             :         {
     391           1 :           BlockLeft.block_index = blocks.size();
     392           1 :           BlockLeft.state_index = max_state_index;
     393           1 :           max_state_index++;
     394           1 :           blocks.push_back(BlockLeft);
     395           1 :           block_flags.push_back(false);
     396           2 :           for (state_type s : BlockLeft.states)
     397             :           {
     398           1 :             block_index_of_a_state[s] = BlockLeft.block_index;
     399             :           }
     400             :         }
     401             : 
     402           2 :         blocks[new_block1].transitions.swap(non_flagged_transitions);
     403           2 :         reset_state_flags_block = BlockLeft.block_index;
     404             : 
     405           2 :         if (BlockLeft.block_index != Bsplit)
     406             :         {
     407           1 :           std::vector<state_type> &reference_to_flagged_states_of_block2 = blocks.back().states;
     408           1 :           for (std::vector<state_type>::const_iterator j = reference_to_flagged_states_of_block2.begin();
     409           2 :                j != reference_to_flagged_states_of_block2.end();
     410           1 :                ++j)
     411             :           {
     412           1 :             block_index_of_a_state[*j] = BlockLeft.block_index;
     413             :           }
     414             :         }
     415           2 :       }
     416             :       else
     417             :       {
     418           0 :         reset_state_flags_block = Bsplit;
     419           0 :         blocks[Bsplit].states.swap(flagged_states);
     420             :       }
     421             :       // reset the state flags
     422           2 :       std::vector<state_type> &flagged_states_to_be_unflagged = blocks[reset_state_flags_block].states;
     423           4 :       for (state_type s : flagged_states_to_be_unflagged)
     424             :       {
     425           2 :         state_flags[s] = false;
     426             :       }
     427           2 :       BL.clear();
     428             :     };
     429           2 :   }
     430             : 
     431           5 :   label_type label(observation_t obs) { return obs.first; }
     432             : 
     433           2 :   block_index_type target(observation_t obs) { return obs.second; }
     434             : 
     435             :   /** \brief Creates a formula that distinguishes a block b1 from the block b2.
     436             :    *  \details creates a minimal depth formula that distinguishes b1 and b2.
     437             :    *  \return A minimal observation depth distinguishing state formula, that is often also minimum negation-depth and
     438             :    * irreducible. */
     439           1 :   formula distinguish(const block_index_type b1, const block_index_type b2)
     440             :   {
     441           1 :     derivatives_t b2_delta;
     442           1 :     observation_t dist_obs;
     443             : 
     444           1 :     level_type lvl = gca_level(b1, b2);
     445             : 
     446           2 :     for (observation_t obs : blocks[b2].outgoing_observations)
     447             :     {
     448           1 :       b2_delta.insert(std::make_pair(label(obs), lift_block(target(obs), lvl - 1)));
     449             :     }
     450             : 
     451           1 :     bool found_dist_obs = false;
     452           1 :     for (observation_t obs : blocks[b1].outgoing_observations)
     453             :     {
     454           1 :       if (b2_delta.find(std::make_pair(label(obs), lift_block(target(obs), lvl - 1))) == b2_delta.end())
     455             :       {
     456           1 :         found_dist_obs = true;
     457           1 :         dist_obs = obs;
     458           1 :         break;
     459             :       }
     460             :     }
     461             : 
     462           1 :     if (!found_dist_obs)
     463             :     {
     464             :       // Greedy strategy did not find negation free formula;
     465           0 :       formula neg_phi = distinguish(b2, b1);
     466           0 :       neg_phi.negated = true;
     467           0 :       set_truths(neg_phi);
     468           0 :       return neg_phi;
     469           0 :     }
     470             : 
     471             :     // Set of t derivates we need to take care of.
     472           1 :     std::set<block_index_type> dT;
     473           2 :     for (observation_t obs : blocks[b2].outgoing_observations)
     474             :     {
     475           1 :       if (label(obs) == label(dist_obs))
     476             :       {
     477           0 :         dT.insert(target(obs));
     478             :       }
     479             :     }
     480           1 :     std::vector<formula> conjunctions;
     481             : 
     482           1 :     while (!dT.empty())
     483             :     {
     484           0 :       level_type max_intersect = 0;
     485           0 :       block_index_type splitBlock = *dT.begin();
     486           0 :       for (block_index_type bid : dT)
     487             :       {
     488           0 :         if (gca_level(target(dist_obs), bid) > max_intersect)
     489             :         {
     490           0 :           splitBlock = bid;
     491           0 :           max_intersect = gca_level(target(dist_obs), bid);
     492             :         }
     493             :       }
     494           0 :       formula f = distinguish(target(dist_obs), splitBlock);
     495           0 :       conjunctions.push_back(f);
     496             : 
     497           0 :       std::set<block_index_type> dTleft;
     498           0 :       std::set_intersection(dT.begin(),
     499             :           dT.end(),
     500             :           f.truths.begin(),
     501             :           f.truths.end(),
     502             :           std::inserter(dTleft, dTleft.begin()));
     503           0 :       assert(dT.size() > dTleft.size());
     504           0 :       dT.swap(dTleft);
     505             :     }
     506           1 :     formula returnf;
     507           1 :     returnf.conjunctions.swap(conjunctions);
     508           1 :     returnf.label = label(dist_obs);
     509           1 :     returnf.negated = false;
     510           1 :     set_truths(returnf);
     511           1 :     return returnf;
     512           1 :   }
     513             : 
     514             :   /** \brief Auxiliarry function that computes the level of the greatest common ancestor.
     515             :    *               In other words a lvl i such that B1 and B2 are i-bisimilar. */
     516           1 :   level_type gca_level(const block_index_type B1, const block_index_type B2)
     517             :   {
     518           1 :     block_index_type b1 = B1, b2 = B2;
     519           1 :     if (B1 < B2)
     520             :     {
     521           0 :       b1 = B2;
     522           0 :       b2 = B1;
     523             :     }
     524           1 :     std::pair<block_index_type, block_index_type> bpair(b1, b2);
     525           1 :     if (greatest_common_ancestor.find(bpair) != greatest_common_ancestor.end())
     526             :     {
     527           0 :       return greatest_common_ancestor[bpair];
     528             :     }
     529           1 :     level_type lvl1 = blocks[b1].level, lvl2 = blocks[b2].level;
     530           1 :     if (b1 == b2)
     531             :     {
     532           0 :       greatest_common_ancestor.emplace(bpair, lvl1);
     533           0 :       return lvl1;
     534             :     }
     535           1 :     block_index_type B1parent = b1, B2parent = b2;
     536             : 
     537           1 :     if (lvl1 <= lvl2)
     538             :     {
     539           1 :       B2parent = blocks[b2].parent_block_index;
     540             :     }
     541           1 :     if (lvl2 <= lvl1)
     542             :     {
     543           1 :       B1parent = blocks[b1].parent_block_index;
     544             :     }
     545           1 :     if (B1parent == B2parent)
     546             :     {
     547           1 :       lvl1 = blocks[b1].level;
     548             :     }
     549             :     else
     550             :     {
     551           0 :       lvl1 = gca_level(B1parent, B2parent);
     552             :     }
     553           1 :     greatest_common_ancestor.emplace(bpair, lvl1);
     554           1 :     return lvl1;
     555             :   }
     556             : 
     557           2 :   block_index_type lift_block(const block_index_type B1, level_type goal)
     558             :   {
     559           2 :     block_index_type B = B1;
     560           4 :     while (blocks[B].level > goal)
     561             :     {
     562           2 :       B = blocks[B].parent_block_index;
     563             :     }
     564           2 :     return B;
     565             :   }
     566             : 
     567             :   /*
     568             :    * \brief Converts the private formula data type to the proper mCRL2 state_formula objects
     569             :    * \param a formula f
     570             :    * \return a state_formula equivalent to f
     571             :    */
     572           1 :   mcrl2::state_formulas::state_formula convert_formula(formula &f)
     573             :   {
     574           1 :     mcrl2::state_formulas::state_formula returnPhi
     575           2 :         = mcrl2::state_formulas::may(create_regular_formula(aut.action_label(f.label)), conjunction(f.conjunctions));
     576             : 
     577           1 :     if (f.negated)
     578             :     {
     579           0 :       return mcrl2::state_formulas::not_(returnPhi);
     580             :     }
     581           1 :     return returnPhi;
     582           1 :   }
     583             : 
     584             :   /**
     585             :    * \brief conjunction Creates a conjunction of state formulas
     586             :    * \param terms The terms of the conjunction
     587             :    * \return The conjunctive state formula
     588             :    */
     589           1 :   mcrl2::state_formulas::state_formula conjunction(std::vector<formula> &conjunctions)
     590             :   {
     591           1 :     std::vector<mcrl2::state_formulas::state_formula> terms;
     592           1 :     for (formula &f : conjunctions)
     593             :     {
     594           0 :       terms.push_back(convert_formula(f));
     595             :     }
     596             :     return utilities::detail::join<mcrl2::state_formulas::state_formula>(
     597             :         terms.begin(),
     598             :         terms.end(),
     599           0 :         [](mcrl2::state_formulas::state_formula a, mcrl2::state_formulas::state_formula b)
     600           0 :         { return mcrl2::state_formulas::and_(a, b); },
     601           2 :         mcrl2::state_formulas::true_());
     602           1 :   }
     603             : 
     604             :   /**
     605             :    * \brief create_regular_formula Creates a regular formula that represents action a
     606             :    * \details In case the action comes from an LTS in the aut or fsm format.
     607             :    * \param a The action for which to create a regular formula
     608             :    * \return The created regular formula
     609             :    */
     610           1 :   regular_formulas::regular_formula create_regular_formula(const mcrl2::lts::action_label_string &a) const
     611             :   {
     612           2 :     return mcrl2::regular_formulas::regular_formula(mcrl2::action_formulas::multi_action(
     613           4 :         process::action_list({process::action(process::action_label(a, {}), {})})));
     614             :   }
     615             : 
     616             :   /**
     617             :    * \brief create_regular_formula Creates a regular formula that represents action a
     618             :    * \details In case the action comes from an LTS in the lts format.
     619             :    * \param a The action for which to create a regular formula
     620             :    * \return The created regular formula
     621             :    */
     622             :   regular_formulas::regular_formula create_regular_formula(const mcrl2::lps::multi_action &a) const
     623             :   {
     624             :     return regular_formulas::regular_formula(action_formulas::multi_action(a.actions()));
     625             :   }
     626             : };
     627             : 
     628             : template < class LTS_TYPE>
     629           1 : bool destructive_bisimulation_compare_minimal_depth(
     630             :     LTS_TYPE & l1,
     631             :     LTS_TYPE & l2,
     632             :     const std::string & counter_example_file)
     633             : {
     634           1 :     std::size_t init_l2 = l2.initial_state() + l1.num_states();
     635           1 :     mcrl2::lts::detail::merge(l1, l2);
     636           1 :     l2.clear(); // No use for l2 anymore.
     637           1 :     detail::bisim_partitioner_minimal_depth<LTS_TYPE> bisim_partitioner_minimal_depth(l1, init_l2);
     638           1 :     if (bisim_partitioner_minimal_depth.in_same_class(l1.initial_state(), init_l2))
     639             :     {
     640           0 :         return true; 
     641             :     }
     642             :     
     643             :     // LTSs are not bisimilar, we can create a counter example. 
     644           1 :     std::string filename = "Counterexample.mcf";
     645           1 :     if (!counter_example_file.empty()) {
     646           0 :         filename = counter_example_file;
     647             :     }
     648             : 
     649           1 :     mcrl2::state_formulas::state_formula counter_example_formula = bisim_partitioner_minimal_depth.dist_formula_mindepth(l1.initial_state(), init_l2);
     650             :     
     651           1 :     std::ofstream counter_file(filename);
     652           1 :     counter_file << mcrl2::state_formulas::pp(counter_example_formula);
     653           1 :     counter_file.close();
     654           1 :     mCRL2log(mcrl2::log::info) << "Saved counterexample to: \"" << filename << "\"" << std::endl;
     655           1 :     return false;
     656           1 : }
     657             : 
     658             : 
     659             : } // namespace detail
     660             : } // namespace lts
     661             : } // namespace mcrl2
     662             : #endif

Generated by: LCOV version 1.14