Commit 4ecd066c authored by Clément Gillard's avatar Clément Gillard Committed by Alexandre Duret-Lutz
Browse files

Several typos

* HACKING: Missing "to", extraneous 's'.
* spot/misc/timer.hh: Extraneous space.
* spot/twa/acc.hh: Extraneous 's', typos.
* spot/twaalgos/genem.hh: Typo.
* spot/twaalgos/sccinfo.cc: Fix indentation.
* spot/twaalgos/sccinfo.hh: Missing 's'.
* tests/python/acc_cond.ipynb: Extraneous 'e', missing comma.
* tests/python/decompose.ipynb: Extraneous 't'.
* tests/python/ltsmin-dve.ipynb: Extraneous verb.
parent 0c32f6b7
...@@ -354,7 +354,7 @@ Exporting symbols ...@@ -354,7 +354,7 @@ Exporting symbols
header. header.
* The directory src/priv/ can be used to store files that are * The directory src/priv/ can be used to store files that are
globaly private the library, and that do not really belongs to globaly private to the library, and that do not really belong to
other directories. other directories.
* Functions and classes that are public should be marked with * Functions and classes that are public should be marked with
......
...@@ -429,7 +429,7 @@ namespace spot ...@@ -429,7 +429,7 @@ namespace spot
} sub; } sub;
}; };
/// \brief An acceptance formulas. /// \brief An acceptance formula.
/// ///
/// Acceptance formulas are stored as a vector of acc_word in a /// Acceptance formulas are stored as a vector of acc_word in a
/// kind of reverse polish notation. The motivation for this /// kind of reverse polish notation. The motivation for this
...@@ -1356,13 +1356,13 @@ namespace spot ...@@ -1356,13 +1356,13 @@ namespace spot
uses_fin_acceptance_ = check_fin_acceptance(); uses_fin_acceptance_ = check_fin_acceptance();
} }
/// \brief Retrieve teh acceptance formula /// \brief Retrieve the acceptance formula
const acc_code& get_acceptance() const const acc_code& get_acceptance() const
{ {
return code_; return code_;
} }
/// \brief Retrieve teh acceptance formula /// \brief Retrieve the acceptance formula
acc_code& get_acceptance() acc_code& get_acceptance()
{ {
return code_; return code_;
...@@ -1793,7 +1793,7 @@ namespace spot ...@@ -1793,7 +1793,7 @@ namespace spot
/// \brief Check potential acceptance of an SCC. /// \brief Check potential acceptance of an SCC.
/// ///
/// Assuming that an SCC intersects all sets in \a /// Assuming that an SCC intersects all sets in \a
/// infinitely_often (i.e., for each set in \a infinetely_often, /// infinitely_often (i.e., for each set in \a infinitely_often,
/// there exist one marked transition in the SCC), and is /// there exist one marked transition in the SCC), and is
/// included in all sets in \a always_present (i.e., all /// included in all sets in \a always_present (i.e., all
/// transitions are marked with \a always_present), this returns /// transitions are marked with \a always_present), this returns
...@@ -1809,7 +1809,7 @@ namespace spot ...@@ -1809,7 +1809,7 @@ namespace spot
/// \brief Return an accepting subset of \a inf /// \brief Return an accepting subset of \a inf
/// ///
/// This function works only on Fin-less acceptance, and returns a /// This function works only on Fin-less acceptance, and returns a
/// subset of \a inf that is enough to satisfies the acceptance /// subset of \a inf that is enough to satisfy the acceptance
/// condition. This is typically used when an accepting SCC that /// condition. This is typically used when an accepting SCC that
/// visits all sets in \a inf has been found, and we want to find /// visits all sets in \a inf has been found, and we want to find
/// an accepting cycle: maybe it is not necessary for the accepting /// an accepting cycle: maybe it is not necessary for the accepting
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
namespace spot namespace spot
{ {
/// \ingroup emptiness_check_algorithms /// \ingroup emptiness_check_algorithms
/// \brief Emptiness check of on automaton, for any acceptance condition. /// \brief Emptiness check of an automaton, for any acceptance condition.
SPOT_API bool SPOT_API bool
generic_emptiness_check(const const_twa_graph_ptr& aut); generic_emptiness_check(const const_twa_graph_ptr& aut);
......
...@@ -364,7 +364,7 @@ namespace spot ...@@ -364,7 +364,7 @@ namespace spot
/// ///
/// Additionally this class can be used on alternating automata, but /// Additionally this class can be used on alternating automata, but
/// in this case, universal transitions are handled like existential /// in this case, universal transitions are handled like existential
/// transitions. It still make sense to check which states belong /// transitions. It still makes sense to check which states belong
/// to the same SCC, but the acceptance information computed by /// to the same SCC, but the acceptance information computed by
/// this class is meaningless. /// this class is meaningless.
class SPOT_API scc_info class SPOT_API scc_info
......
...@@ -421,7 +421,7 @@ ...@@ -421,7 +421,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"The recognized names are the valide values for `acc-name:` in the [HOA format](http://adl.github.io/hoaf/). Additionally numbers may be replaced by ranges of the form `n..m`, in which case a random number is selected in that range." "The recognized names are the valid values for `acc-name:` in the [HOA format](http://adl.github.io/hoaf/). Additionally, numbers may be replaced by ranges of the form `n..m`, in which case a random number is selected in that range."
] ]
}, },
{ {
......
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import spot import spot
spot.setup() spot.setup()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Acceptance conditions # Acceptance conditions
The acceptance condition of an automaton specifies which of its paths are accepting. The acceptance condition of an automaton specifies which of its paths are accepting.
The way acceptance conditions are stored in Spot is derived from the way acceptance conditions are specified in the [HOA format](http://adl.github.io/hoaf/). In HOA, acceptance conditions are given as a line of the form: The way acceptance conditions are stored in Spot is derived from the way acceptance conditions are specified in the [HOA format](http://adl.github.io/hoaf/). In HOA, acceptance conditions are given as a line of the form:
Acceptance: 3 (Inf(0)&Fin(1))|Inf(2) Acceptance: 3 (Inf(0)&Fin(1))|Inf(2)
The number `3` gives the number of acceptance sets used (numbered from `0` to `2` in that case), while the rest of the line is a positive Boolean formula over terms of the form: The number `3` gives the number of acceptance sets used (numbered from `0` to `2` in that case), while the rest of the line is a positive Boolean formula over terms of the form:
- `Inf(n)`, that is true if and only if the set `n` is seen infinitely often, - `Inf(n)`, that is true if and only if the set `n` is seen infinitely often,
- `Fin(n)`, that is true if and only if the set `n` should be seen finitely often, - `Fin(n)`, that is true if and only if the set `n` should be seen finitely often,
- `t`, always true, - `t`, always true,
- `f`, always false. - `f`, always false.
The HOA specifications additionally allows terms of the form `Inf(!n)` or `Fin(!n)` but Spot automatically rewrites those away when reading an HOA file. The HOA specifications additionally allows terms of the form `Inf(!n)` or `Fin(!n)` but Spot automatically rewrites those away when reading an HOA file.
Note that the number of sets given can be larger than what is actually needed by the acceptance formula. Note that the number of sets given can be larger than what is actually needed by the acceptance formula.
Transitions in automata can be tagged as being part of some member sets, and a path in the automaton is accepting if the set of acceptance sets visited along this path satify the acceptance condition. Transitions in automata can be tagged as being part of some member sets, and a path in the automaton is accepting if the set of acceptance sets visited along this path satify the acceptance condition.
Definining acceptance conditions in Spot involves three different types of C++ objects: Definining acceptance conditions in Spot involves three different types of C++ objects:
- `spot::acc_cond` is used to represent an acceptance condition, that is: a number of sets and a formula. - `spot::acc_cond` is used to represent an acceptance condition, that is: a number of sets and a formula.
- `spot::acc_cond::acc_code`, is used to represent Boolean formula for the acceptance condition using a kind of byte code (hence the name) - `spot::acc_cond::acc_code`, is used to represent Boolean formula for the acceptance condition using a kind of byte code (hence the name)
- `spot::acc_cond::mark_t`, is a type of bit-vector used to represent membership to acceptance sets. - `spot::acc_cond::mark_t`, is a type of bit-vector used to represent membership to acceptance sets.
In because Swig's support for nested class is limited, these types are available respectively as `spot.acc_cond`, `spot.acc_code`, and `spot.mark_t` in Python. In because Swig's support for nested class is limited, these types are available respectively as `spot.acc_cond`, `spot.acc_code`, and `spot.mark_t` in Python.
## `mark_t` ## `mark_t`
Let's start with the simpler of these three objects. `mark_t` is a type of bit vector. Its main constructor takes a sequence of set numbers. Let's start with the simpler of these three objects. `mark_t` is a type of bit vector. Its main constructor takes a sequence of set numbers.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(spot.mark_t()) print(spot.mark_t())
``` ```
%%%% Output: stream %%%% Output: stream
{} {}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(spot.mark_t([0, 2, 3])) # works with lists print(spot.mark_t([0, 2, 3])) # works with lists
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,3} {0,2,3}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(spot.mark_t((0, 2, 3))) # works with tuples print(spot.mark_t((0, 2, 3))) # works with tuples
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,3} {0,2,3}
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
As seen above, the sequence of set numbers can be specified using a list or a tuple. While from the Python language point of view, using a tuple is faster than using a list, the overhead to converting all the arguments from Python to C++ and then converting the resuslting back from C++ to Python makes this difference completely negligeable. In the following, we opted to use lists, because brackets are more readable than nested parentheses. As seen above, the sequence of set numbers can be specified using a list or a tuple. While from the Python language point of view, using a tuple is faster than using a list, the overhead to converting all the arguments from Python to C++ and then converting the resuslting back from C++ to Python makes this difference completely negligeable. In the following, we opted to use lists, because brackets are more readable than nested parentheses.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
x = spot.mark_t([0, 2, 3]) x = spot.mark_t([0, 2, 3])
y = spot.mark_t([0, 4]) y = spot.mark_t([0, 4])
print(x | y) print(x | y)
print(x & y) print(x & y)
print(x - y) print(x - y)
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,3,4} {0,2,3,4}
{0} {0}
{2,3} {2,3}
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The bits can be set, cleared, and tested using the `set()`, `clear()`, and `has()` methods: The bits can be set, cleared, and tested using the `set()`, `clear()`, and `has()` methods:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
x.set(5) x.set(5)
print(x) print(x)
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,3,5} {0,2,3,5}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
x.clear(3) x.clear(3)
print(x) print(x)
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,5} {0,2,5}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(x.has(2)) print(x.has(2))
print(x.has(3)) print(x.has(3))
``` ```
%%%% Output: stream %%%% Output: stream
True True
False False
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Left-shifting will increment all set numbers. Left-shifting will increment all set numbers.
This operation is useful when building the product of two automata: all the set number of one automaton have to be shift by the number of sets used in the other automaton. This operation is useful when building the product of two automata: all the set number of one automaton have to be shift by the number of sets used in the other automaton.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(x << 2) print(x << 2)
``` ```
%%%% Output: stream %%%% Output: stream
{2,4,7} {2,4,7}
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The different sets can be iterated over with the `sets()` method, that returns a tuple with the index of all bits set. The different sets can be iterated over with the `sets()` method, that returns a tuple with the index of all bits set.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(x) print(x)
print(list(x.sets())) print(list(x.sets()))
for s in x.sets(): for s in x.sets():
print(s) print(s)
``` ```
%%%% Output: stream %%%% Output: stream
{0,2,5} {0,2,5}
[0, 2, 5] [0, 2, 5]
0 0
2 2
5 5
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
`count()` return the number of sets in a `mark_t`: `count()` return the number of sets in a `mark_t`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
x.count() x.count()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
3 3
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
`lowest()` returns a `mark_t` containing only the lowest set number. This provides another way to iterate overs all set numbers in cases where you need the result as a `mark_t`. `lowest()` returns a `mark_t` containing only the lowest set number. This provides another way to iterate overs all set numbers in cases where you need the result as a `mark_t`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.mark_t([1,3,5]).lowest() spot.mark_t([1,3,5]).lowest()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.mark_t([1]) spot.mark_t([1])
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
v = spot.mark_t([1, 3, 5]) v = spot.mark_t([1, 3, 5])
while v: # this stops once v is empty while v: # this stops once v is empty
b = v.lowest() b = v.lowest()
v -= b v -= b
print(b) print(b)
``` ```
%%%% Output: stream %%%% Output: stream
{1} {1}
{3} {3}
{5} {5}
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
`max_set()` returns the number of the highest set plus one. This is usually used to figure out how many sets we need to declare on the `Acceptance:` line of the HOA format: `max_set()` returns the number of the highest set plus one. This is usually used to figure out how many sets we need to declare on the `Acceptance:` line of the HOA format:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.mark_t([1, 3, 5]).max_set() spot.mark_t([1, 3, 5]).max_set()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
6 6
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## `acc_code` ## `acc_code`
`acc_code` encodes the formula of the acceptance condition using a kind of bytecode that basically corresponds to an encoding in [reverse Polish notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation) in which conjunctions of `Inf(n)` terms, and disjunctions of `Fin(n)` terms are grouped. In particular, the frequently-used genaralized-Büchi acceptance conditions (like `Inf(0)&Inf(1)&Inf(2)`) are always encoded as a single term (like `Inf({0,1,2})`). `acc_code` encodes the formula of the acceptance condition using a kind of bytecode that basically corresponds to an encoding in [reverse Polish notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation) in which conjunctions of `Inf(n)` terms, and disjunctions of `Fin(n)` terms are grouped. In particular, the frequently-used genaralized-Büchi acceptance conditions (like `Inf(0)&Inf(1)&Inf(2)`) are always encoded as a single term (like `Inf({0,1,2})`).
The simplest way to construct an `acc_code` by passing a string that represent the formula to build. The simplest way to construct an `acc_code` by passing a string that represent the formula to build.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_code('(Inf(0)&Fin(1))|Inf(2)') acc = spot.acc_code('(Inf(0)&Fin(1))|Inf(2)')
print(acc) # shows just the formula print(acc) # shows just the formula
acc # shows the acc_code object acc # shows the acc_code object
``` ```
%%%% Output: stream %%%% Output: stream
(Inf(0) & Fin(1)) | Inf(2) (Inf(0) & Fin(1)) | Inf(2)
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Inf(0) & Fin(1)) | Inf(2)") spot.acc_code("(Inf(0) & Fin(1)) | Inf(2)")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You may also use a named acceptance condition: You may also use a named acceptance condition:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code('Rabin 2') spot.acc_code('Rabin 2')
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))") spot.acc_code("(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The recognized names are the valide values for `acc-name:` in the [HOA format](http://adl.github.io/hoaf/). Additionally numbers may be replaced by ranges of the form `n..m`, in which case a random number is selected in that range. The recognized names are the valid values for `acc-name:` in the [HOA format](http://adl.github.io/hoaf/). Additionally, numbers may be replaced by ranges of the form `n..m`, in which case a random number is selected in that range.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(spot.acc_code('Streett 2..4')) print(spot.acc_code('Streett 2..4'))
print(spot.acc_code('Streett 2..4')) print(spot.acc_code('Streett 2..4'))
``` ```
%%%% Output: stream %%%% Output: stream
(Fin(0) | Inf(1)) & (Fin(2) | Inf(3)) & (Fin(4) | Inf(5)) & (Fin(6) | Inf(7)) (Fin(0) | Inf(1)) & (Fin(2) | Inf(3)) & (Fin(4) | Inf(5)) & (Fin(6) | Inf(7))
(Fin(0) | Inf(1)) & (Fin(2) | Inf(3)) (Fin(0) | Inf(1)) & (Fin(2) | Inf(3))
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
It may also be convenient to generate a random acceptance condition. Below we require between 3 and 5 acceptance sets: It may also be convenient to generate a random acceptance condition. Below we require between 3 and 5 acceptance sets:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code('random 3..5') spot.acc_code('random 3..5')
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Fin(3) | Inf(1)) & Inf(4) & (Fin(0)|Fin(2))") spot.acc_code("(Fin(3) | Inf(1)) & Inf(4) & (Fin(0)|Fin(2))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `to_cnf()` and `to_dnf()` functions can be used to rewrite the formula into Conjunctive or Disjunctive normal forms. This functions will simplify the resulting formulas to make them irredundant. The `to_cnf()` and `to_dnf()` functions can be used to rewrite the formula into Conjunctive or Disjunctive normal forms. This functions will simplify the resulting formulas to make them irredundant.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
a = spot.acc_code('parity min odd 5') a = spot.acc_code('parity min odd 5')
a a
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("Fin(0) & (Inf(1) | (Fin(2) & (Inf(3) | Fin(4))))") spot.acc_code("Fin(0) & (Inf(1) | (Fin(2) & (Inf(3) | Fin(4))))")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
a.to_cnf() a.to_cnf()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("Fin(0) & (Inf(1) | Fin(2)) & (Inf(1) | Inf(3) | Fin(4))") spot.acc_code("Fin(0) & (Inf(1) | Fin(2)) & (Inf(1) | Inf(3) | Fin(4))")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
a.to_dnf() a.to_dnf()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Fin(0) & Inf(1)) | (Fin(0) & Fin(2) & Inf(3)) | (Fin(0) & Fin(2) & Fin(4))") spot.acc_code("(Fin(0) & Inf(1)) | (Fin(0) & Fin(2) & Inf(3)) | (Fin(0) & Fin(2) & Fin(4))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The manipulation of `acc_code` objects is quite rudimentary at the moment: they are easy to build, but are harder take appart. In fact we won't attempt to disassemble an `acc_code` object in Python: those things are better done in C++ The manipulation of `acc_code` objects is quite rudimentary at the moment: they are easy to build, but are harder take appart. In fact we won't attempt to disassemble an `acc_code` object in Python: those things are better done in C++
Operators `|`, `|=`, `&`, `&=`, `<<`, and `<<=` can be used with their obvious semantics. Operators `|`, `|=`, `&`, `&=`, `<<`, and `<<=` can be used with their obvious semantics.
Whenever possible, the inplace versions (`|=`, `&=`, `<<=`) should be prefered, because they create less temporary acceptance conditions. Whenever possible, the inplace versions (`|=`, `&=`, `<<=`) should be prefered, because they create less temporary acceptance conditions.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
x = spot.acc_code('Rabin 2') x = spot.acc_code('Rabin 2')
y = spot.acc_code('Rabin 2') << 4 y = spot.acc_code('Rabin 2') << 4
print(x) print(x)
print(y) print(y)
``` ```
%%%% Output: stream %%%% Output: stream
(Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) (Fin(0) & Inf(1)) | (Fin(2) & Inf(3))
(Fin(4) & Inf(5)) | (Fin(6) & Inf(7)) (Fin(4) & Inf(5)) | (Fin(6) & Inf(7))
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(x | y) print(x | y)
print(x & y) print(x & y)
``` ```
%%%% Output: stream %%%% Output: stream
(Fin(4) & Inf(5)) | (Fin(6) & Inf(7)) | (Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) (Fin(4) & Inf(5)) | (Fin(6) & Inf(7)) | (Fin(0) & Inf(1)) | (Fin(2) & Inf(3))
((Fin(4) & Inf(5)) | (Fin(6) & Inf(7))) & ((Fin(0) & Inf(1)) | (Fin(2) & Inf(3))) ((Fin(4) & Inf(5)) | (Fin(6) & Inf(7))) & ((Fin(0) & Inf(1)) | (Fin(2) & Inf(3)))
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `complement()` method returns the complemented acceptance condition: The `complement()` method returns the complemented acceptance condition:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(x) print(x)
print(x.complement()) print(x.complement())
``` ```
%%%% Output: stream %%%% Output: stream
(Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) (Fin(0) & Inf(1)) | (Fin(2) & Inf(3))
(Inf(0) | Fin(1)) & (Inf(2) | Fin(3)) (Inf(0) | Fin(1)) & (Inf(2) | Fin(3))
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Instead of using `acc_code('string')`, it is also possible to build an acceptance formula from atoms like `Inf({...})`, `Fin({...})`, `t`, or `f`. Instead of using `acc_code('string')`, it is also possible to build an acceptance formula from atoms like `Inf({...})`, `Fin({...})`, `t`, or `f`.
Remember that in our encoding for the formula, terms like `Inf(1)&Inf(2)` and `Fin(3)|Fin(4)|Fin(5)` are actually stored as `Inf({1,2})` and `Fin({3,4,5})`, where `{1,2}` and `{3,4,5}` are instance of `mark_t`. These terms can be generated with the Remember that in our encoding for the formula, terms like `Inf(1)&Inf(2)` and `Fin(3)|Fin(4)|Fin(5)` are actually stored as `Inf({1,2})` and `Fin({3,4,5})`, where `{1,2}` and `{3,4,5}` are instance of `mark_t`. These terms can be generated with the
functions `spot.acc_code.inf(mark)` and `spot.acc_code.fin(mark)`. functions `spot.acc_code.inf(mark)` and `spot.acc_code.fin(mark)`.
`Inf({})` is equivalent to `t`, and `Fin({})` is equivalent to `f`, but it's better to use the functions `spot.acc_code.t()` or `spot.acc_code.f()` directly. `Inf({})` is equivalent to `t`, and `Fin({})` is equivalent to `f`, but it's better to use the functions `spot.acc_code.t()` or `spot.acc_code.f()` directly.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code.inf([1,2]) & spot.acc_code.fin([3,4,5]) spot.acc_code.inf([1,2]) & spot.acc_code.fin([3,4,5])
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Fin(3)|Fin(4)|Fin(5)) & (Inf(1)&Inf(2))") spot.acc_code("(Fin(3)|Fin(4)|Fin(5)) & (Inf(1)&Inf(2))")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code.inf([]) spot.acc_code.inf([])
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("t") spot.acc_code("t")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code.t() spot.acc_code.t()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("t") spot.acc_code("t")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code.fin([]) spot.acc_code.fin([])
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("f") spot.acc_code("f")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.acc_code.f() spot.acc_code.f()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("f") spot.acc_code("f")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To evaluate an acceptance condition formula on a run, build a `mark_t` containing all the acceptance sets that are seen infinitely often along this run, and call the `accepting()` method. To evaluate an acceptance condition formula on a run, build a `mark_t` containing all the acceptance sets that are seen infinitely often along this run, and call the `accepting()` method.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_code('Fin(0) & Inf(1) | Inf(2)') acc = spot.acc_code('Fin(0) & Inf(1) | Inf(2)')
print("acc =", acc) print("acc =", acc)
for x in ([0, 1, 2], [1, 2], [0, 1], [0, 2], [0], [1], [2], []): for x in ([0, 1, 2], [1, 2], [0, 1], [0, 2], [0], [1], [2], []):
print("acc.accepting({}) = {}".format(x, acc.accepting(x))) print("acc.accepting({}) = {}".format(x, acc.accepting(x)))
``` ```
%%%% Output: stream %%%% Output: stream
acc = (Fin(0) & Inf(1)) | Inf(2) acc = (Fin(0) & Inf(1)) | Inf(2)
acc.accepting([0, 1, 2]) = True acc.accepting([0, 1, 2]) = True
acc.accepting([1, 2]) = True acc.accepting([1, 2]) = True
acc.accepting([0, 1]) = False acc.accepting([0, 1]) = False
acc.accepting([0, 2]) = True acc.accepting([0, 2]) = True
acc.accepting([0]) = False acc.accepting([0]) = False
acc.accepting([1]) = True acc.accepting([1]) = True
acc.accepting([2]) = True acc.accepting([2]) = True
acc.accepting([]) = False acc.accepting([]) = False
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Finally the method `used_sets()` returns a `mark_t` with all the sets appearing in the formula: Finally the method `used_sets()` returns a `mark_t` with all the sets appearing in the formula:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_code('Fin(0) & Inf(2)') acc = spot.acc_code('Fin(0) & Inf(2)')
print(acc) print(acc)
print(acc.used_sets()) print(acc.used_sets())
print(acc.used_sets().max_set()) print(acc.used_sets().max_set())
``` ```
%%%% Output: stream %%%% Output: stream
Fin(0) & Inf(2) Fin(0) & Inf(2)
{0,2} {0,2}
3 3
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `used_inf_fin_sets()` returns a pair of marks instead, the first one with all sets occuring in `Inf`, and the second one with all sets appearing in `Fin`. The `used_inf_fin_sets()` returns a pair of marks instead, the first one with all sets occuring in `Inf`, and the second one with all sets appearing in `Fin`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.used_inf_fin_sets() acc.used_inf_fin_sets()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
(spot.mark_t([2]), spot.mark_t([0])) (spot.mark_t([2]), spot.mark_t([0]))
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# `acc_cond` # `acc_cond`
Automata store their acceptance condition as an instance of the `acc_cond` class. Automata store their acceptance condition as an instance of the `acc_cond` class.
This class can be thought of as a pair `(n, code)`, where `n` is an integer that tells how many acceptance sets are used, while the `code` is an instance of `acc_code` and encodes the formula over *a subset* of these acceptance sets. We usually have `n == code.used_sets().max_set())`, but `n` can be larger. This class can be thought of as a pair `(n, code)`, where `n` is an integer that tells how many acceptance sets are used, while the `code` is an instance of `acc_code` and encodes the formula over *a subset* of these acceptance sets. We usually have `n == code.used_sets().max_set())`, but `n` can be larger.
It is OK if an automaton declares that is uses 3 sets, even if the acceptance condition formula only uses set number 1. The extra two sets will have no impact on the language, even if they are used in the automaton. It is OK if an automaton declares that is uses 3 sets, even if the acceptance condition formula only uses set number 1. The extra two sets will have no impact on the language, even if they are used in the automaton.
The `acc_cond` objects are usually not created by hand: automata have dedicated methods for that. But for the purpose of this notebook, let's do it: The `acc_cond` objects are usually not created by hand: automata have dedicated methods for that. But for the purpose of this notebook, let's do it:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond(4, spot.acc_code('Rabin 2')) acc = spot.acc_cond(4, spot.acc_code('Rabin 2'))
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))") spot.acc_cond(4, "(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
For convenience, you can pass the string directly: For convenience, you can pass the string directly:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond(4, 'Rabin 2') acc = spot.acc_cond(4, 'Rabin 2')
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))") spot.acc_cond(4, "(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.num_sets() acc.num_sets()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
4 4
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.get_acceptance() acc.get_acceptance()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_code("(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))") spot.acc_code("(Fin(0) & Inf(1)) | (Fin(2) & Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `acc_cond` object can also be constructed using only a number of sets. In that case, the acceptance condition defaults to `t`, and it can be changed to something else later (using `set_acceptance()`). The number of acceptance sets can also be augmented with `add_sets()`. The `acc_cond` object can also be constructed using only a number of sets. In that case, the acceptance condition defaults to `t`, and it can be changed to something else later (using `set_acceptance()`). The number of acceptance sets can also be augmented with `add_sets()`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond(4) acc = spot.acc_cond(4)
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "t") spot.acc_cond(4, "t")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.add_sets(2) acc.add_sets(2)
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(6, "t") spot.acc_cond(6, "t")
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.set_acceptance('Streett 2') acc.set_acceptance('Streett 2')
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(6, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))") spot.acc_cond(6, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Calling the constructor of `acc_cond` by passing just an instance of `acc_code` (or a string that will be passed to the `acc_code` constructor) will automatically set the number of acceptance sets to the minimum needed by the formula: Calling the constructor of `acc_cond` by passing just an instance of `acc_code` (or a string that will be passed to the `acc_code` constructor) will automatically set the number of acceptance sets to the minimum needed by the formula:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond('Streett 2') acc = spot.acc_cond('Streett 2')
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))") spot.acc_cond(4, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The above is in fact just syntactic sugar for: The above is in fact just syntactic sugar for:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
code = spot.acc_code('Streett 2') code = spot.acc_code('Streett 2')
acc = spot.acc_cond(code.used_sets().max_set(), code) acc = spot.acc_cond(code.used_sets().max_set(), code)
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))") spot.acc_cond(4, "(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The common scenario of setting generalized Büchi acceptance can be achieved more efficiently by first setting the number of acceptance sets, and then requiring generalized Büchi acceptance: The common scenario of setting generalized Büchi acceptance can be achieved more efficiently by first setting the number of acceptance sets, and then requiring generalized Büchi acceptance:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond(4) acc = spot.acc_cond(4)
acc.set_generalized_buchi() acc.set_generalized_buchi()
acc acc
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.acc_cond(4, "Inf(0)&Inf(1)&Inf(2)&Inf(3)") spot.acc_cond(4, "Inf(0)&Inf(1)&Inf(2)&Inf(3)")
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `acc_cond` class has several methods for detecting acceptance conditions that match the named acceptance conditions of the HOA format. Note that in the HOA format, `Inf(0)&Inf(1)&Inf(2)&Inf(3)` is only called generalized Büchi if exactly 4 acceptance sets are used. So the following behavior should not be surprising: The `acc_cond` class has several methods for detecting acceptance conditions that match the named acceptance conditions of the HOA format. Note that in the HOA format, `Inf(0)&Inf(1)&Inf(2)&Inf(3)` is only called generalized Büchi if exactly 4 acceptance sets are used. So the following behavior should not be surprising:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(acc) print(acc)
print(acc.is_generalized_buchi()) print(acc.is_generalized_buchi())
acc.add_sets(1) acc.add_sets(1)
print(acc) print(acc)
print(acc.is_generalized_buchi()) print(acc.is_generalized_buchi())
``` ```
%%%% Output: stream %%%% Output: stream
(4, Inf(0)&Inf(1)&Inf(2)&Inf(3)) (4, Inf(0)&Inf(1)&Inf(2)&Inf(3))
True True
(5, Inf(0)&Inf(1)&Inf(2)&Inf(3)) (5, Inf(0)&Inf(1)&Inf(2)&Inf(3))
False False
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Similar methods like `is_t()`, `is_f()`, `is_buchi()`, `is_co_buchi()`, `is_generalized_co_buchi()` all return a Boolean. Similar methods like `is_t()`, `is_f()`, `is_buchi()`, `is_co_buchi()`, `is_generalized_co_buchi()` all return a Boolean.
The `is_rabin()` and `is_streett()` methods, however, return a number of pairs. The number of pairs is always `num_sets()/2` on success, or -1 on failure. The `is_rabin()` and `is_streett()` methods, however, return a number of pairs. The number of pairs is always `num_sets()/2` on success, or -1 on failure.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond('Rabin 2') acc = spot.acc_cond('Rabin 2')
print(acc) print(acc)
print(acc.is_rabin()) print(acc.is_rabin())
print(acc.is_streett()) print(acc.is_streett())
``` ```
%%%% Output: stream %%%% Output: stream
(4, (Fin(0) & Inf(1)) | (Fin(2) & Inf(3))) (4, (Fin(0) & Inf(1)) | (Fin(2) & Inf(3)))
2 2
-1 -1
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The check for parity acceptance returns three Boolean in a list of the form `[matched, max?, odd?]`. If `matched` is `False`, the other values should be ignored. The check for parity acceptance returns three Boolean in a list of the form `[matched, max?, odd?]`. If `matched` is `False`, the other values should be ignored.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond('parity min odd 4') acc = spot.acc_cond('parity min odd 4')
print(acc) print(acc)
print(acc.is_parity()) print(acc.is_parity())
acc.set_generalized_buchi() acc.set_generalized_buchi()
print(acc) print(acc)
print(acc.is_parity()) print(acc.is_parity())
``` ```
%%%% Output: stream %%%% Output: stream
(4, Fin(0) & (Inf(1) | (Fin(2) & Inf(3)))) (4, Fin(0) & (Inf(1) | (Fin(2) & Inf(3))))
[True, False, True] [True, False, True]
(4, Inf(0)&Inf(1)&Inf(2)&Inf(3)) (4, Inf(0)&Inf(1)&Inf(2)&Inf(3))
[False, False, False] [False, False, False]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
If the acceptance condition has some known name, it can be retrieved using the `name()` method. By default the name given is a human-readable string close that used in the HOA format, but with proper accents, and support for name like `Streett-like` or `Rabin-like`. The argument `arg` can specify a different style using the same syntax as in `--format='%[arg]g'` when using the command-line tools. If the acceptance condition has some known name, it can be retrieved using the `name()` method. By default the name given is a human-readable string close that used in the HOA format, but with proper accents, and support for name like `Streett-like` or `Rabin-like`. The argument `arg` can specify a different style using the same syntax as in `--format='%[arg]g'` when using the command-line tools.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(acc.name()) print(acc.name())
print(acc.name("d")) # <- Style used by print_dot(aut, "a") print(acc.name("d")) # <- Style used by print_dot(aut, "a")
print(acc.name("0")) # <- no parameters print(acc.name("0")) # <- no parameters
``` ```
%%%% Output: stream %%%% Output: stream
generalized-Büchi 4 generalized-Büchi 4
gen. Büchi 4 gen. Büchi 4
generalized-Buchi generalized-Buchi
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
`acc_cond` contains a few functions for manipulating `mark_t` instances, these are typically functions that require known the total number of accepting sets declared. `acc_cond` contains a few functions for manipulating `mark_t` instances, these are typically functions that require known the total number of accepting sets declared.
For instance complementing a `mark_t`: For instance complementing a `mark_t`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m = spot.mark_t([1, 3]) m = spot.mark_t([1, 3])
print(acc.comp(m)) print(acc.comp(m))
``` ```
%%%% Output: stream %%%% Output: stream
{0,2} {0,2}
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
`all_sets()` returns a `mark_t` listing all the declared sets: `all_sets()` returns a `mark_t` listing all the declared sets:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc.all_sets() acc.all_sets()
``` ```
%%%% Output: execute_result %%%% Output: execute_result
spot.mark_t([0, 1, 2, 3]) spot.mark_t([0, 1, 2, 3])
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
For convencience, the `accepting()` method of `acc_cond` delegates to that of the `acc_code`. For convencience, the `accepting()` method of `acc_cond` delegates to that of the `acc_code`.
Any set passed to `accepting()` that is not used by the acceptance formula has no influence. Any set passed to `accepting()` that is not used by the acceptance formula has no influence.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("acc =", acc) print("acc =", acc)
for x in ([0, 1, 2, 3, 10], [1, 2]): for x in ([0, 1, 2, 3, 10], [1, 2]):
print("acc.accepting({}) = {}".format(x, acc.accepting(x))) print("acc.accepting({}) = {}".format(x, acc.accepting(x)))
``` ```
%%%% Output: stream %%%% Output: stream
acc = (4, Inf(0)&Inf(1)&Inf(2)&Inf(3)) acc = (4, Inf(0)&Inf(1)&Inf(2)&Inf(3))
acc.accepting([0, 1, 2, 3, 10]) = True acc.accepting([0, 1, 2, 3, 10]) = True
acc.accepting([1, 2]) = False acc.accepting([1, 2]) = False
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Finally the `unsat_mark()` method of `acc_cond` computes an instance of `mark_t` that is unaccepting (i.e., passing this value to `acc.accepting(...)` will return `False` when such a value exist. Not all acceptance conditions have an satisfiable mark. Obviously the `t` acceptance is always satisfiable, and so are all equivalent acceptances (for instance `Fin(1)|Inf(1)`). Finally the `unsat_mark()` method of `acc_cond` computes an instance of `mark_t` that is unaccepting (i.e., passing this value to `acc.accepting(...)` will return `False` when such a value exist. Not all acceptance conditions have an satisfiable mark. Obviously the `t` acceptance is always satisfiable, and so are all equivalent acceptances (for instance `Fin(1)|Inf(1)`).
For this reason, `unsat_mark()` actually returns a pair: `(bool, mark_t)` where the Boolean is `False` iff the acceptance is always satisfiable. When the Boolean is `True`, then the second element of the pair gives a non-accepting mark. For this reason, `unsat_mark()` actually returns a pair: `(bool, mark_t)` where the Boolean is `False` iff the acceptance is always satisfiable. When the Boolean is `True`, then the second element of the pair gives a non-accepting mark.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print(acc) print(acc)
print(acc.unsat_mark()) print(acc.unsat_mark())
``` ```
%%%% Output: stream %%%% Output: stream
(4, Inf(0)&Inf(1)&Inf(2)&Inf(3)) (4, Inf(0)&Inf(1)&Inf(2)&Inf(3))
(True, spot.mark_t([])) (True, spot.mark_t([]))
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond(0) # use 0 acceptance sets, and the default formula (t) acc = spot.acc_cond(0) # use 0 acceptance sets, and the default formula (t)
print(acc) print(acc)
print(acc.unsat_mark()) print(acc.unsat_mark())
``` ```
%%%% Output: stream %%%% Output: stream
(0, t) (0, t)
(False, spot.mark_t([])) (False, spot.mark_t([]))
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
acc = spot.acc_cond('Streett 2') acc = spot.acc_cond('Streett 2')
print(acc) print(acc)
print(acc.unsat_mark()) print(acc.unsat_mark())
``` ```
%%%% Output: stream %%%% Output: stream
(4, (Fin(0) | Inf(1)) & (Fin(2) | Inf(3))) (4, (Fin(0) | Inf(1)) & (Fin(2) | Inf(3)))
(True, spot.mark_t([2])) (True, spot.mark_t([2]))
......
...@@ -5667,17 +5667,17 @@ ...@@ -5667,17 +5667,17 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 22, "execution_count": 21,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"SCC #0 containts states [1]\n", "SCC #0 contains states [1]\n",
"SCC #1 containts states [2]\n", "SCC #1 contains states [2]\n",
"SCC #2 containts states [4]\n", "SCC #2 contains states [4]\n",
"SCC #3 containts states [0, 3]\n" "SCC #3 contains states [0, 3]\n"
] ]
}, },
{ {
...@@ -5986,7 +5986,7 @@ ...@@ -5986,7 +5986,7 @@
"<spot.impl.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fd3d860a240> >" "<spot.impl.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fd3d860a240> >"
] ]
}, },
"execution_count": 22, "execution_count": 21,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
...@@ -5995,7 +5995,7 @@ ...@@ -5995,7 +5995,7 @@
"aut = spot.translate('(Ga -> Gb) W c')\n", "aut = spot.translate('(Ga -> Gb) W c')\n",
"si = spot.scc_info(aut)\n", "si = spot.scc_info(aut)\n",
"for scc in range(si.scc_count()):\n", "for scc in range(si.scc_count()):\n",
" print(\"SCC #{} containts states {}\".format(scc, list(si.states_of(scc))))\n", " print(\"SCC #{} contains states {}\".format(scc, list(si.states_of(scc))))\n",
"display(aut)\n", "display(aut)\n",
"spot.decompose_scc(si, '0,2')" "spot.decompose_scc(si, '0,2')"
] ]
...@@ -6009,7 +6009,7 @@ ...@@ -6009,7 +6009,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 23, "execution_count": 22,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
...@@ -6089,7 +6089,7 @@ ...@@ -6089,7 +6089,7 @@
"<spot.impl.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fd3d850d8d0> >" "<spot.impl.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fd3d850d8d0> >"
] ]
}, },
"execution_count": 23, "execution_count": 22,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
...@@ -6115,7 +6115,7 @@ ...@@ -6115,7 +6115,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.5+" "version": "3.6.6+"
} }
}, },
"nbformat": 4, "nbformat": 4,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
"# The following line causes the notebook to exit with 77 if divine is not \n", "# The following line causes the notebook to exit with 77 if divine is not \n",
"# installed, therefore skipping this test in the test suite.\n", "# installed, therefore skipping this test in the test suite.\n",
"spot.ltsmin.require('divine')\n", "spot.ltsmin.require('divine')\n",
"# This is notebook also tests the limitation of the number of states in the GraphViz output\n", "# This notebook also tests the limitation of the number of states in the GraphViz output\n",
"spot.setup(max_states=10)" "spot.setup(max_states=10)"
] ]
}, },
......
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import spot import spot
import spot.ltsmin import spot.ltsmin
# The following line causes the notebook to exit with 77 if divine is not # The following line causes the notebook to exit with 77 if divine is not
# installed, therefore skipping this test in the test suite. # installed, therefore skipping this test in the test suite.
spot.ltsmin.require('divine') spot.ltsmin.require('divine')
# This is notebook also tests the limitation of the number of states in the GraphViz output # This notebook also tests the limitation of the number of states in the GraphViz output
spot.setup(max_states=10) spot.setup(max_states=10)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
There are two ways to load a DiVinE model: from a file or from a cell. There are two ways to load a DiVinE model: from a file or from a cell.
Loading from a file Loading from a file
------------------- -------------------
We will first start with the file version, however because this notebook should also be a self-contained test case, we start by writing a model into a file. We will first start with the file version, however because this notebook should also be a self-contained test case, we start by writing a model into a file.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!rm -f test1.dve !rm -f test1.dve
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%file test1.dve %%file test1.dve
int a = 0, b = 0; int a = 0, b = 0;
process P { process P {
state x; state x;
init x; init x;
trans trans
x -> x { guard a < 3 && b < 3; effect a = a + 1; }, x -> x { guard a < 3 && b < 3; effect a = a + 1; },
x -> x { guard a < 3 && b < 3; effect b = b + 1; }; x -> x { guard a < 3 && b < 3; effect b = b + 1; };
} }
process Q { process Q {
state wait, work; state wait, work;
init wait; init wait;
trans trans
wait -> work { guard b > 1; }, wait -> work { guard b > 1; },
work -> wait { guard a > 1; }; work -> wait { guard a > 1; };
} }
system async; system async;
``` ```
%%%% Output: stream %%%% Output: stream
Writing test1.dve Writing test1.dve
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `spot.ltsmin.load` function compiles the model using the `ltlmin` interface and load it. This should work with DiVinE models if `divine --LTSmin` works, and with Promela models if `spins` is installed. The `spot.ltsmin.load` function compiles the model using the `ltlmin` interface and load it. This should work with DiVinE models if `divine --LTSmin` works, and with Promela models if `spins` is installed.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m = spot.ltsmin.load('test1.dve') m = spot.ltsmin.load('test1.dve')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Compiling the model creates all several kinds of files. The `test1.dve` file is converted into a C++ source code `test1.dve.cpp` which is then compiled into a shared library `test1.dve2c`. Becauce `spot.ltsmin.load()` has already loaded this shared library, all those files can be erased. If you do not erase the files, `spot.ltsmin.load()` will use the timestamps to decide whether the library should be recompiled or not everytime you load the library. Compiling the model creates all several kinds of files. The `test1.dve` file is converted into a C++ source code `test1.dve.cpp` which is then compiled into a shared library `test1.dve2c`. Becauce `spot.ltsmin.load()` has already loaded this shared library, all those files can be erased. If you do not erase the files, `spot.ltsmin.load()` will use the timestamps to decide whether the library should be recompiled or not everytime you load the library.
For editing and loading DVE file from a notebook, it is a better to use the `%%dve` as shown next. For editing and loading DVE file from a notebook, it is a better to use the `%%dve` as shown next.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!rm -f test1.dve test1.dve.cpp test1.dve2C !rm -f test1.dve test1.dve.cpp test1.dve2C
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Loading from a notebook cell Loading from a notebook cell
---------------------------- ----------------------------
The `%%dve` cell magic implements all of the above steps (saving the model into a temporary file, compiling it, loading it, erasing the temporary files). The variable name that should receive the model (here `m`) should be indicated on the first line, after `%dve`. The `%%dve` cell magic implements all of the above steps (saving the model into a temporary file, compiling it, loading it, erasing the temporary files). The variable name that should receive the model (here `m`) should be indicated on the first line, after `%dve`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%dve m %%dve m
int a = 0, b = 0; int a = 0, b = 0;
process P { process P {
state x; state x;
init x; init x;
trans trans
x -> x { guard a < 3 && b < 3; effect a = a + 1; }, x -> x { guard a < 3 && b < 3; effect a = a + 1; },
x -> x { guard a < 3 && b < 3; effect b = b + 1; }; x -> x { guard a < 3 && b < 3; effect b = b + 1; };
} }
process Q { process Q {
state wait, work; state wait, work;
init wait; init wait;
trans trans
wait -> work { guard b > 1; }, wait -> work { guard b > 1; },
work -> wait { guard a > 1; }; work -> wait { guard a > 1; };
} }
system async; system async;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Working with an ltsmin model Working with an ltsmin model
---------------------------- ----------------------------
Printing an ltsmin model shows some information about the variables it contains and their types, however the `info()` methods provide the data in a map that is easier to work with. Printing an ltsmin model shows some information about the variables it contains and their types, however the `info()` methods provide the data in a map that is easier to work with.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m m
``` ```
%%%% Output: execute_result %%%% Output: execute_result
ltsmin model with the following variables: ltsmin model with the following variables:
a: int a: int
b: int b: int
P: ['x'] P: ['x']
Q: ['wait', 'work'] Q: ['wait', 'work']
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
sorted(m.info().items()) sorted(m.info().items())
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[('state_size', 4), [('state_size', 4),
('types', [('int', []), ('P', ['x']), ('Q', ['wait', 'work'])]), ('types', [('int', []), ('P', ['x']), ('Q', ['wait', 'work'])]),
('variables', [('a', 0), ('b', 0), ('P', 1), ('Q', 2)])] ('variables', [('a', 0), ('b', 0), ('P', 1), ('Q', 2)])]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To obtain a Kripke structure, call `kripke` and supply a list of atomic propositions to observe in the model. To obtain a Kripke structure, call `kripke` and supply a list of atomic propositions to observe in the model.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
k = m.kripke(["a<1", "b>2"]) k = m.kripke(["a<1", "b>2"])
k k
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<spot.impl.kripke; proxy of <Swig Object of type 'std::shared_ptr< spot::kripke > *' at 0x7fb8f84f9870> > <spot.impl.kripke; proxy of <Swig Object of type 'std::shared_ptr< spot::kripke > *' at 0x7fb8f84f9870> >
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
k.show('.<15') k.show('.<15')
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<IPython.core.display.SVG object> <IPython.core.display.SVG object>
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
k.show('.<0') # unlimited output k.show('.<0') # unlimited output
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<IPython.core.display.SVG object> <IPython.core.display.SVG object>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
If we have an LTL proposition to check, we can convert it into an automaton using `spot.translate()`, and synchronize that automaton with the Kripke structure using `spot.otf_product()`. This `otf_product()` function returns product automaton that builds itself on-the-fly, as needed by whatever algorithm "consumes" it (here the display routine). If we have an LTL proposition to check, we can convert it into an automaton using `spot.translate()`, and synchronize that automaton with the Kripke structure using `spot.otf_product()`. This `otf_product()` function returns product automaton that builds itself on-the-fly, as needed by whatever algorithm "consumes" it (here the display routine).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
a = spot.translate('"a<1" U "b>2"'); a a = spot.translate('"a<1" U "b>2"'); a
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fb8f84691e0> > <spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fb8f84691e0> >
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
spot.otf_product(k, a) spot.otf_product(k, a)
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<spot.impl.twa_product; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_product > *' at 0x7fb8f8469360> > <spot.impl.twa_product; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_product > *' at 0x7fb8f8469360> >
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
If we want to create a `model_check` function that takes a model and formula, we need to get the list of atomic propositions used in the formula using `atomic_prop_collect()`. This returns an `atomic_prop_set`: If we want to create a `model_check` function that takes a model and formula, we need to get the list of atomic propositions used in the formula using `atomic_prop_collect()`. This returns an `atomic_prop_set`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
a = spot.atomic_prop_collect(spot.formula('"a < 2" W "b == 1"')); a a = spot.atomic_prop_collect(spot.formula('"a < 2" W "b == 1"')); a
``` ```
%%%% Output: execute_result %%%% Output: execute_result
$\{\unicode{x201C}\mathit{a < 2}\unicode{x201D}, \unicode{x201C}\mathit{b == 1}\unicode{x201D}\}$ $\{\unicode{x201C}\mathit{a < 2}\unicode{x201D}, \unicode{x201C}\mathit{b == 1}\unicode{x201D}\}$
spot.atomic_prop_set([spot.formula("\"a < 2\""), spot.formula("\"b == 1\"")]) spot.atomic_prop_set([spot.formula("\"a < 2\""), spot.formula("\"b == 1\"")])
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def model_check(f, m): def model_check(f, m):
nf = spot.formula.Not(f) nf = spot.formula.Not(f)
ss = m.kripke(spot.atomic_prop_collect(nf)) ss = m.kripke(spot.atomic_prop_collect(nf))
return spot.otf_product(ss, nf.translate()).is_empty() return spot.otf_product(ss, nf.translate()).is_empty()
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model_check('"a<1" R "b > 1"', m) model_check('"a<1" R "b > 1"', m)
``` ```
%%%% Output: execute_result %%%% Output: execute_result
False False
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Instead of `otf_product(x, y).is_empty()` we prefer to call `!x.intersects(y)`. There is also `x.intersecting_run(y)` that can be used to return a counterexample. Instead of `otf_product(x, y).is_empty()` we prefer to call `!x.intersects(y)`. There is also `x.intersecting_run(y)` that can be used to return a counterexample.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def model_debug(f, m): def model_debug(f, m):
nf = spot.formula.Not(f) nf = spot.formula.Not(f)
ss = m.kripke(spot.atomic_prop_collect(nf)) ss = m.kripke(spot.atomic_prop_collect(nf))
return ss.intersecting_run(nf.translate()) return ss.intersecting_run(nf.translate())
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
run = model_debug('"a<1" R "b > 1"', m); print(run) run = model_debug('"a<1" R "b > 1"', m); print(run)
``` ```
%%%% Output: stream %%%% Output: stream
Prefix: Prefix:
a=0, b=0, Q=0 a=0, b=0, Q=0
| "a<1" & !"b > 1" & !dead | "a<1" & !"b > 1" & !dead
a=1, b=0, Q=0 a=1, b=0, Q=0
| !"a<1" & !"b > 1" & !dead | !"a<1" & !"b > 1" & !dead
a=2, b=0, Q=0 a=2, b=0, Q=0
| !"a<1" & !"b > 1" & !dead | !"a<1" & !"b > 1" & !dead
Cycle: Cycle:
a=3, b=0, Q=0 a=3, b=0, Q=0
| !"a<1" & !"b > 1" & dead | !"a<1" & !"b > 1" & dead
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This accepting run can be represented as an automaton (the `True` argument requires the state names to be preserved). This can be more readable. This accepting run can be represented as an automaton (the `True` argument requires the state names to be preserved). This can be more readable.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
run.as_twa(True) run.as_twa(True)
``` ```
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fb8f85624e0> > <spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7fb8f85624e0> >
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Saving Kripke structures to some file # Saving Kripke structures to some file
For experiments, it is sometime useful to save a Kripke structure in the HOA format. The HOA printer will automatically use `state-labels` for Kripke structures. For experiments, it is sometime useful to save a Kripke structure in the HOA format. The HOA printer will automatically use `state-labels` for Kripke structures.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
string = k.to_str('hoa') string = k.to_str('hoa')
print(string) print(string)
``` ```
%%%% Output: stream %%%% Output: stream
HOA: v1 HOA: v1
States: 22 States: 22
Start: 0 Start: 0
AP: 3 "a<1" "b>2" "dead" AP: 3 "a<1" "b>2" "dead"
acc-name: all acc-name: all
Acceptance: 0 t Acceptance: 0 t
properties: state-labels explicit-labels state-acc properties: state-labels explicit-labels state-acc
--BODY-- --BODY--
State: [0&!1&!2] 0 "a=0, b=0, Q=0" State: [0&!1&!2] 0 "a=0, b=0, Q=0"
1 2 1 2
State: [!0&!1&!2] 1 "a=1, b=0, Q=0" State: [!0&!1&!2] 1 "a=1, b=0, Q=0"
3 4 3 4
State: [0&!1&!2] 2 "a=0, b=1, Q=0" State: [0&!1&!2] 2 "a=0, b=1, Q=0"
4 5 4 5
State: [!0&!1&!2] 3 "a=2, b=0, Q=0" State: [!0&!1&!2] 3 "a=2, b=0, Q=0"
6 7 6 7
State: [!0&!1&!2] 4 "a=1, b=1, Q=0" State: [!0&!1&!2] 4 "a=1, b=1, Q=0"
7 8 7 8
State: [0&!1&!2] 5 "a=0, b=2, Q=0" State: [0&!1&!2] 5 "a=0, b=2, Q=0"
8 9 10 8 9 10
State: [!0&!1&2] 6 "a=3, b=0, Q=0" State: [!0&!1&2] 6 "a=3, b=0, Q=0"
6 6
State: [!0&!1&!2] 7 "a=2, b=1, Q=0" State: [!0&!1&!2] 7 "a=2, b=1, Q=0"
11 12 11 12
State: [!0&!1&!2] 8 "a=1, b=2, Q=0" State: [!0&!1&!2] 8 "a=1, b=2, Q=0"
12 13 14 12 13 14
State: [0&1&!2] 9 "a=0, b=3, Q=0" State: [0&1&!2] 9 "a=0, b=3, Q=0"
15 15
State: [0&!1&!2] 10 "a=0, b=2, Q=1" State: [0&!1&!2] 10 "a=0, b=2, Q=1"
14 15 14 15
State: [!0&!1&2] 11 "a=3, b=1, Q=0" State: [!0&!1&2] 11 "a=3, b=1, Q=0"
11 11
State: [!0&!1&!2] 12 "a=2, b=2, Q=0" State: [!0&!1&!2] 12 "a=2, b=2, Q=0"
16 17 18 16 17 18
State: [!0&1&!2] 13 "a=1, b=3, Q=0" State: [!0&1&!2] 13 "a=1, b=3, Q=0"
19 19
State: [!0&!1&!2] 14 "a=1, b=2, Q=1" State: [!0&!1&!2] 14 "a=1, b=2, Q=1"
18 19 18 19
State: [0&1&2] 15 "a=0, b=3, Q=1" State: [0&1&2] 15 "a=0, b=3, Q=1"
15 15
State: [!0&!1&!2] 16 "a=3, b=2, Q=0" State: [!0&!1&!2] 16 "a=3, b=2, Q=0"
20 20
State: [!0&1&!2] 17 "a=2, b=3, Q=0" State: [!0&1&!2] 17 "a=2, b=3, Q=0"
21 21
State: [!0&!1&!2] 18 "a=2, b=2, Q=1" State: [!0&!1&!2] 18 "a=2, b=2, Q=1"
20 21 12 20 21 12
State: [!0&1&2] 19 "a=1, b=3, Q=1" State: [!0&1&2] 19 "a=1, b=3, Q=1"
19 19
State: [!0&!1&!2] 20 "a=3, b=2, Q=1" State: [!0&!1&!2] 20 "a=3, b=2, Q=1"
16 16
State: [!0&1&!2] 21 "a=2, b=3, Q=1" State: [!0&1&!2] 21 "a=2, b=3, Q=1"
17 17
--END-- --END--
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You can load this as a Kripke structure by passing the `want_kripke` option to `spot.automaton()`. The type `kripke_graph` stores the Kripke structure explicitely (like a `twa_graph` stores an automaton explicitely), so you may want to avoid it for very large modelsand use it only for development. You can load this as a Kripke structure by passing the `want_kripke` option to `spot.automaton()`. The type `kripke_graph` stores the Kripke structure explicitely (like a `twa_graph` stores an automaton explicitely), so you may want to avoid it for very large modelsand use it only for development.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
k2 = spot.automaton(string, want_kripke=True) k2 = spot.automaton(string, want_kripke=True)
print(type(k2)) print(type(k2))
k2 k2
``` ```
%%%% Output: stream %%%% Output: stream
<class 'spot.impl.kripke_graph'> <class 'spot.impl.kripke_graph'>
%%%% Output: execute_result %%%% Output: execute_result
[Hidden Image Output] [Hidden Image Output]
<spot.impl.kripke_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::kripke_graph > *' at 0x7fb8f84690f0> > <spot.impl.kripke_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::kripke_graph > *' at 0x7fb8f84690f0> >
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment