org: cleanup tut40

* doc/org/tut40.org: Add more explanations and some cleanup.
* python/spot/__init__.py (set_state_players, get_state_winners,
get_state_players, set_state_player, get_state_winner,
get_state_player, get_strategy): Add these methods to the twa_graph
class for convenience.
* NEWS, doc/org/tut.org: Mention tut40.org.
This commit is contained in:
Alexandre Duret-Lutz 2021-09-30 16:17:48 +02:00
parent 211e7d90d3
commit 88d0d2e112
4 changed files with 283 additions and 330 deletions

7
NEWS
View file

@ -284,8 +284,11 @@ New in spot 2.9.8.dev (not yet released)
Python: Python:
- Bindings for functions related to games and aig-circuits. - Bindings for functions related to games.
See https://spot.lrde.epita.fr/ipynb/games.html for examples. See https://spot.lrde.epita.fr/ipynb/games.html and
https://spot.lrde.epita.fr/tut40.html for examples.
- Bindings for functions related to syntethis and aig-circuits.
- spot::twa::prop_set was previously abstent from the Python - spot::twa::prop_set was previously abstent from the Python
bindings, making it impossible to call make_twa_graph() for copying bindings, making it impossible to call make_twa_graph() for copying

View file

@ -37,6 +37,7 @@ three interfaces supported by Spot: shell commands, Python, or C++.
- [[file:tut22.org][Creating an automaton by adding states and transitions]] - [[file:tut22.org][Creating an automaton by adding states and transitions]]
- [[file:tut23.org][Creating an alternating automaton by adding states and transitions]] - [[file:tut23.org][Creating an alternating automaton by adding states and transitions]]
- [[file:tut24.org][Iterating over alternating automata]] - [[file:tut24.org][Iterating over alternating automata]]
- [[file:tut40.org][Creating a solving a safety game to decide direct simulation]]
- [[file:tut52.org][Creating an explicit Kripke structure]] - [[file:tut52.org][Creating an explicit Kripke structure]]
- [[file:tut90.org][Using the =bdd_dict= to associate atomic proposition to BDD - [[file:tut90.org][Using the =bdd_dict= to associate atomic proposition to BDD
variables, or allocate anonymous BDD variables (advanced)]] variables, or allocate anonymous BDD variables (advanced)]]

View file

