Commit 860d085b authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

* src/tgba/tgbatba.hh, src/tgba/tgbatba.cc

(tgba_tba_proxy::state_is_accepting): New method.
* src/tgbaalgos/magic.hh, src/tgbaalgos/magic.cc: New files.
* src/tgbaalgos/Makefile.am (libtgbaalgos_la_SOURCES,
tgbaalgos_HEADERS): Add them.
* src/tgbatest/ltlmagic.cc, src/tgbatest/ltlmagic.test: New files.
* src/tgbatest/Makefile.am (TESTS, ltlmagic_SOURCES,
check_PROGRAMS): Add them.
parent af928d28
2003-07-28 Alexandre Duret-Lutz <aduret@src.lip6.fr>
* src/tgba/tgbatba.hh, src/tgba/tgbatba.cc
(tgba_tba_proxy::state_is_accepting): New method.
* src/tgbaalgos/magic.hh, src/tgbaalgos/magic.cc: New files.
* src/tgbaalgos/Makefile.am (libtgbaalgos_la_SOURCES,
tgbaalgos_HEADERS): Add them.
* src/tgbatest/ltlmagic.cc, src/tgbatest/ltlmagic.test: New files.
* src/tgbatest/Makefile.am (TESTS, ltlmagic_SOURCES,
check_PROGRAMS): Add them.
2003-07-25 Alexandre Duret-Lutz <aduret@src.lip6.fr> 2003-07-25 Alexandre Duret-Lutz <aduret@src.lip6.fr>
* src/tgba/tgba.hh (tgba::~tgba): Make it public. * src/tgba/tgba.hh (tgba::~tgba): Make it public.
......
...@@ -27,7 +27,7 @@ namespace spot ...@@ -27,7 +27,7 @@ namespace spot
{ {
} }
virtual virtual
~state_tba_proxy() ~state_tba_proxy()
{ {
delete s_; delete s_;
...@@ -45,7 +45,7 @@ namespace spot ...@@ -45,7 +45,7 @@ namespace spot
return acc_; return acc_;
} }
virtual int virtual int
compare(const state* other) const compare(const state* other) const
{ {
const state_tba_proxy* o = dynamic_cast<const state_tba_proxy*>(other); const state_tba_proxy* o = dynamic_cast<const state_tba_proxy*>(other);
...@@ -55,8 +55,8 @@ namespace spot ...@@ -55,8 +55,8 @@ namespace spot
return res; return res;
return acc_.id() - o->accepting_cond().id(); return acc_.id() - o->accepting_cond().id();
} }
virtual virtual
state_tba_proxy* clone() const state_tba_proxy* clone() const
{ {
return new state_tba_proxy(*this); return new state_tba_proxy(*this);
...@@ -75,7 +75,7 @@ namespace spot ...@@ -75,7 +75,7 @@ namespace spot
tgba_tba_proxy_succ_iterator(tgba_succ_iterator* it, tgba_tba_proxy_succ_iterator(tgba_succ_iterator* it,
bdd acc, bdd next_acc, bdd acc, bdd next_acc,
bdd the_accepting_cond) bdd the_accepting_cond)
: it_(it), acc_(acc), next_acc_(next_acc), : it_(it), acc_(acc), next_acc_(next_acc),
the_accepting_cond_(the_accepting_cond) the_accepting_cond_(the_accepting_cond)
{ {
} }
...@@ -87,19 +87,19 @@ namespace spot ...@@ -87,19 +87,19 @@ namespace spot
// iteration // iteration
void void
first() first()
{ {
it_->first(); it_->first();
} }
void void
next() next()
{ {
it_->next(); it_->next();
} }
bool bool
done() const done() const
{ {
return it_->done(); return it_->done();
...@@ -107,14 +107,14 @@ namespace spot ...@@ -107,14 +107,14 @@ namespace spot
// inspection // inspection
state_tba_proxy* state_tba_proxy*
current_state() const current_state() const
{ {
state* s = it_->current_state(); state* s = it_->current_state();
bdd acc; bdd acc;
// Transition in the ACC_ accepting set should be directed // Transition in the ACC_ accepting set should be directed
// to the NEXT_ACC_ accepting set. // to the NEXT_ACC_ accepting set.
if (acc_ == bddtrue if (acc_ == bddtrue
|| (acc_ & it_->current_accepting_conditions()) == acc_) || (acc_ & it_->current_accepting_conditions()) == acc_)
acc = next_acc_; acc = next_acc_;
else else
...@@ -122,13 +122,13 @@ namespace spot ...@@ -122,13 +122,13 @@ namespace spot
return new state_tba_proxy(s, acc); return new state_tba_proxy(s, acc);
} }
bdd bdd
current_condition() const current_condition() const
{ {
return it_->current_condition(); return it_->current_condition();
} }
bdd bdd
current_accepting_conditions() const current_accepting_conditions() const
{ {
return the_accepting_cond_; return the_accepting_cond_;
...@@ -176,7 +176,7 @@ namespace spot ...@@ -176,7 +176,7 @@ namespace spot
get_dict()->unregister_all_my_variables(this); get_dict()->unregister_all_my_variables(this);
} }
state* state*
tgba_tba_proxy::get_init_state() const tgba_tba_proxy::get_init_state() const
{ {
cycle_map::const_iterator i = acc_cycle_.find(bddtrue); cycle_map::const_iterator i = acc_cycle_.find(bddtrue);
...@@ -189,62 +189,71 @@ namespace spot ...@@ -189,62 +189,71 @@ namespace spot
const state* global_state, const state* global_state,
const tgba* global_automaton) const const tgba* global_automaton) const
{ {
const state_tba_proxy* s = const state_tba_proxy* s =
dynamic_cast<const state_tba_proxy*>(local_state); dynamic_cast<const state_tba_proxy*>(local_state);
assert(s); assert(s);
tgba_succ_iterator* it = a_->succ_iter(s->real_state(), tgba_succ_iterator* it = a_->succ_iter(s->real_state(),
global_state, global_automaton); global_state, global_automaton);
bdd acc = s->accepting_cond(); bdd acc = s->accepting_cond();
cycle_map::const_iterator i = acc_cycle_.find(acc); cycle_map::const_iterator i = acc_cycle_.find(acc);
assert(i != acc_cycle_.end()); assert(i != acc_cycle_.end());
return return
new tgba_tba_proxy_succ_iterator(it, acc, i->second, new tgba_tba_proxy_succ_iterator(it, acc, i->second,
(acc == bddtrue) (acc == bddtrue)
? the_accepting_cond_ : bddfalse); ? the_accepting_cond_ : bddfalse);
} }
bdd_dict* bdd_dict*
tgba_tba_proxy::get_dict() const tgba_tba_proxy::get_dict() const
{ {
return a_->get_dict(); return a_->get_dict();
} }
std::string std::string
tgba_tba_proxy::format_state(const state* state) const tgba_tba_proxy::format_state(const state* state) const
{ {
const state_tba_proxy* s = const state_tba_proxy* s =
dynamic_cast<const state_tba_proxy*>(state); dynamic_cast<const state_tba_proxy*>(state);
assert(s); assert(s);
return a_->format_state(s->real_state()) + "(" return a_->format_state(s->real_state()) + "("
+ bdd_format_set(get_dict(), s->accepting_cond()) + ")"; + bdd_format_set(get_dict(), s->accepting_cond()) + ")";
} }
bdd bdd
tgba_tba_proxy::all_accepting_conditions() const tgba_tba_proxy::all_accepting_conditions() const
{ {
return the_accepting_cond_; return the_accepting_cond_;
} }
bdd bdd
tgba_tba_proxy::neg_accepting_conditions() const tgba_tba_proxy::neg_accepting_conditions() const
{ {
return !the_accepting_cond_; return !the_accepting_cond_;
} }
bdd bool
tgba_tba_proxy::state_is_accepting(const state* state) const
{
const state_tba_proxy* s =
dynamic_cast<const state_tba_proxy*>(state);
assert(s);
return bddtrue == s->accepting_cond();
}
bdd
tgba_tba_proxy::compute_support_conditions(const state* state) const tgba_tba_proxy::compute_support_conditions(const state* state) const
{ {
const state_tba_proxy* s = const state_tba_proxy* s =
dynamic_cast<const state_tba_proxy*>(state); dynamic_cast<const state_tba_proxy*>(state);
assert(s); assert(s);
return a_->support_conditions(s->real_state()); return a_->support_conditions(s->real_state());
} }
bdd bdd
tgba_tba_proxy::compute_support_variables(const state* state) const tgba_tba_proxy::compute_support_variables(const state* state) const
{ {
const state_tba_proxy* s = const state_tba_proxy* s =
dynamic_cast<const state_tba_proxy*>(state); dynamic_cast<const state_tba_proxy*>(state);
assert(s); assert(s);
return a_->support_variables(s->real_state()); return a_->support_variables(s->real_state());
......
...@@ -44,6 +44,8 @@ namespace spot ...@@ -44,6 +44,8 @@ namespace spot
virtual bdd all_accepting_conditions() const; virtual bdd all_accepting_conditions() const;
virtual bdd neg_accepting_conditions() const; virtual bdd neg_accepting_conditions() const;
bool state_is_accepting(const state* state) const;
protected: protected:
virtual bdd compute_support_conditions(const state* state) const; virtual bdd compute_support_conditions(const state* state) const;
virtual bdd compute_support_variables(const state* state) const; virtual bdd compute_support_variables(const state* state) const;
......
...@@ -8,6 +8,7 @@ tgbaalgos_HEADERS = \ ...@@ -8,6 +8,7 @@ tgbaalgos_HEADERS = \
dotty.hh \ dotty.hh \
lbtt.hh \ lbtt.hh \
ltl2tgba.hh \ ltl2tgba.hh \
magic.hh \
save.hh save.hh
noinst_LTLIBRARIES = libtgbaalgos.la noinst_LTLIBRARIES = libtgbaalgos.la
...@@ -16,4 +17,5 @@ libtgbaalgos_la_SOURCES = \ ...@@ -16,4 +17,5 @@ libtgbaalgos_la_SOURCES = \
dotty.cc \ dotty.cc \
lbtt.cc \ lbtt.cc \
ltl2tgba.cc \ ltl2tgba.cc \
magic.cc \
save.cc save.cc
#include <iterator>
#include "magic.hh"
#include "tgba/bddprint.hh"
namespace spot
{
const unsigned char seen_without_magic = 1;
const unsigned char seen_with_magic = 2;
magic_search::magic_search(const tgba_tba_proxy* a)
: a(a), x(0)
{
}
magic_search::~magic_search()
{
for (hash_type::iterator i = h.begin(); i != h.end(); ++i)
delete i->first;
if (x)
delete x;
}
void
magic_search::push(const state* s, bool m)
{
tgba_succ_iterator* i = a->succ_iter(s);
i->first();
hash_type::iterator hi = h.find(s);
if (hi == h.end())
{
magic d = { !m, m };
h[s] = d;
}
else
{
hi->second.seen_without |= !m;
hi->second.seen_with |= m;
if (hi->first != s)
delete s;
s = hi->first;
}
magic_state ms = { s, m };
stack.push_front(state_iter_pair(ms, i));
}
bool
magic_search::has(const state* s, bool m) const
{
hash_type::const_iterator i = h.find(s);
if (i == h.end())
return false;
if (!m && i->second.seen_without)
return true;
if (m && i->second.seen_with)
return true;
return false;
}
bool
magic_search::check()
{
if (stack.empty())
// It's a new search.
push(a->get_init_state(), false);
else
// Remove the transition to the cycle root.
tstack.pop_front();
assert(stack.size() == 1 + tstack.size());
while (! stack.empty())
{
recurse:
magic_search::state_iter_pair& p = stack.front();
tgba_succ_iterator* i = p.second;
const bool magic = p.first.m;
while (! i->done())
{
const state* s_prime = i->current_state();
bdd c = i->current_condition();
i->next();
if (magic && 0 == s_prime->compare(x))
{
delete s_prime;
tstack.push_front(c);
assert(stack.size() == tstack.size());
return true;
}
if (! has(s_prime, magic))
{
push(s_prime, magic);
tstack.push_front(c);
goto recurse;
}
delete s_prime;
}
const state* s = p.first.s;
stack.pop_front();
if (! magic && a->state_is_accepting(s))
{
if (! has(s, true))
{
if (x)
delete x;
x = s->clone();
push(s, true);
continue;
}
}
if (! stack.empty())
tstack.pop_front();
}
assert(tstack.empty());
return false;
}
std::ostream&
magic_search::print_result(std::ostream& os) const
{
stack_type::const_reverse_iterator i;
tstack_type::const_reverse_iterator ti;
os << "Prefix:" << std::endl;
const bdd_dict* d = a->get_dict();
for (i = stack.rbegin(), ti = tstack.rbegin();
i != stack.rend(); ++i, ++ti)
{
if (i->first.s->compare(x) == 0)
os <<"Cycle:" <<std::endl;
os << " " << a->format_state(i->first.s) << std::endl;
os << " | " << bdd_format_set(d, *ti) << std::endl;
}
os << " " << a->format_state(x) << std::endl;
return os;
}
}
#ifndef SPOT_TGBAALGOS_MAGIC_HH
# define SPOT_TGBAALGOS_MAGIC_HH
#include <list>
#include <utility>
#include <ostream>
#include "tgba/tgbatba.hh"
namespace spot
{
/// \brief Emptiness check on spot::tgba_tba_proxy automata using
/// the Magic Search algorithm.
///
/// This algorithm comes from
/// \verbatim
/// @InProceedings{ godefroid.93.pstv,
/// author = {Patrice Godefroid and Gerard .J. Holzmann},
/// title = {On the verification of temporal properties},
/// booktitle = {Proceedings of the 13th IFIP TC6/WG6.1 International
/// Symposium on Protocol Specification, Testing, and
/// Verification (PSTV'93)},
/// month = {May},
/// editor = {Andr{\'e} A. S. Danthine and Guy Leduc
/// and Pierre Wolper},
/// address = {Liege, Belgium},
/// pages = {109--124},
/// publisher = {North-Holland},
/// year = {1993},
/// series = {IFIP Transactions},
/// volume = {C-16},
/// isbn = {0-444-81648-8}
/// }
/// \endverbatim
struct magic_search
{
/// Initialize the Magic Search algorithm on the automaton \a a.
magic_search(const tgba_tba_proxy *a);
~magic_search();
/// \brief Perform a Magic Search.
///
/// \return true iff the algorithm has found a new accepting
/// path.
///
/// check() can be called several times until it return false,
/// to enumerate all accepting paths.
bool check();
/// Print the last accepting path found.
std::ostream& print_result(std::ostream& os) const;
private:
// The names "stack", "h", and "x", are those used in the paper.
/// \brief Records whether a state has be seen with the magic bit
/// on or off.
struct magic
{
bool seen_without : 1;
bool seen_with : 1;
};
/// \brief A state for the spot::magic_search algorithm.
struct magic_state
{
const state* s;
bool m; ///< The state of the magic demon.
};
typedef std::pair<magic_state, tgba_succ_iterator*> state_iter_pair;
typedef std::list<state_iter_pair> stack_type;
stack_type stack; ///< Stack of visited states on the path.
typedef std::list<bdd> tstack_type;
/// \brief Stack of transitions.
///
/// This is an addition to the data from the paper.
tstack_type tstack;
// FIXME: use a hash_map.
typedef std::map<const state*, magic, state_ptr_less_than> hash_type;
hash_type h; ///< Map of visited states.
/// Append a new state to the current path.
void push(const state* s, bool m);
/// Check whether we already visited \a s with the Magic bit set to \a m.
bool has(const state* s, bool m) const;
const tgba_tba_proxy* a; ///< The automata to check.
/// The state for which we are currently seeking an SCC.
const state* x;
};
}
#endif // SPOT_TGBAALGOS_MAGIC_HH
...@@ -15,3 +15,4 @@ explprod ...@@ -15,3 +15,4 @@ explprod
tripprod tripprod
mixprod mixprod
spotlbtt spotlbtt
ltlmagic
...@@ -4,16 +4,17 @@ LDADD = ../libspot.la ...@@ -4,16 +4,17 @@ LDADD = ../libspot.la
check_SCRIPTS = defs check_SCRIPTS = defs
# Keep this sorted alphabetically. # Keep this sorted alphabetically.
check_PROGRAMS = \ check_PROGRAMS = \
bddprod \
explicit \ explicit \
readsave \ explprod \
tgbaread \
ltl2tgba \ ltl2tgba \
ltlmagic \
ltlprod \ ltlprod \
bddprod \
explprod \
tripprod \
mixprod \ mixprod \
spotlbtt readsave \
spotlbtt \
tgbaread \
tripprod
# Keep this sorted alphabetically. # Keep this sorted alphabetically.
bddprod_SOURCES = ltlprod.cc bddprod_SOURCES = ltlprod.cc
...@@ -21,6 +22,7 @@ bddprod_CXXFLAGS = -DBDD_CONCRETE_PRODUCT ...@@ -21,6 +22,7 @@ bddprod_CXXFLAGS = -DBDD_CONCRETE_PRODUCT
explicit_SOURCES = explicit.cc explicit_SOURCES = explicit.cc
explprod_SOURCES = explprod.cc explprod_SOURCES = explprod.cc
ltl2tgba_SOURCES = ltl2tgba.cc ltl2tgba_SOURCES = ltl2tgba.cc
ltlmagic_SOURCES = ltlmagic.cc
ltlprod_SOURCES = ltlprod.cc ltlprod_SOURCES = ltlprod.cc
mixprod_SOURCES = mixprod.cc mixprod_SOURCES = mixprod.cc
readsave_SOURCES = readsave.cc readsave_SOURCES = readsave.cc
...@@ -40,7 +42,8 @@ TESTS = \ ...@@ -40,7 +42,8 @@ TESTS = \
explprod.test \ explprod.test \
tripprod.test \ tripprod.test \
mixprod.test \ mixprod.test \
spotlbtt.test spotlbtt.test \
ltlmagic.test