LCOV - code coverage report
Current view: top level - pg/include/mcrl2/pg - SmallProgressMeasures.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 0 17 0.0 %
Date: 2024-04-21 03:44:01 Functions: 0 15 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2009-2013 University of Twente
       2             : // Copyright (c) 2009-2013 Michael Weber <michaelw@cs.utwente.nl>
       3             : // Copyright (c) 2009-2013 Maks Verver <maksverver@geocities.com>
       4             : // Copyright (c) 2009-2013 Eindhoven University of Technology
       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             : 
      10             : #ifndef MCRL2_PG_SMALL_PROGRESS_MEASURES_H
      11             : #define MCRL2_PG_SMALL_PROGRESS_MEASURES_H
      12             : 
      13             : #include "mcrl2/pg/ParityGameSolver.h"
      14             : #include "mcrl2/pg/LiftingStrategy.h"
      15             : #include "mcrl2/utilities/logger.h"
      16             : 
      17             : /*! \defgroup SmallProgressMeasures
      18             : 
      19             :     Classes related to the Small Progress Measures parity game solving algoritm.
      20             : */
      21             : 
      22             : /*! \ingroup SmallProgressMeasures
      23             : 
      24             :     Object used to collect statistics when solving using the SPM algorithm */
      25             : class LiftingStatistics
      26             : {
      27             : public:
      28             :     /*! Construct a statistics object for the given game. */
      29             :     LiftingStatistics(const ParityGame &game, long long max_lifts = -1);
      30             : 
      31             : #if 0
      32             :     /*! Merge statistics from a given object into this object, using the given
      33             :         vertex mapping to map vertex indices (vertex v in `other` has index
      34             :         mapping[v] in this object). */
      35             :     void merge(const LiftingStatistics &other, const verti *mapping = NULL);
      36             : #endif
      37             : 
      38             :     long long lifts_attempted() const { return lifts_attempted_; }
      39             :     long long lifts_succeeded() const { return lifts_succeeded_; }
      40             :     long long lifts_attempted(verti v) const { return vertex_stats_[v].first; }
      41             :     long long lifts_succeeded(verti v) const { return vertex_stats_[v].second; }
      42             : 
      43             :     void add_lifts_attempted(long long count) { lifts_attempted_ += count; }
      44             :     void add_lifts_succeeded(long long count) { lifts_succeeded_ += count; }
      45             :     void add_lifts_attempted(verti v, long long count)
      46             :     {
      47             :         vertex_stats_[v].first += count;
      48             :     }
      49             :     void add_lifts_succeeded(verti v, long long count)
      50             :     {
      51             :         vertex_stats_[v].second += count;
      52             :     }
      53             : 
      54             : private:
      55             :     void record_lift(verti v, bool success);
      56             :     friend class SmallProgressMeasures;
      57             : 
      58             : private:
      59             :     long long lifts_attempted_, lifts_succeeded_;
      60             :     std::vector<std::pair<long long, long long> > vertex_stats_;
      61             :     long long max_lifts_;
      62             : };
      63             : 
      64             : /*! \ingroup SmallProgressMeasures
      65             : 
      66             :     Implements the core of the Small Progress Measures algorithm, which keeps
      67             :     track of progress measure vectors, and allows lifting at vertices.
      68             : 
      69             :     Note that besides these vectors, it tracks the current lifting strategy and
      70             :     (optionally) a statistics object.  All public methods of this class notify
      71             :     the lifting strategy whenever vectors change (due to one of the solve..()
      72             :     lift..() methods), but only when vertices are lifted through one of the
      73             :     solve..() methods, are lifting attempts recording in the statistics object.
      74             : 
      75             :     As a result, the lift..() methods can be used to introduce information from
      76             :     external sources into the game, without affecting the statistics for updates
      77             :     that result from local lifting attempts. These methods are thus used in the
      78             :     two-way approach to propagate information from the dual game, and the MPI
      79             :     recursive solver.
      80             : 
      81             :     Note that this is an abstract base class: subclasses may implement different
      82             :     storage formats for the progress measure vectors.
      83             : */
      84             : class SmallProgressMeasures
      85             : {
      86             : public:
      87             :     //! Maximum number of lifting attempts in a row before checking timer.
      88             :     static const int work_size = 10000;
      89             : 
      90             :     SmallProgressMeasures(
      91             :         const ParityGame &game, ParityGame::Player player,
      92             :         LiftingStatistics *stats = 0,
      93             :         const verti *vmap = 0, verti vmap_size = 0 );
      94             : 
      95             :     virtual ~SmallProgressMeasures();
      96             : 
      97             :     /*! Performs some work on the game using the given lifting strategy (which
      98             :         must have been initialized).
      99             : 
     100             :         @param max_attempts Maximum number of lifts attempted.
     101             :         @return How many attempts remain; i.e. if the result is greater than
     102             :                 zero, the game is succesfully solved.
     103             :         @see initialize_lifting_strategy
     104             :     */
     105             :     long long solve_some( LiftingStrategy &ls,
     106             :                           long long attempts = work_size );
     107             :     long long solve_some( LiftingStrategy2 &ls,
     108             :                           long long attempts = work_size );
     109             : 
     110             :     /*! Performs one lifting attempt, and returns the index of the vertex and
     111             :         whether lifting succeeded. Returns NO_VERTEX if no more vertices were
     112             :         candidates for lifting. */
     113             :     std::pair<verti, bool> solve_one(LiftingStrategy &ls);
     114             :     verti solve_one(LiftingStrategy2 &ls);
     115             : 
     116             :     /*! After the game is solved, this returns the strategy at vertex `v` for
     117             :         the current player, or NO_VERTEX if the vertex is controlled by his
     118             :         opponent (in that case, any move is winning) or if it is won by his
     119             :         opponent (in that case, all moves are losing). */
     120             :     verti get_strategy(verti v) const;
     121             : 
     122             :     /*! Takes an initialized strategy vector and updates it for the current
     123             :         player. The result is valid only after the game is solved. */
     124             :     void get_strategy(ParityGame::Strategy &strat) const;
     125             : 
     126             :     /*! Returns the winning set for the given player by assigning the vertices
     127             :         in the set to the given output iterator. If the game is not completely
     128             :         solved yet, then this returns a subset of the winning set. */
     129             :     template<class OutputIterator>
     130             :     void get_winning_set(ParityGame::Player player, OutputIterator result);
     131             : 
     132             :     /*! Sets the given vertex's progress measure to top, if it isn't already,
     133             :         and returns whether it changed: */
     134             :     inline bool lift_to_top(verti v);
     135             : 
     136             :     /*! Sets the given vertex's progress measure to the given value, if this
     137             :         is greater than the current value, and returns whether it changed.
     138             :         val[] must be an array of length len(v). If carry is set, the new
     139             :         value must be strictly greater (or top). */
     140             :     bool lift_to(verti v, const verti vec2[], bool carry = 0);
     141             : 
     142             :     /*! Returns the same result as lift_to() but without changing any
     143             :         progress measure vectors. */
     144             :     bool less_than(verti v, const verti vec2[], bool carry = 0);
     145             : 
     146             :     /*! For debugging: print current state to stdout */
     147             :     void debug_print() const;
     148             :     void debug_print_vertex(int v) const;
     149             : 
     150             :     /*! For debugging: verify that the current state describes a valid SPM */
     151             :     bool verify_solution();
     152             : 
     153             :     /*! Return the parity to be solved. */
     154           0 :     const ParityGame &game() const { return game_; }
     155             : 
     156             :     /*! Return the player to solve for. */
     157             :     ParityGame::Player player() const { return (ParityGame::Player)p_; }
     158             : 
     159             :     /*! Return the length of the SPM vectors (a positive integer). */
     160             :     int len() const { return len_; }
     161             : 
     162             :     /*! Returns the SPM vector space; an array of len() integers. */
     163             :     const verti *M() const { return M_; }
     164             : 
     165             :     /*! Changes the SPM vector space. `new_M` must be an array of at least
     166             :         `len` non-negative integers. */
     167             :     void set_M(const verti *new_M) { std::copy(new_M, new_M + len_, M_); }
     168             : 
     169             :     /*! Decrements the i'th element of M. */
     170           0 :     void decr_M(int i) { assert(M_[i] > 1); --M_[i]; }
     171             : 
     172             :     /*! Return the number of odd priorities less than or equal to the
     173             :         priority of v. This is the length of the SPM vector for `v`. */
     174           0 :     int len(verti v) const { return (game_.priority(v) + 1 + p_)/2; }
     175             : 
     176             :     /*! Return whether the given SPM vector has top value. */
     177           0 :     bool is_top(const verti vec[]) const { return vec[0] == NO_VERTEX; }
     178             : 
     179             :     /*! Return whether the SPM vector for vertex `v` has top value. */
     180           0 :     bool is_top(verti v) const { return is_top(vec(v)); }
     181             : 
     182             :     // The following functions are implemented in derived classes that
     183             :     // implement the actual storage of progress measure vectors:
     184             : 
     185             :     /*! Return the SPM vector for vertex `v`.
     186             :         This array contains only the components with odd (for Even) or even
     187             :         (for Odd) indices of the vector (since the reset is fixed at zero). */
     188             :     // virtual verti *vec(verti v) = 0;
     189             :     virtual const verti *vec(verti v) const = 0;
     190             : 
     191             :     /*! Assign the first `len(v)` elements of the vector for vertex `w` to the
     192             :         vector for `v`, or its successor if `carry` is set. */
     193             :     virtual void set_vec(verti v, const verti src[], bool carry) = 0;
     194             : 
     195             :     /*! Set the value for vertex `v` to top. */
     196             :     virtual void set_vec_to_top(verti v) = 0;
     197             : 
     198             :     /*! Recalculates the set of dirty vertices, and pushes them into the
     199             :         given lifting strategy (which is assumed to be freshly allocated). */
     200             :     void initialize_lifting_strategy(LiftingStrategy2 &ls);
     201             : 
     202             : protected:
     203             :     /*! Sets odd-controlled vertices with (only) loops to top.
     204             :         Should be called by derived classes after allocating SPM data. */
     205             :     void initialize_loops();
     206             : 
     207             :     /*! Set the SPM vector for vertex `v` to top value. This can decrease the
     208             :         vector space, but nothing else; e.g, the lifting strategy is not
     209             :         notified of the lift. */
     210             :     inline void set_top(verti v);
     211             : 
     212             : private:
     213             :     SmallProgressMeasures(const SmallProgressMeasures &);
     214             :     SmallProgressMeasures &operator=(const SmallProgressMeasures &);
     215             : 
     216             : private:
     217             :     /*! Compares the first `N` elements of the given SPM vectors and returns
     218             :         -1, 0 or 1 to indicate that v is smaller than, equal to, or larger than
     219             :         w (respectively). */
     220             :     inline int vector_cmp(const verti vec1[], const verti vec2[], int N) const;
     221             : 
     222             :     /*! Compares `N` elements of the SPM vectors for the given vertices. */
     223             :     inline int vector_cmp(verti v, verti w, int N) const;
     224             : 
     225             :     /*! Returns whehter vertex `v` is lifted to the maximum successor (true)
     226             :         or minimum successor (false), which in turn depends on whether it is
     227             :         controlled by the player `p_` we are solving for. */
     228           0 :     inline bool take_max(verti v) const { return game_.player(v) != p_; }
     229             : 
     230             :     /*! Returns whether the progress measure for vertex `v` must be strictly
     231             :         greater than its successors or not (which is true if its priority has
     232             :         parity different from the current player). */
     233           0 :     inline bool compare_strict(verti v) const { return game_.priority(v)%2 != p_; }
     234             : 
     235             :     /*! Returns the minimum or maximum successor for vertex `v`,
     236             :         depending on whether take_max is false or true (respectively). */
     237             :     inline verti get_ext_succ(verti v, bool take_max) const;
     238             : 
     239             :     /*! Returns the minimum or maximum successor for vertex `v` depending on
     240             :         whether it is owned by player `p_` or not. */
     241             :     inline verti get_ext_succ(verti v) const;
     242             : 
     243             :     /*! Returns the minimum successor for vertex `v`. */
     244           0 :     inline verti get_min_succ(verti v) const { return get_ext_succ(v, false); }
     245             : 
     246             :     /*! Returns the maximum successor for vertex `v`. */
     247             :     inline verti get_max_succ(verti v) const { return get_ext_succ(v, true); }
     248             : 
     249             :     /*! Returns whether the succesor with the extreme progress measure for the
     250             :         given vertex.  (`Extreme` means minimal for even-controlled vertices,
     251             :         and maximal for odd-controlled vertices.) */
     252           0 :     inline verti get_successor(verti v) const { return strategy_[v]; }
     253             : 
     254             :     /*! Returns whether the given vertex is dirty; i.e. its progress measure
     255             :         is less than its successor (or less than or equal to, if its priority
     256             :         is odd). */
     257           0 :     inline bool is_dirty(verti v) const { return dirty_[v]; }
     258             : 
     259             :     // Allow selected lifting strategies to access the SPM internals:
     260             :     friend class PredecessorLiftingStrategy;
     261             :     friend class MaxMeasureLiftingStrategy2;
     262             :     friend class OldMaxMeasureLiftingStrategy;
     263             : 
     264             : protected:
     265             :     const ParityGame       &game_;     //!< the game being solved
     266             :     const std::size_t           p_;         //!< the player to solve for
     267             :     LiftingStatistics      *stats_;    //!< statistics object to record lifts
     268             :     const verti            *vmap_;     //!< active vertex map (if any)
     269             :     verti                  vmap_size_; //!< size of vertex map
     270             :     std::size_t                 len_;       //!< length of SPM vectors
     271             :     verti                  *M_;        //!< bounds on the SPM vector components
     272             :     ParityGame::Strategy   strategy_;  //!< current strategy
     273             :     bool                   *dirty_;    //!< marks unstable vertices
     274             : };
     275             : 
     276             : /*! \ingroup SmallProgressMeasures
     277             : 
     278             :     A small progress measures implementation that stores progress measure vectors
     279             :     "densely". All `len`*`V` elements are stored in a contiguous array.
     280             : 
     281             :     This is memory efficient and allows fast access and updating of progress
     282             :     measure vectors.  The main limitation is that it does not support concurrent
     283             :     access.
     284             : */
     285             : class DenseSPM : public SmallProgressMeasures
     286             : {
     287             : public:
     288             :     DenseSPM(
     289             :         const ParityGame &game, ParityGame::Player player,
     290             :         LiftingStatistics *stats = 0,
     291             :         const verti *vertex_map = 0, verti vertex_map_size = 0 );
     292             :     ~DenseSPM();
     293             : 
     294             :     // verti *vec(verti v) { return &spm_[(std::size_t)len_*v]; }
     295           0 :     const verti *vec(verti v) const { return &spm_[(std::size_t)len_*v]; }
     296             :     void set_vec(verti v, const verti src[], bool carry) ;
     297             :     void set_vec_to_top(verti v);
     298             : 
     299             : protected:
     300             :     verti *spm_;  //!< array storing the SPM vector data
     301             : };
     302             : 
     303             : 
     304             : /*! \ingroup SmallProgressMeasures
     305             : 
     306             :     A parity game solver based on Marcin Jurdzinski's small progress measures
     307             :     algorithm, with pluggable lifting heuristics. */
     308             : class SmallProgressMeasuresSolver
     309             :     : public ParityGameSolver
     310             : {
     311             : protected:
     312             :     /*! Helper class which is an OutputIterator that sets vertices assigned
     313             :         through it to top in the given SPM representation (which in turn updates
     314             :         the corresponding lifting strategy). */
     315             :     class SetToTopIterator
     316             :     {
     317             :     public:
     318           0 :         SetToTopIterator(SmallProgressMeasures &spm) : spm(spm) { }
     319             :         SetToTopIterator& operator++() { return *this; }
     320           0 :         SetToTopIterator& operator++(int) { return *this; }
     321           0 :         SetToTopIterator& operator*() { return *this; }
     322           0 :         SetToTopIterator& operator=(verti v)
     323             :         {
     324           0 :             spm.lift_to_top(v);
     325           0 :             return *this;
     326             :         }
     327             : 
     328             :     private:
     329             :         SmallProgressMeasures &spm;
     330             :     };
     331             : 
     332             : public:
     333             :     SmallProgressMeasuresSolver( const ParityGame &game,
     334             :                                  std::shared_ptr<LiftingStrategyFactory> lsf,
     335             :                                  bool alternate = false,
     336             :                                  LiftingStatistics *stats = 0,
     337             :                                  const verti *vmap = 0,
     338             :                                  verti vmap_size = 0 );
     339             : 
     340             :     ParityGame::Strategy solve();
     341             : 
     342             :     /*! Solves the game by applying JurdziƄski's proposed algorithm that solves
     343             :         the game for one player only, and then solves a subgame with the
     344             :         remaining vertices. This algorithm is most efficient when the original
     345             :         game is easier to solve than its dual. */
     346             :     virtual ParityGame::Strategy solve_normal();
     347             : 
     348             :     /*! Solves the game using Friedmann's alternate strategy. This allocates
     349             :         solving algorithms for both the normal game and its dual at once, and
     350             :         alternates working on each, exchanging information about solved vertices
     351             :         in the process. */
     352             :     virtual ParityGame::Strategy solve_alternate();
     353             : 
     354             :     /*! Preprocess the game so that vertices with loops either have the loop
     355             :         removed, or have all other edges removed. In the latter case, the vertex
     356             :         is necessarily won by the player corresponding with its parity.
     357             : 
     358             :         This preprocessing operation speeds up solving with small progress
     359             :         measures considerably, though it is superseded by the DecycleSolver
     360             :         which does more general preprocessing. */
     361             :     static void preprocess_game(ParityGame &game);
     362             : 
     363             : private:
     364             :     SmallProgressMeasuresSolver(const SmallProgressMeasuresSolver&);
     365             :     SmallProgressMeasuresSolver &operator=(const SmallProgressMeasuresSolver&);
     366             : 
     367             : protected:
     368             :     std::shared_ptr<LiftingStrategyFactory> lsf_;   //!< factory used to create lifting strategy
     369             :     bool alternate_;                //!< whether to use the alternate algorithm
     370             :     LiftingStatistics *stats_;      //!< object to record lifting statistics
     371             :     const verti *vmap_;             //!< current vertex map
     372             :     const verti vmap_size_;         //!< size of vertex map
     373             : };
     374             : 
     375             : /*! \ingroup SmallProgressMeasures
     376             : 
     377             :     TODO: document this class. */
     378             : class SmallProgressMeasuresSolver2 : public SmallProgressMeasuresSolver
     379             : {
     380             : public:
     381             :     SmallProgressMeasuresSolver2( const ParityGame &game,
     382             :                                   std::shared_ptr<LiftingStrategyFactory> lsf,
     383             :                                   bool alternate = false,
     384             :                                   LiftingStatistics *stats = 0,
     385             :                                   const verti *vmap = 0,
     386             :                                   verti vmap_size = 0 );
     387             : 
     388             :     ParityGame::Strategy solve_normal();
     389             :     ParityGame::Strategy solve_alternate();
     390             : 
     391             : private:
     392             :     SmallProgressMeasuresSolver2(const SmallProgressMeasuresSolver2&);
     393             :     SmallProgressMeasuresSolver2 &operator=(const SmallProgressMeasuresSolver2&);
     394             : };
     395             : 
     396             : /*! \ingroup SmallProgressMeasures
     397             : 
     398             :     Factory class for SmallProgressMeasuresSolver instances */
     399             : class SmallProgressMeasuresSolverFactory : public ParityGameSolverFactory
     400             : {
     401             : public:
     402             :     SmallProgressMeasuresSolverFactory(std::shared_ptr<LiftingStrategyFactory> lsf,
     403             :         int version = 1, bool alt = false, LiftingStatistics *stats = 0 );
     404             : 
     405             :     ParityGameSolver *create( const ParityGame &game,
     406             :                               const verti *vmap,
     407             :                               verti vmap_size );
     408             : 
     409             : private:
     410             :     std::shared_ptr<LiftingStrategyFactory>  lsf_;
     411             :     int                     version_;
     412             :     bool                    alt_;
     413             :     LiftingStatistics       *stats_;
     414             : };
     415             : 
     416             : #include "SmallProgressMeasures_impl.h"
     417             : 
     418             : #endif /* ndef MCRL2_PG_SMALL_PROGRESS_MEASURES_H */

Generated by: LCOV version 1.14