This implements Couvreur's FM'99 ltl2tgba translation.

* src/tgba/bdddict.cc (bdd_dict::is_registered): Split as ...
(bdd_dict::is_registered_proposition, bdd_dict::is_registered_state,
bdd_dict::is_registered_accepting_variable): ... these.
* src/tgba/bdddict.hh: Likewise.
* src/tgba/tgbaexplicit.cc (tgba_explicit::set_init_state): New method.
(tgba_explicit::declare_accepting_condition): Arrange so that this
function can be called during the construction of the automaton.
(tgba_explicit::complement_all_accepting_conditions): New method.
(tgba_explicit::has_accepting_condition): Adjust to call
bdd_dict::is_registered_accepting_variable.
* src/tgba/tgbaexplicit.hh (tgba_explicit::set_init_state,
tgba_explicit::complement_all_accepting_conditions): New methods.
* src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/ltl2tgba_fm.hh:
New files.
* src/tgbaalgos/Makefile.am (tgbaalgos_HEADERS,
libtgbaalgos_la_SOURCES): Add them.
* src/tgbaalgos/ltl2tgba.hh: Add bibtex entry in comment.
* src/tgbatest/Makefile.am (check_PROGRAMS): Remove spotlbtt
and tbalbtt.
(tbalbtt_SOURCES, tbalbtt_CXXFLAGS, spotlbtt_SOURCES): Remove.
* src/tgbatest/spotlbtt.cc: Delete, superseded by "ltl2tgba -F -t".
* src/tgbatest/ltl2tgba.cc: Implement the -f and -F options.
* src/tgbatest/spotlbtt.test: Use "ltl2tgba -F -t" instead of
"spotlbtt", "ltl2tgba -F -t -D" instead of "tbalbtt", and add
also check the ltl2tgba_fm translator.
* wrap/python/spot.i: Wrap ltl2tgba_fm.
* wrap/python/cgi/ltl2tgba.in: Add radio buttons to select
between ltl2tgba and ltl2tgba_fm.
* wrap/python/tests/ltl2tgba.py: Add support for the -f option.
* wrap/python/tests/ltl2tgba.test: Try the -f option.
This commit is contained in:
Alexandre Duret-Lutz 2003-08-15 01:33:09 +00:00
parent 256d800580
commit 2b9f17202c
17 changed files with 820 additions and 159 deletions

View file

@ -1,3 +1,46 @@
2003-08-15 Alexandre Duret-Lutz <adl@gnu.org>
This implements Couvreur's FM'99 ltl2tgba translation.
* src/tgba/bdddict.cc (bdd_dict::is_registered): Split as ...
(bdd_dict::is_registered_proposition, bdd_dict::is_registered_state,
bdd_dict::is_registered_accepting_variable): ... these.
* src/tgba/bdddict.hh: Likewise.
* src/tgba/tgbaexplicit.cc (tgba_explicit::set_init_state): New method.
(tgba_explicit::declare_accepting_condition): Arrange so that this
function can be called during the construction of the automaton.
(tgba_explicit::complement_all_accepting_conditions): New method.
(tgba_explicit::has_accepting_condition): Adjust to call
bdd_dict::is_registered_accepting_variable.
* src/tgba/tgbaexplicit.hh (tgba_explicit::set_init_state,
tgba_explicit::complement_all_accepting_conditions): New methods.
* src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/ltl2tgba_fm.hh:
New files.
* src/tgbaalgos/Makefile.am (tgbaalgos_HEADERS,
libtgbaalgos_la_SOURCES): Add them.
* src/tgbaalgos/ltl2tgba.hh: Add bibtex entry in comment.
* src/tgbatest/Makefile.am (check_PROGRAMS): Remove spotlbtt
and tbalbtt.
(tbalbtt_SOURCES, tbalbtt_CXXFLAGS, spotlbtt_SOURCES): Remove.
* src/tgbatest/spotlbtt.cc: Delete, superseded by "ltl2tgba -F -t".
* src/tgbatest/ltl2tgba.cc: Implement the -f and -F options.
* src/tgbatest/spotlbtt.test: Use "ltl2tgba -F -t" instead of
"spotlbtt", "ltl2tgba -F -t -D" instead of "tbalbtt", and add
also check the ltl2tgba_fm translator.
* wrap/python/spot.i: Wrap ltl2tgba_fm.
* wrap/python/cgi/ltl2tgba.in: Add radio buttons to select
between ltl2tgba and ltl2tgba_fm.
* wrap/python/tests/ltl2tgba.py: Add support for the -f option.
* wrap/python/tests/ltl2tgba.test: Try the -f option.
varnum can be augmented by other allocator. Keep track
of a local varnum (lvarnum) in each allocator.
* src/misc/bddalloc.cc (bdd_allocator::bdd_allocator): Initialize
lvarnum.
(bdd_allocator::extvarnum): New method.
(bdd_allocator::allocate_variables): Use lvarnum and extvarnum.
* src/misc/bddalloc.hh (bdd_allocator::extvarnum): New mathod.
(bdd_allocator::lvarnum): New variable.
2003-08-14 Alexandre Duret-Lutz <aduret@src.lip6.fr> 2003-08-14 Alexandre Duret-Lutz <aduret@src.lip6.fr>
* src/tgba/state.hh, src/tgba/statebdd.hh, src/tgba/statebdd.cc: * src/tgba/state.hh, src/tgba/statebdd.hh, src/tgba/statebdd.cc:

View file

@ -161,30 +161,33 @@ namespace spot
} }
bool bool
bdd_dict::is_registered(const ltl::formula* f, const void* by_me) bdd_dict::is_registered_proposition(const ltl::formula* f, const void* by_me)
{ {
int var;
fv_map::iterator fi = var_map.find(f); fv_map::iterator fi = var_map.find(f);
if (fi != var_map.end()) if (fi == var_map.end())
{ return false;
var = fi->second; ref_set& s = var_refs[fi->second];
} return s.find(by_me) != s.end();
else }
{
fi = now_map.find(f); bool
if (fi != now_map.end()) bdd_dict::is_registered_state(const ltl::formula* f, const void* by_me)
{ {
var = fi->second; fv_map::iterator fi = now_map.find(f);
} if (fi == now_map.end())
else return false;
{ ref_set& s = var_refs[fi->second];
fi = acc_map.find(f); return s.find(by_me) != s.end();
if (fi == acc_map.end()) }
return false;
var = fi->second; bool
} bdd_dict::is_registered_accepting_variable(const ltl::formula* f,
} const void* by_me)
ref_set& s = var_refs[var]; {
fv_map::iterator fi = acc_map.find(f);
if (fi == acc_map.end())
return false;
ref_set& s = var_refs[fi->second];
return s.find(by_me) != s.end(); return s.find(by_me) != s.end();
} }

