tgbagraph: add a merge_transitions() method.

* src/graph/graph.hh: Add some framework to erase transitions, and
defrag the resulting transitions_ vector on demand.  Also remove
the nb_states() and nb_transitions() because num_states() and
num_transitions() already exist.
* src/graphtest/graph.cc, src/graphtest/ngraph.cc: Adjust to
use num_states().
* src/tgba/tgbagraph.hh (merge_transitions): New method.
* src/misc/hash.hh: Add a pair_hash class, needed by
merge_transitions().
* src/graphtest/tgbagraph.cc, src/graphtest/tgbagraph.test: Add states
for transitions removal and merge_transitions().
This commit is contained in:
Alexandre Duret-Lutz 2014-05-21 19:16:50 +02:00
parent 9909699c63
commit 424de90385
7 changed files with 286 additions and 10 deletions

View file

@ -239,11 +239,80 @@ namespace spot
return t_;
}
transition trans() const
{
return t_;
}
protected:
Graph* g_;
transition t_;
};
template <typename Graph>
class SPOT_API killer_trans_iterator: public trans_iterator<Graph>
{
typedef trans_iterator<Graph> super;
public:
typedef typename Graph::state_storage_t state_storage_t;
typedef typename Graph::transition transition;
killer_trans_iterator(Graph* g, transition t, state_storage_t& src):
super(g, t), src_(src), prev_(0)
{
}
killer_trans_iterator operator++()
{
prev_ = this->t_;
this->t_ = this->operator*().next_succ;
return *this;
}
killer_trans_iterator operator++(int)
{
killer_trans_iterator ti = *this;
prev_ = this->t_;
this->t_ = this->operator*().next_succ;
return ti;
}
// Erase the current transition and advance the iterator.
void erase()
{
transition next = this->operator*().next_succ;
// Update source state and previous transitions
if (prev_)
{
this->g_->trans_storage(prev_).next_succ = next;
}
else
{
if (src_.succ == this->t_)
src_.succ = next;
}
if (src_.succ_tail == this->t_)
{
src_.succ_tail = prev_;
assert(next == 0);
}
// Erased transitions have themselves as next_succ.
this->operator*().next_succ = this->t_;
// Advance iterator to next transitions.
this->t_ = next;
++this->g_->killed_trans_;
}
protected:
state_storage_t& src_;
transition prev_;
};
//////////////////////////////////////////////////
// State OUT
//////////////////////////////////////////////////
@ -289,6 +358,7 @@ namespace spot
{
friend class internal::trans_iterator<digraph>;
friend class internal::trans_iterator<const digraph>;
friend class internal::killer_trans_iterator<digraph>;
public:
typedef internal::trans_iterator<digraph> iterator;
@ -325,7 +395,8 @@ namespace spot
protected:
state_vector states_;
trans_vector transitions_;
// Number of erased transitions.
unsigned killed_trans_;
public:
/// \brief construct an empty graph
///
@ -334,6 +405,7 @@ namespace spot
/// limits, but just hints to pre-allocate a data structure that
/// may hold that much items.
digraph(unsigned max_states = 10, unsigned max_trans = 0)
: killed_trans_(0)
{
states_.reserve(max_states);
if (max_trans == 0)
@ -351,7 +423,15 @@ namespace spot
unsigned num_transitions() const
{
return transitions_.size();
return transitions_.size() - killed_trans_ - 1;
}
bool valid_trans(transition t) const
{
// Erased transitions have their next_succ pointing to
// themselves.
return (t < transitions_.size() &&
transitions_[t].next_succ != t);
}
template <typename... Args>
@ -449,26 +529,52 @@ namespace spot
return t;
}
state index_of_state(state_storage_t& ss)
{
assert(!states_.empty());
return &ss - &states_.front();
}
transition index_of_transition(trans_storage_t& tt)
{
assert(!transitions_.empty());
return &tt - &transitions_.front();
}
internal::state_out<digraph>
out(state src)
{
return {this, states_[src].succ};
}
internal::state_out<digraph>
out(state_storage_t& src)
{
return out(index_of_state(src));
}
internal::state_out<const digraph>
out(state src) const
{
return {this, states_[src].succ};
}
unsigned nb_states() const
internal::state_out<const digraph>
out(state_storage_t& src) const
{
return states_.size();
return out(index_of_state(src));
}
unsigned nb_trans() const
internal::killer_trans_iterator<digraph>
out_iteraser(state_storage_t& src)
{
return transitions_.size();
return {this, src.succ, src};
}
internal::killer_trans_iterator<digraph>
out_iteraser(state src)
{
return out_iteraser(state_storage(src));
}
const state_vector& states() const
@ -481,6 +587,42 @@ namespace spot
return states_;
}
void defrag()
{
if (killed_trans_ == 0) // Nothing to do.
return;
// Shift all transitions in transitions_. The algorithm is
// similar to remove_if, but it also keeps the correspondence
// between the old and new index as newidx[old] = new.
unsigned tend = transitions_.size();
std::vector<transition> newidx(tend);
unsigned dest = 1;
for (transition t = 1; t < tend; ++t)
{
if (transitions_[t].next_succ == t)
continue;
if (t != dest)
transitions_[dest] = std::move(transitions_[t]);
newidx[t] = dest;
++dest;
}
transitions_.resize(dest);
killed_trans_ = 0;
// Adjust next_succ pointers in all transitions.
for (transition t = 1; t < dest; ++t)
transitions_[t].next_succ = newidx[transitions_[t].next_succ];
// Adjust succ and succ_tails pointers in all states.
for (auto& s: states_)
{
s.succ = newidx[s.succ];
s.succ_tail = newidx[s.succ_tail];
}
}
};
}