* src/tgbaalgos/ltl2tgba_fm.hh (ltl_to_tgba_fm): Add argument
fair_loop_approx. * src/tgbaalgos/ltl2tgba_fm.cc (ltl_to_tgba_fm): Implement the fair_loop_approx optimization. (ltl_promise_visitor, ltl_possible_fair_loop_visitor, possible_fair_loop_checker): New classes. * src/tgbatest/ltl2tgba.cc: Add the -L option. * src/tgbatest/spotlbtt.test: Exercise fair_loop_approx. * wrap/python/cgi/ltl2tgba.in: Make it an option.
This commit is contained in:
parent
6b06e28f3d
commit
aa5cef3c83
6 changed files with 248 additions and 15 deletions
12
ChangeLog
12
ChangeLog
|
|
@ -1,3 +1,15 @@
|
||||||
|
2004-05-10 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
||||||
|
|
||||||
|
* src/tgbaalgos/ltl2tgba_fm.hh (ltl_to_tgba_fm): Add argument
|
||||||
|
fair_loop_approx.
|
||||||
|
* src/tgbaalgos/ltl2tgba_fm.cc (ltl_to_tgba_fm): Implement the
|
||||||
|
fair_loop_approx optimization.
|
||||||
|
(ltl_promise_visitor, ltl_possible_fair_loop_visitor,
|
||||||
|
possible_fair_loop_checker): New classes.
|
||||||
|
* src/tgbatest/ltl2tgba.cc: Add the -L option.
|
||||||
|
* src/tgbatest/spotlbtt.test: Exercise fair_loop_approx.
|
||||||
|
* wrap/python/cgi/ltl2tgba.in: Make it an option.
|
||||||
|
|
||||||
2004-05-07 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
2004-05-07 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
||||||
|
|
||||||
* src/tgbaalgos/ltl2tgba_fm.hh (ltl_to_tgba_fm): Add argument
|
* src/tgbaalgos/ltl2tgba_fm.hh (ltl_to_tgba_fm): Add argument
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ltlvisit/nenoform.hh"
|
#include "ltlvisit/nenoform.hh"
|
||||||
#include "ltlvisit/destroy.hh"
|
#include "ltlvisit/destroy.hh"
|
||||||
#include "ltlvisit/tostring.hh"
|
#include "ltlvisit/tostring.hh"
|
||||||
|
#include "ltlvisit/postfix.hh"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "tgba/tgbabddconcretefactory.hh"
|
#include "tgba/tgbabddconcretefactory.hh"
|
||||||
|
|
@ -213,7 +214,7 @@ namespace spot
|
||||||
{
|
{
|
||||||
formula* ac = var_to_formula(var);
|
formula* ac = var_to_formula(var);
|
||||||
|
|
||||||
if (! a->has_acceptance_condition(ac))
|
if (!a->has_acceptance_condition(ac))
|
||||||
a->declare_acceptance_condition(clone(ac));
|
a->declare_acceptance_condition(clone(ac));
|
||||||
a->add_acceptance_condition(t, ac);
|
a->add_acceptance_condition(t, ac);
|
||||||
b = high;
|
b = high;
|
||||||
|
|
@ -224,6 +225,50 @@ namespace spot
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Gather all promises of a formula. These are the
|
||||||
|
// right-hand sides of U or F operators.
|
||||||
|
class ltl_promise_visitor: public postfix_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ltl_promise_visitor(translate_dict& dict)
|
||||||
|
: dict_(dict), res_(bddtrue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~ltl_promise_visitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return res_;
|
||||||
|
}
|
||||||
|
|
||||||
|
using postfix_visitor::doit;
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
doit(unop* node)
|
||||||
|
{
|
||||||
|
if (node->op() == unop::F)
|
||||||
|
res_ &= bdd_ithvar(dict_.register_a_variable(node->child()));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
doit(binop* node)
|
||||||
|
{
|
||||||
|
if (node->op() == binop::U)
|
||||||
|
res_ &= bdd_ithvar(dict_.register_a_variable(node->second()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
translate_dict& dict_;
|
||||||
|
bdd res_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// The rewrite rules used here are adapted from Jean-Michel
|
// The rewrite rules used here are adapted from Jean-Michel
|
||||||
// Couvreur's FM paper.
|
// Couvreur's FM paper.
|
||||||
class ltl_trad_visitor: public const_visitor
|
class ltl_trad_visitor: public const_visitor
|
||||||
|
|
@ -239,7 +284,8 @@ namespace spot
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd result() const
|
bdd
|
||||||
|
result() const
|
||||||
{
|
{
|
||||||
return res_;
|
return res_;
|
||||||
}
|
}
|
||||||
|
|
@ -394,6 +440,105 @@ namespace spot
|
||||||
bdd res_;
|
bdd res_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Check whether a formula has a R or G operator at its top-level
|
||||||
|
// (preceding logical operators do not count).
|
||||||
|
class ltl_possible_fair_loop_visitor: public const_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ltl_possible_fair_loop_visitor()
|
||||||
|
: res_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~ltl_possible_fair_loop_visitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return res_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const atomic_prop*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const constant*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const unop* node)
|
||||||
|
{
|
||||||
|
if (node->op() == unop::G)
|
||||||
|
res_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* node)
|
||||||
|
{
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
// r(f1 logical-op f2) = r(f1) logical-op r(f2)
|
||||||
|
case binop::Xor:
|
||||||
|
case binop::Implies:
|
||||||
|
case binop::Equiv:
|
||||||
|
node->first()->accept(*this);
|
||||||
|
if (!res_)
|
||||||
|
node->second()->accept(*this);
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
return;
|
||||||
|
case binop::R:
|
||||||
|
res_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const multop* node)
|
||||||
|
{
|
||||||
|
unsigned s = node->size();
|
||||||
|
for (unsigned n = 0; n < s && !res_; ++n)
|
||||||
|
{
|
||||||
|
node->nth(n)->accept(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool res_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check whether a formula can be part of a fair loop.
|
||||||
|
// Cache the result for efficiency.
|
||||||
|
class possible_fair_loop_checker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool
|
||||||
|
check(const formula* f)
|
||||||
|
{
|
||||||
|
pfl_map::const_iterator i = pfl.find(f);
|
||||||
|
if (i != pfl.end())
|
||||||
|
return i->second;
|
||||||
|
ltl_possible_fair_loop_visitor v;
|
||||||
|
f->accept(v);
|
||||||
|
bool rel = v.result();
|
||||||
|
pfl[f] = rel;
|
||||||
|
return rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef Sgi::hash_map<const formula*, bool, ptr_hash<formula> > pfl_map;
|
||||||
|
pfl_map pfl;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::map<bdd, bdd, bdd_less_than> prom_map;
|
typedef std::map<bdd, bdd, bdd_less_than> prom_map;
|
||||||
|
|
@ -428,8 +573,16 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd promises = bdd_existcomp(label, d.a_set);
|
|
||||||
bdd conds = bdd_existcomp(label, d.var_set);
|
bdd conds = bdd_existcomp(label, d.var_set);
|
||||||
|
bdd promises = bdd_existcomp(label, d.a_set);
|
||||||
|
|
||||||
|
// Clear the promises if the destination is the True state.
|
||||||
|
// (Normally this is already the case unless the fair_loop_approx
|
||||||
|
// optimization is used, because this can get confused by formulae
|
||||||
|
// like X(1): it doesn't know that X(1) is equivalent to 1, so it
|
||||||
|
// puts all promises on arcs going to X(1).)
|
||||||
|
if (dest == constant::true_instance())
|
||||||
|
promises = bddtrue;
|
||||||
|
|
||||||
dest_map::iterator i = dests.find(dest);
|
dest_map::iterator i = dests.find(dest);
|
||||||
if (i == dests.end())
|
if (i == dests.end())
|
||||||
|
|
@ -446,8 +599,11 @@ namespace spot
|
||||||
|
|
||||||
tgba_explicit*
|
tgba_explicit*
|
||||||
ltl_to_tgba_fm(const formula* f, bdd_dict* dict,
|
ltl_to_tgba_fm(const formula* f, bdd_dict* dict,
|
||||||
bool exprop, bool symb_merge, bool branching_postponement)
|
bool exprop, bool symb_merge, bool branching_postponement,
|
||||||
|
bool fair_loop_approx)
|
||||||
{
|
{
|
||||||
|
possible_fair_loop_checker pflc;
|
||||||
|
|
||||||
// Normalize the formula. We want all the negations on
|
// Normalize the formula. We want all the negations on
|
||||||
// the atomic propositions. We also suppress logic
|
// the atomic propositions. We also suppress logic
|
||||||
// abbreviations such as <=>, =>, or XOR, since they
|
// abbreviations such as <=>, =>, or XOR, since they
|
||||||
|
|
@ -463,10 +619,22 @@ namespace spot
|
||||||
// `aR(bRc).(bRc)') are equivalent, and are trivially identified
|
// `aR(bRc).(bRc)') are equivalent, and are trivially identified
|
||||||
// by looking at the set of successors.
|
// by looking at the set of successors.
|
||||||
succ_to_formula canonical_succ;
|
succ_to_formula canonical_succ;
|
||||||
|
// For cosmetics, register 1 initially, so the algorithm will not
|
||||||
|
// register an equivalent formula first.
|
||||||
|
canonical_succ[bddtrue] = constant::true_instance();
|
||||||
|
|
||||||
translate_dict d(dict);
|
translate_dict d(dict);
|
||||||
ltl_trad_visitor v(d);
|
ltl_trad_visitor v(d);
|
||||||
|
|
||||||
|
// Compute the set of all promises occurring inside the formula.
|
||||||
|
bdd all_promises = bddtrue;
|
||||||
|
if (fair_loop_approx)
|
||||||
|
{
|
||||||
|
ltl_promise_visitor pv(d);
|
||||||
|
f2->accept(pv);
|
||||||
|
all_promises = pv.result();
|
||||||
|
}
|
||||||
|
|
||||||
formulae_seen.insert(f2);
|
formulae_seen.insert(f2);
|
||||||
formulae_to_translate.insert(f2);
|
formulae_to_translate.insert(f2);
|
||||||
|
|
||||||
|
|
@ -549,8 +717,14 @@ namespace spot
|
||||||
while ((cube = isop.next()) != bddfalse)
|
while ((cube = isop.next()) != bddfalse)
|
||||||
{
|
{
|
||||||
bdd label = bdd_exist(cube, d.next_set);
|
bdd label = bdd_exist(cube, d.next_set);
|
||||||
formula* dest =
|
bdd dest_bdd = bdd_existcomp(cube, d.next_set);
|
||||||
d.conj_bdd_to_formula(bdd_existcomp(cube, d.next_set));
|
formula* dest = d.conj_bdd_to_formula(dest_bdd);
|
||||||
|
|
||||||
|
// If the destination cannot possibly be part of a fair
|
||||||
|
// loop, make all possible promises.
|
||||||
|
if (fair_loop_approx
|
||||||
|
&& dest_bdd != bddtrue && !pflc.check(dest))
|
||||||
|
label &= all_promises;
|
||||||
|
|
||||||
// If we are not postponing the branching, we can
|
// If we are not postponing the branching, we can
|
||||||
// declare the outgoing transitions immediately.
|
// declare the outgoing transitions immediately.
|
||||||
|
|
@ -575,8 +749,8 @@ namespace spot
|
||||||
if (branching_postponement)
|
if (branching_postponement)
|
||||||
for (succ_map::const_iterator si = succs.begin();
|
for (succ_map::const_iterator si = succs.begin();
|
||||||
si != succs.end(); ++si)
|
si != succs.end(); ++si)
|
||||||
fill_dests (d, symb_merge, formulae_seen, canonical_succ, v,
|
fill_dests(d, symb_merge, formulae_seen, canonical_succ, v,
|
||||||
dests, si->first, si->second);
|
dests, si->first, si->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an arc going to 1 (True). Register it first, that
|
// Check for an arc going to 1 (True). Register it first, that
|
||||||
|
|
@ -602,11 +776,11 @@ namespace spot
|
||||||
bdd cond_for_true = bddfalse;
|
bdd cond_for_true = bddfalse;
|
||||||
if (i != dests.end())
|
if (i != dests.end())
|
||||||
{
|
{
|
||||||
// Transitions going to 1 (true) are not expected to make
|
// There should be only one transition going to 1 (true) ...
|
||||||
// any promises.
|
|
||||||
assert(i->second.size() == 1);
|
assert(i->second.size() == 1);
|
||||||
prom_map::const_iterator j = i->second.find(bddtrue);
|
prom_map::const_iterator j = i->second.begin();
|
||||||
assert(j != i->second.end());
|
// ... and it is not expected to make any promises.
|
||||||
|
assert(j->first == bddtrue);
|
||||||
|
|
||||||
cond_for_true = j->second;
|
cond_for_true = j->second;
|
||||||
tgba_explicit::transition* t =
|
tgba_explicit::transition* t =
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,14 @@ namespace spot
|
||||||
/// publisher = {Springer-Verlag}
|
/// publisher = {Springer-Verlag}
|
||||||
/// }
|
/// }
|
||||||
/// \endverbatim
|
/// \endverbatim
|
||||||
|
///
|
||||||
|
/// If \a fair_loop_approx is set, a really simple characterization of
|
||||||
|
/// unstable state is used to suppress all acceptance conditions from
|
||||||
|
/// incoming transitions.
|
||||||
tgba_explicit* ltl_to_tgba_fm(const ltl::formula* f, bdd_dict* dict,
|
tgba_explicit* ltl_to_tgba_fm(const ltl::formula* f, bdd_dict* dict,
|
||||||
bool exprop = false, bool symb_merge = true,
|
bool exprop = false, bool symb_merge = true,
|
||||||
bool branching_postponement = false);
|
bool branching_postponement = false,
|
||||||
|
bool fair_loop_approx = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SPOT_TGBA_LTL2TGBA_HH
|
#endif // SPOT_TGBA_LTL2TGBA_HH
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ syntax(char* prog)
|
||||||
<< " -f use Couvreur's FM algorithm for translation"
|
<< " -f use Couvreur's FM algorithm for translation"
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< " -F read the formula from the file" << std::endl
|
<< " -F read the formula from the file" << std::endl
|
||||||
|
<< " -L fair-loop approximation (implies -f)" << std::endl
|
||||||
<< " -m magic-search (implies -D), expect a counter-example"
|
<< " -m magic-search (implies -D), expect a counter-example"
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< " -M magic-search (implies -D), expect no counter-example"
|
<< " -M magic-search (implies -D), expect no counter-example"
|
||||||
|
|
@ -113,6 +114,7 @@ main(int argc, char** argv)
|
||||||
bool expect_counter_example = false;
|
bool expect_counter_example = false;
|
||||||
bool from_file = false;
|
bool from_file = false;
|
||||||
bool post_branching = false;
|
bool post_branching = false;
|
||||||
|
bool fair_loop_approx = false;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
|
@ -169,6 +171,11 @@ main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
file_opt = true;
|
file_opt = true;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(argv[formula_index], "-L"))
|
||||||
|
{
|
||||||
|
fair_loop_approx = true;
|
||||||
|
fm_opt = true;
|
||||||
|
}
|
||||||
else if (!strcmp(argv[formula_index], "-m"))
|
else if (!strcmp(argv[formula_index], "-m"))
|
||||||
{
|
{
|
||||||
echeck = MagicSearch;
|
echeck = MagicSearch;
|
||||||
|
|
@ -308,7 +315,8 @@ main(int argc, char** argv)
|
||||||
if (fm_opt)
|
if (fm_opt)
|
||||||
to_free = a = spot::ltl_to_tgba_fm(f, dict, fm_exprop_opt,
|
to_free = a = spot::ltl_to_tgba_fm(f, dict, fm_exprop_opt,
|
||||||
fm_symb_merge_opt,
|
fm_symb_merge_opt,
|
||||||
post_branching);
|
post_branching,
|
||||||
|
fair_loop_approx);
|
||||||
else
|
else
|
||||||
to_free = a = concrete = spot::ltl_to_tgba_lacim(f, dict);
|
to_free = a = concrete = spot::ltl_to_tgba_lacim(f, dict);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,13 @@ Algorithm
|
||||||
Enabled = yes
|
Enabled = yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Spot (Couvreur -- FM post_branch + exprop + flapprox)"
|
||||||
|
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -x -p -L -t'"
|
||||||
|
Enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
Algorithm
|
Algorithm
|
||||||
{
|
{
|
||||||
Name = "Spot (Couvreur -- FM post_branch + exprop), degeneralized"
|
Name = "Spot (Couvreur -- FM post_branch + exprop), degeneralized"
|
||||||
|
|
@ -133,6 +140,13 @@ Algorithm
|
||||||
Enabled = yes
|
Enabled = yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Spot (Couvreur -- FM post_branch + exprop + flapprox), degeneralized"
|
||||||
|
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -p -x -t -L -D'"
|
||||||
|
Enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
Algorithm
|
Algorithm
|
||||||
{
|
{
|
||||||
Name = "Spot (Couvreur -- FM exprop + post_branch), fake"
|
Name = "Spot (Couvreur -- FM exprop + post_branch), fake"
|
||||||
|
|
@ -140,6 +154,20 @@ Algorithm
|
||||||
Enabled = no
|
Enabled = no
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Spot (Couvreur -- FM exprop + flapprox), fake"
|
||||||
|
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -x -L -T'"
|
||||||
|
Enabled = no
|
||||||
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Spot (Couvreur -- FM exprop + post_branch + flapprox), fake"
|
||||||
|
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -x -p -L -T'"
|
||||||
|
Enabled = no
|
||||||
|
}
|
||||||
|
|
||||||
Algorithm
|
Algorithm
|
||||||
{
|
{
|
||||||
Name = "Spot (Couvreur -- FM exprop), fake"
|
Name = "Spot (Couvreur -- FM exprop), fake"
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ options_trans_fm = [
|
||||||
('opt_symb_merge',
|
('opt_symb_merge',
|
||||||
'merge states with same symbolic successor representation', 1),
|
'merge states with same symbolic successor representation', 1),
|
||||||
('opt_branch_post', 'branching postponement', 0),
|
('opt_branch_post', 'branching postponement', 0),
|
||||||
|
('opt_fair_loop_approx','fair-loop approximations', 0),
|
||||||
]
|
]
|
||||||
options_trans_lacim = [
|
options_trans_lacim = [
|
||||||
('show_relation_set',
|
('show_relation_set',
|
||||||
|
|
@ -412,12 +413,17 @@ if opt_branch_post:
|
||||||
branching_postponement = 1
|
branching_postponement = 1
|
||||||
else:
|
else:
|
||||||
branching_postponement = 0
|
branching_postponement = 0
|
||||||
|
if opt_fair_loop_approx:
|
||||||
|
fair_loop_approx = 1
|
||||||
|
else:
|
||||||
|
fair_loop_approx = 0
|
||||||
|
|
||||||
if trans_lacim:
|
if trans_lacim:
|
||||||
automaton = spot.ltl_to_tgba_lacim(f, dict)
|
automaton = spot.ltl_to_tgba_lacim(f, dict)
|
||||||
elif trans_fm:
|
elif trans_fm:
|
||||||
automaton = spot.ltl_to_tgba_fm(f, dict,
|
automaton = spot.ltl_to_tgba_fm(f, dict,
|
||||||
exprop, symb_merge, branching_postponement)
|
exprop, symb_merge, branching_postponement,
|
||||||
|
fair_loop_approx)
|
||||||
|
|
||||||
print 'done.</p>'
|
print 'done.</p>'
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue