* tgbaalgos/emptinesscheck.hh (numbered_state_heap_const_iterator,

numbered_state_heap, numbered_state_heap_hash_map): New classes.
* tgbaalgos/emptinesscheck.cc
(numbered_state_heap_hash_map_const_iterator): New class.
(numbered_state_heap_hash_map): Implement it.
This commit is contained in:
Alexandre Duret-Lutz 2004-04-13 15:32:10 +00:00
parent 7305dbb658
commit d8f5bf608a
3 changed files with 211 additions and 55 deletions

View file

@ -1,5 +1,11 @@
2004-04-13 Alexandre Duret-Lutz <adl@src.lip6.fr> 2004-04-13 Alexandre Duret-Lutz <adl@src.lip6.fr>
* tgbaalgos/emptinesscheck.hh (numbered_state_heap_const_iterator,
numbered_state_heap, numbered_state_heap_hash_map): New classes.
* tgbaalgos/emptinesscheck.cc
(numbered_state_heap_hash_map_const_iterator): New class.
(numbered_state_heap_hash_map): Implement it.
* src/tgbaalgos/emptinesscheck.hh * src/tgbaalgos/emptinesscheck.hh
(explicit_connected_component_factory, (explicit_connected_component_factory,
connected_component_hash_set_factory): New classes. connected_component_hash_set_factory): New classes.

View file