View file

@ -91,8 +91,13 @@ namespace spot
/// Usually called in the destructor if \a me. /// Usually called in the destructor if \a me.
void unregister_all_my_variables(const void* me); void unregister_all_my_variables(const void* me);
/// @{
/// Check whether formula \a f has already been registered by \a by_me. /// Check whether formula \a f has already been registered by \a by_me.
bool is_registered(const ltl::formula* f, const void* by_me); bool is_registered_proposition(const ltl::formula* f, const void* by_me);
bool is_registered_state(const ltl::formula* f, const void* by_me);
bool is_registered_accepting_variable(const ltl::formula* f,
const void* by_me);
/// @}
/// \brief Dump all variables for debugging. /// \brief Dump all variables for debugging.
/// \param os The output stream. /// \param os The output stream.

View file

@ -111,6 +111,7 @@ namespace spot
state_name_map_[s] = name; state_name_map_[s] = name;
// The first state we add is the inititial state. // The first state we add is the inititial state.
// It can also be overridden with set_init_state().
if (! init_) if (! init_)
init_ = s; init_ = s;
@ -119,6 +120,14 @@ namespace spot
return i->second; return i->second;
} }
void
tgba_explicit::set_init_state(const std::string& state)
{
tgba_explicit::state* s = add_state(state);
init_ = s;
}
tgba_explicit::transition* tgba_explicit::transition*
tgba_explicit::create_transition(const std::string& source, tgba_explicit::create_transition(const std::string& source,
const std::string& dest) const std::string& dest)
@ -159,13 +168,40 @@ namespace spot
{ {
int v = dict_->register_accepting_variable(f, this); int v = dict_->register_accepting_variable(f, this);
ltl::destroy(f); ltl::destroy(f);
neg_accepting_conditions_ &= bdd_nithvar(v); bdd neg = bdd_nithvar(v);
neg_accepting_conditions_ &= neg;
// Append neg to all acceptance conditions.
ns_map::iterator i;
for (i = name_state_map_.begin(); i != name_state_map_.end(); ++i)
{
tgba_explicit::state::iterator i2;
for (i2 = i->second->begin(); i2 != i->second->end(); ++i2)
(*i2)->accepting_conditions &= neg;
}
all_accepting_conditions_computed_ = false;
}
void
tgba_explicit::complement_all_accepting_conditions()
{
bdd all = all_accepting_conditions();
ns_map::iterator i;
for (i = name_state_map_.begin(); i != name_state_map_.end(); ++i)
{
tgba_explicit::state::iterator i2;
for (i2 = i->second->begin(); i2 != i->second->end(); ++i2)
{
(*i2)->accepting_conditions = all - (*i2)->accepting_conditions;
}
}
} }
bool bool
tgba_explicit::has_accepting_condition(ltl::formula* f) const tgba_explicit::has_accepting_condition(ltl::formula* f) const
{ {
return dict_->is_registered(f, this); return dict_->is_registered_accepting_variable(f, this);
} }
bdd bdd
@ -185,6 +221,9 @@ namespace spot
assert(0); assert(0);
} }
bdd_dict::fv_map::iterator i = dict_->acc_map.find(f); bdd_dict::fv_map::iterator i = dict_->acc_map.find(f);
assert(has_accepting_condition(f));
/* If this second assert fails and the first doesn't,
things are badly broken. This has already happened. */
assert(i != dict_->acc_map.end()); assert(i != dict_->acc_map.end());
ltl::destroy(f); ltl::destroy(f);
bdd v = bdd_ithvar(i->second); bdd v = bdd_ithvar(i->second);

View file

@ -29,6 +29,8 @@ namespace spot
state* dest; state* dest;
}; };
void set_init_state(const std::string& state);
transition* transition*
create_transition(const std::string& source, const std::string& dest); create_transition(const std::string& source, const std::string& dest);
@ -37,6 +39,7 @@ namespace spot
void declare_accepting_condition(ltl::formula* f); void declare_accepting_condition(ltl::formula* f);
bool has_accepting_condition(ltl::formula* f) const; bool has_accepting_condition(ltl::formula* f) const;
void add_accepting_condition(transition* t, ltl::formula* f); void add_accepting_condition(transition* t, ltl::formula* f);
void complement_all_accepting_conditions();
// tgba interface // tgba interface
virtual ~tgba_explicit(); virtual ~tgba_explicit();

View file

@ -8,6 +8,7 @@ tgbaalgos_HEADERS = \
dotty.hh \ dotty.hh \
lbtt.hh \ lbtt.hh \
ltl2tgba.hh \ ltl2tgba.hh \
ltl2tgba_fm.hh \
magic.hh \ magic.hh \
save.hh save.hh
@ -17,5 +18,6 @@ libtgbaalgos_la_SOURCES = \
dotty.cc \ dotty.cc \
lbtt.cc \ lbtt.cc \
ltl2tgba.cc \ ltl2tgba.cc \
ltl2tgba_fm.cc \
magic.cc \ magic.cc \
save.cc save.cc

View file

@ -7,6 +7,23 @@
namespace spot namespace spot
{ {
/// Build a spot::tgba_bdd_concrete from an LTL formula. /// Build a spot::tgba_bdd_concrete from an LTL formula.
///
/// This is based on the following paper.
/// \verbatim
/// @InProceedings{ couvreur.00.lacim,
/// author = {Jean-Michel Couvreur},
/// title = {Un point de vue symbolique sur la logique temporelle
/// lin{\'e}aire},
/// booktitle = {Actes du Colloque LaCIM 2000},
/// month = {August},
/// year = {2000},
/// pages = {131--140},
/// volume = {27},
/// series = {Publications du LaCIM},
/// publisher = {Universit{\'e} du Qu{\'e}bec {\`a} Montr{\'e}al},
/// editor = {Pierre Leroux}
/// }
/// \endverbatim
tgba_bdd_concrete* ltl_to_tgba(const ltl::formula* f, bdd_dict* dict); tgba_bdd_concrete* ltl_to_tgba(const ltl::formula* f, bdd_dict* dict);
} }

