* src/tgbaalgos/emptinesscheck.cc (emptiness_check::check):

Simplify, reorganize, and comment.
* src/tgbaalgos/emptinesscheck.hh (emptiness_check::root_component):
Rename as ...
(emptiness_check::root): ... this, to follow the paper.
This commit is contained in:
Alexandre Duret-Lutz 2003-10-24 13:50:05 +00:00
parent 26f15224fc
commit e94415c6e6
3 changed files with 133 additions and 83 deletions

View file

@ -1,5 +1,11 @@
2003-10-24 Alexandre Duret-Lutz <adl@src.lip6.fr> 2003-10-24 Alexandre Duret-Lutz <adl@src.lip6.fr>
* src/tgbaalgos/emptinesscheck.cc (emptiness_check::check):
Simplify, reorganize, and comment.
* src/tgbaalgos/emptinesscheck.hh (emptiness_check::root_component):
Rename as ...
(emptiness_check::root): ... this, to follow the paper.
* src/tgbaalgos/emptinesscheck.cc: Remove some superfluous * src/tgbaalgos/emptinesscheck.cc: Remove some superfluous
`emptiness_check::'. `emptiness_check::'.

View file

@ -72,94 +72,138 @@ namespace spot
bool bool
emptiness_check::check() emptiness_check::check()
{ {
// We use five main data in this algorithm:
// * emptiness_check::root, a stack of strongly connected components (SCC),
// * emptiness_check::h, a hash of all visited node, with their order,
// (it is called "Hash" in Couvreur's paper)
// * arc, a stack of acceptance conditions between each of these SCC,
std::stack<bdd> arc;
// * num, the number of visited nodes. Used to set the order of each
// visited node,
int num = 1;
// * todo, the depth-first search stack. This holds pairs of the
// form (STATE, ITERATOR) where ITERATOR is a tgba_succ_iterator
// over the successors of STATE. In our use, ITERATOR should
// always be freed when TODO is popped, but STATE should not because
// it is also used as a key in H.
std::stack<pair_state_iter> todo; std::stack<pair_state_iter> todo;
std::stack<bdd> arc_accepting;
int nbstate = 1; // Setup depth-first search from the initial state.
state* init = aut_->get_init_state(); {
h[init] = 1; state* init = aut_->get_init_state();
root_component.push(connected_component(1)); h[init] = 1;
arc_accepting.push(bddfalse); root.push(connected_component(1));
tgba_succ_iterator* iter_ = aut_->succ_iter(init); arc.push(bddfalse);
iter_->first(); tgba_succ_iterator* iter = aut_->succ_iter(init);
todo.push(pair_state_iter(init, iter_)); iter->first();
todo.push(pair_state_iter(init, iter));
}
while (!todo.empty()) while (!todo.empty())
{ {
pair_state_iter step = todo.top(); assert(root.size() == arc.size());
if (step.second->done())
// We are looking at the next successor in SUCC.
tgba_succ_iterator* succ = todo.top().second;
// If there is no more successor, backtrack.
if (succ->done())
{ {
// We have explored all successors of state CURR.
const state* curr = todo.top().first;
// Backtrack TODO.
todo.pop(); todo.pop();
assert(!root_component.empty());
connected_component comp_tmp = root_component.top(); // When backtracking the root of an SCC, we must also
root_component.pop(); // remove that SCC from the ARC/ROOT stacks. We must
hash_type::iterator i_0 = h.find(step.first); // discard from H all reachable states from this SCC.
assert(i_0 != h.end()); hash_type::iterator i = h.find(curr);
if (comp_tmp.index == i_0->second) assert(i != h.end());
assert(!root.empty());
if (root.top().index == i->second)
{ {
// The current node is a root of a Strong Connected Component. assert(!arc.empty());
remove_component(step.first); arc.pop();
assert(!arc_accepting.empty()); root.pop();
arc_accepting.pop(); remove_component(curr);
} }
else
{ delete succ;
root_component.push(comp_tmp); // Do not delete CURR: it is a key in H.
} continue;
assert(root_component.size() == arc_accepting.size());
} }
else
// We have a successor to look at. Fetch the values
// (destination state, acceptance conditions of the arc)
// we are interested in...
const state* dest = succ->current_state();
bdd acc = succ->current_accepting_conditions();
// ... and point the iterator to the next successor, for
// the next iteration.
succ->next();
// We do not need SUCC from now on.
// Are we going to a new state?
hash_type::iterator i = h.find(dest);
if (i == h.end())
{ {
iter_ = step.second; // Yes. Number it, stack it, and register its successors
state* current_state = iter_->current_state(); // for later processing.
bdd current_accepting = iter_->current_accepting_conditions(); h[dest] = ++num;
hash_type::iterator i = h.find(current_state); root.push(connected_component(num));
iter_->next(); arc.push(acc);
if (i == h.end()) tgba_succ_iterator* iter = aut_->succ_iter(dest);
{ iter->first();
// New node. todo.push(pair_state_iter(dest, iter));
nbstate = nbstate + 1; continue;
assert(nbstate != 0);
h[current_state] = nbstate;
root_component.push(connected_component(nbstate));
arc_accepting.push(current_accepting);
tgba_succ_iterator* iter2 = aut_->succ_iter(current_state);
iter2->first();
todo.push(pair_state_iter(current_state, iter2));
}
else if (i->second != -1)
{
// A node with order != -1 (a seen node not removed).
assert(!root_component.empty());
connected_component comp = root_component.top();
root_component.pop();
bdd new_condition = current_accepting;
int current_index = i->second;
while (comp.index > current_index)
{
// root_component and arc_accepting are popped
// until the head of root_component is less or
// equal to the order of the current state.
assert(!root_component.empty());
comp = root_component.top();
root_component.pop();
new_condition |= comp.condition;
assert(!arc_accepting.empty());
bdd arc_acc = arc_accepting.top();
arc_accepting.pop();
new_condition |= arc_acc;
}
comp.condition |= new_condition;
if (aut_->all_accepting_conditions() == comp.condition)
{
// A failure SCC was found, the automata is not empty.
root_component.push(comp);
return false;
}
root_component.push(comp);
assert(root_component.size() == arc_accepting.size());
}
} }
// We know the state exist. Since a state can have several
// representations (i.e., objects), make sure we delete
// anything but the first one seen (the one used as key in
// H).
if (dest != i->first)
delete dest;
// If we have reached a dead component. Ignore it.
if (i->second == -1)
continue;
// Now this is the most interesting case. We have reached a
// state S1 which is already part of a non-dead SCC. Any such
// non-dead SCC has necessarily been crossed by our path to
// this state: there is a state S2 in our path which belongs
// to this SCC too. We are going to merge all states between
// this S1 and S2 into this SCC.
//
// This merge is easy to do because the order of the SCC in
// ROOT is ascending: we just have to merge all SCCs from the
// top of ROOT that have an index greater to the one of
// the SCC of S2 (called the "threshold").
int threshold = i->second;
while (threshold < root.top().index)
{
assert(!root.empty());
assert(!arc.empty());
acc |= root.top().condition;
acc |= arc.top();
root.pop();
arc.pop();
}
// Note that we do not always have
// threshold == root.top().index
// after this loop, the SCC whose index is threshold might have
// been merged with a lower SCC.
// Accumulate all acceptance conditions into the merged SCC.
root.top().condition |= acc;
if (root.top().condition == aut_->all_accepting_conditions())
// We have found an accepting SCC.
return false;
} }
// The automata is empty. // This automaton recognize no word.
return true; return true;
} }
@ -212,9 +256,9 @@ namespace spot
state_ptr_less_than> path_state; state_ptr_less_than> path_state;
path_state path_map; path_state path_map;
assert(!root_component.empty()); assert(!root.empty());
int comp_size = root_component.size(); int comp_size = root.size();
typedef std::vector<connected_component> vec_compo; typedef std::vector<connected_component> vec_compo;
vec_compo vec_component(comp_size); vec_compo vec_component(comp_size);
std::vector<state_sequence> vec_sequence(comp_size); std::vector<state_sequence> vec_sequence(comp_size);
@ -226,13 +270,13 @@ namespace spot
for (int j = comp_size - 1; j >= 0; j--) for (int j = comp_size - 1; j >= 0; j--)
{ {
vec_component[j] = root_component.top(); vec_component[j] = root.top();
root_component.pop(); root.pop();
} }
int q_index; int q_index;
int tmp_int = 0; int tmp_int = 0;
// Fill the SCC in the stack root_component. // Fill the SCC in the stack root.
for (hash_type::iterator iter_map = h.begin(); for (hash_type::iterator iter_map = h.begin();
iter_map != h.end(); ++iter_map) iter_map != h.end(); ++iter_map)
{ {

View file

@ -73,7 +73,7 @@ namespace spot
private: private:
const tgba* aut_; const tgba* aut_;
std::stack<connected_component> root_component; std::stack<connected_component> root;
state_sequence suffix; state_sequence suffix;
cycle_path period; cycle_path period;