@ -74,6 +74,71 @@ namespace spot
} }
emptiness_check_status::~emptiness_check_status() emptiness_check_status::~emptiness_check_status()
{
}
void
emptiness_check_status::print_stats(std::ostream& os) const
{
os << h.size() << " unique states visited" << std::endl;
os << root.size()
<< " strongly connected components in search stack"
<< std::endl;
}
//////////////////////////////////////////////////////////////////////
class numbered_state_heap_hash_map_const_iterator :
public numbered_state_heap_const_iterator
{
public:
numbered_state_heap_hash_map_const_iterator
(const numbered_state_heap_hash_map::hash_type& h)
: numbered_state_heap_const_iterator(), h(h)
{
}
~numbered_state_heap_hash_map_const_iterator()
{
}
virtual void
first()
{
i = h.begin();
}
virtual void
next()
{
++i;
}
virtual bool
done() const
{
return i == h.end();
}
virtual const state*
get_state() const
{
return i->first;
}
virtual int
get_index() const
{
return i->second;
}
private:
numbered_state_heap_hash_map::hash_type::const_iterator i;
const numbered_state_heap_hash_map::hash_type& h;
};
numbered_state_heap_hash_map::~numbered_state_heap_hash_map()
{ {
// Free keys in H. // Free keys in H.
hash_type::iterator i = h.begin(); hash_type::iterator i = h.begin();
@ -86,8 +151,44 @@ namespace spot
} }
} }
const int*
numbered_state_heap_hash_map::find(const state* s) const
{
hash_type::const_iterator i = h.find(s);
if (i == h.end())
return 0;
return &i->second;
}
int*
numbered_state_heap_hash_map::find(const state* s)
{
hash_type::iterator i = h.find(s);
if (i == h.end())
return 0;
return &i->second;
}
void
numbered_state_heap_hash_map::insert(const state* s, int index)
{
h[s] = index;
}
int
numbered_state_heap_hash_map::size() const
{
return h.size();
}
numbered_state_heap_const_iterator*
numbered_state_heap_hash_map::iterator() const
{
return new numbered_state_heap_hash_map_const_iterator(h);
}
const state* const state*
emptiness_check_status::h_filt(const state* s) const numbered_state_heap_hash_map::filter(const state* s) const
{ {
hash_type::const_iterator i = h.find(s); hash_type::const_iterator i = h.find(s);
assert(i != h.end()); assert(i != h.end());
@ -96,14 +197,6 @@ namespace spot
return i->first; return i->first;
} }
void
emptiness_check_status::print_stats(std::ostream& os) const
{
os << h.size() << " unique states visited" << std::endl;
os << root.size()
<< " strongly connected components in search stack"
<< std::endl;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -133,9 +226,10 @@ namespace spot
// (FROM should be in H, otherwise it means all reachable // (FROM should be in H, otherwise it means all reachable
// states from FROM have already been removed and there is no // states from FROM have already been removed and there is no
// point in calling remove_component.) // point in calling remove_component.)
emptiness_check_status::hash_type::iterator hi = ecs_->h.find(from); int* hi = ecs_->h.find(from);
assert(hi->second != -1); assert(hi);
hi->second = -1; assert(*hi != -1);
*hi = -1;
tgba_succ_iterator* i = ecs_->aut->succ_iter(from); tgba_succ_iterator* i = ecs_->aut->succ_iter(from);
for (;;) for (;;)
@ -144,12 +238,12 @@ namespace spot
for (i->first(); !i->done(); i->next()) for (i->first(); !i->done(); i->next())
{ {
state* s = i->current_state(); state* s = i->current_state();
emptiness_check_status::hash_type::iterator hi = ecs_->h.find(s); int *hi = ecs_->h.find(s);
assert(hi != ecs_->h.end()); assert(hi);
if (hi->second != -1) if (*hi != -1)
{ {
hi->second = -1; *hi = -1;
to_remove.push(ecs_->aut->succ_iter(s)); to_remove.push(ecs_->aut->succ_iter(s));
} }
delete s; delete s;
@ -184,7 +278,7 @@ namespace spot
// Setup depth-first search from the initial state. // Setup depth-first search from the initial state.
{ {
state* init = ecs_->aut->get_init_state(); state* init = ecs_->aut->get_init_state();
ecs_->h[init] = 1; ecs_->h.insert(init, 1);
ecs_->root.push(1); ecs_->root.push(1);
arc.push(bddfalse); arc.push(bddfalse);
tgba_succ_iterator* iter = ecs_->aut->succ_iter(init); tgba_succ_iterator* iter = ecs_->aut->succ_iter(init);
@ -211,10 +305,10 @@ namespace spot
// When backtracking the root of an SCC, we must also // When backtracking the root of an SCC, we must also
// remove that SCC from the ARC/ROOT stacks. We must // remove that SCC from the ARC/ROOT stacks. We must
// discard from H all reachable states from this SCC. // discard from H all reachable states from this SCC.
emptiness_check_status::hash_type::iterator i = ecs_->h.find(curr); int* i = ecs_->h.find(curr);
assert(i != ecs_->h.end()); assert(i);
assert(!ecs_->root.empty()); assert(!ecs_->root.empty());
if (ecs_->root.top().index == i->second) if (ecs_->root.top().index == *i)
{ {
assert(!arc.empty()); assert(!arc.empty());
arc.pop(); arc.pop();
@ -238,12 +332,12 @@ namespace spot
// We do not need SUCC from now on. // We do not need SUCC from now on.
// Are we going to a new state? // Are we going to a new state?
emptiness_check_status::hash_type::iterator i = ecs_->h.find(dest); int* i = ecs_->h.find(dest);
if (i == ecs_->h.end()) if (!i)
{ {
// Yes. Number it, stack it, and register its successors // Yes. Number it, stack it, and register its successors
// for later processing. // for later processing.
ecs_->h[dest] = ++num; ecs_->h.insert(dest, ++num);
ecs_->root.push(num); ecs_->root.push(num);
arc.push(acc); arc.push(acc);
tgba_succ_iterator* iter = ecs_->aut->succ_iter(dest); tgba_succ_iterator* iter = ecs_->aut->succ_iter(dest);
@ -255,11 +349,10 @@ namespace spot
// We know the state exists. Since a state can have several // We know the state exists. Since a state can have several
// representations (i.e., objects), make sure we delete // representations (i.e., objects), make sure we delete
// anything but the first one seen (the one used as key in H). // anything but the first one seen (the one used as key in H).
if (dest != i->first) (void) ecs_->h.filter(dest);
delete dest;
// If we have reached a dead component, ignore it. // If we have reached a dead component, ignore it.
if (i->second == -1) if (*i == -1)
continue; continue;
// Now this is the most interesting case. We have reached a // Now this is the most interesting case. We have reached a
@ -273,7 +366,7 @@ namespace spot
// ROOT is ascending: we just have to merge all SCCs from the // ROOT is ascending: we just have to merge all SCCs from the
// top of ROOT that have an index greater to the one of // top of ROOT that have an index greater to the one of
// the SCC of S2 (called the "threshold"). // the SCC of S2 (called the "threshold").
int threshold = i->second; int threshold = *i;
while (threshold < ecs_->root.top().index) while (threshold < ecs_->root.top().index)
{ {
assert(!ecs_->root.empty()); assert(!ecs_->root.empty());
@ -366,8 +459,8 @@ namespace spot
succ_queue::iterator q = queue.begin(); succ_queue::iterator q = queue.begin();
while (q != queue.end()) while (q != queue.end())
{ {
emptiness_check_status::hash_type::iterator i = ecs_->h.find(q->s); int* i = ecs_->h.find(q->s);
if (i == ecs_->h.end()) if (!i)
{ {
// Skip unknown states. // Skip unknown states.
++q; ++q;
@ -375,7 +468,7 @@ namespace spot
} }
// Skip states from dead SCCs. // Skip states from dead SCCs.
if (i->second != -1) if (*i != -1)
{ {
// Now this is the most interesting case. We have // Now this is the most interesting case. We have
// reached a state S1 which is already part of a // reached a state S1 which is already part of a
@ -391,7 +484,7 @@ namespace spot
// merge all SCCs from the top of ROOT that have // merge all SCCs from the top of ROOT that have
// an index greater to the one of the SCC of S2 // an index greater to the one of the SCC of S2
// (called the "threshold"). // (called the "threshold").
int threshold = i->second; int threshold = *i;
bdd acc = q->acc; bdd acc = q->acc;
while (threshold < ecs_->root.top().index) while (threshold < ecs_->root.top().index)
{ {
@ -422,9 +515,13 @@ namespace spot
for (succ_queue::iterator q = queue.begin(); for (succ_queue::iterator q = queue.begin();
q != queue.end(); ++q) q != queue.end(); ++q)
{ {
emptiness_check_status::hash_type::iterator i = ecs_->h.find(q->s); int* i = ecs_->h.find(q->s);
if (i == ecs_->h.end() || i->first != q->s) if (!i)
delete q->s; delete q->s;
else
// Delete the state if it is a clone
// of a state in the heap.
(void) ecs_->h.filter(q->s);
} }
todo.pop(); todo.pop();
} }
@ -434,8 +531,7 @@ namespace spot
// We know the state exists. Since a state can have several // We know the state exists. Since a state can have several
// representations (i.e., objects), make sure we delete // representations (i.e., objects), make sure we delete
// anything but the first one seen (the one used as key in H). // anything but the first one seen (the one used as key in H).
if (q->s != i->first) (void) ecs_->h.filter(q->s);
delete q->s;
// Remove that state from the queue, so we do not // Remove that state from the queue, so we do not
// recurse into it. // recurse into it.
succ_queue::iterator old = q++; succ_queue::iterator old = q++;
@ -456,10 +552,10 @@ namespace spot
// When backtracking the root of an SCC, we must also // When backtracking the root of an SCC, we must also
// remove that SCC from the ARC/ROOT stacks. We must // remove that SCC from the ARC/ROOT stacks. We must
// discard from H all reachable states from this SCC. // discard from H all reachable states from this SCC.
emptiness_check_status::hash_type::iterator i = ecs_->h.find(curr); int* i = ecs_->h.find(curr);
assert(i != ecs_->h.end()); assert(i);
assert(!ecs_->root.empty()); assert(!ecs_->root.empty());
if (ecs_->root.top().index == i->second) if (ecs_->root.top().index == *i)
{ {
assert(!arc.empty()); assert(!arc.empty());
arc.pop(); arc.pop();
@ -476,7 +572,7 @@ namespace spot
// stacks. // stacks.
successor succ = queue.front(); successor succ = queue.front();
queue.pop_front(); queue.pop_front();
ecs_->h[succ.s] = ++num; ecs_->h.insert(succ.s, ++num);
ecs_->root.push(num); ecs_->root.push(num);
arc.push(succ.acc); arc.push(succ.acc);
todo.push(pair_state_successors(succ.s, succ_queue())); todo.push(pair_state_successors(succ.s, succ_queue()));
@ -556,10 +652,10 @@ namespace spot
assert(root.empty()); assert(root.empty());
// Build the set of states for all SCCs. // Build the set of states for all SCCs.
for (emptiness_check_status::hash_type::const_iterator i = ecs_->h.begin(); numbered_state_heap_const_iterator* i = ecs_->h.iterator();
i != ecs_->h.end(); ++i) for (i->first(); !i->done(); i->next())
{ {
int index = i->second; int index = i->get_index();
// Skip states from dead SCCs. // Skip states from dead SCCs.
if (index < 0) if (index < 0)
continue; continue;
@ -570,10 +666,11 @@ namespace spot
for (j = 1; j < comp_size; ++j) for (j = 1; j < comp_size; ++j)
if (index < scc[j]->index) if (index < scc[j]->index)
break; break;
scc[j - 1]->insert(i->first); scc[j - 1]->insert(i->get_state());
} }
delete i;
suffix.push_front(ecs_->h_filt(ecs_->aut->get_init_state())); suffix.push_front(ecs_->h.filter(ecs_->aut->get_init_state()));
// We build a path trough each SCC in the stack. For the // We build a path trough each SCC in the stack. For the
// first SCC, the starting state is the initial state of the // first SCC, the starting state is the initial state of the

View file

@ -57,24 +57,77 @@ namespace spot
stack_type s; stack_type s;
}; };
class numbered_state_heap_const_iterator
{
public:
virtual ~numbered_state_heap_const_iterator() {}
virtual void first() = 0;
virtual void next() = 0;
virtual bool done() const = 0;
virtual const state* get_state() const = 0;
virtual int get_index() const = 0;
};
class numbered_state_heap
{
public:
virtual ~numbered_state_heap() {}
//@{
/// \brief Is state in the heap?
///
/// Returns 0 if \a s is not in the heap. or a pointer to
/// its number if it is.
virtual const int* find(const state* s) const = 0;
virtual int* find(const state* s) = 0;
//@}
virtual void insert(const state* s, int index) = 0;
virtual int size() const = 0;
virtual numbered_state_heap_const_iterator* iterator() const = 0;
/// \brief Return a state which is equal to \a s, but is in \c h,
/// and free \a s if it is different.
///
/// Doing so simplify memory management, because we don't have to
/// track which state need to be kept or deallocated: all key in
/// \c h should last for the whole life of the emptiness_check.
virtual const state* filter(const state* s) const = 0;
};
class numbered_state_heap_hash_map : public numbered_state_heap
{
public:
virtual ~numbered_state_heap_hash_map();
virtual const int* find(const state* s) const;
virtual int* find(const state* s);
virtual void insert(const state* s, int index);
virtual int size() const;
virtual numbered_state_heap_const_iterator* iterator() const;
virtual const state* filter(const state* s) const;
protected:
typedef Sgi::hash_map<const state*, int,
state_ptr_hash, state_ptr_equal> hash_type;
hash_type h; ///< Map of visited states.
friend class numbered_state_heap_hash_map_const_iterator;
};
class emptiness_check_status class emptiness_check_status
{ {
public: public:
emptiness_check_status(const tgba* aut); emptiness_check_status(const tgba* aut);
~emptiness_check_status(); ~emptiness_check_status();
/// \brief Return a state which is equal to \a s, but is in \c h,
/// and free \a s if it is different. Doing so simplify memory
/// management, because we don't have to track which state need
/// to be kept or deallocated: all key in \c h should last for
/// the whole life of the emptiness_check.
const state* h_filt(const state* s) const;
const tgba* aut; const tgba* aut;
scc_stack root; scc_stack root;
typedef Sgi::hash_map<const state*, int, numbered_state_heap_hash_map h; ///< Map of visited states.
state_ptr_hash, state_ptr_equal> hash_type;
hash_type h; ///< Map of visited states.
/// Output statistics about this object. /// Output statistics about this object.
void print_stats(std::ostream& os) const; void print_stats(std::ostream& os) const;
@ -162,7 +215,7 @@ namespace spot
/// ///
/// Return the representative of \a s in the SCC, and delete \a /// Return the representative of \a s in the SCC, and delete \a
/// s if it is different (acting like /// s if it is different (acting like
/// emptiness_check_status::h_filt), or 0 otherwise. /// numbered_state_heap::filter), or 0 otherwise.
virtual const state* has_state(const state* s) const = 0; virtual const state* has_state(const state* s) const = 0;
/// Insert a new state in the SCC. /// Insert a new state in the SCC.