rename src/ as spot/ and use include <spot/...>
* NEWS: Mention the change. * src/: Rename as ... * spot/: ... this, adjust all headers to include <spot/...> instead of "...", and adjust all Makefile.am to search headers from the top-level directory. * HACKING: Add conventions about #include. * spot/sanity/style.test: Add a few more grep to catch cases that do not follow these conventions. * .gitignore, Makefile.am, README, bench/stutter/Makefile.am, bench/stutter/stutter_invariance_formulas.cc, bench/stutter/stutter_invariance_randomgraph.cc, configure.ac, debian/rules, doc/Doxyfile.in, doc/Makefile.am, doc/org/.dir-locals.el.in, doc/org/g++wrap.in, doc/org/init.el.in, doc/org/tut01.org, doc/org/tut02.org, doc/org/tut03.org, doc/org/tut10.org, doc/org/tut20.org, doc/org/tut21.org, doc/org/tut22.org, doc/org/tut30.org, iface/ltsmin/Makefile.am, iface/ltsmin/kripke.test, iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh, iface/ltsmin/modelcheck.cc, wrap/python/Makefile.am, wrap/python/ajax/spotcgi.in, wrap/python/spot_impl.i, wrap/python/tests/ltl2tgba.py, wrap/python/tests/randgen.py, wrap/python/tests/run.in: Adjust.
This commit is contained in:
parent
1fddfe60ec
commit
f120dd3206
529 changed files with 1308 additions and 1262 deletions
345
spot/twaalgos/compsusp.cc
Normal file
345
spot/twaalgos/compsusp.cc
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 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 <spot/twaalgos/compsusp.hh>
|
||||
#include <spot/twaalgos/sccfilter.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
#include <spot/twa/twagraph.hh>
|
||||
#include <spot/twaalgos/ltl2tgba_fm.hh>
|
||||
#include <spot/twaalgos/minimize.hh>
|
||||
#include <spot/twaalgos/simulation.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <spot/tl/environment.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
typedef std::map<formula, bdd> formula_bdd_map;
|
||||
typedef std::vector<formula> vec;
|
||||
|
||||
// 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 final
|
||||
{
|
||||
public:
|
||||
typedef std::map<formula, formula> fmap_t;
|
||||
ltl_suspender_visitor(fmap_t& g2s, fmap_t& a2o, bool oblig)
|
||||
: g2s_(g2s), a2o_(a2o), oblig_(oblig)
|
||||
{
|
||||
}
|
||||
|
||||
formula
|
||||
visit(formula f)
|
||||
{
|
||||
switch (op o = f.kind())
|
||||
{
|
||||
case op::Or:
|
||||
case op::And:
|
||||
{
|
||||
vec res;
|
||||
vec oblig;
|
||||
vec susp;
|
||||
unsigned mos = f.size();
|
||||
for (unsigned i = 0; i < mos; ++i)
|
||||
{
|
||||
formula c = f[i];
|
||||
if (c.is_boolean())
|
||||
res.push_back(c);
|
||||
else if (oblig_ && c.is_syntactic_obligation())
|
||||
oblig.push_back(c);
|
||||
else if (c.is_eventual() && c.is_universal())
|
||||
susp.push_back(c);
|
||||
else
|
||||
res.push_back(recurse(c));
|
||||
}
|
||||
if (!oblig.empty())
|
||||
{
|
||||
res.push_back(recurse(formula::multop(o, oblig)));
|
||||
}
|
||||
if (!susp.empty())
|
||||
{
|
||||
formula x = formula::multop(o, susp);
|
||||
// Rewrite 'x' as 'G"x"'
|
||||
formula g = recurse(x);
|
||||
if (o == op::And)
|
||||
{
|
||||
res.push_back(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
// res || susp -> (res && G![susp]) || G[susp])
|
||||
auto r = formula::multop(o, res);
|
||||
auto gn = formula::G(formula::Not(g[0]));
|
||||
return formula::Or({formula::And({r, gn}), g});
|
||||
}
|
||||
}
|
||||
return formula::multop(o, res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return f.map([this](formula f)
|
||||
{
|
||||
return this->recurse(f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
formula
|
||||
recurse(formula f)
|
||||
{
|
||||
formula res;
|
||||
if (f.is_boolean())
|
||||
return f;
|
||||
if (oblig_ && f.is_syntactic_obligation())
|
||||
{
|
||||
fmap_t::const_iterator i = assoc_.find(f);
|
||||
if (i != assoc_.end())
|
||||
return i->second;
|
||||
|
||||
std::ostringstream s;
|
||||
print_psl(s << "〈", f) << "〉";
|
||||
res = formula::ap(s.str());
|
||||
a2o_[res] = f;
|
||||
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 formula::G(i->second);
|
||||
|
||||
std::ostringstream s;
|
||||
print_psl(s << '[', f) << "]$";
|
||||
res = formula::ap(s.str());
|
||||
g2s_[res] = f;
|
||||
assoc_[f] = res;
|
||||
return formula::G(res);
|
||||
}
|
||||
return visit(f);
|
||||
}
|
||||
|
||||
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, unsigned> pair_map;
|
||||
typedef std::deque<state_pair> pair_queue;
|
||||
|
||||
static
|
||||
twa_graph_ptr
|
||||
susp_prod(const const_twa_ptr& left, formula f, bdd v)
|
||||
{
|
||||
bdd_dict_ptr dict = left->get_dict();
|
||||
auto right =
|
||||
iterated_simulations(scc_filter(ltl_to_tgba_fm(f, dict, true, true),
|
||||
false));
|
||||
|
||||
twa_graph_ptr res = make_twa_graph(dict);
|
||||
dict->register_all_variables_of(left, res);
|
||||
dict->register_all_variables_of(right, res);
|
||||
dict->unregister_variable(bdd_var(v), res);
|
||||
|
||||
const acc_cond& la = left->acc();
|
||||
const acc_cond& ra = right->acc();
|
||||
res->set_generalized_buchi(la.num_sets() + ra.num_sets());
|
||||
|
||||
acc_cond::mark_t radd = ra.all_sets();
|
||||
|
||||
pair_map seen;
|
||||
pair_queue todo;
|
||||
|
||||
state_pair p(left->get_init_state(), nullptr);
|
||||
const state* ris = right->get_init_state();
|
||||
p.second = ris;
|
||||
unsigned i = res->new_state();
|
||||
seen[p] = i;
|
||||
todo.push_back(p);
|
||||
res->set_init_state(i);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
p = todo.front();
|
||||
todo.pop_front();
|
||||
const state* ls = p.first;
|
||||
const state* rs = p.second;
|
||||
int src = seen[p];
|
||||
|
||||
for (auto li: left->succ(ls))
|
||||
{
|
||||
state_pair d(li->dst(), ris);
|
||||
bdd lc = li->cond();
|
||||
|
||||
twa_succ_iterator* ri = nullptr;
|
||||
// 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 edges
|
||||
// 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;
|
||||
acc_cond::mark_t racc = radd;
|
||||
if (ri)
|
||||
{
|
||||
cond = lc & ri->cond();
|
||||
// Skip incompatible edges.
|
||||
if (cond == bddfalse)
|
||||
{
|
||||
ri->next();
|
||||
continue;
|
||||
}
|
||||
d.second = ri->dst();
|
||||
racc = ri->acc();
|
||||
}
|
||||
|
||||
int dest;
|
||||
pair_map::const_iterator i = seen.find(d);
|
||||
if (i != seen.end()) // Is this an existing state?
|
||||
{
|
||||
dest = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = res->new_state();
|
||||
seen[d] = dest;
|
||||
todo.push_back(d);
|
||||
}
|
||||
|
||||
acc_cond::mark_t a =
|
||||
res->acc().join(la, li->acc(),
|
||||
ra, racc);
|
||||
res->new_edge(src, dest, bdd_exist(cond, v), a);
|
||||
|
||||
if (ri)
|
||||
ri->next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (ri)
|
||||
right->release_iter(ri);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
twa_graph_ptr
|
||||
compsusp(formula f, const bdd_dict_ptr& 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);
|
||||
formula g = v.recurse(f);
|
||||
|
||||
// Translate the patched formula, and remove useless SCCs.
|
||||
twa_graph_ptr res =
|
||||
scc_filter(ltl_to_tgba_fm(g, dict, true, true, false, false,
|
||||
nullptr, nullptr),
|
||||
false);
|
||||
|
||||
if (!no_wdba)
|
||||
{
|
||||
twa_graph_ptr min = minimize_obligation(res, g,
|
||||
nullptr, wdba_smaller);
|
||||
if (min != res)
|
||||
{
|
||||
res = min;
|
||||
no_simulation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_simulation)
|
||||
res = iterated_simulations(res);
|
||||
|
||||
// Create a map of suspended formulae to BDD variables.
|
||||
spot::formula_bdd_map susp;
|
||||
for (auto& it: g2s)
|
||||
{
|
||||
auto j = dict->var_map.find(it.first);
|
||||
// If no BDD variable of this suspended formula exist in the
|
||||
// BDD dict, it means the suspended subformulae was never
|
||||
// actually used in the automaton. Just skip it. FIXME: It
|
||||
// would be better if we had a way to check that the variable
|
||||
// is used in this automaton, and not in some automaton
|
||||
// (sharing the same dictionary.)
|
||||
if (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.
|
||||
{
|
||||
scc_info si(res);
|
||||
|
||||
// Restrict suspvars to the set of suspension labels that occur
|
||||
// in accepting SCC.
|
||||
unsigned sn = si.scc_count();
|
||||
for (unsigned n = 0; n < sn; n++)
|
||||
if (si.is_accepting_scc(n))
|
||||
allaccap &= si.scc_ap_support(n);
|
||||
|
||||
bdd ignored = bdd_exist(suspvars, allaccap);
|
||||
suspvars = bdd_existcomp(suspvars, allaccap);
|
||||
res = scc_filter_susp(res, false, suspvars, ignored, early_susp, &si);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue