Commit 98c8725d by Alexandre Duret-Lutz

### print_dot_psl: fix numbering of commutative operands

* spot/tl/dot.cc: Here.
* NEWS: Mention the bug.
 %% 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) %% 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 %% 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}\}$ ... ...