View file

@ -0,0 +1,483 @@
#include "misc/bddalloc.hh"
#include "ltlast/visitor.hh"
#include "ltlast/allnodes.hh"
#include "ltlvisit/lunabbrev.hh"
#include "ltlvisit/nenoform.hh"
#include "ltlvisit/destroy.hh"
#include "ltlvisit/tostring.hh"
#include <cassert>
#include "tgba/tgbabddconcretefactory.hh"
#include "ltl2tgba_fm.hh"
namespace spot
{
using namespace ltl;
namespace
{
// Helper dictionary. We represent formula using a BDD to simplify
// them, and them translate the BDD back into formulae.
//
// The name of the variables are inspired from Couvreur's FM paper.
// "a" variables are promises (written "a" in the paper)
// "next" variables are X's operands (the "r_X" variables from the paper)
// "var" variables are atomic propositions.
class translate_dict: public bdd_allocator
{
public:
translate_dict()
: bdd_allocator(),
a_set(bddtrue),
var_set(bddtrue),
next_set(bddtrue)
{
}
~translate_dict()
{
fv_map::iterator i;
for (i = a_map.begin(); i != a_map.end(); ++i)
ltl::destroy(i->first);
for (i = var_map.begin(); i != var_map.end(); ++i)
ltl::destroy(i->first);
for (i = next_map.begin(); i != next_map.end(); ++i)
ltl::destroy(i->first);
}
/// Formula-to-BDD-variable maps.
typedef std::map<const ltl::formula*, int> fv_map;
/// BDD-variable-to-formula maps.
typedef std::map<int, const ltl::formula*> vf_map;
fv_map a_map; ///< Maps formulae to "a" BDD variables
vf_map a_formula_map; ///< Maps "a" BDD variables to formulae
fv_map var_map; ///< Maps atomic propisitions to BDD variables
vf_map var_formula_map; ///< Maps BDD variables to atomic propisitions
fv_map next_map; ///< Maps "Next" variables to BDD variables
vf_map next_formula_map; ///< Maps BDD variables to "Next" variables
bdd a_set;
bdd var_set;
bdd next_set;
int
register_proposition(const ltl::formula* f)
{
int num;
// Do not build a variable that already exists.
fv_map::iterator sii = var_map.find(f);
if (sii != var_map.end())
{
num = sii->second;
}
else
{
f = clone(f);
num = allocate_variables(1);
var_map[f] = num;
var_formula_map[num] = f;
}
var_set &= bdd_ithvar(num);
return num;
}
int
register_a_variable(const ltl::formula* f)
{
int num;
// Do not build an accepting variable that already exists.
fv_map::iterator sii = a_map.find(f);
if (sii != a_map.end())
{
num = sii->second;
}
else
{
f = clone(f);
num = allocate_variables(1);
a_map[f] = num;
a_formula_map[num] = f;
}
a_set &= bdd_ithvar(num);
return num;
}
int
register_next_variable(const ltl::formula* f)
{
int num;
// Do not build a Next variable that already exists.
fv_map::iterator sii = next_map.find(f);
if (sii != next_map.end())
{
num = sii->second;
}
else
{
f = clone(f);
num = allocate_variables(1);
next_map[f] = num;
next_formula_map[num] = f;
}
next_set &= bdd_ithvar(num);
return num;
}
std::ostream&
dump(std::ostream& os) const
{
fv_map::const_iterator fi;
os << "Atomic Propositions:" << std::endl;
for (fi = var_map.begin(); fi != var_map.end(); ++fi)
{
os << " " << fi->second << ": ";
to_string(fi->first, os) << std::endl;
}
os << "a Variables:" << std::endl;
for (fi = a_map.begin(); fi != a_map.end(); ++fi)
{
os << " " << fi->second << ": a[";
to_string(fi->first, os) << "]" << std::endl;
}
os << "Next Variables:" << std::endl;
for (fi = next_map.begin(); fi != next_map.end(); ++fi)
{
os << " " << fi->second << ": Next[";
to_string(fi->first, os) << "]" << std::endl;
}
return os;
}
ltl::formula*
var_to_formula(int var) const
{
vf_map::const_iterator isi = next_formula_map.find(var);
if (isi != next_formula_map.end())
return ltl::clone(isi->second);
isi = a_formula_map.find(var);
if (isi != a_formula_map.end())
return ltl::clone(isi->second);
isi = var_formula_map.find(var);
if (isi != var_formula_map.end())
return ltl::clone(isi->second);
assert(0);
}
ltl::formula*
conj_bdd_to_formula(bdd b)
{
if (b == bddfalse)
return ltl::constant::false_instance();
ltl::multop::vec* v = new ltl::multop::vec;
while (b != bddtrue)
{
int var = bdd_var(b);
ltl::formula* res = var_to_formula(var);
bdd high = bdd_high(b);
if (high == bddfalse)
{
res = ltl::unop::instance(ltl::unop::Not, res);
b = bdd_low(b);
}
else
{
b = high;
}
assert(b != bddfalse);
v->push_back(res);
}
return ltl::multop::instance(ltl::multop::And, v);
}
void
conj_bdd_to_atomic_props(tgba_explicit* a, bdd b,
tgba_explicit::transition* t)
{
assert(b != bddfalse);
while (b != bddtrue)
{
int var = bdd_var(b);
ltl::formula* ap = var_to_formula(var);
bdd high = bdd_high(b);
if (high == bddfalse)
{
a->add_neg_condition(t, ap);
b = bdd_low(b);
}
else
{
a->add_condition(t, ap);
b = high;
}
assert(b != bddfalse);
}
}
void
conj_bdd_to_acc(tgba_explicit* a, bdd b, tgba_explicit::transition* t)
{
assert(b != bddfalse);
while (b != bddtrue)
{
int var = bdd_var(b);
bdd high = bdd_high(b);
if (high == bddfalse)
{
// Simply ignore negated accepting variables.
b = bdd_low(b);
}
else
{
ltl::formula* ac = var_to_formula(var);
if (! a->has_accepting_condition(ac))
a->declare_accepting_condition(ltl::clone(ac));
a->add_accepting_condition(t, ac);
ltl::atomic_prop::instance_count();
b = high;
}
assert(b != bddfalse);
}
}
};
// The rewrite rules used here are adapted from Jean-Michel
// Couvreur's FM paper.
class ltl_trad_visitor: public const_visitor
{
public:
ltl_trad_visitor(translate_dict& dict)
: dict_(dict)
{
}
virtual
~ltl_trad_visitor()
{
}
bdd result() const
{
return res_;
}
void
visit(const atomic_prop* node)
{
res_ = bdd_ithvar(dict_.register_proposition(node));
}
void
visit(const constant* node)
{
switch (node->val())
{
case constant::True:
res_ = bddtrue;
return;
case constant::False:
res_ = bddfalse;
return;
}
/* Unreachable code. */
assert(0);
}
void
visit(const unop* node)
{
switch (node->op())
{
case unop::F:
{
// r(Fy) = r(y) + a(y)r(XFy)
bdd y = recurse(node->child());
int a = dict_.register_a_variable(node);
int x = dict_.register_next_variable(node);
res_ = y | (bdd_ithvar(a) & bdd_ithvar(x));
return;
}
case unop::G:
{
// r(Gy) = r(y)r(XGy)
bdd y = recurse(node->child());
int x = dict_.register_next_variable(node);
res_ = y & bdd_ithvar(x);
return;
}
case unop::Not:
{
res_ = bdd_not(recurse(node->child()));
return;
}
case unop::X:
{
int x = dict_.register_next_variable(node->child());
res_ = bdd_ithvar(x);
return;
}
}
/* Unreachable code. */
assert(0);
}
void
visit(const binop* node)
{
bdd f1 = recurse(node->first());
bdd f2 = recurse(node->second());
switch (node->op())
{
case binop::Xor:
res_ = bdd_apply(f1, f2, bddop_xor);
return;
case binop::Implies:
res_ = bdd_apply(f1, f2, bddop_imp);
return;
case binop::Equiv:
res_ = bdd_apply(f1, f2, bddop_biimp);
return;
case binop::U:
{
// r(f1 U f2) = r(f2) + a(f2)r(f1)r(X(f1 U f2))
int a = dict_.register_a_variable(node->second());
int x = dict_.register_next_variable(node);
res_ = f2 | (bdd_ithvar(a) & f1 & bdd_ithvar(x));
return;
}
case binop::R:
{
// r(f1 R f2) = r(f1)r(f2) + r(f2)r(X(f1 U f2))
int x = dict_.register_next_variable(node);
res_ = (f1 & f2) | (f2 & bdd_ithvar(x));
return;
}
}
/* Unreachable code. */
assert(0);
}
void
visit(const multop* node)
{
int op = -1;
switch (node->op())
{
case multop::And:
op = bddop_and;
res_ = bddtrue;
break;
case multop::Or:
op = bddop_or;
res_ = bddfalse;
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
recurse(const formula* f)
{
ltl_trad_visitor v(dict_);
f->accept(v);
return v.result();
}
private:
translate_dict& dict_;
bdd res_;
};
}
tgba_explicit*
ltl_to_tgba_fm(const ltl::formula* f, bdd_dict* dict)
{
// Normalize the formula. We want all the negations on
// the atomic propositions. We also suppress logic
// abbreviations such as <=>, =>, or XOR, since they
// would involve negations at the BDD level.
ltl::formula* f1 = ltl::unabbreviate_logic(f);
ltl::formula* f2 = ltl::negative_normal_form(f1);
ltl::destroy(f1);
std::set<ltl::formula*> formulae_seen;
std::set<ltl::formula*> formulae_to_translate;
formulae_seen.insert(f2);
formulae_to_translate.insert(f2);
tgba_explicit* a = new tgba_explicit(dict);
a->set_init_state(to_string(f2));
while (!formulae_to_translate.empty())
{
// Pick one formula.
ltl::formula* f = *formulae_to_translate.begin();
formulae_to_translate.erase(formulae_to_translate.begin());
// Translate it into a BDD to simplify it.
translate_dict d;
ltl_trad_visitor v(d);
f->accept(v);
bdd res = v.result();
std::string now = to_string(f);
bdd all = res;
bdd outside = !all;
while (all != bddfalse)
{
bdd cube = bdd_satone(all);
cube = bdd_simplify(cube, cube | outside);
all -= cube;
ltl::formula* dest =
d.conj_bdd_to_formula(bdd_existcomp(cube, d.next_set));
std::string next = to_string(dest);
tgba_explicit::transition* t = a->create_transition(now, next);
d.conj_bdd_to_atomic_props(a, bdd_existcomp(cube, d.var_set), t);
d.conj_bdd_to_acc(a, bdd_existcomp(cube, d.a_set), t);
if (formulae_seen.find(dest) == formulae_seen.end())
{
formulae_seen.insert(dest);
formulae_to_translate.insert(dest);
}
else
{
ltl::destroy(dest);
}
}
}
// Free all formulae.
for (std::set<ltl::formula*>::iterator i = formulae_seen.begin();
i != formulae_seen.end(); ++i)
ltl::destroy(*i);
// Turn all promises into real accepting conditions.
a->complement_all_accepting_conditions();
return a;
}
}

View file

@ -0,0 +1,32 @@
#ifndef SPOT_TGBA_LTL2TGBA_FME_HH
# define SPOT_TGBA_LTL2TGBA_FME_HH
#include "ltlast/formula.hh"
#include "tgba/tgbaexplicit.hh"
namespace spot
{
/// \brief Build a spot::tgba_explicit* from an LTL formula.
///
/// This is based on the following paper.
/// \verbatim
/// @InProceedings{couvreur.99.fm,
/// author = {Jean-Michel Couvreur},
/// title = {On-the-fly Verification of Temporal Logic},
/// pages = {253--271},
/// editor = {Jeannette M. Wing and Jim Woodcock and Jim Davies},
/// booktitle = {Proceedings of the World Congress on Formal Methods in the
/// Development of Computing Systems (FM'99)},
/// publisher = {Springer-Verlag},
/// series = {Lecture Notes in Computer Science},
/// volume = {1708},
/// year = {1999},
/// address = {Toulouse, France},
/// month = {September},
/// isbn = {3-540-66587-0}
/// }
/// \endverbatim
tgba_explicit* ltl_to_tgba_fm(const ltl::formula* f, bdd_dict* dict);
}
#endif // SPOT_TGBA_LTL2TGBA_HH

View file

@ -12,8 +12,6 @@ check_PROGRAMS = \
ltlprod \ ltlprod \
mixprod \ mixprod \
readsave \ readsave \
spotlbtt \
tbalbtt \
tgbaread \ tgbaread \
tripprod tripprod
@ -27,11 +25,8 @@ 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
tbalbtt_SOURCES = spotlbtt.cc
tbalbtt_CXXFLAGS = -DTBA
tgbaread_SOURCES = tgbaread.cc tgbaread_SOURCES = tgbaread.cc
tripprod_SOURCES = tripprod.cc tripprod_SOURCES = tripprod.cc
spotlbtt_SOURCES = spotlbtt.cc
# Keep this sorted by STRENGTH. Test basic things first, # Keep this sorted by STRENGTH. Test basic things first,
# because such failures will be easier to diagnose and fix. # because such failures will be easier to diagnose and fix.

View file

@ -1,9 +1,12 @@
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <fstream>
#include <string>
#include "ltlvisit/destroy.hh" #include "ltlvisit/destroy.hh"
#include "ltlast/allnodes.hh" #include "ltlast/allnodes.hh"
#include "ltlparse/public.hh" #include "ltlparse/public.hh"
#include "tgbaalgos/ltl2tgba.hh" #include "tgbaalgos/ltl2tgba.hh"
#include "tgbaalgos/ltl2tgba_fm.hh"
#include "tgba/bddprint.hh" #include "tgba/bddprint.hh"
#include "tgbaalgos/dotty.hh" #include "tgbaalgos/dotty.hh"
#include "tgbaalgos/lbtt.hh" #include "tgbaalgos/lbtt.hh"
@ -13,6 +16,7 @@ void
syntax(char* prog) syntax(char* prog)
{ {
std::cerr << "Usage: "<< prog << " [OPTIONS...] formula" << std::endl std::cerr << "Usage: "<< prog << " [OPTIONS...] formula" << std::endl
<< " "<< prog << " -F [OPTIONS...] file" << std::endl
<< std::endl << std::endl
<< "Options:" << std::endl << "Options:" << std::endl
<< " -a display the accepting_conditions BDD, not the reachability graph" << " -a display the accepting_conditions BDD, not the reachability graph"
@ -20,6 +24,9 @@ syntax(char* prog)
<< " -A same as -a, but as a set" << std::endl << " -A same as -a, but as a set" << std::endl
<< " -d turn on traces during parsing" << std::endl << " -d turn on traces during parsing" << std::endl
<< " -D degeneralize the automaton" << std::endl << " -D degeneralize the automaton" << std::endl
<< " -f use Couvreur's FM algorithm for translation"
<< std::endl
<< " -F read the formula from the file" << std::endl
<< " -r display the relation BDD, not the reachability graph" << " -r display the relation BDD, not the reachability graph"
<< std::endl << std::endl
<< " -R same as -r, but as a set" << std::endl << " -R same as -r, but as a set" << std::endl
@ -36,6 +43,8 @@ main(int argc, char** argv)
bool debug_opt = false; bool debug_opt = false;
bool degeneralize_opt = false; bool degeneralize_opt = false;
bool fm_opt = false;
bool file_opt = false;
int output = 0; int output = 0;
int formula_index = 0; int formula_index = 0;
@ -62,6 +71,14 @@ main(int argc, char** argv)
{ {
degeneralize_opt = true; degeneralize_opt = true;
} }
else if (!strcmp(argv[formula_index], "-f"))
{
fm_opt = true;
}
else if (!strcmp(argv[formula_index], "-F"))
{
file_opt = true;
}
else if (!strcmp(argv[formula_index], "-r")) else if (!strcmp(argv[formula_index], "-r"))
{ {
output = 1; output = 1;
@ -84,19 +101,46 @@ main(int argc, char** argv)
} }
} }
std::string input;
if (file_opt)
{
std::ifstream fin(argv[formula_index]);
if (! fin)
{
std::cerr << "Cannot open " << argv[formula_index] << std::endl;
exit(2);
}
if (! std::getline(fin, input, '\0'))
{
std::cerr << "Cannot read " << argv[formula_index] << std::endl;
exit(2);
}
}
else
{
input = argv[formula_index];
}
spot::ltl::environment& env(spot::ltl::default_environment::instance()); spot::ltl::environment& env(spot::ltl::default_environment::instance());
spot::ltl::parse_error_list pel; spot::ltl::parse_error_list pel;
spot::ltl::formula* f = spot::ltl::parse(argv[formula_index], spot::ltl::formula* f = spot::ltl::parse(input, pel, env, debug_opt);
pel, env, debug_opt);
exit_code = exit_code = spot::ltl::format_parse_errors(std::cerr, input, pel);
spot::ltl::format_parse_errors(std::cerr, argv[formula_index], pel);
spot::bdd_dict* dict = new spot::bdd_dict(); spot::bdd_dict* dict = new spot::bdd_dict();
if (f) if (f)
{ {
spot::tgba_bdd_concrete* concrete = spot::ltl_to_tgba(f, dict); spot::tgba_bdd_concrete* concrete = 0;
spot::tgba* a = concrete; spot::tgba* to_free = 0;
spot::tgba* a = 0;
if (fm_opt)
to_free = a = spot::ltl_to_tgba_fm(f, dict);
else
to_free = a = concrete = spot::ltl_to_tgba(f, dict);
spot::ltl::destroy(f); spot::ltl::destroy(f);
spot::tgba* degeneralized = 0; spot::tgba* degeneralized = 0;
@ -109,20 +153,26 @@ main(int argc, char** argv)
spot::dotty_reachable(std::cout, a); spot::dotty_reachable(std::cout, a);
break; break;
case 1: case 1:
spot::bdd_print_dot(std::cout, concrete->get_dict(), if (concrete)
concrete->get_core_data().relation); spot::bdd_print_dot(std::cout, concrete->get_dict(),
concrete->get_core_data().relation);
break; break;
case 2: case 2:
spot::bdd_print_dot(std::cout, concrete->get_dict(), if (concrete)
concrete->get_core_data().accepting_conditions); spot::bdd_print_dot(std::cout, concrete->get_dict(),
concrete->
get_core_data().accepting_conditions);
break; break;
case 3: case 3:
spot::bdd_print_set(std::cout, concrete->get_dict(), if (concrete)
concrete->get_core_data().relation); spot::bdd_print_set(std::cout, concrete->get_dict(),
concrete->get_core_data().relation);
break; break;
case 4: case 4:
spot::bdd_print_set(std::cout, concrete->get_dict(), if (concrete)
concrete->get_core_data().accepting_conditions); spot::bdd_print_set(std::cout, concrete->get_dict(),
concrete->
get_core_data().accepting_conditions);
break; break;
case 5: case 5:
a->get_dict()->dump(std::cout); a->get_dict()->dump(std::cout);
@ -137,7 +187,7 @@ main(int argc, char** argv)
if (degeneralize_opt) if (degeneralize_opt)
delete degeneralized; delete degeneralized;
delete concrete; delete to_free;
} }
else else
{ {

View file

@ -1,74 +0,0 @@
#include <iostream>
#include <fstream>
#include <cassert>
#include <string>
#include "ltlvisit/destroy.hh"
#include "ltlast/allnodes.hh"
#include "ltlparse/public.hh"
#include "tgbaalgos/ltl2tgba.hh"
#include "tgbaalgos/lbtt.hh"
#include "tgba/tgbatba.hh"
void
syntax(char* prog)
{
std::cerr << "Usage: "<< prog << " file" << std::endl;
exit(2);
}
int
main(int argc, char** argv)
{
int exit_code = 0;
if (argc != 2)
syntax(argv[0]);
std::ifstream fin(argv[1]);
if (! fin)
{
std::cerr << "Cannot open " << argv[1] << std::endl;
exit(2);
}
std::string input;
if (! std::getline(fin, input, '\0'))
{
std::cerr << "Cannot read " << argv[1] << std::endl;
exit(2);
}
spot::bdd_dict* dict = new spot::bdd_dict();
spot::ltl::environment& env(spot::ltl::default_environment::instance());
spot::ltl::parse_error_list pel;
spot::ltl::formula* f = spot::ltl::parse(input, pel, env);
exit_code = spot::ltl::format_parse_errors(std::cerr, input, pel);
if (f)
{
spot::tgba* a;
spot::tgba* c = a = spot::ltl_to_tgba(f, dict);
spot::ltl::destroy(f);
#ifdef TBA
spot::tgba* d = a = new spot::tgba_tba_proxy(a);
#endif
spot::lbtt_reachable(std::cout, a);
#ifdef TBA
delete d;
#endif
delete c;
}
else
{
exit_code = 1;
}
assert(spot::ltl::atomic_prop::instance_count() == 0);
assert(spot::ltl::unop::instance_count() == 0);
assert(spot::ltl::binop::instance_count() == 0);
assert(spot::ltl::multop::instance_count() == 0);
delete dict;
return exit_code;
}

View file

@ -7,15 +7,29 @@ set -e
cat > config <<EOF cat > config <<EOF
Algorithm Algorithm
{ {
Name = "Spot" Name = "Spot (Couvreur -- LaCIM)"
Path = "${LBTT_TRANSLATE} --spot ./spotlbtt" Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -t'"
Enabled = yes Enabled = yes
} }
Algorithm Algorithm
{ {
Name = "Spot TBA" Name = "Spot (Couvreur -- LACIM), degeneralized"
Path = "${LBTT_TRANSLATE} --spot ./tbalbtt" Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -t -D'"
Enabled = yes
}
Algorithm
{
Name = "Spot (Couvreur -- FM)"
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -t'"
Enabled = yes
}
Algorithm
{
Name = "Spot (Couvreur -- FM), degeneralized"
Path = "${LBTT_TRANSLATE} --spot './ltl2tgba -F -f -t -D'"
Enabled = yes Enabled = yes
} }

View file

@ -68,9 +68,29 @@ options = [
('show_lbtt', 'convert automaton for LBTT', 0), ('show_lbtt', 'convert automaton for LBTT', 0),
] ]
default_translator = 'trans_lacim';
translators = [
('trans_lacim', 'Convreur/LaCIM'),
('trans_fm', 'Convreur/FM'),
]
print """<FORM action="%s" method="post"><P> print """<FORM action="%s" method="post"><P>
Formula to translate: <INPUT type="text" name="formula" value="%s"><BR> Formula to translate: <INPUT type="text" name="formula" value="%s"><BR>
Options:<TABLE><TR><TD>""" % (myself, formula) Translator:<TABLE><TR><TD>""" % (myself, formula)
trans = form.getfirst("trans", default_translator)
for opt, desc, in translators:
if trans == opt:
str = "checked"
else:
str = ""
globals()[opt] = str
print '<INPUT type="radio" name="trans" value="%s" %s>%s<br>' % (opt, str,
desc)
print """</TD></TR></TABLE>
Options:<TABLE><TR><TD>"""
for opt, desc, arg, in options: for opt, desc, arg, in options:
if formula: if formula:
@ -140,7 +160,10 @@ dict = spot.bdd_dict()
print '<p>Building automaton...', print '<p>Building automaton...',
sys.stdout.flush() sys.stdout.flush()
concrete = spot.ltl_to_tgba(f, dict) if trans_lacim:
automaton = spot.ltl_to_tgba(f, dict)
elif trans_fm:
automaton = spot.ltl_to_tgba_fm(f, dict)
print 'done.</p>' print 'done.</p>'
sys.stdout.flush() sys.stdout.flush()
@ -148,14 +171,14 @@ sys.stdout.flush()
if show_automaton_dot: if show_automaton_dot:
print '<pre>'; sys.stdout.flush() print '<pre>'; sys.stdout.flush()
s = spot.ostringstream() s = spot.ostringstream()
spot.dotty_reachable(s, concrete) spot.dotty_reachable(s, automaton)
print cgi.escape(s.str()) print cgi.escape(s.str())
del s del s
print '</pre>'; sys.stdout.flush() print '</pre>'; sys.stdout.flush()
if show_automaton_gif: if show_automaton_gif:
outfile = spot.ofstream(imgprefix + '-a.dot') outfile = spot.ofstream(imgprefix + '-a.dot')
spot.dotty_reachable(outfile, concrete) spot.dotty_reachable(outfile, automaton)
del outfile del outfile
os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o', os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o',
imgprefix + '-a.gif', imgprefix + '-a.dot') imgprefix + '-a.gif', imgprefix + '-a.dot')
@ -166,7 +189,7 @@ if show_automaton_gif:
if show_degen_dot or show_degen_gif: if show_degen_dot or show_degen_gif:
print '<H3>Degeneralized automaton</H3>' print '<H3>Degeneralized automaton</H3>'
degen = spot.tgba_tba_proxy(concrete) degen = spot.tgba_tba_proxy(automaton)
if show_degen_dot: if show_degen_dot:
print '<pre>'; sys.stdout.flush() print '<pre>'; sys.stdout.flush()
s = spot.ostringstream() s = spot.ostringstream()
@ -190,21 +213,22 @@ if show_dictionnay:
print '<H3>BDD dictionary</H3>' print '<H3>BDD dictionary</H3>'
print '<pre>' print '<pre>'
sys.stdout.flush() sys.stdout.flush()
concrete.get_dict().dump(spot.get_cout()) automaton.get_dict().dump(spot.get_cout())
print '</pre>' print '</pre>'
if show_relation_dot or show_relation_set or show_relation_gif: if (type(automaton) == spot.tgba_bdd_concrete
and (show_relation_dot or show_relation_set or show_relation_gif)):
print '<H3>Transition relation</H3>' print '<H3>Transition relation</H3>'
if show_relation_dot: if show_relation_dot:
escaped_print_dot(concrete.get_dict(), escaped_print_dot(automaton.get_dict(),
concrete.get_core_data().relation) automaton.get_core_data().relation)
if show_relation_set: if show_relation_set:
escaped_print_set(concrete.get_dict(), escaped_print_set(automaton.get_dict(),
concrete.get_core_data().relation) automaton.get_core_data().relation)
if show_relation_gif: if show_relation_gif:
outfile = spot.ofstream(imgprefix + '-b.dot') outfile = spot.ofstream(imgprefix + '-b.dot')
spot.bdd_print_dot(outfile, concrete.get_dict(), spot.bdd_print_dot(outfile, automaton.get_dict(),
concrete.get_core_data().relation) automaton.get_core_data().relation)
del outfile del outfile
os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o', os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o',
imgprefix + '-b.gif', imgprefix + '-b.dot') imgprefix + '-b.gif', imgprefix + '-b.dot')
@ -212,18 +236,19 @@ if show_relation_dot or show_relation_set or show_relation_gif:
imgprefix + '-b.gif', imgprefix + '-b.png') imgprefix + '-b.gif', imgprefix + '-b.png')
print '<img src="' + imgprefix + '-b.png">' print '<img src="' + imgprefix + '-b.png">'
if show_acceptance_dot or show_acceptance_set or show_acceptance_gif: if (type(automaton) == spot.tgba_bdd_concrete
and (show_acceptance_dot or show_acceptance_set or show_acceptance_gif)):
print '<H3>Acceptance relation</H3>' print '<H3>Acceptance relation</H3>'
if show_acceptance_dot: if show_acceptance_dot:
escaped_print_dot(concrete.get_dict(), escaped_print_dot(automaton.get_dict(),
concrete.get_core_data().accepting_conditions) automaton.get_core_data().accepting_conditions)
if show_acceptance_set: if show_acceptance_set:
escaped_print_set(concrete.get_dict(), escaped_print_set(automaton.get_dict(),
concrete.get_core_data().accepting_conditions) automaton.get_core_data().accepting_conditions)
if show_acceptance_gif: if show_acceptance_gif:
outfile = spot.ofstream(imgprefix + '-c.dot') outfile = spot.ofstream(imgprefix + '-c.dot')
spot.bdd_print_dot(outfile, concrete.get_dict(), spot.bdd_print_dot(outfile, automaton.get_dict(),
concrete.get_core_data().accepting_conditions) automaton.get_core_data().accepting_conditions)
del outfile del outfile
os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o', os.spawnlp(os.P_WAIT, 'dot', 'dot', '-Tgif', '-Gsize=14,14', '-o',
imgprefix + '-c.gif', imgprefix + '-c.dot') imgprefix + '-c.gif', imgprefix + '-c.dot')
@ -237,7 +262,7 @@ if show_lbtt:
print '<H4>Conversion of the generalized automaton</H4>' print '<H4>Conversion of the generalized automaton</H4>'
print '<pre>' print '<pre>'
sys.stdout.flush() sys.stdout.flush()
spot.lbtt_reachable(spot.get_cout(), concrete) spot.lbtt_reachable(spot.get_cout(), automaton)
print '</pre>' print '</pre>'
if degen: if degen:
print '<H4>Conversion of the degeneralized automaton</H4>' print '<H4>Conversion of the degeneralized automaton</H4>'
@ -248,9 +273,10 @@ if show_lbtt:
sys.stdout.flush() sys.stdout.flush()
# Make sure degen is cleared before concrete. spot.destroy(f)
# Make sure degen is cleared before automaton.
del degen del degen
del concrete del automaton
print '<hr>' print '<hr>'
print 'ltl2tgba.py @PACKAGE_VERSION@; Spot', spot.version() print 'ltl2tgba.py @PACKAGE_VERSION@; Spot', spot.version()

