dot: add a <N option

* spot/twaalgos/dot.cc: Implement it.
* spot/taalgos/dot.cc: Ignore it.
* spot/twaalgos/copy.cc, spot/twaalgos/copy.hh: Add option
to limit the number of states.
* tests/python/ltsmin.ipynb: Improve test case.
* tests/Makefile.am: Cleanup the files generated by ltsmin.ipynb.
* python/spot/__init__.py (setup): Add a max_states argument
that default to 50.
* bin/common_aoutput.cc: Mention the <INT option.
* NEWS: Likewise.
This commit is contained in:
Alexandre Duret-Lutz 2016-01-28 17:45:50 +01:00
parent 4571d6dd3a
commit b11c07b351
9 changed files with 1145 additions and 364 deletions

12
NEWS
View file

@ -12,11 +12,23 @@ New in spot 1.99.7a (not yet released)
object can be queried about the supported variables and their object can be queried about the supported variables and their
types. types.
* print_dot() now accepts an option of the form "<N" to specify
a maximum number of states to output. Incomplete states are
then marked appropriately. For a quick example, compare
ltl2tgba -D 'Ga | Gb | Gc' -d'<3'
with
ltl2tgba -D 'Ga | Gb | Gc' -d
Python: Python:
* The ltsmin interface has been binded in Python. See * The ltsmin interface has been binded in Python. See
https://spot.lrde.epita.fr/ipynb/ltsmin.html for a short example. https://spot.lrde.epita.fr/ipynb/ltsmin.html for a short example.
* spot.setup() sets de maximum number of states to display in
automata to 50 by default, as more states is likely to be
unreadable (and slow to process by GraphViz). This can be
overridden by calling spot.setup(max_states=N).
Documentation: Documentation:
* There is a new page giving informal illustrations (and extra * There is a new page giving informal illustrations (and extra

View file

@ -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", { "dot", 'd', "1|a|b|B|c|e|f(FONT)|h|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, "
@ -93,7 +93,8 @@ static const argp_option options[] =
"(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, "
"(+INT) add INT to all set numbers", 0 }, "(+INT) add INT to all set numbers, "
"(<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,
"Output the automaton in HOA format (default). Add letters to select " "Output the automaton in HOA format (default). Add letters to select "
"(i) use implicit labels for complete deterministic automata, " "(i) use implicit labels for complete deterministic automata, "

View file

@ -71,6 +71,8 @@ def setup(**kwargs):
the font to use in the GraphViz output (default: 'Lato') the font to use in the GraphViz output (default: 'Lato')
show_default : str show_default : str
default options for show() default options for show()
max_states : int
maximum number of states in GraphViz output (default: 50)
""" """
import os import os
@ -80,7 +82,8 @@ def setup(**kwargs):
kwargs.get('fillcolor', '#ffffaa')) kwargs.get('fillcolor', '#ffffaa'))
bullets = 'B' if kwargs.get('bullets', True) else '' bullets = 'B' if kwargs.get('bullets', True) else ''
d = 'rf({})'.format(kwargs.get('font', 'Lato')) + bullets max_states = '<' + str(kwargs.get('max_states', 50))
d = 'rf({})'.format(kwargs.get('font', 'Lato')) + bullets + max_states
global _show_default global _show_default
_show_default = kwargs.get('show_default', None) _show_default = kwargs.get('show_default', None)
os.environ['SPOT_DOTDEFAULT'] = d os.environ['SPOT_DOTDEFAULT'] = d

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2010, 2012, 2014, 2015 Laboratoire de Recherche et // Copyright (C) 2010, 2012, 2014, 2015, 2016 Laboratoire de Recherche
// Developpement de l Epita (LRDE). // et Developpement de l Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -84,7 +84,16 @@ namespace spot
case 'v': case 'v':
opt_horizontal_ = false; opt_horizontal_ = false;
break; break;
case '0':
case '1': case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'a': case 'a':
case 'b': case 'b':
case 'B': case 'B':
@ -96,6 +105,8 @@ namespace spot
case 'R': case 'R':
case 's': case 's':
case 't': case 't':
case '+':
case '<':
// All these options are implemented by dotty() on TGBA, // All these options are implemented by dotty() on TGBA,
// but are not implemented here. We simply ignore them, // but are not implemented here. We simply ignore them,
// because raising an exception if they are in // because raising an exception if they are in

View file

@ -27,13 +27,15 @@
namespace spot namespace spot
{ {
twa_graph_ptr twa_graph_ptr
copy(const const_twa_ptr& aut, twa::prop_set p, bool preserve_names) copy(const const_twa_ptr& aut, twa::prop_set p,
bool preserve_names, unsigned max_states)
{ {
twa_graph_ptr out = make_twa_graph(aut->get_dict()); twa_graph_ptr out = make_twa_graph(aut->get_dict());
out->copy_acceptance_of(aut); out->copy_acceptance_of(aut);
out->copy_ap_of(aut); out->copy_ap_of(aut);
out->prop_copy(aut, p); out->prop_copy(aut, p);
std::vector<std::string>* names = nullptr; std::vector<std::string>* names = nullptr;
std::set<unsigned>* incomplete = nullptr;
if (preserve_names) if (preserve_names)
{ {
names = new std::vector<std::string>; names = new std::vector<std::string>;
@ -69,9 +71,27 @@ namespace spot
unsigned src2; unsigned src2;
std::tie(src1, src2) = *todo.front(); std::tie(src1, src2) = *todo.front();
todo.pop_front(); todo.pop_front();
for (auto* t: aut->succ(src1)) for (auto* t: aut->succ(src1))
out->new_edge(src2, new_state(t->dst()), t->cond(), t->acc()); {
if (SPOT_UNLIKELY(max_states < out->num_states()))
{
// If we have reached the max number of state, never try
// to create a new one.
auto i = seen.find(t->dst());
if (i == seen.end())
{
if (!incomplete)
incomplete = new std::set<unsigned>;
incomplete->insert(src2);
continue;
}
out->new_edge(src2, i->second, t->cond(), t->acc());
}
else
{
out->new_edge(src2, new_state(t->dst()), t->cond(), t->acc());
}
}
} }
@ -84,6 +104,8 @@ namespace spot
ptr->destroy(); ptr->destroy();
} }
if (incomplete)
out->set_named_prop("incomplete-states", incomplete);
return out; return out;
} }
} }

View file

@ -33,5 +33,6 @@ namespace spot
/// ///
/// This works for using the abstract interface for automata /// This works for using the abstract interface for automata
SPOT_API twa_graph_ptr SPOT_API twa_graph_ptr
copy(const const_twa_ptr& aut, twa::prop_set p, bool preserve_names = false); copy(const const_twa_ptr& aut, twa::prop_set p,
bool preserve_names = false, unsigned max_states = -1U);
} }

View file

@ -57,6 +57,7 @@ namespace spot
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;
std::set<unsigned>* incomplete_;
std::string* name_ = nullptr; std::string* name_ = nullptr;
acc_cond::mark_t inf_sets_ = 0U; acc_cond::mark_t inf_sets_ = 0U;
acc_cond::mark_t fin_sets_ = 0U; acc_cond::mark_t fin_sets_ = 0U;
@ -83,9 +84,21 @@ namespace spot
}; };
const char*const* palette = palette9; const char*const* palette = palette9;
int palette_mod = 9; int palette_mod = 9;
unsigned max_states_ = -1U;
bool max_states_given_ = false;
public: public:
unsigned max_states()
{
return max_states_;
}
bool max_states_given()
{
return max_states_given_;
}
void void
parse_opts(const char* options) parse_opts(const char* options)
{ {
@ -95,7 +108,7 @@ namespace spot
{ {
case '.': case '.':
{ {
// Copy the value in a string, so future calls to // Copy the value in a string, so future calls to
// parse_opts do not fail if the environment has // parse_opts do not fail if the environment has
// changed. (This matters particularly in an ipython // changed. (This matters particularly in an ipython
// notebook, where it is tempting to redefine // notebook, where it is tempting to redefine
@ -123,6 +136,25 @@ namespace spot
options = end; options = end;
break; break;
} }
case '<':
{
char* end;
max_states_ = strtoul(options, &end, 10);
if (options == end)
throw std::runtime_error
("missing number after '<' in print_dot() options");
if (max_states_ == 0)
{
max_states_ = -1U;
max_states_given_ = false;
}
else
{
max_states_given_ = true;
}
options = end;
break;
}
case '1': case '1':
opt_want_state_names_ = false; opt_want_state_names_ = false;
break; break;
@ -445,6 +477,9 @@ namespace spot
os_ << ", peripheries=2"; os_ << ", peripheries=2";
os_ << "]\n"; os_ << "]\n";
} }
if (incomplete_ && incomplete_->find(s) != incomplete_->end())
os_ << " u" << s << " [label=\"...\", shape=none, width=0, height=0"
"]\n " << s << " -> u" << s << " [style=dashed]\n";
} }
void void
@ -497,6 +532,8 @@ namespace spot
sprod_ = nullptr; sprod_ = nullptr;
} }
} }
incomplete_ =
aut->get_named_prop<std::set<unsigned>>("incomplete-states");
if (opt_name_) if (opt_name_)
name_ = aut_->get_named_prop<std::string>("automaton-name"); name_ = aut_->get_named_prop<std::string>("automaton-name");
mark_states_ = (!opt_force_acc_trans_ mark_states_ = (!opt_force_acc_trans_
@ -566,8 +603,8 @@ namespace spot
{ {
dotty_output d(os, options); dotty_output d(os, options);
auto aut = std::dynamic_pointer_cast<const twa_graph>(g); auto aut = std::dynamic_pointer_cast<const twa_graph>(g);
if (!aut) if (!aut || (d.max_states_given() && aut->num_states() >= d.max_states()))
aut = copy(g, twa::prop_set::all(), true); aut = copy(g, twa::prop_set::all(), true, d.max_states() - 1);
d.print(aut); d.print(aut);
return os; return os;
} }

View file

@ -316,7 +316,7 @@ TESTS_python = \
python/trival.py python/trival.py
endif endif
CLEANFILES = python/finite.dve2C python/finite.dve.cpp CLEANFILES = python/test1.dve python/test1.dve2C python/test1.dve.cpp
SUFFIXES = .ipynb .html SUFFIXES = .ipynb .html
.ipynb.html: .ipynb.html:

File diff suppressed because it is too large Load diff