Introduce compositional suspension (SPIN'13)
* src/tgbaalgos/compsusp.cc, src/tgbaalgos/compsusp.hh: New files. * src/tgbaalgos/Makefile.am: Add them. * src/tgbaalgos/sccfilter.cc, src/tgbaalgos/sccfilter.hh: Add option for suspended labels removal. * src/tgbatest/ltl2tgba.cc, src/tgbatest/spotlbtt.test: Test it.
This commit is contained in:
parent
ba6fe9e183
commit
53c6923567
7 changed files with 741 additions and 38 deletions
|
|
@ -29,6 +29,7 @@ tgbaalgosdir = $(pkgincludedir)/tgbaalgos
|
||||||
|
|
||||||
tgbaalgos_HEADERS = \
|
tgbaalgos_HEADERS = \
|
||||||
bfssteps.hh \
|
bfssteps.hh \
|
||||||
|
compsusp.hh \
|
||||||
cutscc.hh \
|
cutscc.hh \
|
||||||
cycles.hh \
|
cycles.hh \
|
||||||
degen.hh \
|
degen.hh \
|
||||||
|
|
@ -73,6 +74,7 @@ tgbaalgos_HEADERS = \
|
||||||
noinst_LTLIBRARIES = libtgbaalgos.la
|
noinst_LTLIBRARIES = libtgbaalgos.la
|
||||||
libtgbaalgos_la_SOURCES = \
|
libtgbaalgos_la_SOURCES = \
|
||||||
bfssteps.cc \
|
bfssteps.cc \
|
||||||
|
compsusp.cc \
|
||||||
cutscc.cc \
|
cutscc.cc \
|
||||||
cycles.cc \
|
cycles.cc \
|
||||||
degen.cc \
|
degen.cc \
|
||||||
|
|
|
||||||
441
src/tgbaalgos/compsusp.cc
Normal file
441
src/tgbaalgos/compsusp.cc
Normal file
|
|
@ -0,0 +1,441 @@
|
||||||
|
// -*- coding: utf-8 -*-
|
||||||
|
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
|
||||||
|
// de l'Epita (LRDE).
|
||||||
|
//
|
||||||
|
// This file is part of Spot, a model checking library.
|
||||||
|
//
|
||||||
|
// Spot is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
|
// License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "compsusp.hh"
|
||||||
|
#include "sccfilter.hh"
|
||||||
|
#include "scc.hh"
|
||||||
|
#include "tgba/tgbaexplicit.hh"
|
||||||
|
#include "ltl2tgba_fm.hh"
|
||||||
|
#include "minimize.hh"
|
||||||
|
#include "simulation.hh"
|
||||||
|
#include "safety.hh"
|
||||||
|
#include "ltlast/allnodes.hh"
|
||||||
|
#include "ltlvisit/tostring.hh"
|
||||||
|
#include "ltlvisit/clone.hh"
|
||||||
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
#include "ltlenv/environment.hh"
|
||||||
|
|
||||||
|
namespace spot
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
typedef std::map<const ltl::formula*, bdd> formula_bdd_map;
|
||||||
|
|
||||||
|
// An environment to store atomic proposition associated to
|
||||||
|
// suspended variable. (We don't use the default environment to
|
||||||
|
// avoid conflicts with user-defined atomic propositions that
|
||||||
|
// would share the same name.)
|
||||||
|
class suspended_environment: public ltl::environment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const ltl::formula*
|
||||||
|
require(const std::string& s)
|
||||||
|
{
|
||||||
|
return ltl::atomic_prop::instance(s, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string&
|
||||||
|
name()
|
||||||
|
{
|
||||||
|
static std::string name("suspended environment");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static suspended_environment suspenv;
|
||||||
|
|
||||||
|
// Rewrite the suspendable subformulae "s" of an LTL formula in
|
||||||
|
// the form Gg where "g" is an atomic proposition representing
|
||||||
|
// "s". At the same time, populate maps that associate "s" to "g"
|
||||||
|
// and vice-versa.
|
||||||
|
class ltl_suspender_visitor: public ltl::clone_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::map<const ltl::formula*, const ltl::formula*> fmap_t;
|
||||||
|
ltl_suspender_visitor(fmap_t& g2s, fmap_t& a2o, bool oblig)
|
||||||
|
: g2s_(g2s), a2o_(a2o), oblig_(oblig)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const ltl::multop* mo)
|
||||||
|
{
|
||||||
|
ltl::multop::type op = mo->op();
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case ltl::multop::Or:
|
||||||
|
case ltl::multop::And:
|
||||||
|
{
|
||||||
|
ltl::multop::vec* res = new ltl::multop::vec;
|
||||||
|
ltl::multop::vec* oblig = oblig_ ? new ltl::multop::vec : 0;
|
||||||
|
ltl::multop::vec* susp = new ltl::multop::vec;
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
{
|
||||||
|
const ltl::formula* c = mo->nth(i);
|
||||||
|
if (c->is_boolean())
|
||||||
|
res->push_back(c->clone());
|
||||||
|
else if (oblig_ && c->is_syntactic_obligation())
|
||||||
|
oblig->push_back(c->clone());
|
||||||
|
else if (c->is_eventual() && c->is_universal())
|
||||||
|
susp->push_back(c->clone());
|
||||||
|
else
|
||||||
|
res->push_back(recurse(c));
|
||||||
|
}
|
||||||
|
if (!oblig_ || oblig->empty())
|
||||||
|
{
|
||||||
|
delete oblig;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const ltl::formula* o = ltl::multop::instance(op, oblig);
|
||||||
|
res->push_back(recurse(o));
|
||||||
|
o->destroy();
|
||||||
|
}
|
||||||
|
if (susp->empty())
|
||||||
|
{
|
||||||
|
delete susp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const ltl::formula* o = ltl::multop::instance(op, susp);
|
||||||
|
// Rewrite 'o' as 'G"o"'
|
||||||
|
const ltl::formula* g = recurse(o);
|
||||||
|
o->destroy();
|
||||||
|
if (op == ltl::multop::And)
|
||||||
|
{
|
||||||
|
res->push_back(g);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// res || susp -> (res && G![susp]) || G[susp])
|
||||||
|
const ltl::formula* r = ltl::multop::instance(op, res);
|
||||||
|
const ltl::unop* u =
|
||||||
|
down_cast<const ltl::unop*>(g);
|
||||||
|
const ltl::formula* gn =
|
||||||
|
ltl::unop::instance
|
||||||
|
(ltl::unop::G, ltl::unop::instance
|
||||||
|
(ltl::unop::Not, u->child()->clone()));
|
||||||
|
result_ = ltl::multop::instance
|
||||||
|
(ltl::multop::Or, ltl::multop::instance
|
||||||
|
(ltl::multop::And, r, gn),
|
||||||
|
g);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_ = ltl::multop::instance(op, res);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ltl::multop::OrRat:
|
||||||
|
case ltl::multop::AndRat:
|
||||||
|
case ltl::multop::AndNLM:
|
||||||
|
case ltl::multop::Concat:
|
||||||
|
case ltl::multop::Fusion:
|
||||||
|
this->ltl::clone_visitor::visit(mo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ltl::formula*
|
||||||
|
recurse(const ltl::formula* f)
|
||||||
|
{
|
||||||
|
const ltl::formula* res;
|
||||||
|
if (f->is_boolean())
|
||||||
|
return f->clone();
|
||||||
|
if (oblig_ && f->is_syntactic_obligation())
|
||||||
|
{
|
||||||
|
fmap_t::const_iterator i = assoc_.find(f);
|
||||||
|
if (i != assoc_.end())
|
||||||
|
return i->second->clone();
|
||||||
|
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "〈";
|
||||||
|
to_string(f, s);
|
||||||
|
s << "〉";
|
||||||
|
res = suspenv.require(s.str());
|
||||||
|
// We have to clone f, because it is not always a sub-tree
|
||||||
|
// of the original formula. (Think n-ary operators.)
|
||||||
|
a2o_[res] = f->clone();
|
||||||
|
assoc_[f] = res;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (f->is_eventual() && f->is_universal())
|
||||||
|
{
|
||||||
|
fmap_t::const_iterator i = assoc_.find(f);
|
||||||
|
if (i != assoc_.end())
|
||||||
|
return ltl::unop::instance(ltl::unop::G, i->second->clone());
|
||||||
|
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "[";
|
||||||
|
to_string(f, s);
|
||||||
|
s << "]";
|
||||||
|
res = suspenv.require(s.str());
|
||||||
|
// We have to clone f, because it is not always a sub-tree
|
||||||
|
// of the original formula. (Think n-ary operators.)
|
||||||
|
g2s_[res] = f->clone();
|
||||||
|
assoc_[f] = res;
|
||||||
|
return ltl::unop::instance(ltl::unop::G, res);
|
||||||
|
}
|
||||||
|
f->accept(*this);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
fmap_t& g2s_;
|
||||||
|
fmap_t assoc_; // This one is only needed by the visitor.
|
||||||
|
fmap_t& a2o_;
|
||||||
|
bool oblig_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::pair<const state*, const state*> state_pair;
|
||||||
|
|
||||||
|
typedef std::map<state_pair, int> pair_map;
|
||||||
|
typedef std::deque<state_pair> pair_queue;
|
||||||
|
|
||||||
|
static
|
||||||
|
tgba*
|
||||||
|
susp_prod(tgba* left, const ltl::formula* f, bdd v)
|
||||||
|
{
|
||||||
|
bdd_dict* dict = left->get_dict();
|
||||||
|
const tgba* a1 = ltl_to_tgba_fm(f, dict, true, true);
|
||||||
|
|
||||||
|
const tgba* a2 = scc_filter(a1, false);
|
||||||
|
delete a1;
|
||||||
|
const tgba* right = iterated_simulations(a2);
|
||||||
|
delete a2;
|
||||||
|
|
||||||
|
tgba_explicit_number* res = new tgba_explicit_number(dict);
|
||||||
|
dict->register_all_variables_of(left, res);
|
||||||
|
dict->register_all_variables_of(right, res);
|
||||||
|
dict->unregister_variable(bdd_var(v), res);
|
||||||
|
|
||||||
|
// The left and right automata might have acceptance marker in
|
||||||
|
// common.
|
||||||
|
// For the example, assume left has acceptance conditions A,B,C,D
|
||||||
|
// while right has acceptance condition C,D,E,F.
|
||||||
|
|
||||||
|
// Negative acceptance variables...
|
||||||
|
// !A&!B&!C&!D
|
||||||
|
bdd lna = left->neg_acceptance_conditions();
|
||||||
|
// !C&!D&!E&!F
|
||||||
|
bdd rna = right->neg_acceptance_conditions();
|
||||||
|
|
||||||
|
// Missing acceptance variables...
|
||||||
|
// !E&!F
|
||||||
|
bdd lma = bdd_exist(rna, bdd_support(lna));
|
||||||
|
// !A&!B
|
||||||
|
bdd rma = bdd_exist(lna, bdd_support(rna));
|
||||||
|
|
||||||
|
// (A&!B&!C&!D + ... + !A&!B&!C&D) & !E&!F
|
||||||
|
bdd lac = left->all_acceptance_conditions() & lma;
|
||||||
|
// (C&!D&!E&!F + ... + !C&!D&!E&F) & !A&!B
|
||||||
|
bdd rac = right->all_acceptance_conditions() & rma;
|
||||||
|
bdd allacc = lac | rac;
|
||||||
|
res->set_acceptance_conditions(allacc);
|
||||||
|
|
||||||
|
// Acceptance condition to add to all transitions
|
||||||
|
// of the left automaton.
|
||||||
|
// !A&!B&!C&!D&E&!F | !A&!B&!C&!D&!E&F
|
||||||
|
bdd ladd = rac - lma;
|
||||||
|
bdd radd = lac - rma;
|
||||||
|
|
||||||
|
pair_map seen;
|
||||||
|
pair_queue todo;
|
||||||
|
|
||||||
|
state_pair p(left->get_init_state(), 0);
|
||||||
|
state* ris = right->get_init_state();
|
||||||
|
p.second = ris;
|
||||||
|
seen[p] = 0;
|
||||||
|
todo.push_back(p);
|
||||||
|
res->set_init_state(0);
|
||||||
|
|
||||||
|
typedef state_explicit_number::transition trans;
|
||||||
|
|
||||||
|
while (!todo.empty())
|
||||||
|
{
|
||||||
|
p = todo.front();
|
||||||
|
todo.pop_front();
|
||||||
|
const state* ls = p.first;
|
||||||
|
const state* rs = p.second;
|
||||||
|
int src = seen[p];
|
||||||
|
|
||||||
|
tgba_succ_iterator* li = left->succ_iter(ls);
|
||||||
|
for (li->first(); !li->done(); li->next())
|
||||||
|
{
|
||||||
|
state_pair d(li->current_state(), ris);
|
||||||
|
bdd lc = li->current_condition();
|
||||||
|
|
||||||
|
tgba_succ_iterator* ri = 0;
|
||||||
|
// Should we reset the right automaton?
|
||||||
|
if ((lc & v) == lc)
|
||||||
|
{
|
||||||
|
// No.
|
||||||
|
ri = right->succ_iter(rs);
|
||||||
|
ri->first();
|
||||||
|
}
|
||||||
|
// Yes. Reset the right automaton.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.second = ris;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loops over all the right transitions
|
||||||
|
// if RI is defined. Otherwise this just makes
|
||||||
|
// one iteration as if the right automaton was
|
||||||
|
// looping in state 0 with "true".
|
||||||
|
while (!ri || !ri->done())
|
||||||
|
{
|
||||||
|
bdd cond = lc;
|
||||||
|
bdd ra = allacc;
|
||||||
|
if (ri)
|
||||||
|
{
|
||||||
|
cond = lc & ri->current_condition();
|
||||||
|
// Skip incompatible transitions.
|
||||||
|
if (cond == bddfalse)
|
||||||
|
{
|
||||||
|
ri->next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d.second = ri->current_state();
|
||||||
|
ra = (ri->current_acceptance_conditions() & rma) | radd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dest = seen.size();
|
||||||
|
pair_map::const_iterator i = seen.find(d);
|
||||||
|
if (i != seen.end())
|
||||||
|
{
|
||||||
|
dest = i->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
seen[d] = dest;
|
||||||
|
todo.push_back(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
trans* t = res->create_transition(src, dest);
|
||||||
|
t->condition = bdd_exist(cond, v);
|
||||||
|
bdd la = (li->current_acceptance_conditions() & lma) | ladd;
|
||||||
|
t->acceptance_conditions = ra & la;
|
||||||
|
|
||||||
|
if (ri)
|
||||||
|
ri->next();
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete ri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete left;
|
||||||
|
delete right;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tgba*
|
||||||
|
compsusp(const ltl::formula* f, bdd_dict* dict,
|
||||||
|
bool no_wdba, bool no_simulation,
|
||||||
|
bool early_susp, bool no_susp_product, bool wdba_smaller,
|
||||||
|
bool oblig)
|
||||||
|
{
|
||||||
|
ltl_suspender_visitor::fmap_t g2s;
|
||||||
|
ltl_suspender_visitor::fmap_t a2o;
|
||||||
|
ltl_suspender_visitor v(g2s, a2o, oblig);
|
||||||
|
const ltl::formula* g = v.recurse(f);
|
||||||
|
|
||||||
|
tgba* res;
|
||||||
|
{
|
||||||
|
// Translate the patched formula, and remove useless SCCs.
|
||||||
|
tgba* aut = spot::ltl_to_tgba_fm(g, dict, true, true, false, false, 0, 0);
|
||||||
|
res = scc_filter(aut, false);
|
||||||
|
delete aut;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!no_wdba)
|
||||||
|
{
|
||||||
|
tgba* min = minimize_obligation(res, g, 0, wdba_smaller);
|
||||||
|
if (min != res)
|
||||||
|
{
|
||||||
|
delete res;
|
||||||
|
res = min;
|
||||||
|
no_simulation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!no_simulation)
|
||||||
|
{
|
||||||
|
tgba* sim = spot::iterated_simulations(res);
|
||||||
|
delete res;
|
||||||
|
res = sim;
|
||||||
|
}
|
||||||
|
|
||||||
|
spot::formula_bdd_map susp;
|
||||||
|
ltl_suspender_visitor::fmap_t::const_iterator it;
|
||||||
|
for (it = g2s.begin(); it != g2s.end(); ++it)
|
||||||
|
{
|
||||||
|
bdd_dict::fv_map::const_iterator j = dict->var_map.find(it->first);
|
||||||
|
assert(j != dict->var_map.end());
|
||||||
|
susp[it->second] = bdd_ithvar(j->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove suspendable formulae from non-accepting SCCs.
|
||||||
|
bdd suspvars = bddtrue;
|
||||||
|
for (formula_bdd_map::const_iterator i = susp.begin();
|
||||||
|
i != susp.end(); ++i)
|
||||||
|
suspvars &= i->second;
|
||||||
|
|
||||||
|
bdd allaccap = bddtrue; // set of atomic prop used in accepting SCCs.
|
||||||
|
tgba* aut = res;
|
||||||
|
{
|
||||||
|
scc_map sm(aut);
|
||||||
|
sm.build_map();
|
||||||
|
|
||||||
|
// Restrict suspvars to the set of suspension labels that occur
|
||||||
|
// in accepting SCC.
|
||||||
|
unsigned sn = sm.scc_count();
|
||||||
|
for (unsigned n = 0; n < sn; n++)
|
||||||
|
if (sm.accepting(n))
|
||||||
|
allaccap &= sm.ap_set_of(n);
|
||||||
|
|
||||||
|
bdd ignored = bdd_exist(suspvars, allaccap);
|
||||||
|
suspvars = bdd_existcomp(suspvars, allaccap);
|
||||||
|
res = scc_filter(aut, false, &sm, suspvars, early_susp, ignored);
|
||||||
|
}
|
||||||
|
delete aut;
|
||||||
|
|
||||||
|
// Do we need to synchronize any suspended formula?
|
||||||
|
if (!susp.empty() && !no_susp_product)
|
||||||
|
for (formula_bdd_map::const_iterator i = susp.begin();
|
||||||
|
i != susp.end(); ++i)
|
||||||
|
if ((allaccap & i->second) == allaccap)
|
||||||
|
res = susp_prod(res, i->first, i->second);
|
||||||
|
|
||||||
|
g->destroy();
|
||||||
|
|
||||||
|
for (ltl_suspender_visitor::fmap_t::iterator i = g2s.begin();
|
||||||
|
i != g2s.end(); ++i)
|
||||||
|
i->second->destroy();
|
||||||
|
for (ltl_suspender_visitor::fmap_t::iterator i = a2o.begin();
|
||||||
|
i != a2o.end(); ++i)
|
||||||
|
i->second->destroy();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/tgbaalgos/compsusp.hh
Normal file
58
src/tgbaalgos/compsusp.hh
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
// -*- coding: utf-8 -*-
|
||||||
|
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
|
||||||
|
// de l'Epita (LRDE).
|
||||||
|
//
|
||||||
|
// This file is part of Spot, a model checking library.
|
||||||
|
//
|
||||||
|
// Spot is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
|
// License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef SPOT_TGBAALGOS_COMPSUSP_HH
|
||||||
|
# define SPOT_TGBAALGOS_COMPSUSP_HH
|
||||||
|
|
||||||
|
#include "ltlast/formula.hh"
|
||||||
|
|
||||||
|
namespace spot
|
||||||
|
{
|
||||||
|
class tgba;
|
||||||
|
class bdd_dict;
|
||||||
|
|
||||||
|
/// \brief Compositional translation algorithm with resetable
|
||||||
|
/// suspension.
|
||||||
|
///
|
||||||
|
/// Described in "Compositional Approach to Suspension and Other
|
||||||
|
/// Improvements to LTL Translation", Tomáš Babiak, Thomas Badie,
|
||||||
|
/// Alexandre Duret-Lutz, Mojmír Křetínský, Jan Strejček (SPIN'13).
|
||||||
|
///
|
||||||
|
/// If \a no_wdba or \a no_simulation is true, the corresponding
|
||||||
|
/// operation is not performed on the skeleton automaton. If \a
|
||||||
|
/// early_susp is true, then composition starts on the transition
|
||||||
|
/// that enters the accepting SCC, not just in the SCC itself. If
|
||||||
|
/// \a no_susp_product is true, then the composition is not
|
||||||
|
/// performed and the skeleton automaton is returned for debugging.
|
||||||
|
/// If \a wdba_smaller is true, then the WDBA-minimization of the
|
||||||
|
/// skeleton is used only if it produces a smaller automaton.
|
||||||
|
///
|
||||||
|
/// Finally the \a oblig flag is a work in progress and should not
|
||||||
|
/// be set to true.
|
||||||
|
///
|
||||||
|
/// This interface is subject to change, and clients aiming for
|
||||||
|
/// long-term stability should better use the services of the
|
||||||
|
/// spot::translator class instead.
|
||||||
|
tgba* compsusp(const ltl::formula* f, bdd_dict* dict,
|
||||||
|
bool no_wdba = false, bool no_simulation = false,
|
||||||
|
bool early_susp = false, bool no_susp_product = false,
|
||||||
|
bool wdba_smaller = false, bool oblig = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SPOT_TGBAALGOS_COMPSUSP_HH
|
||||||
|
|
@ -195,7 +195,7 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
T* out_;
|
T* out_;
|
||||||
const scc_map& sm_;
|
const scc_map& sm_;
|
||||||
const std::vector<bool>& useless_;
|
const std::vector<bool>& useless_;
|
||||||
|
|
@ -205,14 +205,81 @@ namespace spot
|
||||||
remap_t remap_;
|
remap_t remap_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class filter_iter_susp: public filter_iter<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef filter_iter<T> super;
|
||||||
|
filter_iter_susp(const tgba* a,
|
||||||
|
const scc_map& sm,
|
||||||
|
const std::vector<bool>& useless,
|
||||||
|
remap_table_t& remap_table,
|
||||||
|
unsigned max_num,
|
||||||
|
const std::vector<unsigned>& max_table,
|
||||||
|
bool remove_all_useless,
|
||||||
|
bdd susp_pos, bool early_susp, bdd ignored)
|
||||||
|
: super(a, sm, useless, remap_table, max_num, max_table,
|
||||||
|
remove_all_useless),
|
||||||
|
susp_pos(susp_pos), early_susp(early_susp), ignored(ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
process_link(const state* in_s, int in,
|
||||||
|
const state* out_s, int out,
|
||||||
|
const tgba_succ_iterator* si)
|
||||||
|
{
|
||||||
|
unsigned u = this->sm_.scc_of_state(out_s);
|
||||||
|
unsigned v = this->sm_.scc_of_state(in_s);
|
||||||
|
bool destacc = this->sm_.accepting(u);
|
||||||
|
|
||||||
|
typename super::output_t::state::transition* t =
|
||||||
|
create_transition(this->aut_, this->out_, in_s, in, out_s, out);
|
||||||
|
|
||||||
|
bdd cond = bdd_exist(si->current_condition(), ignored);
|
||||||
|
// Remove suspended variables on transitions going to
|
||||||
|
// non-accepting SCC, or on transition between SCC unless
|
||||||
|
// early_susp is set.
|
||||||
|
if (!destacc || (!early_susp && (u != this->sm_.scc_of_state(in_s))))
|
||||||
|
cond = bdd_exist(cond, susp_pos);
|
||||||
|
|
||||||
|
this->out_->add_conditions(t, cond);
|
||||||
|
|
||||||
|
// Regardless of all_, do not output any acceptance condition
|
||||||
|
// if the destination is not in an accepting SCC.
|
||||||
|
//
|
||||||
|
// If all_ is set, do not output any acceptance condition if the
|
||||||
|
// source is not in the same SCC as dest.
|
||||||
|
//
|
||||||
|
// (See the documentation of scc_filter() for a rational.)
|
||||||
|
if (destacc && (!this->all_ || u == v))
|
||||||
|
{
|
||||||
|
bdd acc = si->current_acceptance_conditions();
|
||||||
|
if (acc == bddfalse)
|
||||||
|
return;
|
||||||
|
t->acceptance_conditions = this->remap_[u][acc.id()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
bdd susp_pos;
|
||||||
|
bool early_susp;
|
||||||
|
bdd ignored;
|
||||||
|
};
|
||||||
|
|
||||||
} // anonymous
|
} // anonymous
|
||||||
|
|
||||||
|
|
||||||
tgba* scc_filter(const tgba* aut, bool remove_all_useless)
|
tgba* scc_filter(const tgba* aut, bool remove_all_useless,
|
||||||
|
scc_map* given_sm, bdd susp, bool early_susp, bdd ignored)
|
||||||
{
|
{
|
||||||
scc_map sm(aut);
|
scc_map* sm = given_sm;
|
||||||
sm.build_map();
|
if (!sm)
|
||||||
scc_stats ss = build_scc_stats(sm);
|
{
|
||||||
|
sm = new scc_map(aut);
|
||||||
|
sm->build_map();
|
||||||
|
}
|
||||||
|
scc_stats ss = build_scc_stats(*sm);
|
||||||
|
|
||||||
remap_table_t remap_table(ss.scc_total);
|
remap_table_t remap_table(ss.scc_total);
|
||||||
std::vector<unsigned> max_table(ss.scc_total);
|
std::vector<unsigned> max_table(ss.scc_total);
|
||||||
|
|
@ -220,9 +287,9 @@ namespace spot
|
||||||
|
|
||||||
for (unsigned n = 0; n < ss.scc_total; ++n)
|
for (unsigned n = 0; n < ss.scc_total; ++n)
|
||||||
{
|
{
|
||||||
if (!sm.accepting(n))
|
if (!sm->accepting(n))
|
||||||
continue;
|
continue;
|
||||||
bdd all = sm.useful_acc_of(n);
|
bdd all = sm->useful_acc_of(n);
|
||||||
bdd negall = aut->neg_acceptance_conditions();
|
bdd negall = aut->neg_acceptance_conditions();
|
||||||
|
|
||||||
// Compute a set of useless acceptance conditions.
|
// Compute a set of useless acceptance conditions.
|
||||||
|
|
@ -304,6 +371,7 @@ namespace spot
|
||||||
remap_table[n].insert(std::make_pair(bdd_var(c), --num));
|
remap_table[n].insert(std::make_pair(bdd_var(c), --num));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tgba* ret;
|
||||||
// In most cases we will create a tgba_explicit_string copy of the
|
// In most cases we will create a tgba_explicit_string copy of the
|
||||||
// initial tgba, but this is not very space efficient as the
|
// initial tgba, but this is not very space efficient as the
|
||||||
// labels are built using the "format_state()" string output of
|
// labels are built using the "format_state()" string output of
|
||||||
|
|
@ -314,36 +382,81 @@ namespace spot
|
||||||
dynamic_cast<const tgba_explicit_formula*>(aut);
|
dynamic_cast<const tgba_explicit_formula*>(aut);
|
||||||
if (af)
|
if (af)
|
||||||
{
|
{
|
||||||
filter_iter<tgba_explicit_formula> fi(af, sm, ss.useless_scc_map,
|
if (susp == bddtrue)
|
||||||
remap_table, max_num,
|
{
|
||||||
max_table, remove_all_useless);
|
filter_iter<tgba_explicit_formula> fi(af, *sm,
|
||||||
fi.run();
|
ss.useless_scc_map,
|
||||||
tgba_explicit_formula* res = fi.result();
|
remap_table, max_num,
|
||||||
res->merge_transitions();
|
max_table,
|
||||||
return res;
|
remove_all_useless);
|
||||||
}
|
fi.run();
|
||||||
const tgba_explicit_string* as =
|
tgba_explicit_formula* res = fi.result();
|
||||||
dynamic_cast<const tgba_explicit_string*>(aut);
|
res->merge_transitions();
|
||||||
if (as)
|
ret = res;
|
||||||
{
|
}
|
||||||
filter_iter<tgba_explicit_string> fi(aut, sm, ss.useless_scc_map,
|
else
|
||||||
remap_table, max_num,
|
{
|
||||||
max_table, remove_all_useless);
|
filter_iter_susp<tgba_explicit_formula> fi(af, *sm,
|
||||||
fi.run();
|
ss.useless_scc_map,
|
||||||
tgba_explicit_string* res = fi.result();
|
remap_table, max_num,
|
||||||
res->merge_transitions();
|
max_table,
|
||||||
return res;
|
remove_all_useless,
|
||||||
|
susp, early_susp,
|
||||||
|
ignored);
|
||||||
|
fi.run();
|
||||||
|
tgba_explicit_formula* res = fi.result();
|
||||||
|
res->merge_transitions();
|
||||||
|
ret = res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filter_iter<tgba_explicit_number> fi(aut, sm, ss.useless_scc_map,
|
const tgba_explicit_string* as =
|
||||||
remap_table, max_num,
|
dynamic_cast<const tgba_explicit_string*>(aut);
|
||||||
max_table, remove_all_useless);
|
if (as)
|
||||||
fi.run();
|
{
|
||||||
tgba_explicit_number* res = fi.result();
|
filter_iter<tgba_explicit_string> fi(aut, *sm, ss.useless_scc_map,
|
||||||
res->merge_transitions();
|
remap_table, max_num,
|
||||||
return res;
|
max_table,
|
||||||
|
remove_all_useless);
|
||||||
|
fi.run();
|
||||||
|
tgba_explicit_string* res = fi.result();
|
||||||
|
res->merge_transitions();
|
||||||
|
ret = res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (susp == bddtrue)
|
||||||
|
{
|
||||||
|
filter_iter<tgba_explicit_number> fi(aut, *sm,
|
||||||
|
ss.useless_scc_map,
|
||||||
|
remap_table, max_num,
|
||||||
|
max_table,
|
||||||
|
remove_all_useless);
|
||||||
|
fi.run();
|
||||||
|
tgba_explicit_number* res = fi.result();
|
||||||
|
res->merge_transitions();
|
||||||
|
ret = res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filter_iter_susp<tgba_explicit_number> fi(aut, *sm,
|
||||||
|
ss.useless_scc_map,
|
||||||
|
remap_table, max_num,
|
||||||
|
max_table,
|
||||||
|
remove_all_useless,
|
||||||
|
susp, early_susp,
|
||||||
|
ignored);
|
||||||
|
fi.run();
|
||||||
|
tgba_explicit_number* res = fi.result();
|
||||||
|
res->merge_transitions();
|
||||||
|
ret = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!given_sm)
|
||||||
|
delete sm;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (C) 2009, 2010 Laboratoire de Recherche et Developpement
|
// Copyright (C) 2009, 2010, 2012 Laboratoire de Recherche et
|
||||||
// de l'Epita (LRDE).
|
// Developpement de l'Epita (LRDE).
|
||||||
//
|
//
|
||||||
// This file is part of Spot, a model checking library.
|
// This file is part of Spot, a model checking library.
|
||||||
//
|
//
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
class scc_map;
|
||||||
|
|
||||||
/// \brief Prune unaccepting SCCs and remove superfluous acceptance
|
/// \brief Prune unaccepting SCCs and remove superfluous acceptance
|
||||||
/// conditions.
|
/// conditions.
|
||||||
|
|
@ -45,7 +46,19 @@ namespace spot
|
||||||
/// remove_all_useless is \c false because some algorithms (like the
|
/// remove_all_useless is \c false because some algorithms (like the
|
||||||
/// degeneralization) will work better if transitions going to an
|
/// degeneralization) will work better if transitions going to an
|
||||||
/// accepting SCC are accepting.
|
/// accepting SCC are accepting.
|
||||||
tgba* scc_filter(const tgba* aut, bool remove_all_useless = false);
|
///
|
||||||
|
/// If \a given_sm is supplied, the function will use its result
|
||||||
|
/// without computing a map of its own.
|
||||||
|
///
|
||||||
|
/// If \a susp is different from bddtrue, it should be a conjunction
|
||||||
|
/// of (positive) variables to be removed from transitions going to
|
||||||
|
/// non-accepting SCCs. If early_susp is false, the previous
|
||||||
|
/// variable are also removed from transitions entering an accepting
|
||||||
|
/// SCC. ignored is a conjunction of positive variables that should
|
||||||
|
/// be removed everywhere.
|
||||||
|
tgba* scc_filter(const tgba* aut, bool remove_all_useless = false,
|
||||||
|
scc_map* given_sm = 0, bdd susp = bddtrue,
|
||||||
|
bool early_susp = false, bdd ignored = bddtrue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@
|
||||||
#include "tgbaalgos/isweakscc.hh"
|
#include "tgbaalgos/isweakscc.hh"
|
||||||
#include "kripkeparse/public.hh"
|
#include "kripkeparse/public.hh"
|
||||||
#include "tgbaalgos/simulation.hh"
|
#include "tgbaalgos/simulation.hh"
|
||||||
|
#include "tgbaalgos/compsusp.hh"
|
||||||
|
|
||||||
#include "taalgos/tgba2ta.hh"
|
#include "taalgos/tgba2ta.hh"
|
||||||
#include "taalgos/dotty.hh"
|
#include "taalgos/dotty.hh"
|
||||||
|
|
@ -137,6 +138,8 @@ syntax(char* prog)
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< " -taa use Tauriainen's TAA-based algorithm for LTL"
|
<< " -taa use Tauriainen's TAA-based algorithm for LTL"
|
||||||
<< std::endl
|
<< std::endl
|
||||||
|
<< " -u use Compositional translation"
|
||||||
|
<< std::endl
|
||||||
<< std::endl
|
<< std::endl
|
||||||
|
|
||||||
<< "Options for Couvreur's FM algorithm (-f):" << std::endl
|
<< "Options for Couvreur's FM algorithm (-f):" << std::endl
|
||||||
|
|
@ -337,7 +340,8 @@ main(int argc, char** argv)
|
||||||
bool utf8_opt = false;
|
bool utf8_opt = false;
|
||||||
enum { NoDegen, DegenTBA, DegenSBA } degeneralize_opt = NoDegen;
|
enum { NoDegen, DegenTBA, DegenSBA } degeneralize_opt = NoDegen;
|
||||||
enum { TransitionLabeled, StateLabeled } labeling_opt = TransitionLabeled;
|
enum { TransitionLabeled, StateLabeled } labeling_opt = TransitionLabeled;
|
||||||
enum { TransFM, TransLaCIM, TransLaCIM_ELTL, TransLaCIM_ELTL_ops, TransTAA }
|
enum { TransFM, TransLaCIM, TransLaCIM_ELTL, TransLaCIM_ELTL_ops, TransTAA,
|
||||||
|
TransCompo }
|
||||||
translation = TransFM;
|
translation = TransFM;
|
||||||
bool fm_red = false;
|
bool fm_red = false;
|
||||||
bool fm_exprop_opt = false;
|
bool fm_exprop_opt = false;
|
||||||
|
|
@ -400,6 +404,11 @@ main(int argc, char** argv)
|
||||||
spot::tgba* temp_iterated_sim = 0;
|
spot::tgba* temp_iterated_sim = 0;
|
||||||
spot::tgba* temp_dont_care_sim = 0;
|
spot::tgba* temp_dont_care_sim = 0;
|
||||||
spot::tgba* temp_dont_care_iterated_sim = 0;
|
spot::tgba* temp_dont_care_iterated_sim = 0;
|
||||||
|
bool cs_nowdba = true;
|
||||||
|
bool cs_wdba_smaller = false;
|
||||||
|
bool cs_nosimul = true;
|
||||||
|
bool cs_early_start = false;
|
||||||
|
bool cs_oblig = false;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
|
@ -826,6 +835,50 @@ main(int argc, char** argv)
|
||||||
tok = strtok(0, ", \t;");
|
tok = strtok(0, ", \t;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!strncmp(argv[formula_index], "-u", 2))
|
||||||
|
{
|
||||||
|
translation = TransCompo;
|
||||||
|
const char* c = argv[formula_index] + 2;
|
||||||
|
while (*c != 0)
|
||||||
|
{
|
||||||
|
switch (*c)
|
||||||
|
{
|
||||||
|
case '2':
|
||||||
|
cs_nowdba = false;
|
||||||
|
cs_wdba_smaller = true;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
cs_nowdba = false;
|
||||||
|
cs_wdba_smaller = false;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
cs_nosimul = false;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
cs_early_start = true;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
cs_nowdba = true;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
cs_nosimul = true;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
cs_early_start = false;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
cs_oblig = true;
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
cs_oblig = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Unknown suboption `" << *c
|
||||||
|
<< "' for option -u" << std::endl;
|
||||||
|
}
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (!strcmp(argv[formula_index], "-v"))
|
else if (!strcmp(argv[formula_index], "-v"))
|
||||||
{
|
{
|
||||||
output = 5;
|
output = 5;
|
||||||
|
|
@ -906,6 +959,7 @@ main(int argc, char** argv)
|
||||||
case TransFM:
|
case TransFM:
|
||||||
case TransLaCIM:
|
case TransLaCIM:
|
||||||
case TransTAA:
|
case TransTAA:
|
||||||
|
case TransCompo:
|
||||||
{
|
{
|
||||||
spot::ltl::parse_error_list pel;
|
spot::ltl::parse_error_list pel;
|
||||||
tm.start("parsing formula");
|
tm.start("parsing formula");
|
||||||
|
|
@ -1037,7 +1091,7 @@ main(int argc, char** argv)
|
||||||
|
|
||||||
if (f->is_psl_formula()
|
if (f->is_psl_formula()
|
||||||
&& !f->is_ltl_formula()
|
&& !f->is_ltl_formula()
|
||||||
&& translation != TransFM)
|
&& (translation != TransFM && translation != TransCompo))
|
||||||
{
|
{
|
||||||
std::cerr << "Only the FM algorithm can translate PSL formulae;"
|
std::cerr << "Only the FM algorithm can translate PSL formulae;"
|
||||||
<< " I'm using it for this formula." << std::endl;
|
<< " I'm using it for this formula." << std::endl;
|
||||||
|
|
@ -1055,6 +1109,13 @@ main(int argc, char** argv)
|
||||||
unobservables,
|
unobservables,
|
||||||
fm_red ? simp : 0);
|
fm_red ? simp : 0);
|
||||||
break;
|
break;
|
||||||
|
case TransCompo:
|
||||||
|
{
|
||||||
|
a = spot::compsusp(f, dict,
|
||||||
|
cs_nowdba, cs_nosimul, cs_early_start,
|
||||||
|
false, cs_wdba_smaller, cs_oblig);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TransTAA:
|
case TransTAA:
|
||||||
a = spot::ltl_to_taa(f, dict, containment);
|
a = spot::ltl_to_taa(f, dict, containment);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,21 @@ Algorithm
|
||||||
Enabled = yes
|
Enabled = yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Compositional Suspension"
|
||||||
|
Path = "${LBTT_TRANSLATE}"
|
||||||
|
Parameters = "--spot '../ltl2tgba -u -F -f -t'"
|
||||||
|
Enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
{
|
||||||
|
Name = "Compositional Suspension (-r4)"
|
||||||
|
Path = "${LBTT_TRANSLATE}"
|
||||||
|
Parameters = "--spot '../ltl2tgba -u -r4 -F -f -t'"
|
||||||
|
Enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
Algorithm
|
Algorithm
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue