Commit 8e19d3f4 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

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.
parent e089509a
......@@ -73,6 +73,9 @@ New in spot 2.3.3.dev (not yet released)
preprocess automata before feeding them to algorithms or tools
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:
- 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>
%traits_swigtype(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>
%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/sccfilter.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 {
unsigned new_univ_edge(unsigned src, const std::vector<unsigned>& v,
bdd cond, acc_cond::mark_t acc = 0U)
......
......@@ -349,6 +349,11 @@ namespace spot
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)
{
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> res;
for (auto src: states_of(scc))
for (auto& t: aut_->out(src))
if (scc_of(t.dst) == scc)
res.insert(t.acc);
for (auto& t: inner_edges_of(scc))
res.insert(t.acc);
return res;
}
acc_cond::mark_t scc_info::acc_sets_of(unsigned scc) const
{
acc_cond::mark_t res = 0U;
for (auto src: states_of(scc))
for (auto& t: aut_->out(src))
if (scc_of(t.dst) == scc)
res |= t.acc;
for (auto& t: inner_edges_of(scc))
res |= t.acc;
return res;
}
......@@ -345,9 +341,8 @@ namespace spot
bdd scc_info::scc_ap_support(unsigned scc) const
{
bdd support = bddtrue;
for (auto s: states_of(scc))
for (auto& t: aut_->out(s))
support &= bdd_support(t.cond);
for (auto& t: edges_of(scc))
support &= bdd_support(t.cond);
return support;
}
......
......@@ -24,6 +24,168 @@
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.
class SPOT_API scc_info_node
......@@ -189,6 +351,37 @@ namespace spot
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
{
return states_of(scc).front();
......
......@@ -51,25 +51,24 @@ namespace spot
bool first = true;
acc_cond::mark_t m = 0U;
if (is_weak)
for (auto src: si->states_of(i))
for (auto& t: aut->out(src))
// In case of a universal edge we only need to check
// the first destination of an inside the SCC, because
// the other have the same t.acc.
if (si->scc_of(*aut->univ_dests(t.dst).begin()) == i)
{
if (first)
{
first = false;
m = t.acc;
}
else if (m != t.acc)
{
is_weak = false;
if (!inweak)
goto exit;
}
}
for (auto& t: si->edges_of(i))
// In case of a universal edge we only need to check if
// the first destination of an edge is inside the SCC,
// because the others have the same t.acc.
if (si->scc_of(*aut->univ_dests(t.dst).begin()) == i)
{
if (first)
{
first = false;
m = t.acc;
}
else if (m != t.acc)
{
is_weak = false;
if (!inweak)
goto exit;
}
}
if (!is_weak && si->is_accepting_scc(i))
{
assert(inweak);
......
......@@ -52,15 +52,28 @@ l3 = si.states_of(3)
l = sorted(list(l0) + list(l1) + list(l2) + list(l3))
assert l == [0, 1, 2, 3, 4]
i = si.initial()
todo = {i}
seen = {i}
trans = []
transi = []
while todo:
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):
if s not in seen:
seen.add(s)
todo.add(s)
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)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment