# -*- coding: utf-8 -*-
#+TITLE: Common output options for automata
#+DESCRIPTION: Options for input and output of ω-automata in Spot's command-line tools
#+SETUPFILE: setup.org
#+HTML_LINK_UP: tools.html
Spot supports different output syntaxes for automata. This page
documents the options, common to all tools where it makes sense, that
are used to specify how to output of automata.
* Common output options
All tools that can output automata implement the following options:
#+BEGIN_SRC sh :results verbatim :exports results
ltl2tgba --help | sed -n '/Output format:/,/^$/p' | sed '1d;$d'
#+END_SRC
#+RESULTS:
#+begin_example
-8, --utf8 enable UTF-8 characters in output (ignored with
--lbtt or --spin)
--check[=PROP] test for the additional property PROP and output
the result in the HOA format (implies -H). PROP
may be any prefix of 'all' (default),
'unambiguous', 'stutter-invariant', or
'strength'.
-d, --dot[=1|a|b|B|c|e|f(FONT)|h|n|N|o|r|R|s|t|v|+INT]
GraphViz's format. Add letters for (1) force
numbered states, (a) acceptance display, (b)
acceptance sets as bullets, (B) bullets except for
Büchi/co-Büchi automata, (c) force circular
nodes, (e) force elliptic nodes, (f(FONT)) use
FONT, (h) horizontal layout, (v) vertical layout,
(n) with name, (N) without name, (o) ordered
transitions, (r) rainbow colors for acceptance
sets, (R) color acceptance sets by Inf/Fin, (s)
with SCCs, (t) force transition-based acceptance,
(+INT) add INT to all set numbers
-H, --hoaf[=i|l|m|s|t|v] Output the automaton in HOA format (default). Add
letters to select (i) use implicit labels for
complete deterministic automata, (s) prefer
state-based acceptance when possible [default],
(t) force transition-based acceptance, (m) mix
state and transition-based acceptance, (k) use
state labels when possible, (l) single-line
output, (v) verbose properties
--lbtt[=t] LBTT's format (add =t to force transition-based
acceptance even on Büchi automata)
--name=FORMAT set the name of the output automaton
-o, --output=FORMAT send output to a file named FORMAT instead of
standard output. The first automaton sent to a
file truncates it unless FORMAT starts with '>>'.
-q, --quiet suppress all normal output
-s, --spin[=6|c] Spin neverclaim (implies --ba). Add letters to
select (6) Spin's 6.2.4 style, (c) comments on
states
--stats=FORMAT output statistics about the automaton
#+end_example
The main three output formats (that can also been used as input to
some of the tools) are [[http://adl.github.io/hoaf/][HOA]] (used by default, or with =-H= or
=--hoaf=), [[http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html][LBTT]] (activated by =--lbtt=), or Spin [[http://spinroot.com/spin/Man/never.html][never claims]]
(activated by =-s= or =--spin=). These three formats also support
*streaming*, i.e., you can concatenate multiple automata (and even mix
these three formats in the same stream), and the tools will be able to
read and process them in sequence.
The other possible outputs are [[http://www.graphviz.org/][GraphViz]] output (=-d= or =--dot=),
various statistics (=--stats=), or nothing at all (=--quiet=). It may
seem strange to ask a tool to not output anything, but it makes sense
when only the exit status matters (for instance using [[file:autfilt.org][=autfilt=]] to
check whether an input automaton has some property) or for timing
purposes.
* HOA output
Details about supported features of the HOA format can be found on a
[[file:hoa.org][separate page]].
The [[http://adl.github.io/hoaf/][HOA]] output should be the preferred format to use if you want to
pass automata between different tools. Since Spot 1.99.7, it is the
default output format, but you can explicitely request it using the
=-H= parameter and this allows passing additional options to the HOA
printer.
Here is an example where [[file:ltl2tgba.org][=ltl2tgba=]] is used to construct two automata:
one for =a U b= and one for =(Ga -> Gb) W c=.
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba 'a U b' '(Ga -> Gb) W c'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1
name: "a U b"
States: 2
Start: 1
AP: 2 "a" "b"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc deterministic
properties: stutter-invariant terminal
--BODY--
State: 0 {0}
[t] 0
State: 1
[1] 0
[0&!1] 1
--END--
HOA: v1
name: "(Gb | F!a) W c"
States: 5
Start: 1
AP: 3 "b" "a" "c"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels trans-acc stutter-invariant
--BODY--
State: 0
[0] 0 {0}
State: 1
[0&1&!2] 0
[!1&!2] 1 {0}
[1&!2] 2
[2] 3
State: 2
[!1&!2] 1 {0}
[1&!2] 2
[!1&2] 3
[1&2] 4
State: 3
[t] 3 {0}
State: 4
[!1] 3
[1] 4
--END--
#+END_SRC
The above output contains two automata, named after the formulas they
represent. Here is a picture of these two automata:
#+NAME: hoafex
#+BEGIN_SRC sh :results verbatim :exports none
ltl2tgba --dot=.cn '(Ga -> Gb) W c' 'a U b' | dot | gvpack |
perl -pe 's/\\\n//g;s/\\/\\\\/g;s/graph G/graph cluster/g'
#+END_SRC
#+RESULTS: hoafex
#+begin_example
digraph root {
graph [bb="0,0,427,231.07",
fontname=Lato,
labelloc=t,
lheight=0.21,
rankdir=LR
];
node [fillcolor="#ffffa0",
fontname=Lato,
label="\\N",
shape=circle,
style=filled
];
edge [fontname=Lato];
subgraph cluster {
graph [bb="",
fontname=Lato,
label=<(Gb | F!a) W c>,
labelloc=t,
lheight=0.21,
lp="197.5,196.66",
lwidth=1.19,
rankdir=LR
];
node [fillcolor="#ffffa0",
fontname=Lato,
height="",
label="\\N",
pos="",
shape=circle,
style=filled,
width=""
];
edge [fontname=Lato,
label="",
lp="",
pos=""
];
I [height=0.013889,
label="",
pos="1,49.168",
style=invis,
width=0.013889];
1 [height=0.5,
label=1,
pos="56,49.168",
width=0.5];
I -> 1 [pos="e,37.942,49.324 1.1549,49.324 2.6725,49.324 15.097,49.324 27.628,49.324"];
1 -> 1 [label=⓿>,
lp="56,100.32",
pos="e,62.379,66.362 49.621,66.362 48.319,76.182 50.445,85.324 56,85.324 59.472,85.324 61.604,81.753 62.398,76.677"];
0 [height=0.5,
label=0,
pos="190,121.17",
width=0.5];
1 -> 0 [label=,
lp="123,113.83",
pos="e,172.99,115.19 70.127,60.572 76.491,65.727 84.391,71.704 92,76.324 115.21,90.42 143.57,103.1 163.61,111.38"];
2 [height=0.5,
label=2,
pos="190,34.168",
width=0.5];
1 -> 2 [label=,
lp="123,64.824",
pos="e,175.09,44.492 73.8,53.268 93.402,57.17 126.62,61.596 154,54.324 158.19,53.213 162.39,51.47 166.37,49.467"];
3 [height=0.5,
label=3,
pos="377,34.168",
width=0.5];
1 -> 3 [label=,
lp="242,9.8246",
pos="e,361.03,25.984 66.027,34.327 72.161,25.632 81.127,15.423 92,10.325 114.02,0 277.48,0.3418 312,7.3246 325.76,10.108 340.24,15.943 351.94,21.478"];
0 -> 0 [label=⓿>,
lp="190,172.33",
pos="e,198.98,137.24 181.02,137.24 178.68,147.48 181.67,157.33 190,157.33 195.47,157.33 198.63,153.08 199.5,147.28"];
2 -> 1 [label=⓿>,
lp="123,35.324",
pos="e,68.596,36.186 173.36,26.591 167.44,24.066 160.55,21.587 154,20.324 126.94,15.113 117.92,10.98 92,20.324 86.432,22.331 81.123,25.651 76.398,29.343"];
2 -> 2 [label=,
lp="190,77.824",
pos="e,198.98,50.24 181.02,50.24 178.68,60.475 181.67,70.324 190,70.324 195.47,70.324 198.63,66.083 199.5,60.274"];
2 -> 3 [label=,
lp="294,105.83",
pos="e,365.94,48.712 203.15,46.686 218.52,61.348 246.56,84.98 276,94.324 291.25,99.165 297.12,100.21 312,94.324 331.12,86.764 347.87,70.495 359.43,56.803"];
4 [height=0.5,
label=4,
pos="294,34.168",
width=0.5];
2 -> 4 [label=,
lp="242,41.824",
pos="e,275.95,34.324 208.3,34.324 224.08,34.324 247.64,34.324 265.91,34.324"];
3 -> 3 [label=<1
⓿>,
lp="377,85.324",
pos="e,384.03,50.989 369.97,50.989 368.41,60.949 370.75,70.324 377,70.324 381,70.324 383.4,66.477 384.2,61.093"];
4 -> 3 [label=,
lp="335.5,41.824",
pos="e,358.85,34.324 312.18,34.324 322.81,34.324 336.69,34.324 348.8,34.324"];
4 -> 4 [label=,
lp="294,77.824",
pos="e,301.03,50.989 286.97,50.989 285.41,60.949 287.75,70.324 294,70.324 298,70.324 300.4,66.477 301.2,61.093"];
}
subgraph cluster_gv1 {
graph [bb="",
fontname=Lato,
label=,
labelloc=t,
lheight=0.21,
lp="81.5,88.5",
lwidth=0.47,
rankdir=LR
];
node [fillcolor="#ffffa0",
fontname=Lato,
height="",
label="\\N",
peripheries="",
pos="",
shape=circle,
style=filled,
width=""
];
edge [fontname=Lato,
label="",
lp="",
pos=""
];
I_gv1 [height=0.013889,
label="",
pos="261,156.17",
style=invis,
width=0.013889];
"1_gv1" [height=0.5,
label=1,
pos="316,156.17",
width=0.5];
I_gv1 -> "1_gv1" [pos="e,297.94,156.17 261.15,156.17 262.67,156.17 275.1,156.17 287.63,156.17"];
"1_gv1" -> "1_gv1" [label=,
lp="316,199.67",
pos="e,322.38,173.21 309.62,173.21 308.32,183.03 310.44,192.17 316,192.17 319.47,192.17 321.6,188.6 322.4,183.52"];
"0_gv1" [height=0.72222,
label=0,
peripheries=2,
pos="401,156.17",
width=0.72222];
"1_gv1" -> "0_gv1" [label=,
lp="356.5,163.67",
pos="e,379,156.17 334.2,156.17 344.16,156.17 357,156.17 368.7,156.17"];
"0_gv1" -> "0_gv1" [label=1,
lp="401,203.67",
pos="e,409.01,176.75 392.99,176.75 391.89,187.01 394.55,196.17 401,196.17 405.13,196.17 407.71,192.41 408.74,187.01"];
}
}
#+end_example
#+BEGIN_SRC dot :file hoafex.png :cmdline -Tpng :var txt=hoafex :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:hoafex.png]]
The [[http://adl.github.io/hoaf/][HOA format]] supports both state and transition-based acceptance.
Although Spot works only with transition-based acceptance, its output
routines default to state-based acceptance whenever possible (this is
the case in the first of these two automata) and use transition-based
acceptance otherwise. You can change this behavior using =-Hs= (or
=--hoaf=s=), =-Ht=, or =-Hm=. Option =s= corresponds to the default
to use state-based acceptance whenever possible. Option =t= forces
transition-based acceptance. For instance compare this output to the
previous one:
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba -Ht 'a U b'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1
name: "a U b"
States: 2
Start: 1
AP: 2 "a" "b"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels trans-acc deterministic
properties: stutter-invariant terminal
--BODY--
State: 0
[t] 0 {0}
State: 1
[1] 0
[0&!1] 1
--END--
#+END_SRC
Option =m= uses mixed acceptance, i.e, some states might use
state-based acceptance while other will not:
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba -Hm '(Ga -> Gb) W c'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1
name: "(Gb | F!a) W c"
States: 5
Start: 1
AP: 3 "b" "a" "c"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels stutter-invariant
--BODY--
State: 0 {0}
[0] 0
State: 1
[0&1&!2] 0
[!1&!2] 1 {0}
[1&!2] 2
[2] 3
State: 2
[!1&!2] 1 {0}
[1&!2] 2
[!1&2] 3
[1&2] 4
State: 3 {0}
[t] 3
State: 4
[!1] 3
[1] 4
--END--
#+END_SRC
It is also possible to output each automaton on a single line, in case
the result should be used with line-based tools or embedded into a CSV
file... Here is an example using both transition-based acceptance,
and single-line output:
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba -Htl 'a U b' '(Ga -> Gb) W c'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1 name: "a U b" States: 2 Start: 1 AP: 2 "a" "b" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels explicit-labels trans-acc deterministic stutter-invariant terminal --BODY-- State: 0 [t] 0 {0} State: 1 [1] 0 [0&!1] 1 --END--
HOA: v1 name: "(Gb | F!a) W c" States: 5 Start: 1 AP: 3 "b" "a" "c" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels explicit-labels trans-acc stutter-invariant --BODY-- State: 0 [0] 0 {0} State: 1 [0&1&!2] 0 [!1&!2] 1 {0} [1&!2] 2 [2] 3 State: 2 [!1&!2] 1 {0} [1&!2] 2 [!1&2] 3 [1&2] 4 State: 3 [t] 3 {0} State: 4 [!1] 3 [1] 4 --END--
#+END_SRC
Finally, version 1.1 of the HOA format can be specified using the
=-H1.1= option. Version 1, which is currently the default, can also
be requested explicitly using =-H1=. The main advantage of version
1.1, as far as Spot is concerned, is that some of negated properties
can be transmitted. For instance, compare
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba -f GFa -f FGa -H1 --check | grep -E '^(HOA|properties|name):'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1
name: "GFa"
properties: trans-labels explicit-labels trans-acc complete
properties: deterministic stutter-invariant
HOA: v1
name: "FGa"
properties: trans-labels explicit-labels state-acc stutter-invariant
properties: weak
#+END_SRC
versus
#+BEGIN_SRC sh :results verbatim :exports both :wrap SRC hoa
ltl2tgba -f GFa -f FGa -H1.1 --check | grep -E '^(HOA|properties|name):'
#+END_SRC
#+RESULTS:
#+BEGIN_SRC hoa
HOA: v1.1
name: "GFa"
properties: trans-labels explicit-labels trans-acc complete
properties: deterministic stutter-invariant !inherently-weak
HOA: v1.1
name: "FGa"
properties: trans-labels explicit-labels state-acc !complete
properties: !deterministic !unambiguous stutter-invariant weak
properties: !terminal
#+END_SRC
The =--check= option inspects the automata for additional properties
such that their strength or whether they are stutter-invariant and
unambiguous. You can see in this example that version 1.1 of the
format carries additional negated properties that could not be
represented in the first version.
* LBTT output
The [[http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html][LBTT]] output has two flavors: state-based (which is used to output
Büchi automata or monitors) or transition-based (for TGBA).
#+BEGIN_SRC sh :results verbatim :exports both
ltl2tgba --ba --lbtt 'p0 U p1'
#+END_SRC
#+RESULTS:
: 2 1
: 0 1 -1
: 1 p1
: 0 & p0 ! p1
: -1
: 1 0 0 -1
: 1 t
: -1
If you want to request transition-based output even for Büchi automata,
use =--lbtt=t=.
#+BEGIN_SRC sh :results verbatim :exports both
ltl2tgba --ba --lbtt=t 'p0 U p1'
#+END_SRC
#+RESULTS:
: 2 1t
: 0 1
: 1 -1 p1
: 0 -1 & p0 ! p1
: -1
: 1 0
: 1 0 -1 t
: -1
Note that the [[http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html][LBTT]] output generalizes the format output by [[http://www.tcs.hut.fi/Software/maria/tools/lbt/][LBT]] with
support for transition-based acceptance. Both formats however are
restricted to atomic propositions of the form =p0=, =p1=, etc... In
case other atomic propositions are used, Spot output them in double
quotes. This other extension of the format is also supported by
[[http://www.ltl2dstar.de/][ltl2dstar]].
#+BEGIN_SRC sh :results verbatim :exports both
ltl2tgba --ba --lbtt 'a U b'
#+END_SRC
#+RESULTS:
: 2 1
: 0 1 -1
: 1 "b"
: 0 & "a" ! "b"
: -1
: 1 0 0 -1
: 1 t
: -1
* Spin output
Spin [[http://spinroot.com/spin/Man/never.html][never claims]] can be requested using =-s= or =--spin=. They can only
represent Büchi automata, so these options imply =--ba=.
#+BEGIN_SRC sh :results verbatim :exports both
ltl2tgba -s 'a U b'
#+END_SRC
#+RESULTS:
: never { /* a U b */
: T0_init:
: if
: :: ((b)) -> goto accept_all
: :: ((a) && (!(b))) -> goto T0_init
: fi;
: accept_all:
: skip
: }
Recent versions of Spin (starting with Spin 6.2.4) output never claims
in a slightly different style that can be requested using either
=-s6= or =--spin=6=:
#+BEGIN_SRC sh :results verbatim :exports both
ltl2tgba -s6 'a U b'
#+END_SRC
#+RESULTS:
: never { /* a U b */
: T0_init:
: do
: :: atomic { ((b)) -> assert(!((b))) }
: :: ((a) && (!(b))) -> goto T0_init
: od;
: accept_all:
: skip
: }
(Note that while Spot is able to read never claims that follow any of
these two styles, it is not capable of interpreting an arbitrary piece
of Promela syntax.)
* Dot output
The =-d= or =--dot= option causes automata to be output in GraphViz's
format.
#+BEGIN_SRC sh :results verbatim :exports code
ltl2tgba '(Ga -> Gb) W c' -d
#+END_SRC
#+NAME: oaut-dot1
#+BEGIN_SRC sh :results verbatim :exports results
SPOT_DOTEXTRA= ltl2tgba '(Ga -> Gb) W c' --dot=
#+END_SRC
#+RESULTS: oaut-dot1
#+begin_example
digraph G {
rankdir=LR
node [shape="circle"]
I [label="", style=invis, width=0]
I -> 1
0 [label="0"]
0 -> 0 [label="b\n{0}"]
1 [label="1"]
1 -> 0 [label="a & b & !c"]
1 -> 1 [label="!a & !c\n{0}"]
1 -> 2 [label="a & !c"]
1 -> 3 [label="c"]
2 [label="2"]
2 -> 1 [label="!a & !c\n{0}"]
2 -> 2 [label="a & !c"]
2 -> 3 [label="!a & c"]
2 -> 4 [label="a & c"]
3 [label="3"]
3 -> 3 [label="1\n{0}"]
4 [label="4"]
4 -> 3 [label="!a"]
4 -> 4 [label="a"]
}
#+end_example
** Converting dot output to images or pdf
This output should be processed with =dot= to be converted into a
picture. For instance use =dot -Tpng= or =dot -Tpdf=.
#+BEGIN_SRC dot :file oaut-dot1.png :cmdline -Tpng :var txt=oaut-dot1 :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:oaut-dot1.png]]
** Customizing the dot output
This output can be customized by passing optional characters to the
=--dot= option. For instance =v= requests a vertical layout (instead
of the default horizontal layout), =c= requests circle states, =s=
causes strongly-connected components to be displayed, =n= causes the
name (see below) of the automaton to be displayed, and =a= causes the
acceptance condition to be shown as well. Option =b= causes sets to
be ouput as bullets (e.g., ⓿ instead of ={0}=); option =r= (for
rainbow) causes sets to be displayed in different colors, while option
=R= also uses colors, but it chooses them depending on whether a set
is used with Fin-acceptance, Inf-acceptance, or both. Option
=C(COLOR)= can be used to color all states using =COLOR=, and the
option =f(FONT)= is used to select a fontname: it is often necessary
when =b= is used to ensure the characters ⓿, ❶, etc. are all selected
from the same font.
#+BEGIN_SRC sh :results verbatim :exports code
ltl2tgba --dot=vcsna '(Ga -> Gb) W c'
#+END_SRC
#+RESULTS:
#+begin_example
digraph G {
label="(Gb | F!a) W c\nInf(0)\n[Büchi]"
labelloc="t"
node [shape="circle"]
edge[arrowhead=vee, arrowsize=.7]
I [label="", style=invis, height=0]
I -> 0
subgraph cluster_0 {
color=green
label=""
1 [label="1"]
}
subgraph cluster_1 {
color=green
label=""
2 [label="2"]
}
subgraph cluster_2 {
color=red
label=""
4 [label="4"]
}
subgraph cluster_3 {
color=green
label=""
0 [label="0"]
3 [label="3"]
}
0 -> 0 [label="!a & !c\n{0}"]
0 -> 1 [label="c"]
0 -> 2 [label="a & b & !c"]
0 -> 3 [label="a & !c"]
1 -> 1 [label="1\n{0}"]
2 -> 2 [label="b\n{0}"]
3 -> 0 [label="!a & !c\n{0}"]
3 -> 1 [label="!a & c"]
3 -> 3 [label="a & !c"]
3 -> 4 [label="a & c"]
4 -> 1 [label="!a"]
4 -> 4 [label="a"]
}
#+end_example
#+NAME: oaut-dot2
#+BEGIN_SRC sh :results verbatim :exports none
SPOT_DOTEXTRA= ltl2tgba --dot=vcsna '(Ga -> Gb) W c'
#+END_SRC
#+RESULTS: oaut-dot2
#+begin_example
digraph G {
label="(Gb | F!a) W c\nInf(0)\n[Büchi]"
labelloc="t"
node [shape="circle"]
I [label="", style=invis, height=0]
I -> 0
subgraph cluster_0 {
color=green
label=""
1 [label="1"]
}
subgraph cluster_1 {
color=green
label=""
2 [label="2"]
}
subgraph cluster_2 {
color=red
label=""
4 [label="4"]
}
subgraph cluster_3 {
color=green
label=""
0 [label="0"]
3 [label="3"]
}
0 -> 0 [label="!a & !c\n{0}"]
0 -> 1 [label="c"]
0 -> 2 [label="a & b & !c"]
0 -> 3 [label="a & !c"]
1 -> 1 [label="1\n{0}"]
2 -> 2 [label="b\n{0}"]
3 -> 0 [label="!a & !c\n{0}"]
3 -> 1 [label="!a & c"]
3 -> 3 [label="a & !c"]
3 -> 4 [label="a & c"]
4 -> 1 [label="!a"]
4 -> 4 [label="a"]
}
#+end_example
#+BEGIN_SRC dot :file oaut-dot2.png :cmdline -Tpng :var txt=oaut-dot2 :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:oaut-dot2.png]]
The acceptance condition is displayed in the same way as in the [[http://adl.github.io/hoaf/][HOA
format]]. Here =Inf(0)= means that runs are accepting if and only if
they visit some the transitions in the set #0 infinitely often. For
well known acceptance conditions (as Büchi in this case), their name
is also displayed in bracket below.
The strongly connected components are displayed using the following colors:
- *green* components contain an accepting cycle
- *red* components contain no accepting cycle
- *black* components are trivial (i.e., they contain no cycle)
- *gray* components are useless (i.e., they are non-accepting, and are only followed by non-accepting components)
Here is an example involving all colors:
#+NAME: oaut-dot3
#+BEGIN_SRC sh :results verbatim :exports none
SPOT_DOTEXTRA= autfilt --dot=cas < 1
subgraph cluster_0 {
color=grey
label=""
5 [label="5"]
6 [label="6"]
}
subgraph cluster_1 {
color=grey
label=""
0 [label="0"]
}
subgraph cluster_2 {
color=green
label=""
8 [label="8"]
9 [label="9"]
}
subgraph cluster_3 {
color=green
label=""
7 [label="7"]
}
subgraph cluster_4 {
color=black
label=""
2 [label="2"]
}
subgraph cluster_5 {
color=red
label=""
4 [label="4"]
}
subgraph cluster_6 {
color=green
label=""
1 [label="1"]
3 [label="3"]
}
0 -> 0 [label="a & b\n{0,1,2}"]
0 -> 0 [label="!a & !b\n{2}"]
0 -> 5 [label="a\n{2}"]
1 -> 4 [label="b"]
1 -> 3 [label="a & !b"]
2 -> 0 [label="a"]
2 -> 7 [label="b"]
3 -> 1 [label="a & b\n{0,1}"]
4 -> 4 [label="!b\n{1,2}"]
4 -> 2 [label="b"]
5 -> 6 [label="1\n{1}"]
6 -> 5 [label="1"]
7 -> 7 [label="!a & b\n{0,2}"]
7 -> 7 [label="a & b\n{0,1}"]
7 -> 8 [label="1"]
8 -> 8 [label="!a & b\n{0,2}"]
8 -> 9 [label="a & b\n{0,1}"]
9 -> 8 [label="!a & b\n{0,1}"]
9 -> 9 [label="a & b\n{0,2}"]
}
#+end_example
#+BEGIN_SRC dot :file oaut-dot3.png :cmdline -Tpng :var txt=oaut-dot3 :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:oaut-dot3.png]]
<>
The dot output can also be customized via two environment variables:
- =SPOT_DOTDEFAULT= contains default arguments for the =--dot= option
(for when it is used implicitly, or used as just =--dot= without
argument). For instance after =export SPOT_DOTDEFAULT=vcsn=, using
=--dot= is equivalent to =--dot=vcsn=. However using =--dot=xyz=
(for any value of =xyz=, even empty) will ignore the
=SPOT_DOTDEFAULT= variable. If the argument of =--dot= contains
a dot character, then this dot is replaced by the contents of
=SPOT_DOTDEFAULT=. So ~--dot=.a~ would be equivalent to =--dot=vcsna=
with our example definition of =SPOT_DOTDEFAULT=.
- =SPOT_DOTEXTRA= may contains an arbitrary string that will be emitted
in the dot output before the first state. This can be used to modify
any attribute. For instance (except for this page, where we had
do demonstrate the various options of =--dot=, and a few pages where
we show the =--dot= output verbatim) all the automata displayed in
this documentation are generated with the following environment
variables set:
#+BEGIN_SRC sh :results verbatim :exports code
export SPOT_DOTDEFAULT='Brf(Lato)C(#ffffa0)'
export SPOT_DOTEXTRA='edge[arrowhead=vee, arrowsize=.7]'
#+END_SRC
** Working with =dot2tex=
:PROPERTIES:
:CUSTOM_ID: dot2tex
:END:
The [[https://github.com/kjellmf/dot2tex][=dot2tex= program]] interacts with GraphViz to converts dot files
into TeX figures. The layout is still done by tools provided by
GraphViz (i.e. =dot=, =neato=, =circo=, ...) but the actual rendering
is done using LaTeX with the TikZ or PSTricks packages. One advantage
is that this allows embedding math formulas into the graph, something
GraphViz alone cannot do. Another advantage is that you can then
easily edit the LaTeX figure, for instance to add additional graphical
elements.
The =dot= formater of Spot has an option =x=, that is convenient to
use with =dot2tex=. This option causes labels to be rendered as LaTeX
mathematical formulas instead of ASCII text.
#+BEGIN_SRC sh :exports code
ltl2tgba 'p0 U p1' --dot=x | dot2tex --autosize --nominsize > out.tex
#+END_SRC
The above command should give you a LaTeX file that compiles to the
following figure:
#+BEGIN_SRC sh :results silent :exports results
ltl2tgba 'p0 U p1' --dot=x | dot2tex --autosize --nominsize > dot2tex.tex
latexmk --pdf dot2tex.tex
convert -density 150 -trim dot2tex.pdf dot2tex.png
latexmk -C dot2tex.tex
rm -f dot2tex.tex
#+END_SRC
[[file:dot2tex.png]]
Caveats:
- =dot2tex= should be called with option =--autosize= in order to
compute the size of each label before calling GraphViz to layout the
graph. This is because GraphViz cannot compute the correct size of
mathematical formulas. Unfortunately, the release of =dot2tex
2.9.0= contains a bug where sizes are intepreted as integers instead
of floats. This can cause labels or shapes to disappear. This bug
of =dot2tex= was fixed in 2014, but at the time of writing
(summer 2017) no new release of =dot2tex= has been made. To work around this,
make sure you install =dot2tex= from its git repository:
#+BEGIN_SRC sh
git clone https://github.com/kjellmf/dot2tex.git
cd dot2tex
sudo python setup.py install
#+END_SRC
- The default size of nodes seems slightly too big for our usage.
Using =--nominsize= is just one way around it. Refer to the
[[https://dot2tex.readthedocs.io/en/latest/][=dot2tex= manual]] for finer ways to set the node size.
- Currently the =x= option of Spot's =--dot= output cannot yet be
combined with the =r=, =R=, an =b= options used to display colored
bullets. (Patches are welcome.)
* Statistics
:PROPERTIES:
:CUSTOM_ID: stats
:END:
The =--stats= option takes format string parameter to specify what and
how statistics should be output.
Most tools support a common set of statistics about the output
automaton (like =%s= for the number of states, =%t= for transitions,
=%e= for edges, etc.). Additional statistics might be available
depending on what the tool does (for instance [[file:autfilt.org][=autfilt=]] also uses
capitaized letters =%S=, =%T=, and =%E= to display the same statistics
about the input automaton). All the available statistics are
displayed when a tool is run with =--help=.
For instance here are the statistics available in [[file:randaut.org][=randaut=]]:
#+BEGIN_SRC sh :results verbatim :exports results
randaut --help | sed -n '/ sequences:/,/^$/p' | sed '1d;$d'
#+END_SRC
#+RESULTS:
#+begin_example
%% a single %
%a number of acceptance sets
%c, %[LETTERS]c number of SCCs; you may filter the SCCs to count
using the following LETTERS, possibly
concatenated: (a) accepting, (r) rejecting, (v)
trivial, (t) terminal, (w) weak, (iw) inherently
weak. Use uppercase letters to negate them.
%d 1 if the output is deterministic, 0 otherwise
%e number of edges
%F seed number
%g acceptance condition (in HOA syntax)
%h the automaton in HOA format on a single line (use
%[opt]h to specify additional options as in
--hoa=opt)
%L automaton number
%m name of the automaton
%n number of nondeterministic states in output
%p 1 if the output is complete, 0 otherwise
%r wall-clock time elapsed in seconds (excluding
parsing)
%R, %[LETTERS]R CPU time (excluding parsing), in seconds; Add
LETTERS to restrict to(u) user time, (s) system
time, (p) parent process, or (c) children
processes.
%s number of states
%t number of transitions
%w one word accepted by the output automaton
#+end_example
In most tools =%F= and =%L= are the input filename and line number,
but as this makes no sense in =randaut=, these two sequences emit
numbers related to the generation of automata.
For instance let's generate 1000 random automata with 100 states and
density 0.2, and just count the number of edges in each automaton. Then
use =R= to summarize the distribution of these values:
#+BEGIN_SRC sh :results verbatim :exports both
randaut -e0.2 -Q100 -n1000 a --stats %e > size.csv
Rscript -e "summary(read.csv('size.csv', header=FALSE, col.names='edges'))"
#+END_SRC
#+RESULTS:
: edges
: Min. :1939
: 1st Qu.:2056
: Median :2083
: Mean :2082
: 3rd Qu.:2107
: Max. :2233
For $Q=100$ states and density $D=0.2$ the expected degree of each
state is $1+(Q-1)D = 1+99\times 0.2 = 20.8$, so the expected number of
edges should be $20.8\times100=2080$.
* Timing
:PROPERTIES:
:CUSTOM_ID: timing
:END:
Two of the statistics are related to time: =%r= displays wall-clock
time, while =%R= displays CPU-time.
#+BEGIN_SRC sh :results verbatim :exports both
genltl --or-gf=1..8 | ltl2tgba --high --stats='%f,%r,%R'
#+END_SRC
#+RESULTS:
: GFp1,0.000502296,0
: GF(p1 | p2),0.000796475,0
: GF(p1 | p2 | p3),0.00215579,0
: GF(p1 | p2 | p3 | p4),0.00441474,0
: GF(p1 | p2 | p3 | p4 | p5),0.00980961,0.01
: GF(p1 | p2 | p3 | p4 | p5 | p6),0.0255462,0.03
: GF(p1 | p2 | p3 | p4 | p5 | p6 | p7),0.121033,0.12
: GF(p1 | p2 | p3 | p4 | p5 | p6 | p7 | p8),0.624101,0.62
Note that =%r= is implemented using the most precise clock available
and usually has nano-second precision, while =%R= uses the =times()=
system call (when available) and is usually only precise up to 1/100
of a second. However, as a wall-clock time, =%r= will also be
affected by the load of the machine: if a machine is overloaded, or
swapping a lot, you may notice a wall-clock time that is significantly
higher than the CPU time measured by =%R=.
Additional arguments may be passed to =%R= to select the time that
must be output. By default, this the CPU-time spent in both user code
and system calls. This can be restricted using one of =u= (user) or
=s= (system). Also by default this includes the CPU-time for the
current process and any of its children: adding =p= (parent) and =c=
(children) will show only the selected time. Note that few tools
actually execute other processes: [[file:autfilt.org][=autfilt=]] and [[file:ltl2tgba.org][=ltl2tgba=]] can do so
when calling a SAT solver for [[file:satmin.org][SAT-based minimization]], and [[file:ltldo.org][=ltldo=]] will
obviously call any listed tool. However in the case of =ltldo= the
measured time is that of executing the other tools, so the result of
=%[p]R= is likely to be always 0.
Here is an example where we use =ltldo= to benchmark the (default)
=--high= option of =ltl2tba= against the =--low= option, computing for
each option the overall wall-clock time, CPU-time spent in =ltldo=,
and CPU-time spent in =ltl2tgba=:
#+BEGIN_SRC sh :results verbatim :exports both
genltl --or-gf=1..8 |
ltldo '{high}ltl2tgba' '{low}ltl2tgba --low' --stats='%T,%f,%r,%[p]R,%[c]R'
#+END_SRC
#+RESULTS:
#+begin_example
high,GFp1,0.0495443,0,0.02
low,GFp1,0.0427718,0,0.03
high,GFp1 | GFp2,0.0449237,0,0.03
low,GFp1 | GFp2,0.0429886,0,0.03
high,GFp1 | GFp2 | GFp3,0.0477704,0.01,0.03
low,GFp1 | GFp2 | GFp3,0.0294271,0,0.01
high,GFp1 | GFp2 | GFp3 | GFp4,0.0250874,0,0.02
low,GFp1 | GFp2 | GFp3 | GFp4,0.0203729,0,0.01
high,GFp1 | GFp2 | GFp3 | GFp4 | GFp5,0.0318887,0,0.03
low,GFp1 | GFp2 | GFp3 | GFp4 | GFp5,0.0207457,0,0.01
high,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6,0.0612968,0,0.05
low,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6,0.0145482,0,0.01
high,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6 | GFp7,0.130631,0,0.12
low,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6 | GFp7,0.0151502,0,0.01
high,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6 | GFp7 | GFp8,0.595865,0,0.59
low,GFp1 | GFp2 | GFp3 | GFp4 | GFp5 | GFp6 | GFp7 | GFp8,0.0160234,0,0.01
#+end_example
* Naming automata
Automata can be given names. This name can be output in the
HOA format, but also in GraphViz output when =--dot=n= is given.
By default, =ltl2tgba= will use the input formula as name. Other
tools have no default name. This name can be changed using the
=--name= option, that takes a format string similar to the one of
=--stats=.
#+NAME: oaut-name
#+BEGIN_SRC sh :results verbatim :exports code
ltl2tgba --name='TGBA for %f' --dot=n 'a U b'
#+END_SRC
#+RESULTS: oaut-name
#+begin_example
digraph G {
rankdir=LR
label="TGBA for a U b"
labelloc="t"
node[style=filled, fillcolor="#ffffa0"] edge[arrowhead=vee, arrowsize=.7]
I [label="", style=invis, width=0]
I -> 1
0 [label="0", peripheries=2]
0 -> 0 [label="1"]
1 [label="1"]
1 -> 0 [label="b"]
1 -> 1 [label="a & !b"]
}
#+end_example
#+BEGIN_SRC dot :file oaut-name.png :cmdline -Tpng :var txt=oaut-name :exports results
$txt
#+END_SRC
#+RESULTS:
[[file:oaut-name.png]]
If you have an automaton saved in the HOA format, you can extract its
name using =autfilt --stats=%M input.hoa=. The =%M= escape sequence is
replaced by the name of the input automaton.
Here is a pipeline of commands that generates five LTL formulas
$\varphi$ such that both $\varphi$ and $\lnot\varphi$ are translated
into a 3-state TGBA by [[file:ltl2tgba.org][=ltl2tgba=]]. It starts by generating an
infinite stream of random LTL formulas using =a= and =b= as atomic
propositions, then it converts these formulas as TGBA (in the HOA
format, therefore carrying the formula as name), filtering only the
TGBA with 3 states and outputting =!(%M)= (that is the negation of the
associated formula), translating the resulting formulas as TGBA, again
retaining only the names (i.e. formulas) of the automata with 3
states, and finally restricting the output to the first 5 matches
using =autfilt -n5=.
#+BEGIN_SRC sh :results verbatim :exports both
randltl -n -1 a b |
ltl2tgba |
autfilt --states=3 --stats='!(%M)' |
ltl2tgba |
autfilt --states=3 --stats=%M -n5
#+END_SRC
#+RESULTS:
: G(b | F(b & Fa))
: (!a | b | (!b & (b W Ga))) & (a | (!b & (b | (!b M F!a))))
: (!a | (!a R b)) & (a | (a U !b))
: !a & F((!a | FG!a) & (a | GFa))
: X(!b W a)
Note that the above result can also be obtained without using
=autfilt= and automata names. We can use the fact that =ltl2tgba
--stats= can output the automaton size, and that =ltl2tgba= is also
capable of [[file:csv.org][reading from a CSV file]] (=-F-/2= instructs =ltl2tgba= to
read the standard input as if it was a CSV file, and to process its
second column):
#+BEGIN_SRC sh :results verbatim :exports both
randltl -n -1 a b | # generate a stream of random LTL formulas
ltl2tgba -F- --stats='%s,!(%f)' | # for each formula output "states,negated formula"
grep '^3,' | # keep only formulas with 3 states
ltl2tgba -F-/2 --stats='%s,%f' | # for each negated formula output "states,formula"
grep '^3,' | # keep only negated formulas with 3 states
head -n5 | cut -d, -f2 # return the five first formulas
#+END_SRC
#+RESULTS:
: G(b | F(b & Fa))
: (!a | b | (!b & (b W Ga))) & (a | (!b & (b | (!b M F!a))))
: (!a | (!a R b)) & (a | (a U !b))
: !a & F((!a | FG!a) & (a | GFa))
: X(!b W a)
Note that the =-F-= argument in the first call to =ltl2tgba= is
superfluous as the tool default to reading from its standard input.
But we put it there for symmetry with the second call.
# LocalWords: num toc html syntaxes ltl tgba sed utf UTF lbtt SCCs
# LocalWords: GraphViz's hoaf HOA LBTT's neverclaim ba SPOT's Gb cn
# LocalWords: GraphViz autfilt acc Buchi hoafex gvpack perl pe bb
# LocalWords: labelloc rankdir subgraph lp pos invis gv png cmdline
# LocalWords: Tpng txt Hs Hm CSV Htl LBT dstar init goto fi Tpdf XF
# LocalWords: oaut vcsn randaut nondeterministic filename csv hoa
# LocalWords: varphi lnot GFb FG
* Naming output
By default, all output is sent to standard output, so you can either
redirect it to a file, or pipe it to another program.
You can also use the =--output= (a.k.a. =-o=) option to specify a
filename where automata should be written. The advantage over
a shell redirection, is that you may build a name using the same
escape sequences as used by =--stats= and =--name=.
For instance =%d= is replaced by 0 or 1 depending on whether the
automaton is deterministic. We can generate 20 random automata, and
output them in two files depending on their determinism:
#+BEGIN_SRC sh :results verbatim :exports both
randaut -n 20 -Q2 -e1 1 -o out-det%d.hoa
autfilt -c out-det0.hoa # Count of non-deterministic automata
autfilt -c out-det1.hoa # Count of deterministic automata
#+END_SRC
#+RESULTS:
: 14
: 6
If you use this feature, beware that the output filename is only
truncated once a first automaton is output to it: so if no automaton
is output for a given filename, the existing file will be left
untouched. For instance if we run the above commands again, but
forcing [[file:randaut.org][=randaut=]] to output 20 *deterministic* automata, it may look
like we produced more than 20 automata:
#+BEGIN_SRC sh :results verbatim :exports both
randaut -D -n 20 -Q2 -e1 1 -o out-det%d.hoa
autfilt -c out-det0.hoa # Count of non-deterministic automata
autfilt -c out-det1.hoa # Count of deterministic automata
#+END_SRC
#+RESULTS:
: 14
: 20
This is because the =out-det0.hoa= file hasn't changed from the
previous execution, while =out-det1.hoa= has been overwritten.
In the case where you want to append to a file instead of overwriting
it, prefix the output filename with =>>= as in
: randaut -D -n 20 -Q2 1 -o '>>out-det%d.hoa'
(You need the quotes so that the shell does not interpret =>>=.)
#+BEGIN_SRC sh :results silent :exports results
rm -f out-det0.hoa out-det1.hoa
#+END_SRC