scc_info: introduce edges_of() and inner_edges_of()
This is motivated by some upcoming patch by Heňo. * spot/twaalgos/sccinfo.hh (edges_of, inner_edges_of): New methods. * spot/twaalgos/sccinfo.cc, spot/twaalgos/strength.cc: Use them. * spot/twa/twagraph.hh (edge_number): Add an overload. * python/spot/impl.i: Bind the new methods. * tests/python/sccinfo.py: Add tests. * NEWS: Mention the changes.
This commit is contained in:
parent
e089509a0c
commit
8e19d3f47e
7 changed files with 265 additions and 30 deletions
3
NEWS
3
NEWS
|
|
@ -73,6 +73,9 @@ New in spot 2.3.3.dev (not yet released)
|
||||||
preprocess automata before feeding them to algorithms or tools
|
preprocess automata before feeding them to algorithms or tools
|
||||||
that expect transitions labeled by letters.
|
that expect transitions labeled by letters.
|
||||||
|
|
||||||
|
- spot::scc_info has two new methods to easily iterate over the
|
||||||
|
edges of an SCC: edges_of() and inner_edges_of().
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
- The 'spot.gen' package exports the functions from libspotgen.
|
- The 'spot.gen' package exports the functions from libspotgen.
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,18 @@ def state_is_accepting(self, src) -> "bool":
|
||||||
%include <spot/twaalgos/totgba.hh>
|
%include <spot/twaalgos/totgba.hh>
|
||||||
%traits_swigtype(spot::scc_info_node);
|
%traits_swigtype(spot::scc_info_node);
|
||||||
%fragment(SWIG_Traits_frag(spot::scc_info_node));
|
%fragment(SWIG_Traits_frag(spot::scc_info_node));
|
||||||
|
%nodefaultctor spot::internal::scc_edges;
|
||||||
|
%typemap(out, optimal="1") spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_all> {
|
||||||
|
$result = SWIG_NewPointerObj((new $1_ltype($1)), $&1_descriptor, SWIG_POINTER_OWN);
|
||||||
|
}
|
||||||
|
%typemap(out, optimal="1") spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_inner_scc> {
|
||||||
|
$result = SWIG_NewPointerObj((new $1_ltype($1)), $&1_descriptor, SWIG_POINTER_OWN);
|
||||||
|
}
|
||||||
|
%noexception spot::scc_info::edges_of;
|
||||||
|
%noexception spot::scc_info::inner_edges_of;
|
||||||
%include <spot/twaalgos/sccinfo.hh>
|
%include <spot/twaalgos/sccinfo.hh>
|
||||||
|
%template(scc_info_scc_edges) spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_all>;
|
||||||
|
%template(scc_info_inner_scc_edges) spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_inner_scc>;
|
||||||
%include <spot/twaalgos/strength.hh>
|
%include <spot/twaalgos/strength.hh>
|
||||||
%include <spot/twaalgos/sccfilter.hh>
|
%include <spot/twaalgos/sccfilter.hh>
|
||||||
%include <spot/twaalgos/stats.hh>
|
%include <spot/twaalgos/stats.hh>
|
||||||
|
|
@ -800,6 +811,22 @@ def state_is_accepting(self, src) -> "bool":
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%extend spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_all> {
|
||||||
|
swig::SwigPyIterator* __iter__(PyObject **PYTHON_SELF)
|
||||||
|
{
|
||||||
|
return swig::make_forward_iterator(self->begin(), self->begin(),
|
||||||
|
self->end(), *PYTHON_SELF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%extend spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_inner_scc> {
|
||||||
|
swig::SwigPyIterator* __iter__(PyObject **PYTHON_SELF)
|
||||||
|
{
|
||||||
|
return swig::make_forward_iterator(self->begin(), self->begin(),
|
||||||
|
self->end(), *PYTHON_SELF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
%extend spot::twa_graph {
|
%extend spot::twa_graph {
|
||||||
unsigned new_univ_edge(unsigned src, const std::vector<unsigned>& v,
|
unsigned new_univ_edge(unsigned src, const std::vector<unsigned>& v,
|
||||||
bdd cond, acc_cond::mark_t acc = 0U)
|
bdd cond, acc_cond::mark_t acc = 0U)
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,11 @@ namespace spot
|
||||||
return i->pos();
|
return i->pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned edge_number(const edge_storage_t& e) const
|
||||||
|
{
|
||||||
|
return g_.index_of_edge(e);
|
||||||
|
}
|
||||||
|
|
||||||
twa_graph_edge_data& edge_data(const twa_succ_iterator* it)
|
twa_graph_edge_data& edge_data(const twa_succ_iterator* it)
|
||||||
{
|
{
|
||||||
return g_.edge_data(edge_number(it));
|
return g_.edge_data(edge_number(it));
|
||||||
|
|
|
||||||
|
|
@ -294,20 +294,16 @@ namespace spot
|
||||||
std::set<acc_cond::mark_t> scc_info::used_acc_of(unsigned scc) const
|
std::set<acc_cond::mark_t> scc_info::used_acc_of(unsigned scc) const
|
||||||
{
|
{
|
||||||
std::set<acc_cond::mark_t> res;
|
std::set<acc_cond::mark_t> res;
|
||||||
for (auto src: states_of(scc))
|
for (auto& t: inner_edges_of(scc))
|
||||||
for (auto& t: aut_->out(src))
|
res.insert(t.acc);
|
||||||
if (scc_of(t.dst) == scc)
|
|
||||||
res.insert(t.acc);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
acc_cond::mark_t scc_info::acc_sets_of(unsigned scc) const
|
acc_cond::mark_t scc_info::acc_sets_of(unsigned scc) const
|
||||||
{
|
{
|
||||||
acc_cond::mark_t res = 0U;
|
acc_cond::mark_t res = 0U;
|
||||||
for (auto src: states_of(scc))
|
for (auto& t: inner_edges_of(scc))
|
||||||
for (auto& t: aut_->out(src))
|
res |= t.acc;
|
||||||
if (scc_of(t.dst) == scc)
|
|
||||||
res |= t.acc;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,9 +341,8 @@ namespace spot
|
||||||
bdd scc_info::scc_ap_support(unsigned scc) const
|
bdd scc_info::scc_ap_support(unsigned scc) const
|
||||||
{
|
{
|
||||||
bdd support = bddtrue;
|
bdd support = bddtrue;
|
||||||
for (auto s: states_of(scc))
|
for (auto& t: edges_of(scc))
|
||||||
for (auto& t: aut_->out(s))
|
support &= bdd_support(t.cond);
|
||||||
support &= bdd_support(t.cond);
|
|
||||||
return support;
|
return support;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,168 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
class scc_info;
|
||||||
|
|
||||||
|
namespace internal
|
||||||
|
{
|
||||||
|
struct keep_all
|
||||||
|
{
|
||||||
|
template <typename Edge>
|
||||||
|
bool operator()(const Edge&) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct keep_inner_scc
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const std::vector<unsigned>& sccof_;
|
||||||
|
unsigned desired_scc_;
|
||||||
|
public:
|
||||||
|
keep_inner_scc(const std::vector<unsigned>& sccof, unsigned desired_scc)
|
||||||
|
: sccof_(sccof), desired_scc_(desired_scc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Edge>
|
||||||
|
bool operator()(const Edge& ed) const noexcept
|
||||||
|
{
|
||||||
|
return sccof_[ed.dst] == desired_scc_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Graph, typename Filter>
|
||||||
|
class SPOT_API scc_edge_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename std::conditional<std::is_const<Graph>::value,
|
||||||
|
const typename Graph::edge_storage_t,
|
||||||
|
typename Graph::edge_storage_t>::type
|
||||||
|
value_type;
|
||||||
|
typedef value_type& reference;
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef std::forward_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
typedef std::vector<unsigned>::const_iterator state_iterator;
|
||||||
|
|
||||||
|
typedef typename std::conditional<std::is_const<Graph>::value,
|
||||||
|
const typename Graph::edge_vector_t,
|
||||||
|
typename Graph::edge_vector_t>::type
|
||||||
|
tv_t;
|
||||||
|
|
||||||
|
typedef typename std::conditional<std::is_const<Graph>::value,
|
||||||
|
const typename Graph::state_vector,
|
||||||
|
typename Graph::state_vector>::type
|
||||||
|
sv_t;
|
||||||
|
protected:
|
||||||
|
|
||||||
|
state_iterator pos_;
|
||||||
|
state_iterator end_;
|
||||||
|
unsigned t_;
|
||||||
|
tv_t* tv_;
|
||||||
|
sv_t* sv_;
|
||||||
|
Filter filt_;
|
||||||
|
|
||||||
|
void inc_state_maybe_()
|
||||||
|
{
|
||||||
|
while (!t_ && (++pos_ != end_))
|
||||||
|
t_ = (*sv_)[*pos_].succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inc_()
|
||||||
|
{
|
||||||
|
t_ = (*tv_)[t_].next_succ;
|
||||||
|
inc_state_maybe_();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
scc_edge_iterator(state_iterator begin, state_iterator end,
|
||||||
|
tv_t* tv, sv_t* sv, Filter filt) noexcept
|
||||||
|
: pos_(begin), end_(end), tv_(tv), sv_(sv), filt_(filt)
|
||||||
|
{
|
||||||
|
if (pos_ == end_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
t_ = (*sv_)[*pos_].succ;
|
||||||
|
inc_state_maybe_();
|
||||||
|
while (pos_ != end_ && !filt_(**this))
|
||||||
|
inc_();
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_edge_iterator& operator++()
|
||||||
|
{
|
||||||
|
do
|
||||||
|
inc_();
|
||||||
|
while (pos_ != end_ && !filt_(**this));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_edge_iterator operator++(int)
|
||||||
|
{
|
||||||
|
scc_edge_iterator old = *this;
|
||||||
|
++*this;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(scc_edge_iterator o) const
|
||||||
|
{
|
||||||
|
return pos_ == o.pos_ && t_ == o.t_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(scc_edge_iterator o) const
|
||||||
|
{
|
||||||
|
return pos_ != o.pos_ || t_ != o.t_;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return (*tv_)[t_];
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return &**this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Graph, typename Filter>
|
||||||
|
class SPOT_API scc_edges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef scc_edge_iterator<Graph, Filter> iter_t;
|
||||||
|
typedef typename iter_t::tv_t tv_t;
|
||||||
|
typedef typename iter_t::sv_t sv_t;
|
||||||
|
typedef typename iter_t::state_iterator state_iterator;
|
||||||
|
private:
|
||||||
|
state_iterator begin_;
|
||||||
|
state_iterator end_;
|
||||||
|
unsigned t_;
|
||||||
|
tv_t* tv_;
|
||||||
|
sv_t* sv_;
|
||||||
|
Filter filt_;
|
||||||
|
public:
|
||||||
|
|
||||||
|
scc_edges(state_iterator begin, state_iterator end,
|
||||||
|
tv_t* tv, sv_t* sv, Filter filt) noexcept
|
||||||
|
: begin_(begin), end_(end), tv_(tv), sv_(sv), filt_(filt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
iter_t begin() const
|
||||||
|
{
|
||||||
|
return {begin_, end_, tv_, sv_, filt_};
|
||||||
|
}
|
||||||
|
|
||||||
|
iter_t end() const
|
||||||
|
{
|
||||||
|
return {end_, end_, nullptr, nullptr, filt_};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Storage for SCC related information.
|
/// Storage for SCC related information.
|
||||||
class SPOT_API scc_info_node
|
class SPOT_API scc_info_node
|
||||||
|
|
@ -189,6 +351,37 @@ namespace spot
|
||||||
return node(scc).states();
|
return node(scc).states();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief A fake container to iterate over all edges leaving any
|
||||||
|
/// state of an SCC.
|
||||||
|
///
|
||||||
|
/// The difference with inner_edges_of() is that edges_of() include
|
||||||
|
/// outgoing edges from all the states, even if they leave the SCC.
|
||||||
|
internal::scc_edges<const twa_graph::graph_t, internal::keep_all>
|
||||||
|
edges_of(unsigned scc) const
|
||||||
|
{
|
||||||
|
auto& states = states_of(scc);
|
||||||
|
return {states.begin(), states.end(),
|
||||||
|
&aut_->edge_vector(), &aut_->states(),
|
||||||
|
internal::keep_all()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief A fake container to iterate over all edges between
|
||||||
|
/// states of an SCC.
|
||||||
|
///
|
||||||
|
/// The difference with edges_of() is that inner_edges_of() ignores
|
||||||
|
/// edges leaving the SCC are ignored.
|
||||||
|
internal::scc_edges<const twa_graph::graph_t, internal::keep_inner_scc>
|
||||||
|
inner_edges_of(unsigned scc) const
|
||||||
|
{
|
||||||
|
if (!aut_->is_existential())
|
||||||
|
throw std::runtime_error
|
||||||
|
("inner_edges_of(): alternating automata are not supported");
|
||||||
|
auto& states = states_of(scc);
|
||||||
|
return {states.begin(), states.end(),
|
||||||
|
&aut_->edge_vector(), &aut_->states(),
|
||||||
|
internal::keep_inner_scc(sccof_, scc)};
|
||||||
|
}
|
||||||
|
|
||||||
unsigned one_state_of(unsigned scc) const
|
unsigned one_state_of(unsigned scc) const
|
||||||
{
|
{
|
||||||
return states_of(scc).front();
|
return states_of(scc).front();
|
||||||
|
|
|
||||||
|
|
@ -51,25 +51,24 @@ namespace spot
|
||||||
bool first = true;
|
bool first = true;
|
||||||
acc_cond::mark_t m = 0U;
|
acc_cond::mark_t m = 0U;
|
||||||
if (is_weak)
|
if (is_weak)
|
||||||
for (auto src: si->states_of(i))
|
for (auto& t: si->edges_of(i))
|
||||||
for (auto& t: aut->out(src))
|
// In case of a universal edge we only need to check if
|
||||||
// In case of a universal edge we only need to check
|
// the first destination of an edge is inside the SCC,
|
||||||
// the first destination of an inside the SCC, because
|
// because the others have the same t.acc.
|
||||||
// the other have the same t.acc.
|
if (si->scc_of(*aut->univ_dests(t.dst).begin()) == i)
|
||||||
if (si->scc_of(*aut->univ_dests(t.dst).begin()) == i)
|
{
|
||||||
{
|
if (first)
|
||||||
if (first)
|
{
|
||||||
{
|
first = false;
|
||||||
first = false;
|
m = t.acc;
|
||||||
m = t.acc;
|
}
|
||||||
}
|
else if (m != t.acc)
|
||||||
else if (m != t.acc)
|
{
|
||||||
{
|
is_weak = false;
|
||||||
is_weak = false;
|
if (!inweak)
|
||||||
if (!inweak)
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!is_weak && si->is_accepting_scc(i))
|
if (!is_weak && si->is_accepting_scc(i))
|
||||||
{
|
{
|
||||||
assert(inweak);
|
assert(inweak);
|
||||||
|
|
|
||||||
|
|
@ -52,15 +52,28 @@ l3 = si.states_of(3)
|
||||||
l = sorted(list(l0) + list(l1) + list(l2) + list(l3))
|
l = sorted(list(l0) + list(l1) + list(l2) + list(l3))
|
||||||
assert l == [0, 1, 2, 3, 4]
|
assert l == [0, 1, 2, 3, 4]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
i = si.initial()
|
i = si.initial()
|
||||||
todo = {i}
|
todo = {i}
|
||||||
seen = {i}
|
seen = {i}
|
||||||
|
trans = []
|
||||||
|
transi = []
|
||||||
while todo:
|
while todo:
|
||||||
e = todo.pop()
|
e = todo.pop()
|
||||||
|
for t in si.edges_of(e):
|
||||||
|
trans.append((t.src, t.dst))
|
||||||
|
for t in si.inner_edges_of(e):
|
||||||
|
transi.append((t.src, t.dst, a.edge_number(t)))
|
||||||
for s in si.succ(e):
|
for s in si.succ(e):
|
||||||
if s not in seen:
|
if s not in seen:
|
||||||
seen.add(s)
|
seen.add(s)
|
||||||
todo.add(s)
|
todo.add(s)
|
||||||
assert seen == {0, 1, 2, 3}
|
assert seen == {0, 1, 2, 3}
|
||||||
|
assert trans == [(1, 0), (1, 1), (1, 2), (1, 3),
|
||||||
|
(2, 1), (2, 2), (2, 3), (2, 4),
|
||||||
|
(0, 0), (3, 3), (4, 3), (4, 4)]
|
||||||
|
assert transi == [(1, 1, 3), (1, 2, 4), (2, 1, 6), (2, 2, 7),
|
||||||
|
(0, 0, 1), (3, 3, 10), (4, 4, 12)]
|
||||||
|
|
||||||
assert not spot.is_weak_automaton(a, si)
|
assert not spot.is_weak_automaton(a, si)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue