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:
parent
4571d6dd3a
commit
b11c07b351
9 changed files with 1145 additions and 364 deletions
12
NEWS
12
NEWS
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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, "
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue