import spot
from IPython.display import display # not needed with recent Jupyter
For interactive use, formulas can be entered as text strings and passed to the spot.formula constructor.
f = spot.formula('p1 U p2 R (p3 & !p4)')
f
g = spot.formula('{a;first_match((b*;c[+])[:*3..5];b)}<>->(GFb & c)'); g
By default the parser recognizes an infix syntax, but when this fails, it tries to read the formula with the LBT syntax:
h = spot.formula('& | a b c'); h
Passing a formula to spot.formula simply returns the formula.
spot.formula(h)
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:
str(f)
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 other hand, 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.
for i in ['spot', 'spin', 'lbt', 'wring', 'utf8', 'latex', 'sclatex', 'mathjax']:
print(f"{i:10}{f.to_str(i)}")
Formulas output via format() of f-strings can also use some convenient shorthand to select the syntax:
print(f"""\
Spin: {f:s}
Spin+parentheses: {f:sp}
Spot (default): {f}
Spot+shell quotes: {f:q}
LBT, right aligned: {f:l:~>40}
LBT, no M/W/R: {f:[MWR]l}""")
The specifiers that can be used with format are documented as follows.
(Note: As this document is part of our test-suite we have to use print(x.__doc__) instead of help(x) to work around some formatting changes introduced in Python 3.12's help().)
print(spot.formula.__format__.__doc__)
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():
f.is_in_nenoform() and f.is_ltl_formula()
g.is_ltl_formula()
Similarly, is_syntactic_stutter_invariant() tells whether the structure of the formula guaranties 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.
f.is_syntactic_stutter_invariant()
spot.formula('{a[*];b}<>->c').is_syntactic_stutter_invariant()
spot.formula('{a[+];b[*]}<>->d').is_syntactic_stutter_invariant()
spot.relabel renames the atomic propositions that occur in a formula, using either letters, or numbered propositions:
gf = spot.formula('(GF_foo_) && "a > b" && "proc[2]@init"'); gf
spot.relabel(gf, spot.Abc)
spot.relabel(gf, spot.Pnn)
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.
print(g); g.show_ast()
Any formula can also be classified in the temporal hierarchy of Manna & Pnueli
g.show_mp_hierarchy()
spot.mp_class(g, 'v')
f = spot.formula('F(a & X(!a & b))'); f
Etessami's rule for removing X (valid only in stutter-invariant formulas)
spot.remove_x(f)
Removing abbreviated operators
f = spot.formula("G(a xor b) -> F(a <-> b)")
spot.unabbreviate(f, "GF^")
spot.unabbreviate(f, "GF^ei")
Nesting level of operators
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"))
Collecting the set of atomic propositions used by a formula:
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
Converting to Suffix Operator Normal Form:
f = spot.formula('G({x*} []-> Fa)')
display(f)
# In addition to the formula, returns a list of newly introduced APs
f, aps = spot.suffix_operator_normal_form(f, 'p')
display(f)
display(aps)