LCOV - code coverage report
Current view: top level - pbes/test - symbolic_parity_game_test.cpp (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 2 2 100.0 %
Date: 2024-05-01 03:37:31 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Maurice Laveaux
       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             : 
      10             : #define BOOST_TEST_MODULE symbolic_parity_game_test
      11             : #include <boost/test/included/unit_test.hpp>
      12             : 
      13           2 : BOOST_AUTO_TEST_CASE(dummy_test)
      14             : {
      15             :   // This is an empty test since at least one test is required.
      16           1 : }
      17             : 
      18             : /*
      19             : // Commented this test since Sylvan causes some address sanitizer failures that I cannot resolve.
      20             : #ifdef MCRL2_ENABLE_SYLVAN
      21             : 
      22             : #include "mcrl2/data/variable.h"
      23             : #include "mcrl2/pbes/symbolic_parity_game.h"
      24             : #include "mcrl2/pbes/symbolic_pbessolve.h"
      25             : #include "mcrl2/pbes/pbes_summand_group.h"
      26             : #include "mcrl2/symbolic/data_index.h"
      27             : #include "mcrl2/symbolic/test_utility.h"
      28             : 
      29             : #include <sylvan_ldd.hpp>
      30             : 
      31             : #include <vector>
      32             : #include <random>
      33             : 
      34             : using namespace mcrl2::pbes_system;
      35             : using namespace mcrl2::symbolic;
      36             : using sylvan::ldds::ldd;
      37             : 
      38             : /// A single randomly generated transition group for parameter i
      39             : summand_group single_transition_group(const mcrl2::data::variable_list& parameters, std::size_t i, std::size_t N, std::size_t D)
      40             : {
      41             :   boost::dynamic_bitset<> dependencies(parameters.size()*2);
      42             :   dependencies[i] = 1;
      43             :   dependencies[i+1] = 1;
      44             : 
      45             :   summand_group group(parameters, dependencies, false);
      46             :   group.L = random_set(N, 2, D);
      47             : 
      48             :   return group;
      49             : }
      50             : 
      51             : /// \brief Generates a random parity game with N vertices and at most P priorities.
      52             : std::tuple<ldd, ldd, std::vector<ldd>, std::vector<summand_group>, std::vector<data_expression_index>> compute_random_game(std::size_t N, std::size_t P)
      53             : {  
      54             :   std::size_t D = 10; // Size of the state space domain.
      55             :   std::size_t length = 4; // Length of the state vector.
      56             : 
      57             :   ldd V = random_set(N, length, D);
      58             :   ldd Veven = random_subset(V, N / 2);
      59             : 
      60             :   ldd remaining = V;
      61             :   std::vector<ldd> prio(P);
      62             :   for (std::size_t i = 0; i < P; ++i)
      63             :   {
      64             :     prio[i] = random_subset(remaining, N/P);
      65             :     remaining = minus(remaining, prio[i]);
      66             :   }
      67             : 
      68             :   // Define some list of parameters.
      69             :   mcrl2::data::variable_list parameters;
      70             :   for (std::size_t j = 0; j < length; ++j)
      71             :   {
      72             :     parameters.emplace_front(mcrl2::data::variable());
      73             :   }
      74             : 
      75             :   // Generate random transition groups.
      76             :   std::vector<summand_group> groups;
      77             :   groups.emplace_back(single_transition_group(parameters, 0, 3*D, D));
      78             :   groups.emplace_back(single_transition_group(parameters, 1, 3*D, D));
      79             :   groups.emplace_back(single_transition_group(parameters, 2, 3*D, D));
      80             :   groups.emplace_back(single_transition_group(parameters, 3, 3*D, D));
      81             : 
      82             :   std::vector<data_expression_index> index;
      83             : 
      84             :   return std::make_tuple(V, Veven, prio, groups, index);
      85             : }
      86             : 
      87             : /// \returns Minimal fixpoint of the given (monotone) predicate transformer A -> A.
      88             : template<typename F>
      89             : ldd least_fixpoint(F pred)
      90             : {  
      91             :   ldd prev, current;
      92             :   do 
      93             :   {
      94             :     prev = current;
      95             :     current = pred(current);
      96             :   }
      97             :   while (current != prev);
      98             : 
      99             :   return current;
     100             : }
     101             : 
     102             : /// \returns Maximal fixpoint of the given (monotone) predicate transformer A -> A.
     103             : template<typename F>
     104             : ldd greatest_fixpoint(F pred, ldd V)
     105             : {  
     106             :   ldd prev;
     107             :   ldd current = V;
     108             :   do 
     109             :   {
     110             :     prev = current;
     111             :     current = pred(current);
     112             :   }
     113             :   while (current != prev);
     114             : 
     115             :   return current;
     116             : }
     117             : 
     118             : /// \returns The predecessors of U.
     119             : ldd pre(const symbolic_parity_game& G, const ldd& V, const ldd& U)
     120             : {
     121             :   return G.predecessors(V, U);
     122             : }
     123             : 
     124             : /// \returns The control predecessors of U.
     125             : ldd cpre(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U)
     126             : {
     127             :   std::array<const ldd, 2> Vplayer = G.players(V);
     128             : 
     129             :   return union_(
     130             :     intersect(Vplayer[alpha],        pre(G, V, U)),
     131             :     minus(Vplayer[1 - alpha], union_(pre(G, V, minus(V, U)), G.sinks(V, V)))
     132             :   );
     133             : }
     134             : 
     135             : /// \returns Standard attractor set computation.
     136             : ldd attr(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U)
     137             : {
     138             :   return least_fixpoint(
     139             :     [&](const ldd& Z) -> ldd {
     140             :       return union_(U, cpre(G, V, alpha, Z));
     141             :     });
     142             : }
     143             : 
     144             : /// \returns The safe control predecessors of U.
     145             : ldd spre(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U, const ldd& I)
     146             : {
     147             :   std::array<const ldd, 2> Vplayer = G.players(V);
     148             : 
     149             :   return union_(
     150             :     intersect(Vplayer[alpha], G.predecessors(V, U)),
     151             :     minus(Vplayer[1 - alpha], union_(union_(G.predecessors(V, minus(V, U)), G.sinks(V, V)), I))
     152             :   );
     153             : }
     154             : 
     155             : /// \returns Safe attractor set computation.
     156             : ldd sattr(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U, const ldd& I)
     157             : {
     158             :   return least_fixpoint(
     159             :     [&](const ldd& Z) -> ldd {
     160             :       return union_(U, spre(G, V, alpha, Z, I));
     161             :     });
     162             : }
     163             : 
     164             : /// \brief Computes the monotone control predecessor.
     165             : ldd Mcpre(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& Z, const ldd& U, std::size_t c)
     166             : {
     167             :   return intersect(G.prio_above(V, c), cpre(G, V, alpha, union_(Z, U)));
     168             : }
     169             : 
     170             : /// \brief Computes monotone attractor for priority c.
     171             : ldd Mattr(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U, std::size_t c)
     172             : {
     173             :   return least_fixpoint(
     174             :     [&](const ldd& Z) -> ldd
     175             :     {
     176             :       return Mcpre(G, V, alpha, Z, U, c);
     177             :     }
     178             :   );
     179             : }
     180             : 
     181             : /// \returns Safe vertices computation.
     182             : ldd safe(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& I)
     183             : {
     184             :   std::array<const ldd, 2> Vplayer = G.players(V);
     185             :   return minus(V, attr(G, V, 1-alpha, intersect(Vplayer[1-alpha], I)));
     186             : }
     187             : 
     188             : /// \brief Computes fatal attractor for priority c.
     189             : ldd fatal_attractor(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, std::size_t c, const ldd& I)
     190             : {
     191             :   ldd Pequal = intersect(V, G.ranks().at(c));
     192             :   const ldd Vsafe = safe(G, V, alpha, I);
     193             : 
     194             :   return greatest_fixpoint(
     195             :     [&](const ldd& Z) -> ldd
     196             :     {
     197             :       // G \cap safe(G) is here represented by restricting V to Vsafe.
     198             :       return intersect(intersect(Pequal, Vsafe), Mattr(G, Vsafe, alpha, Z, c));
     199             :     },
     200             :     Vsafe
     201             :   );
     202             : }
     203             : 
     204             : /// \brief Computes the safe monotone control predecessor.
     205             : ldd sMcpre(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& Z, const ldd& U, std::size_t c, const ldd& I)
     206             : {
     207             :   return intersect(G.prio_above(V, c), spre(G, V, alpha, union_(Z, U), I));
     208             : }
     209             : 
     210             : /// \brief Computes monotone attractor for priority c.
     211             : ldd sMattr(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& U, std::size_t c, const ldd& I)
     212             : {
     213             :   return least_fixpoint(
     214             :     [&](const ldd& Z) -> ldd
     215             :     {
     216             :       return sMcpre(G, V, alpha, Z, U, c, I);
     217             :     }
     218             :   );
     219             : }
     220             : 
     221             : /// \brief Computes fatal attractor for priority c.
     222             : ldd safe_fatal_attractor(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, std::size_t c, const ldd& I)
     223             : {
     224             :   ldd Pequal = intersect(V, G.ranks().at(c));
     225             : 
     226             :   return greatest_fixpoint(
     227             :     [&](const ldd& Z) -> ldd
     228             :     {
     229             :       return intersect(Pequal, sMattr(G, V, alpha, Z, c, I));
     230             :     },
     231             :     V
     232             :   );
     233             : }
     234             : 
     235             : /// \brief Returns the solitair cycles.
     236             : ldd solitair_cycles(const symbolic_parity_game& G, const ldd& V, std::size_t alpha)
     237             : {
     238             :   std::array<const ldd, 2> Vplayer = G.players(V);
     239             :   std::array<const ldd, 2> parity = G.parity(V);
     240             : 
     241             :   return greatest_fixpoint(
     242             :     [&](const ldd& Z) -> ldd {
     243             :       return intersect(intersect(Vplayer[alpha], parity[alpha]), pre(G, V, Z));
     244             :     },
     245             :     V
     246             :   );
     247             : }
     248             : 
     249             : /// \brief Returns the forced cycles.
     250             : ldd forced_cycles(const symbolic_parity_game& G, const ldd& V, std::size_t alpha, const ldd& I)
     251             : {
     252             :   std::array<const ldd, 2> parity = G.parity(V);
     253             :   const ldd Vsafe = safe(G, V, alpha, I);
     254             : 
     255             :   return greatest_fixpoint(
     256             :     [&](const ldd& Z) -> ldd {
     257             :       return intersect(intersect(parity[alpha], Vsafe), cpre(G, V, alpha, Z));
     258             :     },
     259             :     V
     260             :   );
     261             : }
     262             : 
     263             : BOOST_AUTO_TEST_CASE(random_test_attractor)
     264             : {
     265             :   initialise_sylvan();
     266             : 
     267             :   for (std::size_t i = 0; i < 100; ++i)
     268             :   {
     269             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     270             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     271             : 
     272             :     ldd U = random_subset(V, 250);
     273             : 
     274             :     std::array<const ldd, 2> Vplayer = G.players(V);
     275             :     ldd result = G.safe_attractor(U, 0, V, Vplayer);
     276             :     ldd expected = attr(G, V, 0, U);
     277             :     std::cerr << satcount(result) << std::endl;
     278             : 
     279             :     BOOST_CHECK_EQUAL(result, expected);
     280             :   }
     281             :   
     282             :   quit_sylvan();
     283             : }
     284             : 
     285             : BOOST_AUTO_TEST_CASE(random_test_safe_attractor)
     286             : {
     287             :   initialise_sylvan();
     288             : 
     289             :   for (std::size_t i = 0; i < 100; ++i)
     290             :   {
     291             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     292             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     293             : 
     294             :     ldd U = random_subset(V, 250);
     295             :     ldd I = random_subset(minus(V, U), 250);
     296             : 
     297             :     std::array<const ldd, 2> Vplayer = G.players(V);
     298             :     ldd result = G.safe_attractor(U, 0, V, Vplayer, I);
     299             :     ldd expected = sattr(G, V, 0, U, I);
     300             :     std::cerr << satcount(result) << std::endl;
     301             : 
     302             :     BOOST_CHECK_EQUAL(result, expected);
     303             :   }
     304             :   
     305             :   quit_sylvan();
     306             : }
     307             : 
     308             : BOOST_AUTO_TEST_CASE(random_test_chaining_safe_attractor)
     309             : {
     310             :   initialise_sylvan();
     311             : 
     312             :   for (std::size_t i = 0; i < 100; ++i)
     313             :   {
     314             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     315             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, true);
     316             : 
     317             :     ldd U = random_subset(V, 250);
     318             :     ldd I = random_subset(minus(V, U), 250);
     319             : 
     320             :     std::array<const ldd, 2> Vplayer = G.players(V);
     321             :     ldd result = G.safe_attractor(U, 0, V, Vplayer, I);
     322             :     ldd expected = sattr(G, V, 0, U, I);
     323             :     std::cerr << satcount(result) << std::endl;
     324             : 
     325             :     BOOST_CHECK_EQUAL(result, expected);
     326             :   }
     327             :   
     328             :   quit_sylvan();
     329             : }
     330             : 
     331             : BOOST_AUTO_TEST_CASE(random_test_monotone_attractor)
     332             : {
     333             :   initialise_sylvan();
     334             : 
     335             :   for (std::size_t i = 0; i < 100; ++i)
     336             :   {
     337             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     338             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     339             : 
     340             :     ldd U = random_subset(V, 250);
     341             : 
     342             :     std::array<const ldd, 2> Vplayer = G.players(V);
     343             :     ldd result = G.safe_monotone_attractor(U, 0, 0, V, Vplayer);
     344             :     ldd expected = Mattr(G, V, 0, U, 0);
     345             :     std::cerr << satcount(result) << std::endl;
     346             : 
     347             :     BOOST_CHECK_EQUAL(result, expected);
     348             :   }
     349             :   
     350             :   quit_sylvan();
     351             : }
     352             : 
     353             : BOOST_AUTO_TEST_CASE(random_test_safe_monotone_attractor)
     354             : {
     355             :   initialise_sylvan();
     356             : 
     357             :   for (std::size_t i = 0; i < 100; ++i)
     358             :   {
     359             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     360             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     361             : 
     362             :     ldd U = random_subset(V, 250);
     363             :     ldd I = random_subset(minus(V, U), 250);
     364             : 
     365             :     std::array<const ldd, 2> Vplayer = G.players(V);
     366             :     ldd result = G.safe_monotone_attractor(U, 0, 0, V, Vplayer, I);
     367             :     ldd expected = sMattr(G, V, 0, U, 0, I);
     368             :     std::cerr << satcount(result) << std::endl;
     369             : 
     370             :     BOOST_CHECK_EQUAL(result, expected);
     371             :   }
     372             : 
     373             :   quit_sylvan();
     374             : }
     375             : 
     376             : BOOST_AUTO_TEST_CASE(random_test_chaining_monotone_attractor)
     377             : {
     378             :   initialise_sylvan();
     379             : 
     380             :   for (std::size_t i = 0; i < 100; ++i)
     381             :   {
     382             :     auto [V, Veven, prio, groups, index] = compute_random_game(5000, 2);
     383             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, true);
     384             : 
     385             :     ldd U = random_subset(V, 250);
     386             :     ldd I = random_subset(minus(V, U), 250);
     387             : 
     388             :     std::array<const ldd, 2> Vplayer = G.players(V);
     389             :     ldd result = G.safe_monotone_attractor(U, 0, 0, V, Vplayer, I);
     390             :     ldd expected = sMattr(G, V, 0, U, 0, I);
     391             :     std::cerr << satcount(result) << std::endl;
     392             : 
     393             :     BOOST_CHECK_EQUAL(result, expected);
     394             :   }
     395             : 
     396             :   quit_sylvan();
     397             : }
     398             : 
     399             : BOOST_AUTO_TEST_CASE(random_test_solitair_cycles)
     400             : {
     401             :   initialise_sylvan();
     402             :   
     403             :   for (std::size_t i = 0; i < 1; ++i)
     404             :   {
     405             :     auto [V, Veven, prio, groups, index] = compute_random_game(100, 2);
     406             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     407             :     mcrl2::pbes_system::symbolic_pbessolve_algorithm solver(G);
     408             : 
     409             :     ldd initial = random_subset(V, 1);
     410             :     ldd I;
     411             : 
     412             :     // Use V as initial vertex so it does not terminate early.
     413             :     std::pair<ldd, ldd> Vresult = solver.detect_solitair_cycles(V, V, I, false, G.sinks(V, V));
     414             : 
     415             :     std::array<ldd, 2> winning;
     416             :     ldd Vtotal = G.compute_total_graph(V, sylvan::ldds::empty_set(), G.sinks(V, V), winning);
     417             : 
     418             :     std::pair<ldd, ldd> Vexpected = { union_(winning[0], attr(G, V, 0, solitair_cycles(G, V, 0))), 
     419             :                                       union_(winning[1], attr(G, V, 1, solitair_cycles(G, V, 1))) };
     420             :     BOOST_CHECK_EQUAL(Vresult.first, Vexpected.first);
     421             :     BOOST_CHECK_EQUAL(Vresult.second, Vexpected.second);
     422             :   }
     423             :   
     424             :   quit_sylvan();
     425             : }
     426             : 
     427             : BOOST_AUTO_TEST_CASE(random_test_solitair_cycles)
     428             : {
     429             :   initialise_sylvan();
     430             :   
     431             :   for (std::size_t i = 0; i < 1; ++i)
     432             :   {
     433             :     auto [V, Veven, prio, groups, index] = compute_random_game(500, 2);
     434             :     symbolic_parity_game G(groups, index, V, Veven, prio, false, false);
     435             :     mcrl2::pbes_system::symbolic_pbessolve_algorithm solver(G);
     436             : 
     437             :     ldd initial = random_subset(V, 1);
     438             :     ldd I;
     439             : 
     440             :     // Use V as initial vertex so it does not terminate early.
     441             :     std::pair<ldd, ldd> Vresult = solver.detect_forced_cycles(V, V, I, false, G.sinks(V, V));
     442             : 
     443             :     std::array<ldd, 2> winning;
     444             :     ldd Vtotal = G.compute_total_graph(V, sylvan::ldds::empty_set(), G.sinks(V, V), winning);
     445             : 
     446             :     std::pair<ldd, ldd> Vexpected = { union_(winning[0], attr(G, V, 0, forced_cycles(G, V, 0, I))), 
     447             :                                       union_(winning[1], attr(G, V, 1, forced_cycles(G, V, 1, I))) };
     448             :     BOOST_CHECK_EQUAL(Vresult.first, Vexpected.first);
     449             :     BOOST_CHECK_EQUAL(Vresult.second, Vexpected.second);
     450             :   }
     451             :   
     452             :   quit_sylvan();
     453             : }
     454             : 
     455             : #endif // MCRL2_ENABLE_SYLVAN
     456             : */

Generated by: LCOV version 1.14