import spot
from spot.jupyter import display_inline
spot.setup(show_default='.vtnA')
TL;DR Model checking a system $M$ against a specification $\varphi$ amounts to check whether $\mathscr{L}(M\otimes A)=\emptyset$, where $A$ is an automaton for $\lnot\varphi$. If we have some extra knowledge $K$ about $M$, we can replace the previous check by $\mathscr{L}(M\otimes B)=\emptyset$ where $B$ is an automaton simpler than $A$, but that has been built from $A$ and $K$.
Assuming A is the the automaton for $\lnot\varphi$, and that K is some apriori knowledge about the system to check, this combines A and K into a bounded automaton (Ab), whose labels can then be simplified (Aminato). The resulting automaton can be further minimized. The whole process reduces both the size and the strength of the automaton.
A = spot.translate('F(a & c) | G(Fb & F!b)')
K = 'FG(b) & G(c)'
Ab = spot.update_bounds_given(A, K)
Aminato = spot.bounds_simplify(Ab)
Asimpl = Aminato.postprocess('small')
display_inline(A, Ab, Aminato, Asimpl)
This demonstrate the stutterize_given(A, [K1, K2,...], type) function that attempts to build a stutter-invariant automaton. It takes an automaton A to simplify, a list of knowledges [K1, K2, ...] that can be used for simplification, and a type of simplification (True for the relax variant, and False for the restrict variant.) It returns a stutter-invariant automaton if it manages to build one (property prop_stutter_invariant() will be set on the result in this case), or the original automaton otherwise.
A = spot.translate('XFa'); K = spot.translate('!a', 'Buchi')
sirelax = spot.stutterize_given(A, [K], True).postprocess('small')
sirestrict = spot.stutterize_given(A, [K], False).postprocess('small')
display_inline(A, K, sirestrict, sirelax, per_row=2)
This is probably one of the very first example we worked with, but it does not show as much as the one we kept for Figure 3.
aut1 = spot.translate('a | G(Fc & F!q)')
aut2 = spot.update_bounds_given(aut1, 'G(q)')
aut3 = spot.bounds_simplify(aut2)
display_inline(aut1, aut2, aut3, aut3.postprocess())
The following example shows how multiple knowledge can be accumulated in the bounded automaton.
aut1 = spot.translate('a U (b U c)')
aut2 = spot.update_bounds_given(aut1, 'G(b -> Xc)')
aut3 = spot.update_bounds_given(aut2, '!a')
aut4 = spot.bounds_simplify(aut3)
display_inline(aut1, aut2, aut3, aut4, aut4.postprocess(), show='.vt')
The following example also accumulates some (trivial) knowledge. It builds an automaton that is unfortunately stutter-sensitive because of the relaxed initial transition. If that transition was restricted to p0&p1, the automaton would be stutter-insensitive.
aut1 = spot.translate('F(!p0 | X!p1)')
aut2 = spot.update_bounds_given(aut1, 'p0')
aut3 = spot.update_bounds_given(aut2, 'p1')
aut4 = spot.bounds_simplify(aut3)
display_inline(aut1, aut2, aut3, aut4)
Passing this result through sutterize_given and then postprocess() achieves the desired reduction.
sg = spot.stutterize_given(aut1, [spot.translate("p0"), spot.translate("p1")])
display_inline(sg, sg.postprocess())