LCOV - code coverage report
Current view: top level - lps/include/mcrl2/lps - untime.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 75 92 81.5 %
Date: 2024-04-21 03:44:01 Functions: 5 10 50.0 %
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 untime.h
      10             : /// \brief Removes time from a linear process.
      11             : 
      12             : #ifndef MCRL2_LPS_UNTIME_H
      13             : #define MCRL2_LPS_UNTIME_H
      14             : 
      15             : #include "mcrl2/data/fourier_motzkin.h"
      16             : #include "mcrl2/lps/detail/lps_algorithm.h"
      17             : 
      18             : namespace mcrl2
      19             : {
      20             : 
      21             : namespace lps
      22             : {
      23             : 
      24             : namespace detail
      25             : {
      26             : 
      27             : template <class INITIALIZER>
      28             : INITIALIZER make_process_initializer(const data::data_expression_list& expressions, const INITIALIZER& init);
      29             : 
      30             : template <>
      31           3 : process_initializer make_process_initializer(const data::data_expression_list& expressions, const process_initializer& /* init */)
      32             : {
      33           3 :   return lps::process_initializer(expressions);
      34             : }
      35             : 
      36             : template <>
      37           0 : stochastic_process_initializer make_process_initializer(const data::data_expression_list& expressions, const stochastic_process_initializer& init)
      38             : {
      39           0 :   return stochastic_process_initializer(expressions, init.distribution());
      40             : }
      41             : 
      42             : } // namespace detail
      43             : 
      44             : template <typename Specification>
      45             : class untime_algorithm: public detail::lps_algorithm<Specification>
      46             : {
      47             :   protected:
      48             :     typedef typename detail::lps_algorithm<Specification> super;
      49             :     typedef typename Specification::process_type process_type;
      50             :     typedef typename process_type::action_summand_type action_summand_type;
      51             :     using super::m_spec;
      52             : 
      53             :     bool m_add_invariants;
      54             :     bool m_apply_fm;
      55             :     const data::rewriter& m_rewriter;
      56             : 
      57             :   protected:
      58             :     /// \brief Variable denoting the time at which the last action occurred
      59             :     data::variable m_last_action_time;
      60             : 
      61             :     /// \brief Data expression expressing the invariant for variables relating to time
      62             :     /// \details For all parameters x relating to time, the expression 0<=x && x<=m_last_action_time,
      63             :     ///         provided that in the initial vector the variable x gets the value 0, and in each summand the
      64             :     ///         new value for x is either x, or the value that is assigned to last action time, which is the time
      65             :     ///         tag of the action in that summand.
      66             :     data::data_expression m_time_invariant;
      67             : 
      68             :     /// \brief Identifier generator, for generating fresh identifiers.
      69             :     data::set_identifier_generator m_identifier_generator;
      70             : 
      71             :     /// \brief Data expression expressing the invariant for variables relating to time
      72             :     ///\details For all parameters x relating to time, the expression 0<=x && x<=m_last_action_time is returned,
      73             :     ///         provided that in the initial vector the variable x gets the value 0, and in each summand the
      74             :     ///         new value for x is either x, or the value that is assigned to last action time, which is the time
      75             :     ///         tag of the action in that summand.
      76           3 :     data::data_expression calculate_time_invariant()
      77             :     {
      78           3 :       const data::data_expression real_zero= data::sort_real::real_(0);
      79             : 
      80             :       // The vector below contains exactly one boolean for each parameter. As long as the value
      81             :       // for the elements in the vector is true, it is a candidate time variable.
      82           3 :       std::vector <bool> time_variable_candidates(m_spec.process().process_parameters().size(),true);
      83           3 :       std::vector <bool>::iterator j=time_variable_candidates.begin() ;
      84           3 :       mCRL2log(log::verbose) << "For untiming to function optimally, it is assumed that the input lps is rewritten to normal form" << std::endl;
      85             : 
      86           3 :       const data::data_expression_list& process_parameters = m_spec.initial_process().expressions();
      87           6 :       for (data::data_expression_list::const_iterator k = process_parameters.begin(); k != process_parameters.end(); ++j, ++k)
      88             :       {
      89           3 :         if (*k != real_zero)
      90             :         {
      91           3 :           (*j) = false;
      92             :         }
      93             :       }
      94             : 
      95           3 :       assert(j == time_variable_candidates.end());
      96             : 
      97           3 :       auto const& summands = m_spec.process().action_summands();
      98          11 :       for (auto i = summands.begin(); i != summands.end(); ++i)
      99             :       {
     100           8 :         const data::data_expression_list summand_arguments = i->next_state(m_spec.process().process_parameters());
     101           8 :         std::vector <bool>::iterator j = time_variable_candidates.begin();
     102           8 :         data::variable_list::const_iterator l=m_spec.process().process_parameters().begin();
     103          16 :         for (data::data_expression_list::const_iterator k=summand_arguments.begin() ;
     104          24 :              k!=summand_arguments.end(); ++j, ++k, l++)
     105             :         {
     106           8 :           if ((*k!=real_zero)&&(*k!=*l)&&(*k!=i->multi_action().time()))
     107             :           {
     108           8 :             (*j)=false;
     109             :           }
     110             :         }
     111           8 :         assert(j==time_variable_candidates.end());
     112             :       }
     113             : 
     114           3 :       data::data_expression time_invariant(data::sort_bool::true_());
     115           3 :       j=time_variable_candidates.begin();
     116           3 :       for (data::variable_list::const_iterator k=m_spec.process().process_parameters().begin();
     117           6 :            k!=m_spec.process().process_parameters().end() ; ++j, ++k)
     118             :       {
     119           3 :         if (*j)
     120             :         {
     121           0 :           data::variable kvar(*k);
     122           0 :           data::variable lat(m_last_action_time);
     123           0 :           time_invariant=data::lazy::and_(time_invariant,
     124             :                                           data::lazy::and_(data::less_equal(real_zero,kvar),
     125             :                                               data::less_equal(kvar,lat)));
     126           0 :         }
     127             :       }
     128           3 :       assert(j==time_variable_candidates.end());
     129           3 :       mCRL2log(log::verbose) << "Time invariant " << data::pp(time_invariant) << std::endl;
     130           6 :       return time_invariant;
     131           3 :     }
     132             : 
     133             :     /// \brief Apply untime to an action summand.
     134           8 :     void untime(action_summand_type& s)
     135             :     {
     136             :       using namespace mcrl2::data;
     137           8 :       if (s.has_time())
     138             :       {
     139             :         // Extend the original condition with an additional argument t.i(d,e.i)>m_last_action_time && t.i(d,e.i) > 0
     140          18 :         s.condition() = lazy::and_(s.condition(),
     141           6 :                                          lazy::and_(greater(s.multi_action().time(),m_last_action_time),
     142           6 :                                              greater(s.multi_action().time(), sort_real::real_(0))));
     143             : 
     144             :         // Extend original assignments to include m_last_action_time := t.i(d,e.i)
     145           6 :         s.assignments()=push_back(s.assignments(),assignment(m_last_action_time,s.multi_action().time()));
     146             : 
     147             :         // Remove time
     148           6 :         s.multi_action() = multi_action(s.multi_action().actions());
     149             : 
     150             :         // Try to apply Fourier-Motzkin elimination to simplify
     151           6 :         std::set< variable > variables_in_action = process::find_all_variables(s.multi_action());
     152           6 :         std::set< variable > variables_in_assignments = process::find_all_variables(s.assignments());
     153             :         // Split the variables that do/do not occur in actions and assignments.
     154           6 :         variable_list do_occur, do_not_occur;
     155             : 
     156          12 :         for(const variable& v: s.summation_variables())
     157             :         {
     158           0 :           if (variables_in_action.count(v)>0 || variables_in_assignments.count(v)>0)
     159             :           {
     160           0 :             do_occur.push_front(v);
     161             :           }
     162             :           else
     163             :           {
     164           0 :             do_not_occur.push_front(v);
     165             :           }
     166             :         }
     167             : 
     168             :         // Only apply Fourier Motzkin elimination to the new condition if the user
     169             :         // explicitly asked for it.
     170             :         // The application of Fourier Motzkin can change the structure of the
     171             :         // condition significantly. Therefore, its application hides the real
     172             :         // outcome of the untime algorithm.
     173           6 :         if(m_apply_fm)
     174             :         {
     175             :           try
     176             :           {
     177           0 :             variable_list remaining_variables;
     178           0 :             data_expression new_condition;
     179           0 :             fourier_motzkin(s.condition(), do_not_occur, new_condition, remaining_variables, m_rewriter);
     180           0 :             s.condition() = new_condition;
     181           0 :             s.summation_variables() = do_occur + remaining_variables;
     182           0 :           }
     183           0 :           catch(mcrl2::runtime_error& e)
     184             :           {
     185             :             // The application of Fourier Motzkin failed because of mixed Real and
     186             :             // non-Real variables. We leave the original condition, but show a
     187             :             // warning to the user
     188           0 :             mCRL2log(log::debug) << "Application of Fourier Motzkin failed with the message\n" << e.what() << std::endl;
     189             :           }
     190             :         }
     191           6 :       }
     192             :       else
     193             :       {
     194             :         // Add a new summation variable (this is allowed because according to an axiom the following equality holds):
     195             :         // c -> a . X == sum t:Real . c -> a@t . X
     196           4 :         variable time_var(m_identifier_generator("time_var"), sort_real::real_());
     197           2 :         s.summation_variables().push_front(time_var);
     198             : 
     199             :         // Extend the original condition with an additional argument time_var > m_last_action_time && time_var > 0
     200           2 :         s.condition() = lazy::and_(s.condition(),
     201             :                                          lazy::and_(greater(time_var, m_last_action_time),
     202             :                                              greater(time_var, sort_real::real_(0))));
     203             : 
     204             :         // Extend original assignments to include m_last_action_time := time_var
     205           2 :         s.assignments()=push_back(s.assignments(),assignment(m_last_action_time, time_var));
     206           2 :       } // s.has_time()
     207             : 
     208             :       // Add the condition m_last_action_time>=0, which holds, and which is generally a useful fact for further processing.
     209           8 :       s.condition() = lazy::and_(s.condition(),m_time_invariant);
     210           8 :     }
     211             : 
     212             : 
     213             : 
     214             : 
     215             :   public:
     216           4 :     untime_algorithm(Specification& spec, bool add_invariants, bool apply_fourier_motzkin, const data::rewriter& r)
     217             :       : detail::lps_algorithm<Specification>(spec),
     218           4 :         m_add_invariants(add_invariants),
     219           4 :         m_apply_fm(apply_fourier_motzkin),
     220           4 :         m_rewriter(r)
     221             :     {
     222           4 :       m_identifier_generator.add_identifiers(lps::find_identifiers(spec));
     223           4 :       m_identifier_generator.add_identifiers(data::function_and_mapping_identifiers(spec.data()));
     224           4 :     }
     225             : 
     226           4 :     void run()
     227             :     {
     228           4 :       m_spec.process().deadlock_summands() = deadlock_summand_vector();
     229           8 :       m_spec.process().deadlock_summands().push_back(
     230           8 :         deadlock_summand(data::variable_list(), data::sort_bool::true_(), deadlock()));
     231             : 
     232           4 :       if (m_spec.process().has_time())
     233             :       {
     234           3 :         mCRL2log(log::verbose) << "Untiming " << m_spec.process().summand_count() << " summands" << std::endl;
     235             : 
     236             :         // Create extra parameter m_last_action_time and add it to the list of process parameters,
     237             :         // m_last_action_time is used later on in the code
     238           3 :         m_last_action_time = data::variable(m_identifier_generator("last_action_time"), data::sort_real::real_());
     239           3 :         mCRL2log(log::verbose) << "Introduced variable " << data::pp(m_last_action_time) << " to denote time of last action" << std::endl;
     240             : 
     241             :         // Should happen before updating the process
     242           3 :         m_time_invariant = m_add_invariants ? calculate_time_invariant() : (data::data_expression) data::sort_bool::true_();
     243             : 
     244           3 :         m_spec.process().process_parameters()=push_back(m_spec.process().process_parameters(),m_last_action_time);
     245           3 :         data::data_expression_list init = m_spec.initial_process().expressions();
     246           3 :         init = push_back(init, data::sort_real::real_(0));
     247           3 :         m_spec.initial_process() = detail::make_process_initializer(init, m_spec.initial_process());
     248             : 
     249          11 :         for(action_summand_type& s: m_spec.process().action_summands())
     250             :         {
     251           8 :           untime(s);
     252             :         }
     253           3 :       }
     254           4 :     }
     255             : };
     256             : 
     257             : } // namespace lps
     258             : 
     259             : } // namespace mcrl2
     260             : 
     261             : #endif // MCRL2_LPS_UNTIME_H

Generated by: LCOV version 1.14