python: allow iterating over the successors of a state
Fixes #118. * spot/twa/twagraph.hh: Avoid using graph_t::state to help Swig. * wrap/python/spot_impl.i: Add a __str__ function for acc_cond::mark_t. * doc/org/tut21.org: Add the Python version. * doc/org/tut.org: Move tut21.org to the Python/C++ section. * NEWS: Update.
This commit is contained in:
parent
4b853865b9
commit
9313222e95
5 changed files with 151 additions and 13 deletions
4
NEWS
4
NEWS
|
|
@ -35,6 +35,10 @@ New in spot 1.99.6a (not yet released)
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
|
* Iterating over the transitions leaving a state (the
|
||||||
|
twa_graph::out() C++ function) is now possible in Python. See
|
||||||
|
https://spot.lrde.epita.fr/tut21.html for a demonstration.
|
||||||
|
|
||||||
Documentation:
|
Documentation:
|
||||||
|
|
||||||
* There is a new page explaining how to compile example programs and
|
* There is a new page explaining how to compile example programs and
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
|
|
||||||
This section contains code examples for using Spot. This is a work in
|
This section contains code examples for using Spot. This is a work in
|
||||||
progress. Feel free to [[mailto:spot@lrde.epita.fr][send]] suggestion of small tasks you would like
|
progress. Feel free to [[mailto:spot@lrde.epita.fr][send]] suggestions of small tasks you would like
|
||||||
to see illustrated here.
|
to see illustrated here.
|
||||||
|
|
||||||
If you have difficulties compiling the C++ examples, check out [[file:compile.org][these
|
If you have difficulties compiling the C++ examples, check out [[file:compile.org][these
|
||||||
|
|
@ -25,14 +25,13 @@ three interfaces supported by Spot: shell commands, Python, or C++.
|
||||||
* Examples in Python and C++
|
* Examples in Python and C++
|
||||||
|
|
||||||
- [[file:tut03.org][Constructing and transforming formulas]]
|
- [[file:tut03.org][Constructing and transforming formulas]]
|
||||||
|
- [[file:tut21.org][Custom print of an automaton]]
|
||||||
|
|
||||||
* Examples in
|
* Examples in C++ only
|
||||||
C++ only
|
|
||||||
|
|
||||||
The following examples are too low-level to be implemented in shell or
|
The following examples are too low-level to be implemented in shell or
|
||||||
Python (at least at the moment), so they are purely C++ so far.
|
Python (at least at the moment), so they are purely C++ so far.
|
||||||
|
|
||||||
- [[file:tut21.org][Custom print of an automaton]]
|
|
||||||
- [[file:tut22.org][Creating an automaton in C++]]
|
- [[file:tut22.org][Creating an automaton in C++]]
|
||||||
|
|
||||||
* Examples in Python only
|
* Examples in Python only
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,23 @@
|
||||||
#+SETUPFILE: setup.org
|
#+SETUPFILE: setup.org
|
||||||
#+HTML_LINK_UP: tut.html
|
#+HTML_LINK_UP: tut.html
|
||||||
|
|
||||||
This example demonstrates how to iterate over an automaton. We will
|
This example demonstrates how to iterate over an automaton in C++ and
|
||||||
only show how to do this in C++: the Python bindings for the automata
|
Python. This case uses automata stored entirely in memory as a graph:
|
||||||
are not yet supporting these low-level iterations, and the shell
|
states are numbered by integers, and transitions can be seen as tuple
|
||||||
commands aren't up to the task either.
|
of the form
|
||||||
|
$(\mathit{src},\mathit{dst},\mathit{cond},\mathit{accsets})$ where
|
||||||
|
$\mathit{src}$ and $\mathit{dst}$ are integer denoting the extremities
|
||||||
|
of the transition, $\mathit{cond}$ is a BDD representing the label
|
||||||
|
(a.k.a. guard), and $\mathit{accsets}$ is an object of type
|
||||||
|
=acc_cond::mark_t= encoding the membership to each acceptance sets
|
||||||
|
(=acc_cond::mark_t= is basically a bit vector).
|
||||||
|
|
||||||
|
The interface available for those graph-based automata allows random
|
||||||
|
access to any state of the graph, hence the code given bellow can do a
|
||||||
|
simple loop over all states of the automaton. Spot also supports a
|
||||||
|
different kind of interface (not demonstrated here) to iterate over
|
||||||
|
automata that are constructed on-the-fly and where such a loop would
|
||||||
|
be impossible.
|
||||||
|
|
||||||
First let's create an example automaton in HOA format.
|
First let's create an example automaton in HOA format.
|
||||||
|
|
||||||
|
|
@ -42,9 +55,11 @@ State: 3
|
||||||
--END--
|
--END--
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
* C++
|
||||||
|
|
||||||
We now write some C++ to load this automaton [[file:tut20.org][as we did before]], and in
|
We now write some C++ to load this automaton [[file:tut20.org][as we did before]], and in
|
||||||
=custom_print()= we print it out in a custom way by explicitly
|
=custom_print()= we print it out in a custom way by explicitly
|
||||||
iterating over it states and edges.
|
iterating over its states and edges.
|
||||||
|
|
||||||
The only tricky part is to print the edge labels. Since they are
|
The only tricky part is to print the edge labels. Since they are
|
||||||
BDDs, printing them directly would just show the identifiers of BDDs
|
BDDs, printing them directly would just show the identifiers of BDDs
|
||||||
|
|
@ -201,6 +216,111 @@ State 3:
|
||||||
acc sets = {}
|
acc sets = {}
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
|
* Python
|
||||||
|
|
||||||
|
Here is the very same example, but written in Python:
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output :exports both
|
||||||
|
import spot
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_yes(pos, neg=False):
|
||||||
|
if neg:
|
||||||
|
return "no"
|
||||||
|
return "yes" if pos else "maybe"
|
||||||
|
|
||||||
|
|
||||||
|
def custom_print(aut):
|
||||||
|
bdict = aut.get_dict()
|
||||||
|
print("Acceptance:", aut.get_acceptance())
|
||||||
|
print("Number of sets:", aut.num_sets())
|
||||||
|
print("Number of states: ", aut.num_states())
|
||||||
|
print("Initial states: ", aut.get_init_state_number())
|
||||||
|
print("Atomic propositions:", end='')
|
||||||
|
for ap in aut.ap():
|
||||||
|
print(' ', ap, ' (=', bdict.varnum(ap), ')', sep='', end='')
|
||||||
|
print()
|
||||||
|
# Templated methods are not available in Python, so we cannot
|
||||||
|
# retrieve/attach arbitrary objects from/to the automaton. However the
|
||||||
|
# Python bindings have get_name() and set_name() to access the
|
||||||
|
# "automaton-name" property.
|
||||||
|
name = aut.get_name()
|
||||||
|
if name:
|
||||||
|
print("Name: ", name)
|
||||||
|
print("Deterministic:", maybe_yes(aut.prop_deterministic()))
|
||||||
|
print("Unambiguous:", maybe_yes(aut.prop_unambiguous()))
|
||||||
|
print("State-Based Acc:", maybe_yes(aut.prop_state_acc()))
|
||||||
|
print("Terminal:", maybe_yes(aut.prop_terminal()))
|
||||||
|
print("Weak:", maybe_yes(aut.prop_weak()))
|
||||||
|
print("Inherently Weak:", maybe_yes(aut.prop_inherently_weak()))
|
||||||
|
print("Stutter Invariant:", maybe_yes(aut.prop_stutter_invariant(),
|
||||||
|
aut.prop_stutter_sensitive()))
|
||||||
|
|
||||||
|
for s in range(0, aut.num_states()):
|
||||||
|
print("State {}:".format(s))
|
||||||
|
for t in aut.out(s):
|
||||||
|
print(" edge({} -> {})".format(t.src, t.dst))
|
||||||
|
# bdd_print_formula() is designed to print on a std::ostream, and
|
||||||
|
# is inconveniant to use in Python. Instead we use
|
||||||
|
# bdd_format_formula() as this simply returns a string.
|
||||||
|
print(" label =", spot.bdd_format_formula(bdict, t.cond))
|
||||||
|
print(" acc sets =", t.acc)
|
||||||
|
|
||||||
|
|
||||||
|
custom_print(spot.automaton("tut21.hoa"))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
#+begin_example
|
||||||
|
Acceptance: Inf(0)&Inf(1)
|
||||||
|
Number of sets: 2
|
||||||
|
Number of states: 4
|
||||||
|
Initial states: 0
|
||||||
|
Atomic propositions: a (=0) b (=1) c (=2)
|
||||||
|
Name: Fa | G(Fb & Fc)
|
||||||
|
Deterministic: maybe
|
||||||
|
Unambiguous: yes
|
||||||
|
State-Based Acc: maybe
|
||||||
|
Terminal: maybe
|
||||||
|
Weak: maybe
|
||||||
|
Inherently Weak: maybe
|
||||||
|
Stutter Invariant: yes
|
||||||
|
State 0:
|
||||||
|
edge(0 -> 1)
|
||||||
|
label = a
|
||||||
|
acc sets = {}
|
||||||
|
edge(0 -> 2)
|
||||||
|
label = !a
|
||||||
|
acc sets = {}
|
||||||
|
edge(0 -> 3)
|
||||||
|
label = !a
|
||||||
|
acc sets = {}
|
||||||
|
State 1:
|
||||||
|
edge(1 -> 1)
|
||||||
|
label = 1
|
||||||
|
acc sets = {0,1}
|
||||||
|
State 2:
|
||||||
|
edge(2 -> 1)
|
||||||
|
label = a
|
||||||
|
acc sets = {}
|
||||||
|
edge(2 -> 2)
|
||||||
|
label = !a
|
||||||
|
acc sets = {}
|
||||||
|
State 3:
|
||||||
|
edge(3 -> 3)
|
||||||
|
label = !a & b & c
|
||||||
|
acc sets = {0,1}
|
||||||
|
edge(3 -> 3)
|
||||||
|
label = !a & !b & c
|
||||||
|
acc sets = {1}
|
||||||
|
edge(3 -> 3)
|
||||||
|
label = !a & b & !c
|
||||||
|
acc sets = {0}
|
||||||
|
edge(3 -> 3)
|
||||||
|
label = !a & !b & !c
|
||||||
|
acc sets = {}
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
|
||||||
#+BEGIN_SRC sh :results silent :exports results
|
#+BEGIN_SRC sh :results silent :exports results
|
||||||
rm -f tut21.hoa
|
rm -f tut21.hoa
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,12 @@ namespace spot
|
||||||
public:
|
public:
|
||||||
typedef digraph<twa_graph_state, twa_graph_edge_data> graph_t;
|
typedef digraph<twa_graph_state, twa_graph_edge_data> graph_t;
|
||||||
typedef graph_t::edge_storage_t edge_storage_t;
|
typedef graph_t::edge_storage_t edge_storage_t;
|
||||||
|
// We avoid using graph_t::state because graph_t is not
|
||||||
|
// instantiated in the SWIG bindings, and SWIG would therefore
|
||||||
|
// handle graph_t::state as an abstract type.
|
||||||
|
typedef unsigned state_num;
|
||||||
|
static_assert(std::is_same<typename graph_t::state, state_num>::value,
|
||||||
|
"type mismatch");
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
graph_t g_;
|
graph_t g_;
|
||||||
|
|
@ -247,7 +253,7 @@ namespace spot
|
||||||
return g_.num_edges();
|
return g_.num_edges();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_init_state(graph_t::state s)
|
void set_init_state(state_num s)
|
||||||
{
|
{
|
||||||
assert(s < num_states());
|
assert(s < num_states());
|
||||||
init_number_ = s;
|
init_number_ = s;
|
||||||
|
|
@ -258,7 +264,7 @@ namespace spot
|
||||||
set_init_state(state_number(s));
|
set_init_state(state_number(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
graph_t::state get_init_state_number() const
|
state_num get_init_state_number() const
|
||||||
{
|
{
|
||||||
if (num_states() == 0)
|
if (num_states() == 0)
|
||||||
const_cast<graph_t&>(g_).new_state();
|
const_cast<graph_t&>(g_).new_state();
|
||||||
|
|
@ -290,7 +296,7 @@ namespace spot
|
||||||
return new twa_graph_succ_iterator<graph_t>(&g_, s->succ);
|
return new twa_graph_succ_iterator<graph_t>(&g_, s->succ);
|
||||||
}
|
}
|
||||||
|
|
||||||
graph_t::state
|
state_num
|
||||||
state_number(const state* st) const
|
state_number(const state* st) const
|
||||||
{
|
{
|
||||||
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
|
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
|
||||||
|
|
@ -299,7 +305,7 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
const twa_graph_state*
|
const twa_graph_state*
|
||||||
state_from_number(graph_t::state n) const
|
state_from_number(state_num n) const
|
||||||
{
|
{
|
||||||
return &g_.state_data(n);
|
return &g_.state_data(n);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,15 @@ namespace std {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%extend spot::acc_cond::mark_t {
|
||||||
|
std::string __str__()
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << *self;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
%extend spot::twa_run {
|
%extend spot::twa_run {
|
||||||
std::string __str__()
|
std::string __str__()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue