Commit fbb9e437 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

dot: add x option for dot2tex

* spot/twa/acc.cc, spot/twa/acc.hh: Add a LaTeX output for acceptance
conditions.
* spot/twaalgos/dot.cc: Implement the 'x' option and refactor the code
a bit to limit duplication.
* tests/core/dot2tex.test: New test case (requires dot2tex).
* tests/Makefile.am: Add dot2tex.test.
* tests/core/alternating.test, tests/core/readsave.test,
tests/python/automata-io.ipynb: Adjust expected output.
* NEWS, doc/org/oaut.org: Mention the new option.
parent b242122c
......@@ -75,6 +75,11 @@ New in spot 2.3.5.dev (not yet released)
We plan to enable 'a' by default in a future release, so a new
option 'A' has been added to hide the acceptance condition.
- The print_dot() function has a new experimental option 'x' to
output labels are LaTeX formulas. This is meant to be used in
conjunction with the dot2tex tool. See
https://spot.lrde.epita.fr/oaut.html#dot2tex
- A new named property for automata called "original-states" can be
used to record the origin of a state before transformation. It is
currently defined by the degeneralization algorithms, and by
......
......@@ -579,6 +579,8 @@ digraph G {
}
#+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=.
......@@ -589,6 +591,8 @@ $txt
#+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=
......@@ -866,6 +870,63 @@ 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
......
......@@ -63,20 +63,45 @@ namespace spot
os << v;
}
template<bool html>
enum code_output {HTML, TEXT, LATEX};
template<enum code_output style>
static void
print_code(std::ostream& os,
const acc_cond::acc_code& code, unsigned pos,
std::function<void(std::ostream&, int)> set_printer)
{
const char* op = " | ";
const char* op_ = style == LATEX ? " \\lor " : " | ";
auto& w = code[pos];
const char* negated = "";
const char* negated_pre = "";
const char* negated_post = "";
auto set_neg = [&]() {
if (style == LATEX)
{
negated_pre = "\\overline{";
negated_post = "}";
}
else
{
negated_pre = "!";
}
};
bool top = pos == code.size() - 1;
switch (w.sub.op)
{
case acc_cond::acc_op::And:
op = html ? " &amp; " : " & ";
switch (style)
{
case HTML:
op_ = " &amp; ";
break;
case TEXT:
op_ = " & ";
break;
case LATEX:
op_ = " \\land ";
break;
}
SPOT_FALLTHROUGH;
case acc_cond::acc_op::Or:
{
......@@ -90,8 +115,8 @@ namespace spot
if (first)
first = false;
else
os << op;
print_code<html>(os, code, pos, set_printer);
os << op_;
print_code<style>(os, code, pos, set_printer);
pos -= code[pos].sub.size;
}
if (!top)
......@@ -99,14 +124,17 @@ namespace spot
}
break;
case acc_cond::acc_op::InfNeg:
negated = "!";
set_neg();
SPOT_FALLTHROUGH;
case acc_cond::acc_op::Inf:
{
auto a = code[pos - 1].mark.id;
if (a == 0U)
{
os << 't';
if (style == LATEX)
os << "\\mathsf{t}";
else
os << 't';
}
else
{
......@@ -115,16 +143,32 @@ namespace spot
top = code[pos - 1].mark.count() == 1;
unsigned level = 0;
const char* and_ = "";
const char* and_next_ = []() {
// The lack of surrounding space in HTML and
// TEXT is on purpose: we want to
// distinguish those grouped "Inf"s from
// other terms that are ANDed together.
switch (style)
{
case HTML:
return "&amp;";
case TEXT:
return "&";
case LATEX:
return " \\land ";
}
}();
if (!top)
os << '(';
const char* inf_ = (style == LATEX) ? "\\mathsf{Inf}(" : "Inf(";
while (a)
{
if (a & 1)
{
os << and_ << "Inf(" << negated;
os << and_ << inf_ << negated_pre;
set_printer(os, level);
os << ')';
and_ = html ? "&amp;" : "&";
os << negated_post << ')';
and_ = and_next_;
}
a >>= 1;
++level;
......@@ -135,14 +179,17 @@ namespace spot
}
break;
case acc_cond::acc_op::FinNeg:
negated = "!";
set_neg();
SPOT_FALLTHROUGH;
case acc_cond::acc_op::Fin:
{
auto a = code[pos - 1].mark.id;
if (a == 0U)
{
os << 'f';
if (style == LATEX)
os << "\\mathsf{f}";
else
os << 'f';
}
else
{
......@@ -153,14 +200,19 @@ namespace spot
const char* or_ = "";
if (!top)
os << '(';
const char* fin_ = (style == LATEX) ? "\\mathsf{Fin}(" : "Fin(";
while (a)
{
if (a & 1)
{
os << or_ << "Fin(" << negated;
os << or_ << fin_ << negated_pre;
set_printer(os, level);
os << ')';
or_ = "|";
os << negated_post << ')';
// The lack of surrounding space in HTML and
// TEXT is on purpose: we want to distinguish
// those grouped "Fin"s from other terms that
// are ORed together.
or_ = style == LATEX ? " \\lor " : "|";
}
a >>= 1;
++level;
......@@ -1407,7 +1459,7 @@ namespace spot
if (empty())
os << 't';
else
print_code<true>(os, *this, size() - 1,
print_code<HTML>(os, *this, size() - 1,
set_printer ? set_printer : default_set_printer);
return os;
}
......@@ -1420,7 +1472,20 @@ namespace spot
if (empty())
os << 't';
else
print_code<false>(os, *this, size() - 1,
print_code<TEXT>(os, *this, size() - 1,
set_printer ? set_printer : default_set_printer);
return os;
}
std::ostream&
acc_cond::acc_code::to_latex(std::ostream& os,
std::function<void(std::ostream&, int)>
set_printer) const
{
if (empty())
os << "\\mathsf{t}";
else
print_code<LATEX>(os, *this, size() - 1,
set_printer ? set_printer : default_set_printer);
return os;
}
......
......@@ -892,6 +892,13 @@ namespace spot
std::function<void(std::ostream&, int)>
set_printer = nullptr) const;
// Print the acceptance as Latex. The set_printer function can
// be used to implement customized output for set numbers.
std::ostream&
to_latex(std::ostream& os,
std::function<void(std::ostream&, int)>
set_printer = nullptr) const;
/// \brief Construct an acc_code from a string.
///
......
This diff is collapsed.
......@@ -219,6 +219,7 @@ TESTS_twa = \
core/gragsa.test \
core/dstar.test \
core/readsave.test \
core/dot2tex.test \
core/ltldo.test \
core/ltldo2.test \
core/maskacc.test \
......
......@@ -573,27 +573,27 @@ digraph G {
subgraph cluster_0 {
color=green
label=""
4 [label="t"]
4 [label=<t>]
}
subgraph cluster_1 {
color=green
label=""
1 [label="G(a & b)"]
1 [label=<G(a &amp; b)>]
}
subgraph cluster_2 {
color=red
label=""
2 [label="F!a"]
2 [label=<F!a>]
}
subgraph cluster_3 {
color=red
label=""
3 [label="F!b"]
3 [label=<F!b>]
}
subgraph cluster_4 {
color=green
label=""
0 [label="c R (c | G(a & b) | (F!b & F!a))"]
0 [label=<c R (c | G(a &amp; b) | (F!b &amp; F!a))>]
-1 [label=<>,shape=point]
-4 [label=<>,shape=point]
-7 [label=<>,shape=point]
......@@ -669,26 +669,26 @@ digraph G {
subgraph cluster_0 {
color=green
label=""
4 [label="t"]
4 [label=<t>]
}
subgraph cluster_1 {
color=red
label=""
2 [label="F!a"]
2 [label=<F!a>]
}
subgraph cluster_2 {
color=red
label=""
3 [label="F!b"]
3 [label=<F!b>]
}
subgraph cluster_3 {
color=green
label=""
0 [label="c R (c | G(a & b) | (F!b & F!a))"]
0 [label=<c R (c | G(a &amp; b) | (F!b &amp; F!a))>]
-1 [label=<>,shape=point]
-4 [label=<>,shape=point]
-7 [label=<>,shape=point]
1 [label="G(a & b)"]
1 [label=<G(a &amp; b)>]
-10 [label=<>,shape=point]
}
0 -> 4 [label=<c>]
......@@ -852,22 +852,22 @@ digraph G {
subgraph cluster_0 {
color=green
label=""
3 [label="t"]
3 [label=<t>]
}
subgraph cluster_1 {
color=green
label=""
1 [label="Fa"]
1 [label=<Fa>]
}
subgraph cluster_2 {
color=green
label=""
2 [label="G!a"]
2 [label=<G!a>]
}
subgraph cluster_3 {
color=green
label=""
0 [label="G((b & Fa) | (!b & G!a))"]
0 [label=<G((b &amp; Fa) | (!b &amp; G!a))>]
-1 [label=<>,shape=point]
-4 [label=<>,shape=point]
}
......
#!/bin/sh
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Laboratoire de Recherche et Développement
# de l'Epita (LRDE).
#
# This file is part of Spot, a model checking library.
#
# Spot is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Spot is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
. ./defs
set -e
# This tests our support for LaTeX-embedded dot, for dot2tex.
# Skip this test if we don't find all the tools we need.
(latexmk --version) || exit 77
(pdflatex --version) || exit 77
(dot2tex --version) || exit 77
(dot -V) || exit 77
ltl2tgba 'a U b' --dot=scanx >out.dot
dot2tex --autosize --nominsize out.dot >out.tex
pdflatex out.tex
ltl2tgba 'p0 U p1' --dot=tax >out2.dot
dot2tex --autosize --nominsize out2.dot >out2.tex
grep -F 'p_{0}' out2.tex
grep -F 'mathsf{Inf}' out2.tex
pdflatex out2.tex
......@@ -406,7 +406,7 @@ digraph G {
edge [fontname="Lato"]
I [label="", style=invis, width=0]
I -> 0
0 [label="0"]
0 [label=<0>]
0 -> 0 [label=<a &amp; b<br/>$zero$one>]
0 -> 0 [label=<!a &amp; !b>]
0 -> 0 [label=<!a &amp; b<br/>$one>]
......
......@@ -88,9 +88,9 @@
" size=\"10.2,5\" edge[arrowhead=vee, arrowsize=.7]\n",
" I [label=\"\", style=invis, width=0]\n",
" I -> 1\n",
" 0 [label=\"0\", peripheries=2]\n",
" 0 [label=<0>, peripheries=2]\n",
" 0 -> 0 [label=<1>]\n",
" 1 [label=\"1\"]\n",
" 1 [label=<1>]\n",
" 1 -> 0 [label=<b>]\n",
" 1 -> 1 [label=<a &amp; !b>]\n",
"}\n",
......@@ -862,4 +862,4 @@
"metadata": {}
}
]
}
\ No newline at end of file
}
Markdown is supported
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