@ -1,25 +1,31 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#+TITLE: Using games to compute simulation #+TITLE: Using games to check a simulation
#+DESCRIPTION: Code example for iterating over ω-automata in Spot #+DESCRIPTION: Code example for using games in Spot
#+INCLUDE: setup.org #+INCLUDE: setup.org
#+HTML_LINK_UP: tut.html #+HTML_LINK_UP: tut.html
#+PROPERTY: header-args:sh :results verbatim :exports both #+PROPERTY: header-args:sh :results verbatim :exports both
#+PROPERTY: header-args:python :results output :exports both #+PROPERTY: header-args:python :results output :exports both
#+PROPERTY: header-args:C+++ :results verbatim :exports both #+PROPERTY: header-args:C+++ :results verbatim :exports both
This example demonstrates how to find the simulations between states using This example demonstrates how to use Spot's game interface to compute
games in C++ and python. This algorithm is describe in Fair Simulation a simulation-relation between the states of an automaton. This
Relations, Parity Games, and State Space Reduction for Büchi Automata by R. algorithm is inspired from [[https://homepages.inf.ed.ac.uk/kousha/siam_j2005.pdf][Fair Simulation Relations, Parity Games,
Schuller Borbely, Kousha Etessami and Thomas Wilke. and State Space Reduction for Büchi Automata (Kousha Etessami and
Thomas Wilke, and Rebecca A. Schuller)]].
The simulation based reduction (spot.simuation() and spot.reduce_direct_sim()) The code below is intended for demonstration of how to construct and
does not use this method as is it slower. use games. Spot contains some other (and faster) implementation to
reduce an automaton using simulation-based reductions (see
=spot.simulation()= and =spot.reduce_direct_sim()=).
When all the moves are in the game, we check if the spoiler has a winning Let us start with a definition of simulation for transition-based
strategy from a pair of state. If yes, there is simulation otherwise there are generalized-Büchi automata: A state $s'$ simulates $s$ iff for any
no. transition $(s,c,a,d)$ leaving $s$, there exists a transition
$(s',c',a',d')$ leaving $s'$ with a condition $c'$ that covers $c$,
some colors $a'\supseteq a$ that covers the colors of $a$ other
transition, and reaching a destination state $d'$ that simulates $d$.
We will compute the simulation between 4 and 0 in the following automaton. In the following automaton, for instance, XXX
#+NAME: tut40in #+NAME: tut40in
#+BEGIN_SRC hoa #+BEGIN_SRC hoa
@ -27,23 +33,21 @@ HOA: v1
States: 6 States: 6
Start: 0 Start: 0
AP: 2 "a" "b" AP: 2 "a" "b"
acc-name: co-Buchi Acceptance: 1 Inf(0)
Acceptance: 1 Fin(0)
properties: trans-labels explicit-labels state-acc very-weak
--BODY-- --BODY--
State: 0 State: 0
[1] 1 [1] 1
[1] 2 [1] 2
State: 1 {0} State: 1
[0&1] 1 [0&1] 1
State: 2 State: 2
[0] 3 [0] 3
State: 3 State: 3
[1] 3 [1] 3 {0}
State: 4 State: 4
[1] 5 [1] 5
State: 5 State: 5
[0] 5 [0] 5 {0}
--END-- --END--
#+END_SRC #+END_SRC
@ -52,39 +56,9 @@ State: 5
cat >tut40.hoa <<EOF cat >tut40.hoa <<EOF
<<tut40in>> <<tut40in>>
EOF EOF
autfilt --dot tut40.hoa autfilt --dot='.#' tut40.hoa
#+END_SRC #+END_SRC
#+RESULTS: tut40dot
#+begin_example
digraph "" {
rankdir=LR
label=<<br/>[co-Büchi]>
labelloc="t"
node [shape="circle"]
node [style="filled", fillcolor="#ffffa0"]
fontname="Lato"
node [fontname="Lato"]
edge [fontname="Lato"]
node[fontsize=12] fontsize=12 stylesheet="spot.css" edge[arrowhead=vee, arrowsize=.7, fontsize=12]
I [label="", style=invis, width=0]
I -> 0
0 [label=<0>]
0 -> 1 [label=<b>]
0 -> 2 [label=<b>]
1 [label=<1>, peripheries=2]
1 -> 1 [label=<a &amp; b>]
2 [label=<2>]
2 -> 3 [label=<a>]
3 [label=<3>]
3 -> 3 [label=<b>]
4 [label=<4>]
4 -> 5 [label=<b>]
5 [label=<5>]
5 -> 5 [label=<a>]
}
#+end_example
#+BEGIN_SRC dot :file tut40in.svg :var txt=tut40dot :exports results #+BEGIN_SRC dot :file tut40in.svg :var txt=tut40dot :exports results
$txt $txt
#+END_SRC #+END_SRC
@ -92,122 +66,175 @@ digraph "" {
#+RESULTS: #+RESULTS:
[[file:tut40in.svg]] [[file:tut40in.svg]]
Whether two states are in simulation can be decided as a game between
two players. If the game is in state $(q,q')$, spoiler (player 0)
first selects a transition from state $q$, and duplicator (player 1)
then has to chose a compatible transition from state $q'$. Duplicator
of course wins if it always manages to select compatibles transitions,
otherwise spoiler wins.
The game arena can be encoded by associating each state to a pair of
integers. States owned by player 0 (rounded rectangles) are pairs
$(q,q')$ denoting the position of each player. States owned by player
1 (diamonds) are pairs $(e,q')$ where $e$ is the number of the
edges that player 0 just took (those numbers appears as =#1=, =#2=,
etc. in the previous picture).
Here is how the game arena look like starting from $(q,q')=(4,0)$:
#+NAME: game40
#+BEGIN_SRC python :exports results :noweb yes
<<build_game>>
aut = spot.automaton("""
<<tut40in>>
""")
g = direct_sim_game(aut, 4, 0)
#+END_SRC
#+NAME: game40unsolved
#+BEGIN_SRC python :exports none :noweb yes
<<game40>>
print(g.to_str('dot', '.g'))
#+END_SRC
#+BEGIN_SRC dot :file tut40gameunsolved.svg :var txt=game40unsolved :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:tut40gameunsolved.svg]]
In this game, player 1, wins if it has a strategy to force the game to
satisfy the acceptance condition. Here the acceptance condition is
just true, so any infinite play will satisfy it.
Clearly, it is enough for player 1 to always go to $(5,1)$ when
possible. If Spot is used to solve this game, the result can be
presented as follows, where greens states represents states from which
player 1 has a winning strategy, and red states are states from which
player 0 has a winning strategy. The highlighted arrows show those
strategies.
#+NAME: game40solved
#+BEGIN_SRC python :exports none :noweb yes
<<game40>>
spot.solve_safety_game(g)
spot.highlight_strategy(g)
print(g.to_str('dot', '.g'))
#+END_SRC
#+BEGIN_SRC dot :file tut40gamesolved.svg :var txt=game40solved :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:tut40gamesolved.svg]]
Since player 1 is winning from state $(4,0)$, we know that state 4
simulates state 0. Also since player 1 would also win from state
$(5,1)$, we can tell that state 5 simulates state 1. We also learn
that state 5 does not simulates states 2 and 3. We could build other
games, or add more state to this game, to learn about other pairs of
states.
* Python * Python
We now look at how to create such a game in Python.
Essentially, a game in Spot is just an automaton equiped with a
special property to indicate the owner of each states. So it can be
created using the usual interface:
First a need to create a game. The game is build by alternating the possible
moves of the two players: the spoiler (even) and the duplicator (odd). The
spoiler starts from its state and chose a transition and move to the
destination. Then, the duplicator search for a transition from its state with a
compatible condition and acceptance.
#+NAME: build_game #+NAME: build_game
#+BEGIN_SRC python :exports code #+BEGIN_SRC python :exports code
import spot import spot
from spot import buddy from spot import buddy
def direct_sim_game(aut, s1, s2):
if s1 >= aut.num_states() or s2 >= aut.num_states():
raise ValueError('invalid state number')
game = spot.make_twa_graph(aut.get_dict()) def direct_sim_game(aut, s1, s2):
if s1 >= aut.num_states() or s2 >= aut.num_states():
raise ValueError('invalid state number')
assert aut.acc().is_generalized_buchi()
# The names are a pair of int. If even owns the state, the first integer game = spot.make_twa_graph()
# is the number of the edge that spoiler has taken during this turn (so # The names of the states are pairs of integers
# we have the current position of the spoiler (the destination of the # (q,q') for states owned by player 0
# edge), the condition and acceptance that duplicator must find). If # (e,q') for states owned by player 1
# it is an odd state, the first integer is the state where the spoiler # These arrays are indiced by state numbers.
# is. In either case, the second is always the duplicator's state. names = []
names = [] owners = []
owners = [] # The reverse assotiation (x,y) -> state number
# must be kept for each player, since (x,y) can mean two different thing.
s_orig_states = {}
d_orig_states = {}
# a list of player 0 states to process
todo = []
# Keep the game's state from its name. We need two maps as two states can # Create the state (i, j) for a player if it does not exist yet and
# have the same name (but not the same owner). For example (0, 1) can # returns the state's number in the game.
# describe a spoiler state (the edge 0, the state 1) and a duplicator def get_game_state(player, i, j):
# state (the states 0 and 1). orig_state = s_orig_states if player else d_orig_states
s_orig_states = {} if (i, j) in orig_state:
d_orig_states = {}
todo = []
# Create the state (i, j) for a player if it does not exist yet and
# returns its state's number in the game.
def get_and_create_game_state(player, i, j):
orig_state = s_orig_states if player else d_orig_states
if not (i, j) in orig_state:
s = game.new_state()
names.append((i, j))
owners.append(player)
orig_state[(i, j)] = s
# If it is the duplicator turn and the state does not exist
# yet, we need to process it.
if not player:
todo.append(s)
return s
else:
return orig_state[(i, j)] return orig_state[(i, j)]
s = game.new_state()
names.append((i, j))
owners.append(player)
orig_state[(i, j)] = s
# If it is a new state for Player 0 (spoiler)
# we need to process it.
if not player:
todo.append(s)
return s
get_and_create_game_state(False, s1, s2); game.set_init_state(get_game_state(False, s1, s2))
(all_inf, _) = aut.get_acceptance().used_inf_fin_sets() while todo:
cur = todo.pop()
# todo contains only player 0's states, named with pairs
# of states.
(s_src, d_src) = names[cur]
while todo: # Player 0 is allowed to pick edge from s_src:
cur = todo.pop() for s_edge in aut.out(s_src):
(s_src, d_src) = names[cur] edge_idx = aut.edge_number(s_edge)
st2 = get_game_state(True, edge_idx, d_src)
# label the edge with true, because it's an automaton,
# but we do not use this label for the game.
game.new_edge(cur, st2, buddy.bddtrue)
# Spoiler starts by picking an edge from s_src. If it does not exist # Player 1 then try to find an edge with the
# yet, we add a new edge in the game from (s_src,d_src) to # a compatible same condition and colors, from d_src.
# (edge_idx,d_src). for d_edge in aut.out(d_src):
for s_edge in aut.out(s_src): if (buddy.bdd_implies(d_edge.cond, s_edge.cond) \
edge_idx = aut.edge_number(s_edge) and d_edge.acc.subset(s_edge.acc)):
cur2 = get_and_create_game_state(True, edge_idx, d_src) st3 = get_game_state(False, s_edge.dst, d_edge.dst)
game.new_edge(st2, st3, buddy.bddtrue)
game.new_edge(cur, cur2, buddy.bddtrue) # Name each state with a string, just so we can read the pairs
# when the automaton is displayed.
# Duplicator try to find an edge with the same condition from d_src. game.set_state_names(list(map(str, names)))
# If it does not exist yet, it adds a new edge from # This only line is actually what turns an automaton into a game!
# (s_edge.dst,d_src) to (s_edge.dst, d_edge.dst). game.set_state_players(owners)
for d_edge in aut.out(d_src): return game
if buddy.bdd_implies(d_edge.cond, s_edge.cond) \
and (s_edge.acc ^ all_inf).subset(d_edge.acc ^ all_inf):
cur3 = get_and_create_game_state(False, s_edge.dst, d_edge.dst)
game.new_edge(cur2, cur3, buddy.bddtrue)
spot.set_state_players(game, owners)
names = [s for s in map(str, names)]
game.set_state_names(names)
return game
#+END_SRC #+END_SRC
Then we solve it. This game is a safety game, the odd player tries to block To solve a safety game =g= that has been created by the above method,
the even player into a dead end. Even win if it has a winning strategy, aka it is enough to just call =solve_safety_game(g)=. This function
there is no dead end in the game. returns the player winning in the initial state. However, as a
side-effect it defines additional automaton properties that indicate
the winner of each state, and the associated strategy.
#+BEGIN_SRC :export code Therefore to list all simulation pairs we learned from a game starting
spot.solve_safety_game(g) in state $(i,j)$, we could proceed as follow:
#+END_SRC
Next, we get the winners for each state. We only check the even's state as it
is the only ones that describe states pairs (the odd's ones describe the
edge taken and a state). If odd wins from this state, then the first state
simulates the second.
#+NAME: computesim_tut40 #+NAME: computesim_tut40
#+BEGIN_SRC python :exports code #+BEGIN_SRC python :exports code
def compute_simulation(aut, i, j): def list_simulations(aut, i, j):
g = direct_sim_game(aut, i, j) g = direct_sim_game(aut, i, j)
spot.solve_safety_game(g) spot.solve_safety_game(g)
winners = spot.get_state_winners(g) winners = g.get_state_winners()
owners = spot.get_state_players(g) owners = g.get_state_players()
names = g.get_state_names() names = g.get_state_names()
simulations = [] simulations = []
@ -218,220 +245,139 @@ def compute_simulation(aut, i, j):
return simulations return simulations
#+END_SRC #+END_SRC
#+RESULTS: computesim_tut40 On our running example, that gives:
Compute all simulations found when checking if 4 simulates 0. #+BEGIN_SRC python :results verbatim :exports both :noweb strip-export
<<game40>>
#+BEGIN_SRC python :exports code <<computesim_tut40>>
print(compute_simulation(aut, 4, 0)) print(list_simulations(aut, 4, 0))
#+END_SRC #+END_SRC
#+RESULTS: #+RESULTS:
#+BEGIN_SRC python :exports results :results verbatim :noweb yes
import spot
from spot import buddy
aut = spot.automaton('''
<<tut40in>>
''')
<<build_game>>
<<computesim_tut40>>
print(compute_simulation(aut, 4, 0))
#+END_SRC
#+RESULTS:
: [(4, 0), (5, 1)]
* C++ * C++
Here is some almost equivalent code in C++.
The equivalent code in C++: Here instead of naming states with strings, we use the "product-states"
property, which is usually used to display pair of integers that come from a
product of automata.
#+NAME: cppCompute #+NAME: cppCompute
#+BEGIN_SRC C++ :exports code #+BEGIN_SRC C++ :exports code
#include <spot/twaalgos/game.hh> #include <spot/twaalgos/game.hh>
#include <spot/twa/twagraph.hh> #include <spot/twa/twagraph.hh>
spot::twa_graph_ptr direct_sim_game(spot::const_twa_graph_ptr aut, spot::twa_graph_ptr direct_sim_game(spot::const_twa_graph_ptr aut,
unsigned s1, unsigned s2) unsigned s1, unsigned s2)
{
if (s1 >= aut->num_states() || s2 >= aut->num_states())
throw std::runtime_error("direct_sim_game(): invalid state number");
auto game = spot::make_twa_graph(aut->get_dict());
game->copy_ap_of(aut);
auto names = new std::vector<std::pair<unsigned, unsigned>>();
game->set_named_prop("product-states", names);
names->reserve(aut->num_states());
auto owners = new std::vector<bool>();
game->set_named_prop("state-player", owners);
owners->reserve(aut->num_states());
std::map<std::pair<unsigned, unsigned>, unsigned> s_orig_states;
std::map<std::pair<unsigned, unsigned>, unsigned> d_orig_states;
std::vector<unsigned> todo;
todo.reserve(aut->num_states());
auto& g = game->get_graph();
auto all_inf = aut->get_acceptance().used_inf_fin_sets().first;
auto new_state = [&](bool player, unsigned s1, unsigned s2)
{ {
unsigned s; if (s1 >= aut->num_states() || s2 >= aut->num_states())
auto& m = player ? s_orig_states : d_orig_states; throw std::runtime_error("direct_sim_game(): invalid state number");
auto it = std::find_if(m.begin(), m.end(), [s1, s2](auto elm) auto game = spot::make_twa_graph(spot::make_bdd_dict());
{ return s1 == elm.first.first && s2 == elm.first.second; });
if (it == m.end()) auto names = new std::vector<std::pair<unsigned, unsigned>>();
{ game->set_named_prop("product-states", names);
s = game->new_state();
names->emplace_back(s1, s2);
owners->push_back(player);
m.insert({{s1, s2}, s});
if (!player) auto owners = new std::vector<bool>();
todo.push_back(s); game->set_named_prop("state-player", owners);
}
else
{
s = it->second;
}
return s; std::map<std::pair<unsigned, unsigned>, unsigned> s_orig_states;
}; std::map<std::pair<unsigned, unsigned>, unsigned> d_orig_states;
std::vector<unsigned> todo;
new_state(false, s1, s2); auto new_state = [&](bool player, unsigned s1, unsigned s2)
while (!todo.empty())
{ {
unsigned cur = todo.back(); auto& m = player ? s_orig_states : d_orig_states;
unsigned s_src = (*names)[cur].first; if (auto it = m.find({s1, s2}); it != m.end())
unsigned d_src = (*names)[cur].second; return it->second;
todo.pop_back(); unsigned s = game->new_state();
names->emplace_back(s1, s2);
owners->push_back(player);
m.insert({{s1, s2}, s});
if (!player)
todo.push_back(s);
return s;
};
for (const auto& s_edge : aut->out(s_src)) game->set_init_state(new_state(false, s1, s2));
{ while (!todo.empty())
unsigned edge_idx = g.index_of_edge(s_edge);
unsigned cur2 = new_state(true, edge_idx, d_src);
game->new_edge(cur, cur2, bddtrue);
for (const auto& d_edge : aut->out(d_src))
{
if (bdd_implies(d_edge.cond, s_edge.cond)
&& (s_edge.acc ^ all_inf).subset(d_edge.acc ^ all_inf))
{
unsigned cur3 = new_state(false, s_edge.dst, d_edge.dst);
game->new_edge(cur2, cur3, bddtrue);
}
}
}
}
return game;
}
bool compute_simulation(spot::const_twa_graph_ptr aut,
unsigned i, unsigned j,
std::vector<bool>& can_sim)
{
auto g = direct_sim_game(aut, i, j);
bool i_simulates_j = spot::solve_safety_game(g);
std::vector<bool> winners = spot::get_state_winners(g);
std::vector<bool> owners = spot::get_state_players(g);
typedef std::vector<std::pair<unsigned, unsigned>> names_t;
auto names = *g->get_named_prop<names_t>("product-states");
unsigned n = g->num_states();
unsigned n_aut = aut->num_states();
for (unsigned i = 0; i < n; ++i)
if (winners[i] && !owners[i])
{ {
auto [u, v] = names[i]; unsigned cur = todo.back();
can_sim[u * n_aut + v] = true; todo.pop_back();
auto [s_src, d_src] = (*names)[cur];
for (const auto& s_edge : aut->out(s_src))
{
unsigned edge_idx = aut->edge_number(s_edge);
unsigned st2 = new_state(true, edge_idx, d_src);
game->new_edge(cur, st2, bddtrue);
for (const auto& d_edge : aut->out(d_src))
if (bdd_implies(d_edge.cond, s_edge.cond)
&& d_edge.acc.subset(s_edge.acc))
{
unsigned st3 = new_state(false, s_edge.dst, d_edge.dst);
game->new_edge(st2, st3, bddtrue);
}
}
} }
return i_simulates_j; return game;
} }
std::vector<std::pair<int,int>>
list_simulation(spot::const_twa_graph_ptr aut,
unsigned i, unsigned j)
{
auto g = direct_sim_game(aut, i, j);
spot::solve_safety_game(g);
const std::vector<bool>& winners = spot::get_state_winners(g);
const std::vector<bool>& owners = spot::get_state_players(g);
typedef std::vector<std::pair<unsigned, unsigned>> names_t;
auto names = *g->get_named_prop<names_t>("product-states");
std::vector<std::pair<int,int>> res;
unsigned n = g->num_states();
for (unsigned i = 0; i < n; ++i)
if (winners[i] && !owners[i])
res.emplace_back(names[i]);
return res;
}
#+END_SRC #+END_SRC
#+RESULTS: cppCompute Now to execute the above code on our example automaton, we just
need to read the automaton from a file.
Computes and print the simulations found when checking if 4 simulates 0.
#+NAME: cppCallCompute
#+BEGIN_SRC text :exports none
unsigned n = aut->num_states();
std::vector<bool> can_sim(n*n);
compute_simulation(aut, 4, 0, can_sim);
for (unsigned i = 0; i < n; ++i)
for (unsigned j = 0; j < n; ++j)
if (can_sim[i * n + j])
std::cout << i << " simulates " << j << '\n';
#+END_SRC
#+BEGIN_SRC C++ :exports code :noweb yes
<<cppCallCompute>>
#+END_SRC
#+RESULTS: cppCallCompute
#+NAME: finalcpp #+NAME: finalcpp
#+BEGIN_SRC C++ :results verbatim :exports results :noweb yes #+BEGIN_SRC C++ :results verbatim :exports both :noweb strip-export
#include <iostream> #include <iostream>
#include <spot/twa/twagraph.hh> #include <spot/twa/twagraph.hh>
#include <spot/parseaut/public.hh>
<<cppCompute>> <<cppCompute>>
int main()
int main() {
{ spot::parsed_aut_ptr pa = parse_aut("tut40.hoa", spot::make_bdd_dict());
spot::bdd_dict_ptr dict = spot::make_bdd_dict(); if (pa->format_errors(std::cerr))
spot::twa_graph_ptr aut = make_twa_graph(dict); return 1;
if (pa->aborted)
bdd p1 = bdd_ithvar(aut->register_ap("p1")); {
bdd p2 = bdd_ithvar(aut->register_ap("p2")); std::cerr << "--ABORT-- read\n";
return 1;
aut->set_acceptance(1, "t"); }
aut->prop_state_acc(true); for (auto [i,j]: list_simulation(pa->aut, 4, 0))
std::cout << i << " simulates " << j << '\n';
aut->new_states(6); return 0;
aut->set_init_state(0U); }
aut->new_edge(0, 1, p2);
aut->new_edge(1, 1, p1 & p2, {0});
aut->new_edge(0, 2, p2);
aut->new_edge(2, 3, p1);
aut->new_edge(3, 3, p2);
aut->new_edge(4, 5, p2);
aut->new_edge(5, 5, p1);
unsigned n = aut->num_states();
std::vector<bool> can_sim(n*n);
compute_simulation(aut, 4, 0, can_sim);
for (unsigned i = 0; i < n; ++i)
for (unsigned j = 0; j < n; ++j)
if (can_sim[i * n + j])
std::cout << i << " simulates " << j << '\n';
return 0;
}
#+END_SRC #+END_SRC
#+RESULTS: finalcpp #+RESULTS: finalcpp
: 4 simulates 0 : 4 simulates 0
: 5 simulates 1 : 5 simulates 1
#+BEGIN_SRC sh :results silent :exports results
rm -f tut40.hoa
#+END_SRC

View file

@ -1031,7 +1031,10 @@ def _add_twa_graph(meth, name=None):
for meth in ('scc_filter', 'scc_filter_states', for meth in ('scc_filter', 'scc_filter_states',
'is_deterministic', 'is_unambiguous', 'is_deterministic', 'is_unambiguous',
'contains'): 'contains', 'get_strategy',
'set_state_players', 'get_state_players',
'set_state_player', 'get_state_player',
'get_state_winners', 'get_state_winner'):
_add_twa_graph(meth) _add_twa_graph(meth)
_add_twa_graph('are_equivalent', 'equivalent_to') _add_twa_graph('are_equivalent', 'equivalent_to')