Commit 98c8725d authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

print_dot_psl: fix numbering of commutative operands

* spot/tl/dot.cc: Here.
* tests/python/formulas.ipynb: Add test case.
* NEWS: Mention the bug.
parent dc1f7133
Pipeline #5199 passed with stages
in 151 minutes and 40 seconds
New in spot 2.7.0.dev (not yet release)
Nothing yet.
Bugs fixed:
- The print_dot_psl() function would incorrectly number all but the
first children of commutative n-ary operators: in this case no
numbering was expected.
New in spot 2.7 (2018-12-11)
......
......@@ -122,7 +122,8 @@ namespace spot
else if (childnum == -1)
os_ << " [taillabel=\"R\"]";
os_ << ";\n";
++childnum;
if (childnum)
++childnum;
}
return src;
......
%% Cell type:markdown id: tags:
Handling LTL and PSL formulas
=============================
%% Cell type:code id: tags:
``` python
import spot
from IPython.display import display # not needed with recent Jupyter
```
%% Cell type:markdown id: tags:
For interactive use, formulas can be entered as text strings and passed to the `spot.formula` constructor.
%% Cell type:code id: tags:
``` python
f = spot.formula('p1 U p2 R (p3 & !p4)')
f
```
%% Output
$p_{1} \mathbin{\mathsf{U}} (p_{2} \mathbin{\mathsf{R}} (p_{3} \land \lnot p_{4}))$
spot.formula("p1 U (p2 R (p3 & !p4))")
%% Cell type:code id: tags:
``` python
g = spot.formula('{a;b*;c[+]}<>->GFb'); g
g = spot.formula('{a;b*;c[+]}<>->(GFb & c)'); g
```
%% Output
$\{a \mathbin{\mathsf{;}} b^{\star} \mathbin{\mathsf{;}} c^+\}\mathrel{\Diamond\kern-1.7pt\raise.4pt\hbox{$\mathord{\rightarrow}$}} \mathsf{G} \mathsf{F} b$
spot.formula("{a;b[*];c[+]}<>-> GFb")
$\{a \mathbin{\mathsf{;}} b^{\star} \mathbin{\mathsf{;}} c^+\}\mathrel{\Diamond\kern-1.7pt\raise.4pt\hbox{$\mathord{\rightarrow}$}} (c \land \mathsf{G} \mathsf{F} b)$
spot.formula("{a;b[*];c[+]}<>-> (c & GFb)")
%% Cell type:markdown id: tags:
By default the parser recognizes an infix syntax, but when this fails, it tries to read the formula with the [LBT](http://www.tcs.hut.fi/Software/maria/tools/lbt/) syntax:
%% Cell type:code id: tags:
``` python
h = spot.formula('& | a b c'); h
```
%% Output
$c \land (a \lor b)$
spot.formula("c & (a | b)")
%% Cell type:markdown id: tags:
Passing a `formula` to `spot.formula` simply returns the formula.
%% Cell type:code id: tags:
``` python
spot.formula(h)
```
%% Output
$c \land (a \lor b)$
spot.formula("c & (a | b)")
%% Cell type:markdown id: tags:
By default, a formula object is presented using mathjax as above.
When a formula is converted to string you get Spot's syntax by default:
%% Cell type:code id: tags:
``` python
str(f)
```
%% Output
'p1 U (p2 R (p3 & !p4))'
%% Cell type:markdown id: tags:
If you prefer to print the string in another syntax, you may use the `to_str()` method, with an argument that indicates the output format to use. The `latex` format assumes that you will the define macros such as `\U`, `\R` to render all operators as you wish. On the otherhand, the `sclatex` (with `sc` for self-contained) format hard-codes the rendering of each of those operators: this is almost the same output that is used to render formulas using MathJax in a notebook. `sclatex` and `mathjax` only differ in the rendering of double-quoted atomic propositions.
%% Cell type:code id: tags:
``` python
for i in ['spot', 'spin', 'lbt', 'wring', 'utf8', 'latex', 'sclatex', 'mathjax']:
print("%-10s%s" % (i, f.to_str(i)))
```
%% Output
spot p1 U (p2 R (p3 & !p4))
spin p1 U (p2 V (p3 && !p4))
lbt U p1 V p2 & p3 ! p4
wring (p1=1) U ((p2=1) R ((p3=1) * (p4=0)))
utf8 p1 U (p2 R (p3∧¬p4))
latex p_{1} \U (p_{2} \R (p_{3} \land \lnot p_{4}))
sclatex p_{1} \mathbin{\mathsf{U}} (p_{2} \mathbin{\mathsf{R}} (p_{3} \land \lnot p_{4}))
mathjax p_{1} \mathbin{\mathsf{U}} (p_{2} \mathbin{\mathsf{R}} (p_{3} \land \lnot p_{4}))
%% Cell type:markdown id: tags:
Formulas output via `format()` can also use some convenient shorthand to select the syntax:
%% Cell type:code id: tags:
``` python
print("""\
Spin: {0:s}
Spin+parentheses: {0:sp}
Spot (default): {0}
Spot+shell quotes: {0:q}
LBT, right aligned: {0:l:~>40}
LBT, no M/W/R: {0:[MWR]l}""".format(f))
```
%% Output
Spin: p1 U (p2 V (p3 && !p4))
Spin+parentheses: (p1) U ((p2) V ((p3) && (!(p4))))
Spot (default): p1 U (p2 R (p3 & !p4))
Spot+shell quotes: 'p1 U (p2 R (p3 & !p4))'
LBT, right aligned: ~~~~~~~~~~~~~~~~~~~~~U p1 V p2 & p3 ! p4
LBT, no M/W/R: U p1 U & p3 ! p4 | & & p2 p3 ! p4 G & p3 ! p4
%% Cell type:markdown id: tags:
The specifiers that can be used with `format` are documented as follows:
%% Cell type:code id: tags:
``` python
help(spot.formula.__format__)
```
%% Output
Help on function __format__ in module spot:
__format__(self, spec)
Format the formula according to `spec`.
Parameters
----------
spec : str, optional
a list of letters that specify how the formula
should be formatted.
Supported specifiers
--------------------
- 'f': use Spot's syntax (default)
- '8': use Spot's syntax in UTF-8 mode
- 's': use Spin's syntax
- 'l': use LBT's syntax
- 'w': use Wring's syntax
- 'x': use LaTeX output
- 'X': use self-contained LaTeX output
- 'j': use self-contained LaTeX output, adjusted for MathJax
Add some of those letters for additional options:
- 'p': use full parentheses
- 'c': escape the formula for CSV output (this will
enclose the formula in double quotes, and escape
any included double quotes)
- 'h': escape the formula for HTML output
- 'd': escape double quotes and backslash,
for use in C-strings (the outermost double
quotes are *not* added)
- 'q': quote and escape for shell output, using single
quotes or double quotes depending on the contents.
- '[...]': rewrite away all the operators specified in brackets,
using spot.unabbreviate().
- ':spec': pass the remaining specification to the
formating function for strings.
%% Cell type:markdown id: tags:
A `spot.formula` object has a number of built-in predicates whose value have been computed when the formula was constructed. For instance you can check whether a formula is in negative normal form using `is_in_nenoform()`, and you can make sure it is an LTL formula (i.e. not a PSL formula) using `is_ltl_formula()`:
%% Cell type:code id: tags:
``` python
f.is_in_nenoform() and f.is_ltl_formula()
```
%% Output
True
%% Cell type:code id: tags:
``` python
g.is_ltl_formula()
```
%% Output
False
%% Cell type:markdown id: tags:
Similarly, `is_syntactic_stutter_invariant()` tells wether the structure of the formula guarranties it to be stutter invariant. For LTL formula, this means the `X` operator should not be used. For PSL formula, this function capture all formulas built using the [siPSL grammar](http://www.daxc.de/eth/paper/09atva.pdf).
%% Cell type:code id: tags:
``` python
f.is_syntactic_stutter_invariant()
```
%% Output
True
%% Cell type:code id: tags:
``` python
spot.formula('{a[*];b}<>->c').is_syntactic_stutter_invariant()
```
%% Output
False
%% Cell type:code id: tags:
``` python
spot.formula('{a[+];b[*]}<>->d').is_syntactic_stutter_invariant()
```
%% Output
True
%% Cell type:markdown id: tags:
`spot.relabel` renames the atomic propositions that occur in a formula, using either letters, or numbered propositions:
%% Cell type:code id: tags:
``` python
gf = spot.formula('(GF_foo_) && "a > b" && "proc[2]@init"'); gf
```
%% Output
$\unicode{x201C}\mathit{a > b}\unicode{x201D} \land \unicode{x201C}\mathit{proc[2]@init}\unicode{x201D} \land \mathsf{G} \mathsf{F} \mathit{\_foo\_}$
spot.formula("\"a > b\" & \"proc[2]@init\" & GF_foo_")
%% Cell type:code id: tags:
``` python
spot.relabel(gf, spot.Abc)
```
%% Output
$a \land b \land \mathsf{G} \mathsf{F} c$
spot.formula("a & b & GFc")
%% Cell type:code id: tags:
``` python
spot.relabel(gf, spot.Pnn)
```
%% Output
$p_{0} \land p_{1} \land \mathsf{G} \mathsf{F} p_{2}$
spot.formula("p0 & p1 & GFp2")
%% Cell type:markdown id: tags:
The AST of any formula can be displayed with `show_ast()`. Despite the name, this is not a tree but a DAG, because identical subtrees are merged. Binary operators have their left and right operands denoted with `L` and `R`, while non-commutative n-ary operators have their operands numbered.
%% Cell type:code id: tags:
``` python
print(g); g.show_ast()
```
%% Output
{a;b[*];c[+]}<>-> GFb
{a;b[*];c[+]}<>-> (c & GFb)
<IPython.core.display.SVG object>
%% Cell type:markdown id: tags:
Any formula can also be classified in the temporal hierarchy of Manna & Pnueli
%% Cell type:code id: tags:
``` python
g.show_mp_hierarchy()
```
%% Output
<IPython.core.display.SVG object>
%% Cell type:code id: tags:
``` python
spot.mp_class(g, 'v')
```
%% Output
'recurrence'
%% Cell type:code id: tags:
``` python
f = spot.formula('F(a & X(!a & b))'); f
```
%% Output
$\mathsf{F} (a \land \mathsf{X} (\lnot a \land b))$
spot.formula("F(a & X(!a & b))")
%% Cell type:markdown id: tags:
Etessami's rule for removing X (valid only in stutter-invariant formulas)
%% Cell type:code id: tags:
``` python
spot.remove_x(f)
```
%% Output
$\mathsf{F} (a \land ((a \land (a \mathbin{\mathsf{U}} (\lnot a \land b)) \land ((\lnot b \mathbin{\mathsf{U}} \lnot a) \lor (b \mathbin{\mathsf{U}} \lnot a))) \lor (\lnot a \land (\lnot a \mathbin{\mathsf{U}} (a \land \lnot a \land b)) \land ((\lnot b \mathbin{\mathsf{U}} a) \lor (b \mathbin{\mathsf{U}} a))) \lor (b \land (b \mathbin{\mathsf{U}} (\lnot a \land b \land \lnot b)) \land ((\lnot a \mathbin{\mathsf{U}} \lnot b) \lor (a \mathbin{\mathsf{U}} \lnot b))) \lor (\lnot b \land (\lnot b \mathbin{\mathsf{U}} (\lnot a \land b)) \land ((\lnot a \mathbin{\mathsf{U}} b) \lor (a \mathbin{\mathsf{U}} b))) \lor (\lnot a \land b \land (\mathsf{G} \lnot a \lor \mathsf{G} a) \land (\mathsf{G} \lnot b \lor \mathsf{G} b))))$
spot.formula("F(a & ((a & (a U (!a & b)) & ((!b U !a) | (b U !a))) | (!a & (!a U (a & !a & b)) & ((!b U a) | (b U a))) | (b & (b U (!a & b & !b)) & ((!a U !b) | (a U !b))) | (!b & (!b U (!a & b)) & ((!a U b) | (a U b))) | (!a & b & (G!a | Ga) & (G!b | Gb))))")
%% Cell type:markdown id: tags:
Removing abbreviated operators
%% Cell type:code id: tags:
``` python
f = spot.formula("G(a xor b) -> F(a <-> b)")
spot.unabbreviate(f, "GF^")
```
%% Output
$(\bot \mathbin{\mathsf{R}} \lnot (a \leftrightarrow b)) \rightarrow (\top \mathbin{\mathsf{U}} (a \leftrightarrow b))$
spot.formula("(0 R !(a <-> b)) -> (1 U (a <-> b))")
%% Cell type:code id: tags:
``` python
spot.unabbreviate(f, "GF^ei")
```
%% Output
$(\top \mathbin{\mathsf{U}} ((a \land b) \lor (\lnot a \land \lnot b))) \lor \lnot (\bot \mathbin{\mathsf{R}} ((\lnot a \land b) \lor (a \land \lnot b)))$
spot.formula("(1 U ((a & b) | (!a & !b))) | !(0 R ((!a & b) | (a & !b)))")
%% Cell type:markdown id: tags:
Nesting level of operators
%% Cell type:code id: tags:
``` python
f = spot.formula('F(b & X(a U b U ((a W Fb) | (c U d))))')
print("U", spot.nesting_depth(f, spot.op_U))
print("F", spot.nesting_depth(f, spot.op_F))
# These following two are syntactic sugar for the above two
print("U", spot.nesting_depth(f, "U"))
print("F", spot.nesting_depth(f, "F"))
# If you want to consider "U" and "F" are a similar type of
# operator, you can count both with
print("FU", spot.nesting_depth(f, "FU"))
```
%% Output
U 3
F 2
U 3
F 2
FU 4
%% Cell type:markdown id: tags:
Collecting the set of atomic propositions used by a formula:
%% Cell type:code id: tags:
``` python
ap = spot.atomic_prop_collect(f)
print(repr(ap)) # print as an atomic_prop_set object
print(ap) # print as a string
display(ap) # LaTeX-style, for notebooks
```
%% Output
spot.atomic_prop_set([spot.formula("a"), spot.formula("b"), spot.formula("c"), spot.formula("d")])
{"a", "b", "c", "d"}
$\{\unicode{x201C}a\unicode{x201D}, \unicode{x201C}b\unicode{x201D}, \unicode{x201C}c\unicode{x201D}, \unicode{x201C}d\unicode{x201D}\}$
......
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