* src/tgbaalgos/gtec/ce.cc (couvreur99_check_result::accepting_path):

Rewrite as ...
(couvreur99_check_result::accepting_cycle): ... this less complex
implementation.
(couvreur99_check_result::complete_cycle): Delete.
* src/tgbatest/emptchke.test: More explicit examples.
This commit is contained in:
Alexandre Duret-Lutz 2004-11-08 17:39:48 +00:00
parent 263afcd22a
commit 9d0bcae806
4 changed files with 154 additions and 207 deletions

View file

@ -1,5 +1,12 @@
2004-11-08 Alexandre Duret-Lutz <adl@src.lip6.fr> 2004-11-08 Alexandre Duret-Lutz <adl@src.lip6.fr>
* src/tgbaalgos/gtec/ce.cc (couvreur99_check_result::accepting_path):
Rewrite as ...
(couvreur99_check_result::accepting_cycle): ... this less complex
implementation.
(couvreur99_check_result::complete_cycle): Delete.
* src/tgbatest/emptchke.test: More explicit examples.
* src/tgbaalgos/replayrun.cc (replay_tgba_run): Do not leak * src/tgbaalgos/replayrun.cc (replay_tgba_run): Do not leak
the initial state when no valid outgoing transition is found. the initial state when no valid outgoing transition is found.

View file