View file

@ -49,6 +49,7 @@
#include "tgba/tgbatba.hh" #include "tgba/tgbatba.hh"
#include "tgbaalgos/ltl2tgba.hh" #include "tgbaalgos/ltl2tgba.hh"
#include "tgbaalgos/ltl2tgba_fm.hh"
#include "tgbaalgos/dotty.hh" #include "tgbaalgos/dotty.hh"
#include "tgbaalgos/lbtt.hh" #include "tgbaalgos/lbtt.hh"
#include "tgbaalgos/magic.hh" #include "tgbaalgos/magic.hh"
@ -84,6 +85,7 @@ using namespace spot;
%include "ltlvisit/tunabbrev.hh" %include "ltlvisit/tunabbrev.hh"
%feature("new") spot::ltl_to_tgba; %feature("new") spot::ltl_to_tgba;
%feature("new") spot::ltl_to_tgba_fm;
%feature("new") spot::tgba::get_init_state; %feature("new") spot::tgba::get_init_state;
%feature("new") spot::tgba::succ_iter; %feature("new") spot::tgba::succ_iter;
%feature("new") spot::tgba_succ_iterator::current_state; %feature("new") spot::tgba_succ_iterator::current_state;
@ -104,6 +106,7 @@ using namespace spot;
%include "tgba/tgbatba.hh" %include "tgba/tgbatba.hh"
%include "tgbaalgos/ltl2tgba.hh" %include "tgbaalgos/ltl2tgba.hh"
%include "tgbaalgos/ltl2tgba_fm.hh"
%include "tgbaalgos/dotty.hh" %include "tgbaalgos/dotty.hh"
%include "tgbaalgos/lbtt.hh" %include "tgbaalgos/lbtt.hh"
%include "tgbaalgos/magic.hh" %include "tgbaalgos/magic.hh"

View file

@ -14,6 +14,7 @@ Options:
-A same as -a, but as a set -A same as -a, but as a set
-d turn on traces during parsing -d turn on traces during parsing
-D degeneralize the automaton -D degeneralize the automaton
-f use Couvreur's FM algorithm for translation
-r display the relation BDD, not the reachability graph -r display the relation BDD, not the reachability graph
-R same as -r, but as a set -R same as -r, but as a set
-t display reachable states in LBTT's format -t display reachable states in LBTT's format
@ -23,7 +24,7 @@ Options:
prog = sys.argv[0] prog = sys.argv[0]
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'aAdDrRtv') opts, args = getopt.getopt(sys.argv[1:], 'aAdDfrRtv')
except getopt.GetoptError: except getopt.GetoptError:
usage(prog) usage(prog)
@ -31,6 +32,7 @@ exit_code = 0
debug_opt = 0 debug_opt = 0
degeneralize_opt = None degeneralize_opt = None
output = 0 output = 0
fm_opt = 0
for o, a in opts: for o, a in opts:
if o == '-a': if o == '-a':
@ -41,6 +43,8 @@ for o, a in opts:
debug_opt = 1 debug_opt = 1
elif o == '-D': elif o == '-D':
degeneralize_opt = 1 degeneralize_opt = 1
elif o == '-f':
fm_opt = 1
elif o == '-r': elif o == '-r':
output = 1 output = 1
elif o == '-R': elif o == '-R':
@ -69,10 +73,13 @@ if spot.format_parse_errors(cerr, args[0], p):
dict = spot.bdd_dict() dict = spot.bdd_dict()
if f: if f:
concrete = spot.ltl_to_tgba(f, dict) if fm_opt:
a = spot.ltl_to_tgba_fm(f, dict)
concrete = 0
else:
a = concrete = spot.ltl_to_tgba(f, dict)
spot.destroy(f) spot.destroy(f)
del f del f
a = concrete
degeneralized = None degeneralized = None
if degeneralize_opt: if degeneralize_opt:
@ -81,18 +88,22 @@ if f:
if output == 0: if output == 0:
spot.dotty_reachable(cout, a) spot.dotty_reachable(cout, a)
elif output == 1: elif output == 1:
spot.bdd_print_dot(cout, concrete.get_dict(), if concrete:
concrete.get_core_data().relation) spot.bdd_print_dot(cout, concrete.get_dict(),
concrete.get_core_data().relation)
elif output == 2: elif output == 2:
spot.bdd_print_dot(cout, concrete.get_dict(), if concrete:
concrete.get_core_data().accepting_conditions) spot.bdd_print_dot(cout, concrete.get_dict(),
concrete.get_core_data().accepting_conditions)
elif output == 3: elif output == 3:
spot.bdd_print_set(cout, concrete.get_dict(), if concrete:
concrete.get_core_data().relation) spot.bdd_print_set(cout, concrete.get_dict(),
concrete.get_core_data().relation)
print print
elif output == 4: elif output == 4:
spot.bdd_print_set(cout, concrete.get_dict(), if concrete:
concrete.get_core_data().accepting_conditions) spot.bdd_print_set(cout, concrete.get_dict(),
concrete.get_core_data().accepting_conditions)
print print
elif output == 5: elif output == 5:
a.get_dict().dump(cout) a.get_dict().dump(cout)
@ -116,4 +127,3 @@ assert spot.atomic_prop.instance_count() == 0
assert spot.unop.instance_count() == 0 assert spot.unop.instance_count() == 0
assert spot.binop.instance_count() == 0 assert spot.binop.instance_count() == 0
assert spot.multop.instance_count() == 0 assert spot.multop.instance_count() == 0

View file

@ -14,3 +14,13 @@ set -e
./run ltl2tgba.py 'Fa & Xb & GFc & Gd' ./run ltl2tgba.py 'Fa & Xb & GFc & Gd'
./run ltl2tgba.py 'Fa & Xa & GFc & Gc' ./run ltl2tgba.py 'Fa & Xa & GFc & Gc'
./run ltl2tgba.py 'Fc & X(a | Xb) & GF(a | Xb) & Gc' ./run ltl2tgba.py 'Fc & X(a | Xb) & GF(a | Xb) & Gc'
./run ltl2tgba.py -f a
./run ltl2tgba.py -f 'a U b'
./run ltl2tgba.py -f 'X a'
./run ltl2tgba.py -f 'a & b & c'
./run ltl2tgba.py -f 'a | b | (c U (d & (g U (h ^ i))))'
./run ltl2tgba.py -f 'Xa & (b U !a) & (b U !a)'
./run ltl2tgba.py -f 'Fa & Xb & GFc & Gd'
./run ltl2tgba.py -f 'Fa & Xa & GFc & Gc'
./run ltl2tgba.py -f 'Fc & X(a | Xb) & GF(a | Xb) & Gc'