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:
Alexandre Duret-Lutz 2017-05-09 22:12:42 +02:00
parent e089509a0c
commit 8e19d3f47e
7 changed files with 265 additions and 30 deletions

3
NEWS
View file

@ -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.

View file

@ -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)

View file

@ -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));

View file

@ -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;
} }

View file

@ -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();

View file

@ -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);

View file

@ -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)