@ -166,7 +166,7 @@ namespace spot
} }
} }
accepting_path(scc[comp_size - 1], run_->prefix.back().s, accepting_cycle(scc[comp_size - 1], run_->prefix.back().s,
scc[comp_size - 1]->condition); scc[comp_size - 1]->condition);
run_->prefix.pop_back(); // this state belongs to the cycle. run_->prefix.pop_back(); // this state belongs to the cycle.
@ -187,79 +187,6 @@ namespace spot
return run_; return run_;
} }
void
couvreur99_check_result::complete_cycle(const explicit_connected_component*
scc,
const state* from,
const state* to)
{
// If by chance our cycle already ends on the state we have
// to reach back, we are done.
if (from == to
&& !run_->cycle.empty())
return;
// Records backlinks to parent state during the BFS.
// (This also stores the propositions of this link.)
std::map<const state*, tgba_run::step, state_ptr_less_than> father;
// BFS queue.
std::deque<const state*> todo;
// Initial state.
todo.push_back(from);
while (!todo.empty())
{
const state* src = todo.front();
todo.pop_front();
tgba_succ_iterator* i = ecs_->aut->succ_iter(src);
for (i->first(); !i->done(); i->next())
{
const state* dest = i->current_state();
// Do not escape this SCC or visit a state already visited.
const state* h_dest = scc->has_state(dest);
if (!h_dest)
{
delete dest;
continue;
}
if (father.find(h_dest) != father.end())
continue;
bdd cond = i->current_condition();
bdd acc = i->current_acceptance_conditions();
tgba_run::step s = { src, cond, acc };
// If we have reached our destination, unwind the path
// and populate RUN_->CYCLE.
if (h_dest == to)
{
tgba_run::steps p;
while (s.s != from)
{
p.push_front(s);
s = father[s.s];
}
p.push_front(s);
run_->cycle.splice(run_->cycle.end(), p);
// Exit the BFS, but release all iterators first.
todo.clear();
break;
}
// Common case: record backlinks and continue BFS.
todo.push_back(h_dest);
father[h_dest] = s;
}
delete i;
}
}
namespace namespace
{ {
struct triplet struct triplet
@ -278,146 +205,91 @@ namespace spot
} }
void void
couvreur99_check_result::accepting_path(const explicit_connected_component* couvreur99_check_result::accepting_cycle(const explicit_connected_component*
scc, scc,
const state* start, bdd const state* start, bdd
acc_to_traverse) acc_to_traverse)
{ {
// State seen during the DFS. // Compute an accepting cycle using successive BFS that are
typedef Sgi::hash_set<const state*, // restarted from the point reached after we have discovered a
state_ptr_hash, state_ptr_equal> set_type; // transition with a new acceptance conditions.
set_type seen; //
// DFS stack. // This idea is taken from Product<T>::findWitness in LBTT 1.1.2.
std::stack<triplet> todo; const state* substart = start;
do
while (acc_to_traverse != bddfalse)
{ {
// Records backlinks to parent state during the BFS.
// (This also stores the propositions of this link.)
std::map<const state*, tgba_run::step, state_ptr_less_than> father;
// BFS queue.
std::deque<const state*> todo;
// Initial state. // Initial state.
{ todo.push_back(substart);
tgba_succ_iterator* i = ecs_->aut->succ_iter(start);
i->first();
todo.push(triplet(start, i, bddfalse));
seen.insert(start);
}
// The path being explored currently.
tgba_run::steps path;
// The best path seen so far.
tgba_run::steps best_path;
// The end state of the base path.
const state* best_end = 0;
// The acceptance conditions traversed by BEST_PATH.
bdd best_acc = bddfalse;
while (!todo.empty()) while (!todo.empty())
{ {
tgba_succ_iterator* iter = todo.top().iter; const state* src = todo.front();
const state* s = todo.top().s; todo.pop_front();
tgba_succ_iterator* i = ecs_->aut->succ_iter(src);
// Nothing more to explore, backtrack. for (i->first(); !i->done(); i->next())
if (iter->done())
{ {
todo.pop(); const state* dest = i->current_state();
delete iter;
seen.erase(s);
if (!todo.empty())
{
assert(!path.empty());
path.pop_back();
}
continue;
}
// We must not escape the current SCC. // Do not escape this SCC
const state* dest = iter->current_state();
const state* h_dest = scc->has_state(dest); const state* h_dest = scc->has_state(dest);
if (!h_dest) if (!h_dest)
{ {
delete dest; delete dest;
iter->next();
continue; continue;
} }
bdd acc = iter->current_acceptance_conditions() | todo.top().acc; bdd cond = i->current_condition();
tgba_run::step st = { s, iter->current_condition(), bdd acc = i->current_acceptance_conditions();
iter->current_acceptance_conditions() }; tgba_run::step s = { src, cond, acc };
path.push_back(st);
// Advance iterator for next step. // If this new step diminish the number of acceptance
iter->next(); // conditions, record the path so far and start a new
// BFS for the remaining acceptance conditions.
if (seen.find(h_dest) == seen.end())
{
// A new state: continue the DFS.
tgba_succ_iterator* di = ecs_->aut->succ_iter(h_dest);
di->first();
todo.push(triplet(h_dest, di, acc));
seen.insert(h_dest);
continue;
}
// We have completed a full cycle.
// If we already have a best path, let see if the current
// one is better.
if (!best_path.empty())
{
// When comparing the merits of two paths, only the
// acceptance conditions we are trying the traverse
// are important.
bdd acc_restrict = acc & acc_to_traverse;
bdd best_acc_restrict = best_acc & acc_to_traverse;
// If the best path and the current one traverse the
// same acceptance conditions, we keep the shorter
// path. Otherwise, we keep the path which has the
// more acceptance conditions.
if (best_acc_restrict == acc_restrict)
{
if (best_path.size() <= path.size())
goto backtrack_path;
}
else
{
// `best_acc_restrict >> acc_restrict' is true
// when the set of acceptance conditions of
// best_acc_restrict is included in the set of
// acceptance conditions of acc_restrict.
// //
// FIXME: It would be better to count the number // If we have already collected all acceptance conditions,
// of acceptance conditions. // we stop if we cycle back to the start of the cycle.
if (bddtrue != (best_acc_restrict >> acc_restrict)) bdd less_acc = acc_to_traverse - acc;
goto backtrack_path; if (less_acc != acc_to_traverse
|| (acc_to_traverse == bddfalse
&& h_dest == start))
{
acc_to_traverse = less_acc;
tgba_run::steps p;
while (s.s != substart)
{
p.push_front(s);
s = father[s.s];
} }
p.push_front(s);
run_->cycle.splice(run_->cycle.end(), p);
// Exit this BFS, and start a new one at h_dest.
todo.clear();
substart = h_dest;
break;
} }
// The current path the best one. // Common case: record backlinks and continue BFS
best_path = path; // for unvisited states.
best_acc = acc; if (father.find(h_dest) == father.end())
best_end = h_dest; {
todo.push_back(h_dest);
backtrack_path: father[h_dest] = s;
// Continue exploration from parent to find better paths.
// (Do not pop PATH if ITER is done, because that will be
// done at the top of the loop, among other things.)
if (!iter->done())
path.pop_back();
} }
// Append our best path to the run_->cycle.
for (tgba_run::steps::iterator it = best_path.begin();
it != best_path.end(); ++it)
run_->cycle.push_back(*it);
// Prepare to find another path for the remaining acceptance
// conditions.
acc_to_traverse -= best_acc;
start = best_end;
} }
delete i;
// Complete the path so that it goes back to its beginning, }
// forming a cycle. }
complete_cycle(scc, start, run_->prefix.back().s); while (acc_to_traverse != bddfalse || substart != start);
} }
void void

View file

@ -43,16 +43,11 @@ namespace spot
void print_stats(std::ostream& os) const; void print_stats(std::ostream& os) const;
protected: protected:
/// Called by couvreur99_check_result to find a path which traverses all /// Called by accepting_run() to find a cycle which traverses all
/// acceptance conditions in the accepted SCC. /// acceptance conditions in the accepted SCC.
void accepting_path (const explicit_connected_component* scc, void accepting_cycle(const explicit_connected_component* scc,
const state* start, bdd acc_to_traverse); const state* start, bdd acc_to_traverse);
/// Complete a cycle that caraterise the period of the counter
/// example. Append a sequence to the path given by accepting_path.
void complete_cycle(const explicit_connected_component* scc,
const state* from, const state* to);
private: private:
const couvreur99_check_status* ecs_; const couvreur99_check_status* ecs_;
const explicit_connected_component_factory* eccf_; const explicit_connected_component_factory* eccf_;

View file

@ -25,6 +25,15 @@
set -e set -e
expect_ce()
{
run 0 ./ltl2tgba -e -X "$1"
run 0 ./ltl2tgba -e -D -X "$1"
run 0 ./ltl2tgba -ecouvreur99_shy -X "$1"
run 0 ./ltl2tgba -ecouvreur99_shy -D -X "$1"
run 0 ./ltl2tgba -emagic_search -X "$1"
}
cat >input <<'EOF' cat >input <<'EOF'
acc = c d; acc = c d;
s1, "s2", "a & !b", c d; s1, "s2", "a & !b", c d;
@ -32,6 +41,70 @@ s1, "s2", "a & !b", c d;
"state 3", s1,,; "state 3", s1,,;
EOF EOF
run 0 ./ltl2tgba -e -X input expect_ce input
run 0 ./ltl2tgba -ecouvreur99_shy -X input
run 0 ./ltl2tgba -emagic_search -X input # ________
# / v
# >a--->d--->g
# /^ /^ /^
# L | L | L |{A}
# b->c e->f h->i
#
cat >input <<'EOF'
acc = A;
a, b, "1",;
b, c, "1",;
c, a, "1",;
a, d, "1",;
d, e, "1",;
e, f, "1",;
f, d, "1",;
d, g, "1",;
g, h, "1",;
h, i, "1",;
i, g, "1", A;
a, g, "1",;
EOF
expect_ce input
# v
# d->a
# ^ |
# | v
# c<-b<-.
# ^ |A |B
# B| v |
# `--e->f
#
# The arcs are ordered so that Couvreur99 succeed after exploring
# the following subgraph (which is one accepting SCC):
#
# v
# d->a
# ^ |
# | v
# c<-b<-.
# |A |B
# v |
# e->f
#
# However when computing a counter-example the greedy BFS algorithm
# will fail to return the minimal a->b->e->f->b run. Indeed it first
# walks through a->b->e (which gives acceptance condition A), and
# prefer to continue with e->c (because it gives acceptance condition B),
# and finally closes the cycle with c->d->a
#
cat >input <<'EOF'
acc = A B;
a, b, "1",;
b, c, "1",;
c, d, "1",;
d, a, "1",;
b, e, "1", A;
e, f, "1",;
f, b, "1", B;
e, c, "1", B;
EOF
expect_ce input