dot: add option "k"
Fixes #134. * spot/twaalgos/dot.cc: Implement it. * bin/common_aoutput.cc, spot/twaalgos/dot.hh, NEWS: Document it. * tests/core/readsave.test, tests/python/ltsmin.ipynb: Test it.
This commit is contained in:
parent
5a5f83f468
commit
a9b4560f3d
6 changed files with 528 additions and 451 deletions
4
NEWS
4
NEWS
|
|
@ -19,6 +19,10 @@ New in spot 1.99.7a (not yet released)
|
||||||
with
|
with
|
||||||
ltl2tgba -D 'Ga | Gb | Gc' -d
|
ltl2tgba -D 'Ga | Gb | Gc' -d
|
||||||
|
|
||||||
|
* print_dot() now has option "k" to use state-based labels when
|
||||||
|
possible. This is similar to the "k" option already supported
|
||||||
|
by print_hoa(), and is useful when printing Kripke structures.
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
* The ltsmin interface has been binded in Python. See
|
* The ltsmin interface has been binded in Python. See
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ static const argp_option options[] =
|
||||||
{
|
{
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
{ nullptr, 0, nullptr, 0, "Output format:", 3 },
|
{ nullptr, 0, nullptr, 0, "Output format:", 3 },
|
||||||
{ "dot", 'd', "1|a|b|B|c|e|f(FONT)|h|n|N|o|r|R|s|t|v|+INT|<INT",
|
{ "dot", 'd', "1|a|b|B|c|e|f(FONT)|h|k|n|N|o|r|R|s|t|v|+INT|<INT",
|
||||||
OPTION_ARG_OPTIONAL,
|
OPTION_ARG_OPTIONAL,
|
||||||
"GraphViz's format. Add letters for "
|
"GraphViz's format. Add letters for "
|
||||||
"(1) force numbered states, "
|
"(1) force numbered states, "
|
||||||
|
|
@ -88,11 +88,13 @@ static const argp_option options[] =
|
||||||
"(B) bullets except for Büchi/co-Büchi automata, "
|
"(B) bullets except for Büchi/co-Büchi automata, "
|
||||||
"(c) force circular nodes, (e) force elliptic nodes, "
|
"(c) force circular nodes, (e) force elliptic nodes, "
|
||||||
"(f(FONT)) use FONT, (h) horizontal layout, "
|
"(f(FONT)) use FONT, (h) horizontal layout, "
|
||||||
"(v) vertical layout, (n) with name, (N) without name, "
|
"(k) use state labels when possible, "
|
||||||
|
"(n) with name, (N) without name, "
|
||||||
"(o) ordered transitions, "
|
"(o) ordered transitions, "
|
||||||
"(r) rainbow colors for acceptance sets, "
|
"(r) rainbow colors for acceptance sets, "
|
||||||
"(R) color acceptance sets by Inf/Fin, (s) with SCCs, "
|
"(R) color acceptance sets by Inf/Fin, (s) with SCCs, "
|
||||||
"(t) force transition-based acceptance, "
|
"(t) force transition-based acceptance, "
|
||||||
|
"(v) vertical layout, "
|
||||||
"(+INT) add INT to all set numbers, "
|
"(+INT) add INT to all set numbers, "
|
||||||
"(<INT) display at most INT states", 0 },
|
"(<INT) display at most INT states", 0 },
|
||||||
{ "hoaf", 'H', "i|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
{ "hoaf", 'H', "i|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ namespace spot
|
||||||
bool mark_states_ = false;
|
bool mark_states_ = false;
|
||||||
bool opt_scc_ = false;
|
bool opt_scc_ = false;
|
||||||
bool opt_html_labels_ = false;
|
bool opt_html_labels_ = false;
|
||||||
|
bool opt_state_labels_ = false;
|
||||||
const_twa_graph_ptr aut_;
|
const_twa_graph_ptr aut_;
|
||||||
std::vector<std::string>* sn_ = nullptr;
|
std::vector<std::string>* sn_ = nullptr;
|
||||||
std::vector<std::pair<unsigned, unsigned>>* sprod_ = nullptr;
|
std::vector<std::pair<unsigned, unsigned>>* sprod_ = nullptr;
|
||||||
|
|
@ -192,6 +193,9 @@ namespace spot
|
||||||
case 'h':
|
case 'h':
|
||||||
opt_horizontal_ = true;
|
opt_horizontal_ = true;
|
||||||
break;
|
break;
|
||||||
|
case 'k':
|
||||||
|
opt_state_labels_ = true;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
opt_name_ = true;
|
opt_name_ = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -319,6 +323,21 @@ namespace spot
|
||||||
os_ << '}';
|
os_ << '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
state_label(unsigned s) const
|
||||||
|
{
|
||||||
|
bdd label = bddfalse;
|
||||||
|
for (auto& t: aut_->out(s))
|
||||||
|
{
|
||||||
|
label = t.cond;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (label == bddfalse
|
||||||
|
&& incomplete_ && incomplete_->find(s) != incomplete_->end())
|
||||||
|
return "...";
|
||||||
|
return bdd_format_formula(aut_->get_dict(), label);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
|
|
@ -440,6 +459,8 @@ namespace spot
|
||||||
os_ << "\\n";
|
os_ << "\\n";
|
||||||
output_set(acc);
|
output_set(acc);
|
||||||
}
|
}
|
||||||
|
if (opt_state_labels_)
|
||||||
|
escape_str(os_ << "\\n", state_label(s));
|
||||||
os_ << '"';
|
os_ << '"';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -456,6 +477,8 @@ namespace spot
|
||||||
os_ << "<br/>";
|
os_ << "<br/>";
|
||||||
output_html_set(acc);
|
output_html_set(acc);
|
||||||
}
|
}
|
||||||
|
if (opt_state_labels_)
|
||||||
|
escape_html(os_ << "<br/>", state_label(s));
|
||||||
os_ << '>';
|
os_ << '>';
|
||||||
}
|
}
|
||||||
os_ << "]\n";
|
os_ << "]\n";
|
||||||
|
|
@ -470,6 +493,8 @@ namespace spot
|
||||||
os_ << (*sprod_)[s].first << ',' << (*sprod_)[s].second;
|
os_ << (*sprod_)[s].first << ',' << (*sprod_)[s].second;
|
||||||
else
|
else
|
||||||
os_ << s;
|
os_ << s;
|
||||||
|
if (opt_state_labels_)
|
||||||
|
escape_str(os_ << "\\n", state_label(s));
|
||||||
os_ << '"';
|
os_ << '"';
|
||||||
// Use state_acc_sets(), not state_is_accepting() because
|
// Use state_acc_sets(), not state_is_accepting() because
|
||||||
// on co-Büchi automata we want to mark the rejecting
|
// on co-Büchi automata we want to mark the rejecting
|
||||||
|
|
@ -486,8 +511,10 @@ namespace spot
|
||||||
void
|
void
|
||||||
process_link(const twa_graph::edge_storage_t& t, int number)
|
process_link(const twa_graph::edge_storage_t& t, int number)
|
||||||
{
|
{
|
||||||
std::string label = bdd_format_formula(aut_->get_dict(), t.cond);
|
|
||||||
os_ << " " << t.src << " -> " << t.dst;
|
os_ << " " << t.src << " -> " << t.dst;
|
||||||
|
std::string label;
|
||||||
|
if (!opt_state_labels_)
|
||||||
|
label = bdd_format_formula(aut_->get_dict(), t.cond);
|
||||||
if (!opt_html_labels_)
|
if (!opt_html_labels_)
|
||||||
{
|
{
|
||||||
os_ << " [label=\"";
|
os_ << " [label=\"";
|
||||||
|
|
@ -495,6 +522,7 @@ namespace spot
|
||||||
if (!mark_states_)
|
if (!mark_states_)
|
||||||
if (auto a = t.acc)
|
if (auto a = t.acc)
|
||||||
{
|
{
|
||||||
|
if (!opt_state_labels_)
|
||||||
os_ << "\\n";
|
os_ << "\\n";
|
||||||
output_set(a);
|
output_set(a);
|
||||||
}
|
}
|
||||||
|
|
@ -507,6 +535,7 @@ namespace spot
|
||||||
if (!mark_states_)
|
if (!mark_states_)
|
||||||
if (auto a = t.acc)
|
if (auto a = t.acc)
|
||||||
{
|
{
|
||||||
|
if (!opt_state_labels_)
|
||||||
os_ << "<br/>";
|
os_ << "<br/>";
|
||||||
output_html_set(a);
|
output_html_set(a);
|
||||||
}
|
}
|
||||||
|
|
@ -533,6 +562,27 @@ namespace spot
|
||||||
sprod_ = nullptr;
|
sprod_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (opt_state_labels_)
|
||||||
|
{
|
||||||
|
// Verify that we can use state labels for this automaton.
|
||||||
|
unsigned n = aut->num_states();
|
||||||
|
for (unsigned s = 0; s < n; ++s)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
bdd cond = bddfalse;
|
||||||
|
for (auto& t: aut->out(s))
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
cond = t.cond;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else if (t.cond != cond)
|
||||||
|
{
|
||||||
|
opt_state_labels_ = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
incomplete_ =
|
incomplete_ =
|
||||||
aut->get_named_prop<std::set<unsigned>>("incomplete-states");
|
aut->get_named_prop<std::set<unsigned>>("incomplete-states");
|
||||||
if (opt_name_)
|
if (opt_name_)
|
||||||
|
|
@ -541,11 +591,12 @@ namespace spot
|
||||||
&& aut_->prop_state_acc().is_true());
|
&& aut_->prop_state_acc().is_true());
|
||||||
if (opt_shape_ == ShapeAuto)
|
if (opt_shape_ == ShapeAuto)
|
||||||
{
|
{
|
||||||
if (sn_ || sprod_ || aut->num_states() > 100)
|
if (sn_ || sprod_ || aut->num_states() > 100 || opt_state_labels_)
|
||||||
{
|
{
|
||||||
opt_shape_ = ShapeEllipse;
|
opt_shape_ = ShapeEllipse;
|
||||||
// If all state names are short, prefer circles.
|
// If all state names are short, prefer circles.
|
||||||
if (sn_ && std::all_of(sn_->begin(), sn_->end(),
|
if (!opt_state_labels_ &&
|
||||||
|
sn_ && std::all_of(sn_->begin(), sn_->end(),
|
||||||
[](const std::string& s)
|
[](const std::string& s)
|
||||||
{ return s.size() <= 2; }))
|
{ return s.size() <= 2; }))
|
||||||
opt_shape_ = ShapeCircle;
|
opt_shape_ = ShapeCircle;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// -*- coding: utf-8 -*-
|
// -*- coding: utf-8 -*-
|
||||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
|
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 Laboratoire de Recherche
|
||||||
// et Developpement de l'Epita (LRDE).
|
// et Developpement de l'Epita (LRDE).
|
||||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||||
|
|
@ -39,7 +39,7 @@ namespace spot
|
||||||
/// supported: 'v' for vertical output, 'h' for horizontal output,
|
/// supported: 'v' for vertical output, 'h' for horizontal output,
|
||||||
/// 't' force transition-based acceptance, 'N' hide the name of the
|
/// 't' force transition-based acceptance, 'N' hide the name of the
|
||||||
/// automaton, 'n' shows the name, 'c' uses circle-shaped states,
|
/// automaton, 'n' shows the name, 'c' uses circle-shaped states,
|
||||||
/// 'a' shows the acceptance.
|
/// 'a' shows the acceptance, 'k' uses state-based labels if possible.
|
||||||
SPOT_API std::ostream&
|
SPOT_API std::ostream&
|
||||||
print_dot(std::ostream& os,
|
print_dot(std::ostream& os,
|
||||||
const const_twa_ptr& g,
|
const const_twa_ptr& g,
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,8 @@ test 1 = `autfilt -H input --complete | autfilt --is-complete --count`
|
||||||
|
|
||||||
|
|
||||||
# The SPOT_DEFAULT_FORMAT envvar should be ignored if --dot is given.
|
# The SPOT_DEFAULT_FORMAT envvar should be ignored if --dot is given.
|
||||||
SPOT_DEFAULT_FORMAT=hoa ltl2tgba --dot=a 'GFa & GFb' >output
|
# --dot=k should be ignored when not applicable.
|
||||||
|
SPOT_DEFAULT_FORMAT=hoa ltl2tgba --dot=ak 'GFa & GFb' >output
|
||||||
cat output
|
cat output
|
||||||
cat >expected <<EOF
|
cat >expected <<EOF
|
||||||
digraph G {
|
digraph G {
|
||||||
|
|
@ -859,6 +860,49 @@ EOF
|
||||||
|
|
||||||
diff output6 expect6
|
diff output6 expect6
|
||||||
|
|
||||||
|
run 0 autfilt -dk input6 >output6d
|
||||||
|
cat >expect6d <<EOF
|
||||||
|
digraph G {
|
||||||
|
rankdir=LR
|
||||||
|
I [label="", style=invis, width=0]
|
||||||
|
I -> 1
|
||||||
|
0 [label="0\nb"]
|
||||||
|
0 -> 2 [label=""]
|
||||||
|
0 -> 1 [label=""]
|
||||||
|
0 -> 1 [label=""]
|
||||||
|
1 [label="1\na"]
|
||||||
|
1 -> 0 [label=""]
|
||||||
|
1 -> 1 [label=""]
|
||||||
|
2 [label="2\na", peripheries=2]
|
||||||
|
2 -> 2 [label=""]
|
||||||
|
2 -> 0 [label=""]
|
||||||
|
2 -> 1 [label=""]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
diff output6d expect6d
|
||||||
|
|
||||||
|
run 0 autfilt -dbark input6 >output6d2
|
||||||
|
cat >expect6d2 <<EOF
|
||||||
|
digraph G {
|
||||||
|
rankdir=LR
|
||||||
|
label=<Inf(<font color="#5DA5DA">⓿</font>)>
|
||||||
|
labelloc="t"
|
||||||
|
I [label="", style=invis, width=0]
|
||||||
|
I -> 1
|
||||||
|
0 [label=<0<br/>b>]
|
||||||
|
0 -> 2 [label=<>]
|
||||||
|
0 -> 1 [label=<>]
|
||||||
|
0 -> 1 [label=<>]
|
||||||
|
1 [label=<1<br/>a>]
|
||||||
|
1 -> 0 [label=<>]
|
||||||
|
1 -> 1 [label=<>]
|
||||||
|
2 [label=<2<br/><font color="#5DA5DA">⓿</font><br/>a>]
|
||||||
|
2 -> 2 [label=<>]
|
||||||
|
2 -> 0 [label=<>]
|
||||||
|
2 -> 1 [label=<>]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
diff output6d2 expect6d2
|
||||||
|
|
||||||
cat >input7 <<EOF
|
cat >input7 <<EOF
|
||||||
HOA: v1
|
HOA: v1
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue