Update the FM translation to handle <>->, []->, *, ;, #e.
* src/tgbaalgos/ltl2tgba_fm.cc: Implement translation for recently introduced operators. * src/tgbatest/ltl2tgba.test: Add some PSL tests.
This commit is contained in:
parent
21e89f400a
commit
bd9136a98e
2 changed files with 530 additions and 73 deletions
|
|
@ -32,10 +32,14 @@
|
||||||
#include "ltlvisit/tostring.hh"
|
#include "ltlvisit/tostring.hh"
|
||||||
#include "ltlvisit/postfix.hh"
|
#include "ltlvisit/postfix.hh"
|
||||||
#include "ltlvisit/apcollect.hh"
|
#include "ltlvisit/apcollect.hh"
|
||||||
|
#include "ltlvisit/mark.hh"
|
||||||
|
#include "ltlvisit/tostring.hh"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "ltl2tgba_fm.hh"
|
#include "ltl2tgba_fm.hh"
|
||||||
#include "ltlvisit/contain.hh"
|
#include "ltlvisit/contain.hh"
|
||||||
|
#include "ltlvisit/consterm.hh"
|
||||||
|
#include "tgba/bddprint.hh"
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
@ -154,7 +158,7 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
formula*
|
formula*
|
||||||
conj_bdd_to_formula(bdd b)
|
conj_bdd_to_formula(bdd b) const
|
||||||
{
|
{
|
||||||
if (b == bddfalse)
|
if (b == bddfalse)
|
||||||
return constant::false_instance();
|
return constant::false_instance();
|
||||||
|
|
@ -225,6 +229,26 @@ namespace spot
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Debugging function.
|
||||||
|
std::ostream&
|
||||||
|
trace_ltl_bdd(const translate_dict& d, bdd f)
|
||||||
|
{
|
||||||
|
minato_isop isop(f);
|
||||||
|
bdd cube;
|
||||||
|
while ((cube = isop.next()) != bddfalse)
|
||||||
|
{
|
||||||
|
bdd label = bdd_exist(cube, d.next_set);
|
||||||
|
bdd dest_bdd = bdd_existcomp(cube, d.next_set);
|
||||||
|
const formula* dest = d.conj_bdd_to_formula(dest_bdd);
|
||||||
|
bdd_print_set(std::cerr, d.dict, label) << " => "
|
||||||
|
<< to_string(dest)
|
||||||
|
<< std::endl;
|
||||||
|
dest->destroy();
|
||||||
|
}
|
||||||
|
return std::cerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Gather all promises of a formula. These are the
|
// Gather all promises of a formula. These are the
|
||||||
// right-hand sides of U or F operators.
|
// right-hand sides of U or F operators.
|
||||||
|
|
@ -268,14 +292,226 @@ namespace spot
|
||||||
bdd res_;
|
bdd res_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Rewrite rule for rational operators.
|
||||||
|
class ratexp_trad_visitor: public const_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ratexp_trad_visitor(translate_dict& dict,
|
||||||
|
formula* to_concat = 0)
|
||||||
|
: dict_(dict), to_concat_(to_concat)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~ratexp_trad_visitor()
|
||||||
|
{
|
||||||
|
if (to_concat_)
|
||||||
|
to_concat_->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return res_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd next_to_concat()
|
||||||
|
{
|
||||||
|
if (!to_concat_)
|
||||||
|
to_concat_ = constant::empty_word_instance();
|
||||||
|
int x = dict_.register_next_variable(to_concat_);
|
||||||
|
return bdd_ithvar(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd now_to_concat()
|
||||||
|
{
|
||||||
|
if (to_concat_)
|
||||||
|
{
|
||||||
|
if (to_concat_ == constant::empty_word_instance())
|
||||||
|
return bddfalse;
|
||||||
|
bdd n = recurse(to_concat_);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return bddfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const atomic_prop* node)
|
||||||
|
{
|
||||||
|
res_ = (bdd_ithvar(dict_.register_proposition(node))
|
||||||
|
& next_to_concat());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const constant* node)
|
||||||
|
{
|
||||||
|
switch (node->val())
|
||||||
|
{
|
||||||
|
case constant::True:
|
||||||
|
res_ = next_to_concat();
|
||||||
|
return;
|
||||||
|
case constant::False:
|
||||||
|
res_ = bddfalse;
|
||||||
|
return;
|
||||||
|
case constant::EmptyWord:
|
||||||
|
res_ = now_to_concat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const unop* node)
|
||||||
|
{
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
case unop::F:
|
||||||
|
case unop::G:
|
||||||
|
case unop::Not:
|
||||||
|
case unop::X:
|
||||||
|
case unop::Finish:
|
||||||
|
assert(!"not a rational operator");
|
||||||
|
return;
|
||||||
|
case unop::Star:
|
||||||
|
{
|
||||||
|
formula* f;
|
||||||
|
if (to_concat_)
|
||||||
|
f = multop::instance(multop::Concat, node->clone(),
|
||||||
|
to_concat_->clone());
|
||||||
|
else
|
||||||
|
f = node->clone();
|
||||||
|
|
||||||
|
res_ = recurse(node->child(), f) | now_to_concat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop*)
|
||||||
|
{
|
||||||
|
assert(!"not a rational operator");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const automatop*)
|
||||||
|
{
|
||||||
|
assert(!"not a rational operator");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const multop* node)
|
||||||
|
{
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
case multop::And:
|
||||||
|
{
|
||||||
|
res_ = bddtrue;
|
||||||
|
unsigned s = node->size();
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
{
|
||||||
|
bdd res = recurse(node->nth(n));
|
||||||
|
// trace_ltl_bdd(dict_, res);
|
||||||
|
res_ &= res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cerr << "Pre-Concat:" << std::endl;
|
||||||
|
//trace_ltl_bdd(dict_, res_);
|
||||||
|
|
||||||
|
if (to_concat_)
|
||||||
|
{
|
||||||
|
// If we have translated (a* & b*) in (a* & b*);c, we
|
||||||
|
// have to append ";c" to all destinations.
|
||||||
|
|
||||||
|
minato_isop isop(res_);
|
||||||
|
bdd cube;
|
||||||
|
res_ = bddfalse;
|
||||||
|
while ((cube = isop.next()) != bddfalse)
|
||||||
|
{
|
||||||
|
bdd label = bdd_exist(cube, dict_.next_set);
|
||||||
|
bdd dest_bdd = bdd_existcomp(cube, dict_.next_set);
|
||||||
|
formula* dest = dict_.conj_bdd_to_formula(dest_bdd);
|
||||||
|
formula* dest2;
|
||||||
|
int x;
|
||||||
|
if (dest == constant::empty_word_instance())
|
||||||
|
{
|
||||||
|
res_ |= label & next_to_concat();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dest2 = multop::instance(multop::Concat, dest,
|
||||||
|
to_concat_->clone());
|
||||||
|
if (dest2 != constant::false_instance())
|
||||||
|
{
|
||||||
|
x = dict_.register_next_variable(dest2);
|
||||||
|
dest2->destroy();
|
||||||
|
res_ |= label & bdd_ithvar(x);
|
||||||
|
}
|
||||||
|
if (constant_term_as_bool(node))
|
||||||
|
res_ |= label & next_to_concat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case multop::Or:
|
||||||
|
{
|
||||||
|
res_ = bddfalse;
|
||||||
|
unsigned s = node->size();
|
||||||
|
if (to_concat_)
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
res_ |= recurse(node->nth(n), to_concat_->clone());
|
||||||
|
else
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
res_ |= recurse(node->nth(n));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case multop::Concat:
|
||||||
|
{
|
||||||
|
multop::vec* v = new multop::vec;
|
||||||
|
unsigned s = node->size();
|
||||||
|
v->reserve(s);
|
||||||
|
for (unsigned n = 1; n < s; ++n)
|
||||||
|
v->push_back(node->nth(n)->clone());
|
||||||
|
if (to_concat_)
|
||||||
|
v->push_back(to_concat_->clone());
|
||||||
|
res_ = recurse(node->nth(0),
|
||||||
|
multop::instance(multop::Concat, v));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
recurse(const formula* f, formula* to_concat = 0)
|
||||||
|
{
|
||||||
|
ratexp_trad_visitor v(dict_, to_concat);
|
||||||
|
f->accept(v);
|
||||||
|
return v.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
translate_dict& dict_;
|
||||||
|
bdd res_;
|
||||||
|
formula* to_concat_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// 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, augmented to support rational operators.
|
||||||
class ltl_trad_visitor: public const_visitor
|
class ltl_trad_visitor: public const_visitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ltl_trad_visitor(translate_dict& dict)
|
ltl_trad_visitor(translate_dict& dict, bool mark_all = false)
|
||||||
: dict_(dict)
|
: dict_(dict), rat_seen_(false), has_marked_(false), mark_all_(mark_all)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,12 +520,38 @@ namespace spot
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reset(bool mark_all)
|
||||||
|
{
|
||||||
|
rat_seen_ = false;
|
||||||
|
has_marked_ = false;
|
||||||
|
mark_all_ = mark_all;
|
||||||
|
}
|
||||||
|
|
||||||
bdd
|
bdd
|
||||||
result() const
|
result() const
|
||||||
{
|
{
|
||||||
return res_;
|
return res_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const translate_dict&
|
||||||
|
get_dict() const
|
||||||
|
{
|
||||||
|
return dict_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
has_rational() const
|
||||||
|
{
|
||||||
|
return rat_seen_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
has_marked() const
|
||||||
|
{
|
||||||
|
return has_marked_;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const atomic_prop* node)
|
visit(const atomic_prop* node)
|
||||||
{
|
{
|
||||||
|
|
@ -308,7 +570,8 @@ namespace spot
|
||||||
res_ = bddfalse;
|
res_ = bddfalse;
|
||||||
return;
|
return;
|
||||||
case constant::EmptyWord:
|
case constant::EmptyWord:
|
||||||
assert(!"unsupported operator");
|
assert(!"Not an LTL operator");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/* Unreachable code. */
|
/* Unreachable code. */
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
@ -327,7 +590,7 @@ namespace spot
|
||||||
int a = dict_.register_a_variable(child);
|
int a = dict_.register_a_variable(child);
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
res_ = y | (bdd_ithvar(a) & bdd_ithvar(x));
|
res_ = y | (bdd_ithvar(a) & bdd_ithvar(x));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case unop::G:
|
case unop::G:
|
||||||
{
|
{
|
||||||
|
|
@ -347,84 +610,183 @@ namespace spot
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
bdd y = recurse(child);
|
bdd y = recurse(child);
|
||||||
res_ = y & bdd_ithvar(x);
|
res_ = y & bdd_ithvar(x);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case unop::Not:
|
case unop::Not:
|
||||||
{
|
{
|
||||||
// r(!y) = !r(y)
|
// r(!y) = !r(y)
|
||||||
res_ = bdd_not(recurse(node->child()));
|
res_ = bdd_not(recurse(node->child()));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case unop::X:
|
case unop::X:
|
||||||
{
|
{
|
||||||
// r(Xy) = Next[y]
|
// r(Xy) = Next[y]
|
||||||
int x = dict_.register_next_variable(node->child());
|
int x = dict_.register_next_variable(node->child());
|
||||||
res_ = bdd_ithvar(x);
|
res_ = bdd_ithvar(x);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case unop::Finish:
|
case unop::Finish:
|
||||||
case unop::Star:
|
|
||||||
assert(!"unsupported operator");
|
assert(!"unsupported operator");
|
||||||
|
break;
|
||||||
|
case unop::Star:
|
||||||
|
assert(!"Not an LTL operator");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* node)
|
visit(const binop* node)
|
||||||
{
|
{
|
||||||
bdd f1 = recurse(node->first());
|
binop::type op = node->op();
|
||||||
bdd f2 = recurse(node->second());
|
|
||||||
|
|
||||||
switch (node->op())
|
switch (op)
|
||||||
{
|
{
|
||||||
// r(f1 logical-op f2) = r(f1) logical-op r(f2)
|
// r(f1 logical-op f2) = r(f1) logical-op r(f2)
|
||||||
case binop::Xor:
|
case binop::Xor:
|
||||||
res_ = bdd_apply(f1, f2, bddop_xor);
|
{
|
||||||
return;
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_xor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case binop::Implies:
|
case binop::Implies:
|
||||||
res_ = bdd_apply(f1, f2, bddop_imp);
|
{
|
||||||
return;
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_imp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case binop::Equiv:
|
case binop::Equiv:
|
||||||
res_ = bdd_apply(f1, f2, bddop_biimp);
|
{
|
||||||
return;
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_biimp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case binop::U:
|
case binop::U:
|
||||||
{
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
// r(f1 U f2) = r(f2) + a(f2)r(f1)r(X(f1 U f2))
|
// r(f1 U f2) = r(f2) + a(f2)r(f1)r(X(f1 U f2))
|
||||||
int a = dict_.register_a_variable(node->second());
|
int a = dict_.register_a_variable(node->second());
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
res_ = f2 | (bdd_ithvar(a) & f1 & bdd_ithvar(x));
|
res_ = f2 | (bdd_ithvar(a) & f1 & bdd_ithvar(x));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case binop::W:
|
case binop::W:
|
||||||
{
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
// r(f1 W f2) = r(f2) + r(f1)r(X(f1 U f2))
|
// r(f1 W f2) = r(f2) + r(f1)r(X(f1 U f2))
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
res_ = f2 | (f1 & bdd_ithvar(x));
|
res_ = f2 | (f1 & bdd_ithvar(x));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case binop::R:
|
case binop::R:
|
||||||
{
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
// r(f1 R f2) = r(f1)r(f2) + r(f2)r(X(f1 U f2))
|
// r(f1 R f2) = r(f1)r(f2) + r(f2)r(X(f1 U f2))
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
res_ = (f1 & f2) | (f2 & bdd_ithvar(x));
|
res_ = (f1 & f2) | (f2 & bdd_ithvar(x));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case binop::M:
|
case binop::M:
|
||||||
{
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
// r(f1 M f2) = r(f1)r(f2) + a(f1)r(f2)r(X(f1 M f2))
|
// r(f1 M f2) = r(f1)r(f2) + a(f1)r(f2)r(X(f1 M f2))
|
||||||
int a = dict_.register_a_variable(node->first());
|
int a = dict_.register_a_variable(node->first());
|
||||||
int x = dict_.register_next_variable(node);
|
int x = dict_.register_next_variable(node);
|
||||||
res_ = (f1 & f2) | (bdd_ithvar(a) & f2 & bdd_ithvar(x));
|
res_ = (f1 & f2) | (bdd_ithvar(a) & f2 & bdd_ithvar(x));
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case binop::UConcat:
|
case binop::EConcatMarked:
|
||||||
|
has_marked_ = true;
|
||||||
|
/* fall through */
|
||||||
case binop::EConcat:
|
case binop::EConcat:
|
||||||
assert(!"unsupported operator");
|
rat_seen_ = true;
|
||||||
|
{
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
ratexp_trad_visitor v(dict_);
|
||||||
|
node->first()->accept(v);
|
||||||
|
bdd f1 = v.result();
|
||||||
|
|
||||||
|
if (mark_all_)
|
||||||
|
{
|
||||||
|
op = binop::EConcatMarked;
|
||||||
|
has_marked_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recognize f2 on transitions going to destinations
|
||||||
|
// that accept the empty word.
|
||||||
|
minato_isop isop(f1);
|
||||||
|
bdd cube;
|
||||||
|
res_ = bddfalse;
|
||||||
|
while ((cube = isop.next()) != bddfalse)
|
||||||
|
{
|
||||||
|
bdd label = bdd_exist(cube, dict_.next_set);
|
||||||
|
bdd dest_bdd = bdd_existcomp(cube, dict_.next_set);
|
||||||
|
formula* dest = dict_.conj_bdd_to_formula(dest_bdd);
|
||||||
|
formula* dest2;
|
||||||
|
int x;
|
||||||
|
if (dest == constant::empty_word_instance())
|
||||||
|
{
|
||||||
|
res_ |= label & f2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dest2 = binop::instance(op, dest,
|
||||||
|
node->second()->clone());
|
||||||
|
if (dest2 != constant::false_instance())
|
||||||
|
{
|
||||||
|
x = dict_.register_next_variable(dest2);
|
||||||
|
dest2->destroy();
|
||||||
|
res_ |= label & bdd_ithvar(x);
|
||||||
|
}
|
||||||
|
if (constant_term_as_bool(dest))
|
||||||
|
res_ |= label & f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binop::UConcat:
|
||||||
|
{
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
ratexp_trad_visitor v(dict_);
|
||||||
|
node->first()->accept(v);
|
||||||
|
bdd f1 = v.result();
|
||||||
|
|
||||||
|
// Transitions going to destinations accepting the empty
|
||||||
|
// word should recognize f2, and the automaton should be
|
||||||
|
// understood as universal.
|
||||||
|
minato_isop isop(f1);
|
||||||
|
bdd cube;
|
||||||
|
res_ = bddtrue;
|
||||||
|
while ((cube = isop.next()) != bddfalse)
|
||||||
|
{
|
||||||
|
bdd label = bdd_exist(cube, dict_.next_set);
|
||||||
|
bdd dest_bdd = bdd_existcomp(cube, dict_.next_set);
|
||||||
|
formula* dest = dict_.conj_bdd_to_formula(dest_bdd);
|
||||||
|
formula* dest2;
|
||||||
|
bdd udest;
|
||||||
|
|
||||||
|
dest2 = binop::instance(op, dest,
|
||||||
|
node->second()->clone());
|
||||||
|
udest = bdd_ithvar(dict_.register_next_variable(dest2));
|
||||||
|
|
||||||
|
if (constant_term_as_bool(dest))
|
||||||
|
udest &= f2;
|
||||||
|
|
||||||
|
dest2->destroy();
|
||||||
|
label = bdd_apply(label, udest, bddop_imp);
|
||||||
|
|
||||||
|
res_ &= label;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -436,33 +798,43 @@ namespace spot
|
||||||
void
|
void
|
||||||
visit(const multop* node)
|
visit(const multop* node)
|
||||||
{
|
{
|
||||||
int op = -1;
|
|
||||||
switch (node->op())
|
switch (node->op())
|
||||||
{
|
{
|
||||||
case multop::And:
|
case multop::And:
|
||||||
op = bddop_and;
|
{
|
||||||
res_ = bddtrue;
|
res_ = bddtrue;
|
||||||
break;
|
unsigned s = node->size();
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
{
|
||||||
|
bdd res = recurse(node->nth(n));
|
||||||
|
//std::cerr << "=== in And" << std::endl;
|
||||||
|
//trace_ltl_bdd(dict_, res);
|
||||||
|
res_ &= res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case multop::Or:
|
case multop::Or:
|
||||||
op = bddop_or;
|
{
|
||||||
res_ = bddfalse;
|
res_ = bddfalse;
|
||||||
break;
|
unsigned s = node->size();
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
res_ |= recurse(node->nth(n));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case multop::Concat:
|
case multop::Concat:
|
||||||
assert(!"unsupported operator");
|
assert(!"Not an LTL operator");
|
||||||
}
|
break;
|
||||||
assert(op != -1);
|
|
||||||
unsigned s = node->size();
|
|
||||||
for (unsigned n = 0; n < s; ++n)
|
|
||||||
{
|
|
||||||
res_ = bdd_apply(res_, recurse(node->nth(n)), op);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd
|
bdd
|
||||||
recurse(const formula* f)
|
recurse(const formula* f)
|
||||||
{
|
{
|
||||||
ltl_trad_visitor v(dict_);
|
ltl_trad_visitor v(dict_, mark_all_);
|
||||||
f->accept(v);
|
f->accept(v);
|
||||||
|
rat_seen_ |= v.has_rational();
|
||||||
|
has_marked_ |= v.has_marked();
|
||||||
return v.result();
|
return v.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,6 +842,9 @@ namespace spot
|
||||||
private:
|
private:
|
||||||
translate_dict& dict_;
|
translate_dict& dict_;
|
||||||
bdd res_;
|
bdd res_;
|
||||||
|
bool rat_seen_;
|
||||||
|
bool has_marked_;
|
||||||
|
bool mark_all_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -533,7 +908,9 @@ namespace spot
|
||||||
return;
|
return;
|
||||||
case binop::UConcat:
|
case binop::UConcat:
|
||||||
case binop::EConcat:
|
case binop::EConcat:
|
||||||
|
case binop::EConcatMarked:
|
||||||
node->second()->accept(*this);
|
node->second()->accept(*this);
|
||||||
|
// FIXME: we might need to add Acc[1]
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Unreachable code. */
|
/* Unreachable code. */
|
||||||
|
|
@ -590,7 +967,8 @@ namespace spot
|
||||||
bool fair_loop_approx, bdd all_promises)
|
bool fair_loop_approx, bdd all_promises)
|
||||||
: v_(d),
|
: v_(d),
|
||||||
fair_loop_approx_(fair_loop_approx),
|
fair_loop_approx_(fair_loop_approx),
|
||||||
all_promises_(all_promises)
|
all_promises_(all_promises),
|
||||||
|
d_(d)
|
||||||
{
|
{
|
||||||
// For cosmetics, register 1 initially, so the algorithm will
|
// For cosmetics, register 1 initially, so the algorithm will
|
||||||
// not register an equivalent formula first.
|
// not register an equivalent formula first.
|
||||||
|
|
@ -608,7 +986,14 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd
|
struct translated
|
||||||
|
{
|
||||||
|
bdd symbolic;
|
||||||
|
bool has_rational:1;
|
||||||
|
bool has_marked:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const translated&
|
||||||
translate(const formula* f, bool* new_flag = 0)
|
translate(const formula* f, bool* new_flag = 0)
|
||||||
{
|
{
|
||||||
// Use the cached result if available.
|
// Use the cached result if available.
|
||||||
|
|
@ -620,8 +1005,62 @@ namespace spot
|
||||||
*new_flag = true;
|
*new_flag = true;
|
||||||
|
|
||||||
// Perform the actual translation.
|
// Perform the actual translation.
|
||||||
|
v_.reset(!has_mark(f));
|
||||||
f->accept(v_);
|
f->accept(v_);
|
||||||
bdd res = v_.result();
|
translated t;
|
||||||
|
t.symbolic = v_.result();
|
||||||
|
t.has_rational = v_.has_rational();
|
||||||
|
t.has_marked = v_.has_marked();
|
||||||
|
|
||||||
|
// std::cerr << "-----" << std::endl;
|
||||||
|
// std::cerr << "Formula: " << to_string(f) << std::endl;
|
||||||
|
// std::cerr << "Rational: " << t.has_rational << std::endl;
|
||||||
|
// std::cerr << "Marked: " << t.has_marked << std::endl;
|
||||||
|
// std::cerr << "Mark all: " << !has_mark(f) << std::endl;
|
||||||
|
// std::cerr << "Transitions:" << std::endl;
|
||||||
|
// trace_ltl_bdd(v_.get_dict(), t.symbolic);
|
||||||
|
|
||||||
|
if (t.has_rational)
|
||||||
|
{
|
||||||
|
bdd res = bddfalse;
|
||||||
|
|
||||||
|
minato_isop isop(t.symbolic);
|
||||||
|
bdd cube;
|
||||||
|
while ((cube = isop.next()) != bddfalse)
|
||||||
|
{
|
||||||
|
bdd label = bdd_exist(cube, d_.next_set);
|
||||||
|
bdd dest_bdd = bdd_existcomp(cube, d_.next_set);
|
||||||
|
formula* dest =
|
||||||
|
d_.conj_bdd_to_formula(dest_bdd);
|
||||||
|
|
||||||
|
// Handle a Miyano-Hayashi style unrolling for
|
||||||
|
// rational operators. Marked nodes correspond to
|
||||||
|
// subformulae in the Miyano-Hayashi set.
|
||||||
|
if (simplify_mark(dest))
|
||||||
|
{
|
||||||
|
// Make the promise that we will exit marked sets.
|
||||||
|
int a =
|
||||||
|
d_.register_a_variable(constant::true_instance());
|
||||||
|
label &= bdd_ithvar(a);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We have left marked operators, but still
|
||||||
|
// have other rational operator to check.
|
||||||
|
// Start a new marked cycle.
|
||||||
|
formula* dest2 = mark_concat_ops(dest);
|
||||||
|
dest->destroy();
|
||||||
|
dest = dest2;
|
||||||
|
}
|
||||||
|
// Note that simplify_mark may have changed dest.
|
||||||
|
dest_bdd = bdd_ithvar(d_.register_next_variable(dest));
|
||||||
|
dest->destroy();
|
||||||
|
res |= label & dest_bdd;
|
||||||
|
}
|
||||||
|
t.symbolic = res;
|
||||||
|
// std::cerr << "Marking rewriting:" << std::endl;
|
||||||
|
// trace_ltl_bdd(v_.get_dict(), t.symbolic);
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the fair-loop approximation if requested.
|
// Apply the fair-loop approximation if requested.
|
||||||
if (fair_loop_approx_)
|
if (fair_loop_approx_)
|
||||||
|
|
@ -631,22 +1070,21 @@ namespace spot
|
||||||
if (fair_loop_approx_
|
if (fair_loop_approx_
|
||||||
&& f != constant::true_instance()
|
&& f != constant::true_instance()
|
||||||
&& !pflc_.check(f))
|
&& !pflc_.check(f))
|
||||||
res &= all_promises_;
|
t.symbolic &= all_promises_;
|
||||||
}
|
}
|
||||||
|
|
||||||
f2b_[f->clone()] = res;
|
|
||||||
|
|
||||||
// Register the reverse mapping if it is not already done.
|
// Register the reverse mapping if it is not already done.
|
||||||
if (b2f_.find(res) == b2f_.end())
|
if (b2f_.find(t.symbolic) == b2f_.end())
|
||||||
b2f_[res] = f;
|
b2f_[t.symbolic] = f;
|
||||||
return res;
|
|
||||||
|
return f2b_[f->clone()] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formula*
|
const formula*
|
||||||
canonize(const formula* f)
|
canonize(const formula* f)
|
||||||
{
|
{
|
||||||
bool new_variable = false;
|
bool new_variable = false;
|
||||||
bdd b = translate(f, &new_variable);
|
bdd b = translate(f, &new_variable).symbolic;
|
||||||
|
|
||||||
bdd_to_formula_map::iterator i = b2f_.find(b);
|
bdd_to_formula_map::iterator i = b2f_.find(b);
|
||||||
// Since we have just translated the formula, it is
|
// Since we have just translated the formula, it is
|
||||||
|
|
@ -673,12 +1111,13 @@ namespace spot
|
||||||
// We do this because many formulae (such as `aR(bRc)' and
|
// We do this because many formulae (such as `aR(bRc)' and
|
||||||
// `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.
|
||||||
typedef std::map<const formula*, bdd> formula_to_bdd_map;
|
typedef std::map<const formula*, translated> formula_to_bdd_map;
|
||||||
formula_to_bdd_map f2b_;
|
formula_to_bdd_map f2b_;
|
||||||
|
|
||||||
possible_fair_loop_checker pflc_;
|
possible_fair_loop_checker pflc_;
|
||||||
bool fair_loop_approx_;
|
bool fair_loop_approx_;
|
||||||
bdd all_promises_;
|
bdd all_promises_;
|
||||||
|
translate_dict& d_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -781,6 +1220,10 @@ namespace spot
|
||||||
|
|
||||||
tgba_explicit_formula* a = new tgba_explicit_formula(dict);
|
tgba_explicit_formula* a = new tgba_explicit_formula(dict);
|
||||||
|
|
||||||
|
// This is in case the initial state is equivalent to true...
|
||||||
|
if (symb_merge)
|
||||||
|
f2 = const_cast<formula*>(fc.canonize(f2));
|
||||||
|
|
||||||
formulae_to_translate.insert(f2);
|
formulae_to_translate.insert(f2);
|
||||||
a->set_init_state(f2);
|
a->set_init_state(f2);
|
||||||
|
|
||||||
|
|
@ -791,7 +1234,8 @@ namespace spot
|
||||||
formulae_to_translate.erase(formulae_to_translate.begin());
|
formulae_to_translate.erase(formulae_to_translate.begin());
|
||||||
|
|
||||||
// Translate it into a BDD to simplify it.
|
// Translate it into a BDD to simplify it.
|
||||||
bdd res = fc.translate(now);
|
const formula_canonizer::translated& t = fc.translate(now);
|
||||||
|
bdd res = t.symbolic;
|
||||||
|
|
||||||
// Handle exclusive events.
|
// Handle exclusive events.
|
||||||
if (unobs)
|
if (unobs)
|
||||||
|
|
|
||||||
|
|
@ -27,30 +27,43 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
check ()
|
check_psl ()
|
||||||
{
|
{
|
||||||
run 0 ../ltl2tgba -l "$1"
|
# We don't check the output, but just running these might be enough to
|
||||||
run 0 ../ltl2tgba -f "$1"
|
# trigger assertions.
|
||||||
run 0 ../ltl2tgba -f -FC "$1"
|
run 0 ../ltl2tgba -f -FC "$1"
|
||||||
|
# Make cross products with FM
|
||||||
|
run 0 ../ltl2tgba -f -R3 -b "$1" > out.tgba
|
||||||
|
run 0 ../ltl2tgba -f -R3 -Pout.tgba -E "!($1)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# We don't check the output, but just running these might be enough to
|
check_ltl ()
|
||||||
# trigger assertions.
|
{
|
||||||
|
check_psl "$@"
|
||||||
|
# Make cross products with LaCIM
|
||||||
|
run 0 ../ltl2tgba -l -R3b -b "$1" > out.tgba
|
||||||
|
run 0 ../ltl2tgba -l -R3b -Pout.tgba -E "!($1)"
|
||||||
|
}
|
||||||
|
|
||||||
check a
|
check_ltl a
|
||||||
check 'a U b'
|
check_ltl 'a U b'
|
||||||
check 'X a'
|
check_ltl 'X a'
|
||||||
check 'a & b & c'
|
check_ltl 'a & b & c'
|
||||||
check 'a | b | (c U (d & (g U (h ^ i))))'
|
check_ltl 'a | b | (c U (d & (g U (h ^ i))))'
|
||||||
check 'Xa & (b U !a) & (b U !a)'
|
check_ltl 'Xa & (b U !a) & (b U !a)'
|
||||||
check 'Fa & Xb & GFc & Gd'
|
check_ltl 'Fa & Xb & GFc & Gd'
|
||||||
check 'Fa & Xa & GFc & Gc'
|
check_ltl 'Fa & Xa & GFc & Gc'
|
||||||
check 'Fc & X(a | Xb) & GF(a | Xb) & Gc'
|
check_ltl 'Fc & X(a | Xb) & GF(a | Xb) & Gc'
|
||||||
check 'a R (b R c)'
|
check_ltl 'a R (b R c)'
|
||||||
check '(a U b) U (c U d)'
|
check_ltl '(a U b) U (c U d)'
|
||||||
|
|
||||||
check '((Xp2)U(X(1)))&(p1 R(p2 R p0))'
|
check_ltl '((Xp2)U(X(1)))&(p1 R(p2 R p0))'
|
||||||
|
|
||||||
|
check_psl '{a*;c}<>->GFb'
|
||||||
|
check_psl '{((a*;b;c)*)&((b*;a;c)*)}<>->x'
|
||||||
|
check_psl '{(g;y;r)*}<>->x'
|
||||||
|
check_psl 'G({(g;y;r)*}<>->x)'
|
||||||
|
check_psl 'G({(a;b)*}<>->x)&G({(c;d)*}<>->y)'
|
||||||
|
|
||||||
# Make sure 'a U (b U c)' has 3 states and 6 transitions,
|
# Make sure 'a U (b U c)' has 3 states and 6 transitions,
|
||||||
# before and after degeneralization.
|
# before and after degeneralization.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue