zlktree: cleanup the interface, and add interactive ACD
* tests/python/_zlktree.ipynb: Remove and replace by... * tests/python/zlktree.ipynb: ... this more documented notebook. * tests/Makefile.am: Adjust. * doc/org/tut.org, NEWS: Mention zlktree.ipynb. * spot/twaalgos/zlktree.hh, spot/twaalgos/zlktree.cc, python/spot/__init__.py: Cleanup interface, and add support for interactive display.
This commit is contained in:
parent
dc17762e14
commit
5c5790039b
8 changed files with 6754 additions and 6130 deletions
16
NEWS
16
NEWS
|
|
@ -239,16 +239,12 @@ New in spot 2.9.8.dev (not yet released)
|
||||||
Additionally, this function now returns the number of states that
|
Additionally, this function now returns the number of states that
|
||||||
have been merged (and therefore removed from the automaton).
|
have been merged (and therefore removed from the automaton).
|
||||||
|
|
||||||
- spot::zielonka_tree is a new class that can be constructed from
|
- spot::zielonka_tree and spot::acd are new class that implement the
|
||||||
any acceptance condition to help paritizing it.
|
Zielonka Tree and Alternatic Cycle Decomposition, based on a paper
|
||||||
spot::zielonka_tree_transform() will paritize an automaton using
|
by Casares et al. (ICALP'21). Those structures can be used to
|
||||||
the Zielong Tree of its acceptance. Similarly, spot::acd class
|
paritize any automaton, and more. The graphical rendering of ACD
|
||||||
implement the Alternating Cycle Decomposition of any automaton.
|
in Jupyter notebooks is Spot's first interactive output. See
|
||||||
The spot::acd_transform() function uses it to paritize any
|
https://spot.lrde.epita.fr/ipynb/zlktree.html for more.
|
||||||
automaton optimally. These two transformations are based on a
|
|
||||||
paper by Casares et al. (ICALP'21). The python bindings for
|
|
||||||
spot.zielonka_tree and spot.acd will display those structure
|
|
||||||
graphically, making it easier to explore those concepts.
|
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ real notebooks instead.
|
||||||
- [[https://spot.lrde.epita.fr/ipynb/stutter-inv.html][=stutter-inv.ipynb=]] working with stutter-invariant formulas properties.
|
- [[https://spot.lrde.epita.fr/ipynb/stutter-inv.html][=stutter-inv.ipynb=]] working with stutter-invariant formulas properties.
|
||||||
- [[https://spot.lrde.epita.fr/ipynb/satmin.html][=satmin.ipynb=]] Python interface for [[file:satmin.org][SAT-based minimization of deterministic ω-automata]].
|
- [[https://spot.lrde.epita.fr/ipynb/satmin.html][=satmin.ipynb=]] Python interface for [[file:satmin.org][SAT-based minimization of deterministic ω-automata]].
|
||||||
- [[https://spot.lrde.epita.fr/ipynb/twagraph-internals.html][=twagraph-internals.ipynb=]] Inner workings of the =twa_graph= class.
|
- [[https://spot.lrde.epita.fr/ipynb/twagraph-internals.html][=twagraph-internals.ipynb=]] Inner workings of the =twa_graph= class.
|
||||||
|
- [[https://spot.lrde.epita.fr/ipynb/zlktree.html][=zlktree.ipynb=]] demonstration of Zielonka Trees and ACD
|
||||||
|
|
||||||
# LocalWords: utf html bdd IPython ipynb io randaut accparse acc
|
# LocalWords: utf html bdd IPython ipynb io randaut accparse acc
|
||||||
# LocalWords: cond randltl genltl genaut scc testingaut ltsmin dve
|
# LocalWords: cond randltl genltl genaut scc testingaut ltsmin dve
|
||||||
|
|
|
||||||
|
|
@ -436,14 +436,81 @@ class zielonka_tree:
|
||||||
self.dot(ostr)
|
self.dot(ostr)
|
||||||
return _ostream_to_svg(ostr)
|
return _ostream_to_svg(ostr)
|
||||||
|
|
||||||
|
_acdnum = 0
|
||||||
|
|
||||||
@_extend(acd)
|
@_extend(acd)
|
||||||
class acd:
|
class acd:
|
||||||
def _repr_svg_(self):
|
def _repr_svg_(self, id=None):
|
||||||
"""Output the ACD as SVG"""
|
"""Output the ACD as SVG"""
|
||||||
ostr = ostringstream()
|
ostr = ostringstream()
|
||||||
self.dot(ostr)
|
self.dot(ostr, id)
|
||||||
return _ostream_to_svg(ostr)
|
return _ostream_to_svg(ostr)
|
||||||
|
|
||||||
|
def _repr_html_(self):
|
||||||
|
global _acdnum
|
||||||
|
num = _acdnum
|
||||||
|
_acdnum += 1
|
||||||
|
style = '''
|
||||||
|
.acdhigh ellipse,.acdacc ellipse,.acdacc path,.acdacc polygon{stroke:green;}
|
||||||
|
.acdhigh polygon,.acdrej ellipse,.acdrej path,.acdrej polygon{stroke:red;}
|
||||||
|
.acdbold ellipse,.acdbold polygon,.acdbold path{stroke-width:2;}
|
||||||
|
.acdrej polygon{fill:red;}
|
||||||
|
.acdacc polygon{fill:green;}
|
||||||
|
'''
|
||||||
|
js = '''
|
||||||
|
function acd{num}_clear(){{
|
||||||
|
$("#acd{num} .node,#acdaut{num} .node,#acdaut{num} .edge")
|
||||||
|
.removeClass("acdhigh acdbold acdacc acdrej");
|
||||||
|
}};
|
||||||
|
function acd{num}_state(state){{
|
||||||
|
acd{num}_clear();
|
||||||
|
$("#acd{num} .acdS" + state).addClass("acdhigh acdbold");
|
||||||
|
$("#acdaut{num} #S" + state).addClass("acdbold");
|
||||||
|
}};
|
||||||
|
function acd{num}_edge(edge){{
|
||||||
|
acd{num}_clear();
|
||||||
|
var theedge = $('#acdaut{num} #E' + edge)
|
||||||
|
var classList = theedge.attr('class').split(/\s+/);
|
||||||
|
$.each(classList, function(index, item) {{
|
||||||
|
if (item.startsWith('acdN')) {{
|
||||||
|
$("#acd{num} #" + item.substring(3)).addClass("acdhigh acdbold");
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
theedge.addClass("acdbold");
|
||||||
|
}};
|
||||||
|
function acd{num}_node(node, acc){{
|
||||||
|
acd{num}_clear();
|
||||||
|
$("#acdaut{num} .acdN" + node).addClass(acc
|
||||||
|
? "acdacc acdbold"
|
||||||
|
: "acdrej acdbold");
|
||||||
|
$("#acd{num} #N" + node).addClass("acdbold acdhigh");
|
||||||
|
}};'''.format(num=num)
|
||||||
|
me = 0
|
||||||
|
for n in range(self.node_count()):
|
||||||
|
for e in self.edges_of_node(n):
|
||||||
|
me = max(e, me)
|
||||||
|
js += '$("#acdaut{num} #E{e}").addClass("acdN{n}");'\
|
||||||
|
.format(num=num, e=e, n=n)
|
||||||
|
for e in range(1, me + 1):
|
||||||
|
js += '$("#acdaut{num} #E{e}")'\
|
||||||
|
'.click(function(){{acd{num}_edge({e});}});'\
|
||||||
|
.format(num=num, e=e)
|
||||||
|
for s in range(self.get_aut().num_states()):
|
||||||
|
js += '$("#acdaut{num} #S{s}")'\
|
||||||
|
'.click(function(){{acd{num}_state({s});}});'\
|
||||||
|
.format(num=num, s=s)
|
||||||
|
for n in range(self.node_count()):
|
||||||
|
v = int(self.node_acceptance(n))
|
||||||
|
js += '$("#acd{num} #N{n}")'\
|
||||||
|
'.click(function(){{acd{num}_node({n}, {v});}});'\
|
||||||
|
.format(num=num, n=n, v=v)
|
||||||
|
html = '<style>{}</style><div>{}</div><div>{}</div><script>{}</script>'\
|
||||||
|
.format(style,
|
||||||
|
self.get_aut().show('.i(acdaut{})'.format(num)).data,
|
||||||
|
self._repr_svg_("acd{}".format(num)),
|
||||||
|
js);
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
def automata(*sources, timeout=None, ignore_abort=True,
|
def automata(*sources, timeout=None, ignore_abort=True,
|
||||||
trust_hoa=True, no_sid=False, debug=False,
|
trust_hoa=True, no_sid=False, debug=False,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <spot/twaalgos/zlktree.hh>
|
#include <spot/twaalgos/zlktree.hh>
|
||||||
#include <spot/twaalgos/genem.hh>
|
#include <spot/twaalgos/genem.hh>
|
||||||
|
#include <spot/misc/escape.hh>
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
@ -138,6 +139,9 @@ namespace spot
|
||||||
has_streett_shape_ = false;
|
has_streett_shape_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool empty_is_accepting = code.accepting(acc_cond::mark_t{});
|
||||||
|
empty_is_even_ = empty_is_accepting == is_even_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void zielonka_tree::dot(std::ostream& os) const
|
void zielonka_tree::dot(std::ostream& os) const
|
||||||
|
|
@ -175,8 +179,10 @@ namespace spot
|
||||||
("zielonka_tree::step(): incorrect branch number");
|
("zielonka_tree::step(): incorrect branch number");
|
||||||
|
|
||||||
if (!colors)
|
if (!colors)
|
||||||
return {branch, nodes_[branch].level + 1};
|
{
|
||||||
|
unsigned lvl = nodes_[branch].level;
|
||||||
|
return {branch, lvl + ((lvl & 1) == empty_is_even_)};
|
||||||
|
}
|
||||||
unsigned child = 0;
|
unsigned child = 0;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
|
@ -325,17 +331,6 @@ namespace spot
|
||||||
+ " is too large");
|
+ " is too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<unsigned, unsigned>
|
|
||||||
acd::step(unsigned branch, unsigned edge) const
|
|
||||||
{
|
|
||||||
if (SPOT_UNLIKELY(nodes_.size() < branch || nodes_[branch].first_child))
|
|
||||||
throw std::runtime_error
|
|
||||||
("acd::next_branch(): incorrect branch number");
|
|
||||||
// FIXME
|
|
||||||
(void)edge;
|
|
||||||
return {branch, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
acd::acd(const const_twa_graph_ptr& aut)
|
acd::acd(const const_twa_graph_ptr& aut)
|
||||||
: si_(new scc_info(aut)), own_si_(true), trees_(si_->scc_count())
|
: si_(new scc_info(aut)), own_si_(true), trees_(si_->scc_count())
|
||||||
{
|
{
|
||||||
|
|
@ -485,7 +480,7 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned acd::leftmost_branch_(unsigned n, unsigned state)
|
unsigned acd::leftmost_branch_(unsigned n, unsigned state) const
|
||||||
{
|
{
|
||||||
loop:
|
loop:
|
||||||
unsigned first_child = nodes_[n].first_child;
|
unsigned first_child = nodes_[n].first_child;
|
||||||
|
|
@ -507,42 +502,45 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned acd::first_branch(unsigned s, unsigned scc)
|
unsigned acd::first_branch(unsigned s) const
|
||||||
{
|
{
|
||||||
if (scc > trees_.size())
|
if (SPOT_UNLIKELY(aut_->num_states() < s))
|
||||||
report_invalid_scc_number(scc, "first_branch");
|
throw std::runtime_error("first_branch(): unknown state " +
|
||||||
|
std::to_string(s));
|
||||||
|
unsigned scc = si_->scc_of(s);
|
||||||
if (trees_[scc].trivial) // the branch is irrelevant for transiant SCCs
|
if (trees_[scc].trivial) // the branch is irrelevant for transiant SCCs
|
||||||
return 0;
|
return 0;
|
||||||
unsigned n = trees_[scc].root;
|
unsigned n = trees_[scc].root;
|
||||||
if (SPOT_UNLIKELY(!nodes_[n].states[s]))
|
assert(nodes_[n].states[s]);
|
||||||
throw std::runtime_error("first_branch(): state " +
|
|
||||||
std::to_string(s) + " not found in SCC " +
|
|
||||||
std::to_string(scc));
|
|
||||||
return leftmost_branch_(n, s);
|
return leftmost_branch_(n, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<unsigned, unsigned>
|
std::pair<unsigned, unsigned>
|
||||||
acd::step(unsigned branch, unsigned edge)
|
acd::step(unsigned branch, unsigned edge) const
|
||||||
{
|
{
|
||||||
if (SPOT_UNLIKELY(nodes_.size() < branch))
|
if (SPOT_UNLIKELY(nodes_.size() < branch))
|
||||||
throw std::runtime_error("acd::step(): incorrect branch number");
|
throw std::runtime_error("acd::step(): incorrect branch number");
|
||||||
|
if (SPOT_UNLIKELY(nodes_[branch].edges.size() < edge))
|
||||||
|
throw std::runtime_error("acd::step(): incorrect edge number");
|
||||||
|
|
||||||
unsigned child = 0;
|
unsigned child = 0;
|
||||||
unsigned obranch = branch;
|
unsigned dst = aut_->edge_storage(edge).dst;
|
||||||
while (!nodes_[branch].edges[edge])
|
while (!nodes_[branch].edges[edge])
|
||||||
{
|
{
|
||||||
unsigned parent = nodes_[branch].parent;
|
unsigned parent = nodes_[branch].parent;
|
||||||
if (SPOT_UNLIKELY(branch == parent))
|
if (SPOT_UNLIKELY(branch == parent))
|
||||||
throw std::runtime_error("acd::step(): edge " +
|
{
|
||||||
std::to_string(edge) +
|
// We are changing SCC, so the level emitted does not
|
||||||
" is not on branch " +
|
// matter.
|
||||||
std::to_string(obranch));
|
assert(si_->scc_of(aut_->edge_storage(edge).src)
|
||||||
|
!= si_->scc_of(dst));
|
||||||
|
return { first_branch(dst), 0 };
|
||||||
|
}
|
||||||
child = branch;
|
child = branch;
|
||||||
branch = parent;
|
branch = parent;
|
||||||
}
|
}
|
||||||
unsigned lvl = nodes_[branch].level;
|
unsigned lvl = nodes_[branch].level;
|
||||||
unsigned dst = aut_->edge_storage(edge).dst;
|
|
||||||
if (child != 0)
|
if (child != 0)
|
||||||
{
|
{
|
||||||
unsigned start_child = child;
|
unsigned start_child = child;
|
||||||
|
|
@ -562,10 +560,14 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void acd::dot(std::ostream& os) const
|
void acd::dot(std::ostream& os, const char* id) const
|
||||||
{
|
{
|
||||||
os << "digraph acd {\n labelloc=\"t\"\n label=\"\n"
|
os << "digraph acd {\n labelloc=\"t\"\n label=\"\n"
|
||||||
<< (is_even_ ? "min even\"" : "min odd\"\n");
|
<< (is_even_ ? "min even\"" : "min odd\"\n");
|
||||||
|
if (id)
|
||||||
|
escape_str(os << " id=\"", id)
|
||||||
|
// fill the node so that the entire node is clickable
|
||||||
|
<< "\"\n node [id=\"N\\N\", style=filled, fillcolor=white]\n";
|
||||||
unsigned ns = nodes_.size();
|
unsigned ns = nodes_.size();
|
||||||
for (unsigned n = 0; n < ns; ++n)
|
for (unsigned n = 0; n < ns; ++n)
|
||||||
{
|
{
|
||||||
|
|
@ -642,9 +644,22 @@ namespace spot
|
||||||
os << "\nlvl: " << nodes_[n].level;
|
os << "\nlvl: " << nodes_[n].level;
|
||||||
if (!first_child)
|
if (!first_child)
|
||||||
os << "\n<" << n << '>';
|
os << "\n<" << n << '>';
|
||||||
|
// use a fillcolor so that the entire node is clickable
|
||||||
os << "\", shape="
|
os << "\", shape="
|
||||||
<< (((nodes_[n].level & 1) ^ is_even_) ? "ellipse" : "box")
|
<< (((nodes_[n].level & 1) ^ is_even_) ? "ellipse" : "box");
|
||||||
<< "]\n";
|
if (id)
|
||||||
|
{
|
||||||
|
os << " class=\"";
|
||||||
|
const char* sep = "";
|
||||||
|
for (unsigned n = 0; n < nstates; ++n)
|
||||||
|
if (states[n] && si_->scc_of(n) == scc)
|
||||||
|
{
|
||||||
|
os << sep << "acdS" << n << '\n';
|
||||||
|
sep = " ";
|
||||||
|
}
|
||||||
|
os << '\"';
|
||||||
|
}
|
||||||
|
os << "]\n";
|
||||||
if (first_child)
|
if (first_child)
|
||||||
{
|
{
|
||||||
unsigned child = first_child;
|
unsigned child = first_child;
|
||||||
|
|
@ -659,6 +674,27 @@ namespace spot
|
||||||
os << "}\n";
|
os << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool acd::node_acceptance(unsigned n) const
|
||||||
|
{
|
||||||
|
if (SPOT_UNLIKELY(nodes_.size() < n))
|
||||||
|
throw std::runtime_error("acd::node_acceptance(): unknown node");
|
||||||
|
return (nodes_[n].level & 1) ^ is_even_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned> acd::edges_of_node(unsigned n) const
|
||||||
|
{
|
||||||
|
if (SPOT_UNLIKELY(nodes_.size() < n))
|
||||||
|
throw std::runtime_error("acd::edges_of_node(): unknown node");
|
||||||
|
std::vector<unsigned> res;
|
||||||
|
unsigned scc = nodes_[n].scc;
|
||||||
|
auto& edges = nodes_[n].edges;
|
||||||
|
unsigned nedges = edges.size();
|
||||||
|
for (unsigned e = 1; e < nedges; ++e)
|
||||||
|
if (edges[e] && si_->scc_of(aut_->edge_storage(e).dst) == scc)
|
||||||
|
res.push_back(e);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
twa_graph_ptr
|
twa_graph_ptr
|
||||||
acd_transform(const const_twa_graph_ptr& a, bool colored)
|
acd_transform(const const_twa_graph_ptr& a, bool colored)
|
||||||
{
|
{
|
||||||
|
|
@ -714,7 +750,7 @@ namespace spot
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned init = a->get_init_state_number();
|
unsigned init = a->get_init_state_number();
|
||||||
zlk_state s(init, theacd.first_branch(init, si.scc_of(init)));
|
zlk_state s(init, theacd.first_branch(init));
|
||||||
new_state(s);
|
new_state(s);
|
||||||
unsigned max_color = 0;
|
unsigned max_color = 0;
|
||||||
bool is_even = theacd.is_even();
|
bool is_even = theacd.is_even();
|
||||||
|
|
@ -734,7 +770,7 @@ namespace spot
|
||||||
unsigned dst_scc = si.scc_of(i.dst);
|
unsigned dst_scc = si.scc_of(i.dst);
|
||||||
if (dst_scc != src_scc)
|
if (dst_scc != src_scc)
|
||||||
{
|
{
|
||||||
newbranch = theacd.first_branch(i.dst, dst_scc);
|
newbranch = theacd.first_branch(i.dst);
|
||||||
prio = 0;
|
prio = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,12 @@ namespace spot
|
||||||
/// definition since it allows a set of colors to be processed,
|
/// definition since it allows a set of colors to be processed,
|
||||||
/// and not exactly one color. When multiple colors are given,
|
/// and not exactly one color. When multiple colors are given,
|
||||||
/// the minimum corresponding level is returned. When no color is
|
/// the minimum corresponding level is returned. When no color is
|
||||||
/// given, the branch is not changed and the level returned is one
|
/// given, the branch is not changed and the level returned might
|
||||||
/// more than the depth of that branch (this is as if the tree add
|
/// be one more than the depth of that branch (as if the tree had
|
||||||
/// another layer of leaves labeled by the empty sets, that do not
|
/// another layer of leaves labeled by the empty sets, that we
|
||||||
/// store for simplicity).
|
/// do not store). Whether the depth for no color is the depth
|
||||||
|
/// of the node or one more depend on whether the absence of
|
||||||
|
/// color had the same parity has the current node.
|
||||||
std::pair<unsigned, unsigned>
|
std::pair<unsigned, unsigned>
|
||||||
step(unsigned branch, acc_cond::mark_t colors) const;
|
step(unsigned branch, acc_cond::mark_t colors) const;
|
||||||
|
|
||||||
|
|
@ -127,6 +129,7 @@ namespace spot
|
||||||
unsigned one_branch_ = 0;
|
unsigned one_branch_ = 0;
|
||||||
unsigned num_branches_ = 0;
|
unsigned num_branches_ = 0;
|
||||||
bool is_even_;
|
bool is_even_;
|
||||||
|
bool empty_is_even_;
|
||||||
bool has_rabin_shape_ = true;
|
bool has_rabin_shape_ = true;
|
||||||
bool has_streett_shape_ = true;
|
bool has_streett_shape_ = true;
|
||||||
};
|
};
|
||||||
|
|
@ -164,19 +167,34 @@ namespace spot
|
||||||
|
|
||||||
~acd();
|
~acd();
|
||||||
|
|
||||||
/// \brief Walk through the ACD.
|
/// \brief Step through the ACD.
|
||||||
///
|
///
|
||||||
/// Given a \a branch number, and an edge, this returns
|
/// Given a \a branch number, and an edge, this returns
|
||||||
/// a pair (new branch, level), as needed in definition 4.6 of
|
/// a pair (new branch, level), as needed in definition 4.6 of
|
||||||
/// \cite casares.21.icalp (or definition 4.20 in the full version).
|
/// \cite casares.21.icalp (or definition 4.20 in the full version).
|
||||||
/// We do not have to specify any SCC, because the branch number are
|
|
||||||
/// different in each SCC.
|
|
||||||
///
|
///
|
||||||
/// The level correspond to the priority of a minimum parity acceptance
|
/// The level correspond to the priority of a minimum parity acceptance
|
||||||
/// condition, with the parity odd/even as specified by is_even().
|
/// condition, with the parity odd/even as specified by is_even().
|
||||||
std::pair<unsigned, unsigned>
|
std::pair<unsigned, unsigned>
|
||||||
step(unsigned branch, unsigned edge) const;
|
step(unsigned branch, unsigned edge) const;
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Return the list of edges covered by node n of the ACD.
|
||||||
|
///
|
||||||
|
/// This is mostly used for interactive display.
|
||||||
|
std::vector<unsigned> edges_of_node(unsigned n) const;
|
||||||
|
|
||||||
|
/// \brief Return the number of nodes in the the ACD forest.
|
||||||
|
unsigned node_count() const
|
||||||
|
{
|
||||||
|
return nodes_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tell whether a node store accepting or rejecting cycles.
|
||||||
|
///
|
||||||
|
/// This is mostly used for interactive display.
|
||||||
|
bool node_acceptance(unsigned n) const;
|
||||||
|
|
||||||
/// \brief Whether the ACD corresponds to a min even or min odd
|
/// \brief Whether the ACD corresponds to a min even or min odd
|
||||||
/// parity acceptance in SCC \a scc.
|
/// parity acceptance in SCC \a scc.
|
||||||
bool is_even(unsigned scc) const
|
bool is_even(unsigned scc) const
|
||||||
|
|
@ -198,20 +216,9 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Return the first branch for \a state
|
/// \brief Return the first branch for \a state
|
||||||
///
|
unsigned first_branch(unsigned state) const;
|
||||||
/// \a scc should correspond to the SCC containing \a state.
|
|
||||||
/// (this class does not store the scc_info passed at construction)
|
|
||||||
unsigned first_branch(unsigned state, unsigned scc);
|
|
||||||
|
|
||||||
/// \brief Step into the ACD
|
unsigned scc_max_level(unsigned scc) const
|
||||||
///
|
|
||||||
/// Given an edge \a edge on branch \a branch,
|
|
||||||
/// return a pair (new branch, level) giving the proirity (\a level) to
|
|
||||||
/// emit, and the branch of the destination state.
|
|
||||||
std::pair<unsigned, unsigned>
|
|
||||||
step(unsigned branch, unsigned edge);
|
|
||||||
|
|
||||||
unsigned scc_max_level(unsigned scc)
|
|
||||||
{
|
{
|
||||||
if (scc >= scc_count_)
|
if (scc >= scc_count_)
|
||||||
report_invalid_scc_number(scc, "scc_max_level");
|
report_invalid_scc_number(scc, "scc_max_level");
|
||||||
|
|
@ -244,8 +251,17 @@ namespace spot
|
||||||
return has_streett_shape() && has_rabin_shape();
|
return has_streett_shape() && has_rabin_shape();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Return the automaton on which the ACD is defined.
|
||||||
|
const const_twa_graph_ptr get_aut() const
|
||||||
|
{
|
||||||
|
return aut_;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Render the ACD as in GraphViz format.
|
/// \brief Render the ACD as in GraphViz format.
|
||||||
void dot(std::ostream&) const;
|
///
|
||||||
|
/// If \a id is given, it is used to give the graph an id, and,
|
||||||
|
/// all nodes will get ids as well.
|
||||||
|
void dot(std::ostream&, const char* id = nullptr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const scc_info* si_;
|
const scc_info* si_;
|
||||||
|
|
@ -312,7 +328,7 @@ namespace spot
|
||||||
void build_();
|
void build_();
|
||||||
|
|
||||||
// leftmost branch of \a node that contains \a state
|
// leftmost branch of \a node that contains \a state
|
||||||
unsigned leftmost_branch_(unsigned node, unsigned state);
|
unsigned leftmost_branch_(unsigned node, unsigned state) const;
|
||||||
|
|
||||||
#ifndef SWIG
|
#ifndef SWIG
|
||||||
[[noreturn]] static
|
[[noreturn]] static
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,7 @@ TESTS_python = \
|
||||||
python/twagraph.py \
|
python/twagraph.py \
|
||||||
python/toweak.py \
|
python/toweak.py \
|
||||||
python/_word.ipynb \
|
python/_word.ipynb \
|
||||||
python/_zlktree.ipynb \
|
python/zlktree.ipynb \
|
||||||
$(TESTS_ipython)
|
$(TESTS_ipython)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
6570
tests/python/zlktree.ipynb
Normal file
6570
tests/python/zlktree.ipynb
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue