LCOV - code coverage report
Current view: top level - data/include/mcrl2/data - enumerator_with_iterator.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 140 155 90.3 %
Date: 2024-04-17 03:40:49 Functions: 62 67 92.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Wieger Wesselink
       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 mcrl2/data/enumerator_with_iterator.h
      10             : /// \brief add your file description here.
      11             : 
      12             : #ifndef MCRL2_DATA_ENUMERATOR_WITH_ITERATOR_H
      13             : #define MCRL2_DATA_ENUMERATOR_WITH_ITERATOR_H
      14             : 
      15             : #include "mcrl2/atermpp/detail/aterm_configuration.h"
      16             : #include "mcrl2/data/enumerator.h"
      17             : 
      18             : namespace mcrl2 {
      19             : 
      20             : namespace data {
      21             : 
      22             : struct is_not_false
      23             : {
      24        1715 :   bool operator()(const data_expression& x) const
      25             :   {
      26        1715 :     return !sort_bool::is_false_function_symbol(x);
      27             :   }
      28             : };
      29             : 
      30             : struct is_not_true
      31             : {
      32             :   bool operator()(const data_expression& x) const
      33             :   {
      34             :     return !sort_bool::is_true_function_symbol(x);
      35             :   }
      36             : };
      37             : 
      38             : /// \brief An enumerator algorithm that generates solutions of a condition.
      39             : template <typename Rewriter = data::rewriter, typename DataRewriter = data::rewriter>
      40             : class enumerator_algorithm_without_callback: public enumerator_algorithm<Rewriter, DataRewriter>
      41             : {
      42             :   protected:
      43             :     typedef enumerator_algorithm<Rewriter, DataRewriter> super;
      44             :     using super::r;
      45             :     using super::R;
      46             :     using super::dataspec;
      47             :     using super::id_generator;
      48             :     using super::m_max_count;
      49             :     using super::rewrite;
      50             : 
      51             :     /// \brief throw_exceptions If true, an exception is thrown when the enumeration is aborted.
      52             :     bool m_throw_exceptions;
      53             : 
      54             :     template <typename EnumeratorListElement>
      55           1 :     void cannot_enumerate(EnumeratorListElement& e, const std::string& msg) const
      56             :     {
      57           1 :       e.invalidate();
      58           1 :       if (m_throw_exceptions)
      59             :       {
      60           1 :         throw mcrl2::runtime_error(msg);
      61             :       }
      62           0 :     }
      63             : 
      64             :     // add element without additional variables
      65             :     template <typename EnumeratorListElement, typename MutableSubstitution, typename Filter, typename Expression>
      66       23815 :     void add_element(enumerator_queue<EnumeratorListElement>& P,
      67             :                      MutableSubstitution& sigma,
      68             :                      Filter accept,
      69             :                      const data::variable_list& variables,
      70             :                      const Expression& phi,
      71             :                      const EnumeratorListElement& p,
      72             :                      const data::variable& v,
      73             :                      const data::data_expression& e) const
      74             :     {
      75       23815 :       auto phi1 = rewrite(phi, sigma);
      76       23815 :       if (accept(phi1))
      77             :       {
      78       16473 :         P.push_back(EnumeratorListElement(variables, phi1, p, v, e));
      79             :       }
      80       23815 :     }
      81             : 
      82             :     // add element with additional variables
      83             :     template <typename EnumeratorListElement, typename MutableSubstitution, typename Filter, typename Expression>
      84        1001 :     void add_element(enumerator_queue<EnumeratorListElement>& P,
      85             :                      MutableSubstitution& sigma,
      86             :                      Filter accept,
      87             :                      const data::variable_list& variables,
      88             :                      const data::variable_list& added_variables,
      89             :                      const Expression& phi,
      90             :                      const EnumeratorListElement& p,
      91             :                      const data::variable& v,
      92             :                      const data::data_expression& e) const
      93             :     {
      94        1001 :       auto phi1 = rewrite(phi, sigma);
      95        1001 :       if (accept(phi1))
      96             :       {
      97             :         // Additional variables are put at the end of the list!
      98         999 :         P.push_back(EnumeratorListElement(variables + added_variables, phi1, p, v, e));
      99             :       }
     100        1001 :     }
     101             : 
     102             :     // specialization for enumerator_list_element; in this case we are not interested in the substitutions,
     103             :     // and this allows an optimization
     104             :     template <typename MutableSubstitution, typename Filter, typename Expression>
     105       10916 :     void add_element(enumerator_queue<enumerator_list_element<Expression>>& P,
     106             :                      MutableSubstitution& sigma,
     107             :                      Filter accept,
     108             :                      const data::variable_list& variables,
     109             :                      const data::variable_list& added_variables,
     110             :                      const Expression& phi,
     111             :                      const enumerator_list_element<Expression>& p,
     112             :                      const data::variable& v,
     113             :                      const data::data_expression& e) const
     114             :     {
     115       10916 :       auto phi1 = rewrite(phi, sigma);
     116       10916 :       if (accept(phi1))
     117             :       {
     118       10910 :         if (phi1 == phi)
     119             :         {
     120             :           // Discard the added_variables, since we know they do not appear in phi1
     121         545 :           P.push_back(enumerator_list_element<Expression>(variables, phi1, p, v, e));
     122             :         }
     123             :         else
     124             :         {
     125             :           // Additional variables are put at the end of the list!
     126       10365 :           P.push_back(enumerator_list_element<Expression>(variables + added_variables, phi1, p, v, e));
     127             :         }
     128             :         //mCRL2log(log::debug) << "  <add-element> " << P.back() << " with assignment " << v << " := " << e << std::endl;
     129             :       }
     130       10916 :     }
     131             : 
     132             :   public:
     133         606 :     enumerator_algorithm_without_callback(const Rewriter& R,
     134             :                          const data::data_specification& dataspec,
     135             :                          const DataRewriter& r,
     136             :                          enumerator_identifier_generator& id_generator,
     137             :                          std::size_t max_count = (std::numeric_limits<std::size_t>::max)(),
     138             :                          bool throw_exceptions = false
     139             :     )
     140         606 :             : super(R, dataspec, r, id_generator, false, max_count), m_throw_exceptions(throw_exceptions)
     141         606 :     {}
     142             : 
     143             :     enumerator_algorithm_without_callback(const enumerator_algorithm<Rewriter, DataRewriter>&) = delete;
     144             : 
     145             :     template <typename T>
     146             :     struct always_false
     147             :     {
     148             :       bool operator()(const T&) { return false; }
     149             :     };
     150             : 
     151             :     /// \brief Enumerates the front element of the todo list P.
     152             :     /// \param P The todo list of the algorithm.
     153             :     /// \param sigma A mutable substitution that is applied by the rewriter.
     154             :     /// \param accept Elements p for which accept(p) is false are discarded.
     155             :     /// \pre !P.empty()
     156             :     template <typename EnumeratorListElement, typename MutableSubstitution, typename Filter>
     157       17828 :     void step(enumerator_queue<EnumeratorListElement>& P, MutableSubstitution& sigma, Filter accept) const
     158             :     {
     159       17828 :       assert(!P.empty());
     160             : 
     161       17828 :       auto p = P.front();
     162       17828 :       const auto& v = p.variables();
     163       17828 :       const auto& phi = p.expression();
     164             :       //mCRL2log(log::debug) << "  <process-element> " << p << std::endl;
     165       17828 :       P.pop_front();
     166             : 
     167       17828 :       const auto& v1 = v.front();
     168       17828 :       const auto& v_tail = v.tail();
     169       17828 :       const auto& v1_sort = v1.sort();
     170             : 
     171       17828 :       if (data::is_function_sort(v1_sort))
     172             :       {
     173           1 :         const function_sort& function = atermpp::down_cast<function_sort>(v1_sort);
     174           1 :         if (dataspec.is_certainly_finite(function))
     175             :         {
     176           1 :           data_expression_vector function_sorts;
     177           1 :           variable_list function_parameter_list;
     178           1 :           bool result = detail::compute_finite_function_sorts(function, id_generator, dataspec, r, function_sorts, function_parameter_list);
     179           1 :           if (!result)
     180             :           {
     181           0 :             cannot_enumerate(p, "Sort " + data::pp(v1_sort) + " has too many elements to enumerate.");
     182             :           }
     183             : 
     184           1 :           data_expression sigma_v1 = sigma(v1);
     185           5 :           for (const data_expression& f: function_sorts)
     186             :           {
     187           4 :             sigma[v1] = f;
     188           4 :             add_element(P, sigma, accept, v_tail, phi, p, v1, f);
     189             :           }
     190           1 :           sigma[v1] = sigma_v1;
     191           1 :         }
     192             :         else
     193             :         {
     194           0 :           cannot_enumerate(p, "Cannot enumerate elements of function sort " + data::pp(v1_sort) + ".");
     195             :         }
     196             :       }
     197       17827 :       else if (sort_set::is_set(v1_sort))
     198             :       {
     199           1 :         const sort_expression& element_sort = atermpp::down_cast<container_sort>(v1_sort).element_sort();
     200           1 :         if (dataspec.is_certainly_finite(element_sort))
     201             :         {
     202           4 :           const data_expression lambda_term = abstraction(lambda_binder(), { variable(id_generator(), element_sort) }, sort_bool::false_());
     203           2 :           const variable fset_variable(id_generator(), sort_fset::fset(element_sort));
     204           1 :           data_expression e = sort_set::constructor(element_sort, lambda_term, fset_variable);
     205           1 :           data_expression sigma_v1 = sigma(v1);
     206           1 :           sigma[v1] = e;
     207           2 :           add_element(P, sigma, accept, v_tail, { fset_variable }, phi, p, v1, e);
     208           1 :           sigma[v1] = sigma_v1;
     209           1 :         }
     210             :         else
     211             :         {
     212           0 :           cannot_enumerate(p, "Cannot enumerate elements of set sort " + data::pp(v1_sort) + ".");
     213           0 :           return;
     214             :         }
     215             :       }
     216       17826 :       else if (sort_fset::is_fset(v1_sort))
     217             :       {
     218           1 :         const auto& fset = atermpp::down_cast<container_sort>(v1_sort);
     219           1 :         if (dataspec.is_certainly_finite(fset.element_sort()))
     220             :         {
     221           1 :           data_expression_vector set_elements;
     222           1 :           bool result = detail::compute_finite_set_elements(fset, dataspec, r, sigma, set_elements, id_generator);
     223           1 :           if (!result)
     224             :           {
     225           0 :             cannot_enumerate(p, "Finite set sort " + data::pp(v1_sort) + " has too many elements to enumerate.");
     226             :           }
     227             : 
     228           1 :           data_expression sigma_v1 = sigma(v1);
     229           5 :           for (const data_expression& e: set_elements)
     230             :           {
     231           4 :             sigma[v1] = e;
     232           4 :             add_element(P, sigma, accept, v_tail, phi, p, v1, e);
     233             :           }
     234           1 :           sigma[v1] = sigma_v1;
     235           1 :         }
     236             :         else
     237             :         {
     238           0 :           cannot_enumerate(p, "Cannot enumerate elements of finite set sort " + data::pp(v1_sort) + ".");
     239           0 :           return;
     240             :         }
     241             :       }
     242       17825 :       else if (sort_bag::is_bag(v1_sort))
     243             :       {
     244           0 :         cannot_enumerate(p, "Cannot enumerate elements of bag sort " + data::pp(v1_sort) + ".");
     245           0 :         return;
     246             :       }
     247       17825 :       else if (sort_fbag::is_fbag(v1_sort))
     248             :       {
     249           0 :         cannot_enumerate(p, "Cannot enumerate elements of finite bag sort " + data::pp(v1_sort) + ".");
     250           0 :         return;
     251             :       }
     252             :       else
     253             :       {
     254       17825 :         const function_symbol_vector& C = dataspec.constructors(v1_sort);
     255       17825 :         if (!C.empty())
     256             :         {
     257       53547 :           for (const function_symbol& c: C)
     258             :           {
     259       35723 :             if (data::is_function_sort(c.sort()))
     260             :             {
     261       11916 :               auto const& domain = atermpp::down_cast<data::function_sort>(c.sort()).domain();
     262       34901 :               data::variable_list y(domain.begin(), domain.end(), [&](const data::sort_expression& s) { return data::variable(id_generator(), s); });
     263             :               // TODO: We want to apply datar without the substitution sigma, but that is currently an inefficient operation of data::rewriter.
     264       11916 :               data_expression cy = r(application(c, y.begin(), y.end()), sigma);
     265       11916 :               sigma[v1] = cy;
     266       11916 :               add_element(P, sigma, accept, v_tail, y, phi, p, v1, cy);
     267       11916 :               sigma[v1] = v1;
     268       11916 :             }
     269             :             else
     270             :             {
     271             :               // TODO: We want to apply r without the substitution sigma, but that is currently an inefficient operation of data::rewriter.
     272       23807 :               const auto e1 = r(c, sigma);
     273       23807 :               sigma[v1] = e1;
     274       23807 :               add_element(P, sigma, accept, v_tail, phi, p, v1, e1);
     275       23807 :               sigma[v1] = v1;
     276       23807 :             }
     277             :           }
     278             :         }
     279             :         else
     280             :         {
     281           4 :           cannot_enumerate(p, "Cannot enumerate elements of sort " + data::pp(v1_sort) + " without constructors.");
     282           0 :           return;
     283             :         }
     284             :       }
     285       17828 :     }
     286             : 
     287             :     /// \brief Enumerates the front elements of the todo list P until a solution
     288             :     /// has been found, or until P is empty.
     289             :     /// \param P The todo list of the algorithm.
     290             :     /// \param sigma A substitution.
     291             :     /// \param accept Elements p for which accept(p) is false are discarded.
     292             :     /// \return The number of elements that have been processed
     293             :     /// \post Either P.empty() or P.front().is_solution()
     294             :     template <typename EnumeratorListElement, typename MutableSubstitution, typename Filter>
     295        2307 :     std::size_t next(enumerator_queue<EnumeratorListElement>& P, MutableSubstitution& sigma, Filter accept) const
     296             :     {
     297        2307 :       std::size_t count = 0;
     298       20134 :       while (!P.empty())
     299             :       {
     300       20077 :         if (count++ >= m_max_count)
     301             :         {
     302         149 :           break;
     303             :         }
     304       19928 :         if (P.front().is_solution())
     305             :         {
     306        2100 :           break;
     307             :         }
     308             :         else
     309             :         {
     310       17828 :           step(P, sigma, accept);
     311             :         }
     312             :       }
     313        2306 :       return count;
     314             :     }
     315             : 
     316         149 :     bool throw_exceptions() const
     317             :     {
     318         149 :       return m_throw_exceptions;
     319             :     }
     320             : };
     321             : 
     322             : /// \brief An enumerator algorithm with an iterator interface.
     323             : template <typename Rewriter = data::rewriter, typename EnumeratorListElement = enumerator_list_element_with_substitution<>, typename Filter = data::is_not_false, typename DataRewriter = data::rewriter, typename MutableSubstitution = data::mutable_indexed_substitution<> >
     324             : class enumerator_algorithm_with_iterator: public enumerator_algorithm_without_callback<Rewriter, DataRewriter>
     325             : {
     326             :   protected:
     327             :     Filter m_accept;
     328             : 
     329             :   public:
     330             :     typedef enumerator_algorithm_without_callback<Rewriter, DataRewriter> super;
     331             :     using super::rewrite;
     332             : 
     333             :     /// \brief A class to enumerate solutions for terms.
     334             :     /// \details Solutions are presented as data_expression_lists of the same length as
     335             :     ///          the list of variables for which a solution is sought.
     336             :     class iterator: public boost::iterator_facade<iterator, const EnumeratorListElement, boost::forward_traversal_tag>
     337             :     {
     338             :       protected:
     339             :         enumerator_algorithm_with_iterator<Rewriter, EnumeratorListElement, Filter, DataRewriter, MutableSubstitution>* E;
     340             :         MutableSubstitution* sigma;
     341             :         enumerator_queue<EnumeratorListElement>* P;
     342             :         Filter accept;
     343             :         std::size_t count;
     344             : 
     345          11 :         static enumerator_queue<EnumeratorListElement>& default_deque()
     346             :         {
     347             : #ifdef MCRL2_ENABLE_MULTITHREADING 
     348             :           static_assert(mcrl2::utilities::detail::GlobalThreadSafe);
     349          11 :           thread_local enumerator_queue<EnumeratorListElement> result; // Changed this static variable to thread local,
     350             :                                                                        // as it could be the cause of a thread conflict. 
     351             : #else
     352             :           static_assert(!mcrl2::utilities::detail::GlobalThreadSafe);
     353             :           static enumerator_queue<EnumeratorListElement> result; // Changed this static variable to thread local,
     354             : #endif
     355          11 :           return result;
     356             :         }
     357             : 
     358             :       public:
     359         607 :         iterator(enumerator_algorithm_with_iterator<Rewriter, EnumeratorListElement, Filter, DataRewriter, MutableSubstitution>* E_,
     360             :                  enumerator_queue<EnumeratorListElement>* P_,
     361             :                  MutableSubstitution* sigma_,
     362             :                  Filter accept_ = Filter()
     363             :         )
     364         607 :                 : E(E_), sigma(sigma_), P(P_), accept(accept_), count(0)
     365             :         {
     366         607 :           count += E->next(*P, *sigma, accept);
     367         606 :         }
     368             : 
     369          11 :         explicit iterator(Filter accept_ = Filter())
     370          11 :                 : E(nullptr), sigma(nullptr), P(&default_deque()), accept(accept_), count(0)
     371          11 :         { }
     372             : 
     373             :       protected:
     374             :         friend class boost::iterator_core_access;
     375             : 
     376        1700 :         void increment()
     377             :         {
     378        1700 :           assert(!P->empty());
     379        1700 :           P->pop_front();
     380        1700 :           count += E->next(*P, *sigma, accept);
     381        1700 :           if (count >= E->max_count())
     382             :           {
     383         149 :             if (E->throw_exceptions())
     384             :             {
     385         149 :               std::ostringstream out;
     386         149 :               out << "enumeration was aborted, since it did not complete within " << E->max_count() << " iterations";
     387         149 :               throw mcrl2::runtime_error(out.str());
     388         149 :             }
     389             :             else
     390             :             {
     391           0 :               P->clear();
     392           0 :               return;
     393             :             }
     394             :           }
     395             :         }
     396             : 
     397        2181 :         bool equal(iterator const& other) const
     398             :         {
     399        2181 :           return P->size() == other.P->size();
     400             :         }
     401             : 
     402        2099 :         const EnumeratorListElement& dereference() const
     403             :         {
     404        2099 :           assert(!P->empty());
     405        2099 :           return P->front();
     406             :         }
     407             :     };
     408             : 
     409         606 :     enumerator_algorithm_with_iterator(
     410             :             const Rewriter& R,
     411             :             const data::data_specification& dataspec,
     412             :             const DataRewriter& datar,
     413             :             enumerator_identifier_generator& id_generator,
     414             :             std::size_t max_count = (std::numeric_limits<std::size_t>::max)(),
     415             :             bool throw_exceptions = false,
     416             :             const Filter& f = Filter())
     417             :             : super(R, dataspec, datar, id_generator, max_count, throw_exceptions)
     418         606 :             , m_accept(f)
     419         606 :     {}
     420             : 
     421             :     /// \brief Returns an iterator that enumerates solutions for variables that satisfy a condition
     422             :     /// \param sigma A mutable substitution that is applied by the rewriter contained in E
     423             :     /// \param P The condition that is solved, together with the list of variables
     424             :     /// Otherwise an invalidated enumerator element is returned when it is dereferenced.
     425         608 :     iterator begin(MutableSubstitution& sigma, enumerator_queue<EnumeratorListElement>& P)
     426             :     {
     427         608 :       assert(P.size() == 1);
     428         608 :       auto& p = P.front();
     429         608 :       p.expression() = rewrite(p.expression(), sigma);
     430         608 :       if (m_accept(p.expression()))
     431             :       {
     432         607 :         return iterator(const_cast<enumerator_algorithm_with_iterator<Rewriter, EnumeratorListElement, Filter, DataRewriter, MutableSubstitution>*>(this), &P, &sigma, m_accept);
     433             :       }
     434             :       else
     435             :       {
     436           1 :         return end();
     437             :       }
     438             :     }
     439             : 
     440        2182 :     const iterator& end()
     441             :     {
     442             : #ifdef MCRL2_ENABLE_MULTITHREADING
     443             :       static_assert(mcrl2::utilities::detail::GlobalThreadSafe);
     444        2182 :       thread_local iterator result(m_accept);  
     445             : #else
     446             :       static_assert(!mcrl2::utilities::detail::GlobalThreadSafe);
     447             :       static iterator result(m_accept);  
     448             : #endif
     449        2182 :       return result;
     450             :     }
     451             : };
     452             : 
     453             : } // namespace data
     454             : 
     455             : } // namespace mcrl2
     456             : 
     457             : #endif // MCRL2_DATA_ENUMERATOR_WITH_ITERATOR_H

Generated by: LCOV version 1.14