This helps working around missing C functions like strcasecmp that do not exist everywhere (e.g. on Cygwin), and for which lib/ supplies a replacement. Unfortunately we do not have such build in our current continuous integration suite, so we cannot easily detect files where such config.h inclusion would be useful. Therefore this patch simply makes it mandatory to include config.h in *.cc files. Including this in public *.hh file is currently forbidden. * spot/gen/automata.cc, spot/gen/formulas.cc, spot/kripke/fairkripke.cc, spot/kripke/kripke.cc, spot/ltsmin/ltsmin.cc, spot/misc/game.cc, spot/parseaut/fmterror.cc, spot/parsetl/fmterror.cc, spot/parsetl/parsetl.yy, spot/priv/bddalloc.cc, spot/priv/freelist.cc, spot/priv/satcommon.cc, spot/priv/trim.cc, spot/priv/weight.cc, spot/ta/ta.cc, spot/ta/taexplicit.cc, spot/ta/taproduct.cc, spot/ta/tgtaexplicit.cc, spot/ta/tgtaproduct.cc, spot/taalgos/dot.cc, spot/taalgos/emptinessta.cc, spot/taalgos/minimize.cc, spot/taalgos/reachiter.cc, spot/taalgos/statessetbuilder.cc, spot/taalgos/stats.cc, spot/taalgos/tgba2ta.cc, spot/tl/apcollect.cc, spot/tl/contain.cc, spot/tl/declenv.cc, spot/tl/defaultenv.cc, spot/tl/dot.cc, spot/tl/exclusive.cc, spot/tl/hierarchy.cc, spot/tl/length.cc, spot/tl/ltlf.cc, spot/tl/mark.cc, spot/tl/mutation.cc, spot/tl/nenoform.cc, spot/tl/print.cc, spot/tl/randomltl.cc, spot/tl/relabel.cc, spot/tl/remove_x.cc, spot/tl/simplify.cc, spot/tl/snf.cc, spot/tl/unabbrev.cc, spot/twa/acc.cc, spot/twa/bdddict.cc, spot/twa/bddprint.cc, spot/twa/formula2bdd.cc, spot/twa/taatgba.cc, spot/twa/twa.cc, spot/twa/twagraph.cc, spot/twa/twaproduct.cc, spot/twaalgos/aiger.cc, spot/twaalgos/alternation.cc, spot/twaalgos/are_isomorphic.cc, spot/twaalgos/bfssteps.cc, spot/twaalgos/canonicalize.cc, spot/twaalgos/cleanacc.cc, spot/twaalgos/cobuchi.cc, spot/twaalgos/complement.cc, spot/twaalgos/complete.cc, spot/twaalgos/compsusp.cc, spot/twaalgos/couvreurnew.cc, spot/twaalgos/cycles.cc, spot/twaalgos/degen.cc, spot/twaalgos/determinize.cc, spot/twaalgos/dot.cc, spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc, spot/twaalgos/dualize.cc, spot/twaalgos/emptiness.cc, spot/twaalgos/gtec/ce.cc, spot/twaalgos/gtec/gtec.cc, spot/twaalgos/gtec/sccstack.cc, spot/twaalgos/gtec/status.cc, spot/twaalgos/gv04.cc, spot/twaalgos/hoa.cc, spot/twaalgos/iscolored.cc, spot/twaalgos/isdet.cc, spot/twaalgos/isunamb.cc, spot/twaalgos/isweakscc.cc, spot/twaalgos/langmap.cc, spot/twaalgos/lbtt.cc, spot/twaalgos/ltl2taa.cc, spot/twaalgos/ltl2tgba_fm.cc, spot/twaalgos/magic.cc, spot/twaalgos/mask.cc, spot/twaalgos/minimize.cc, spot/twaalgos/neverclaim.cc, spot/twaalgos/parity.cc, spot/twaalgos/postproc.cc, spot/twaalgos/powerset.cc, spot/twaalgos/product.cc, spot/twaalgos/rabin2parity.cc, spot/twaalgos/randomgraph.cc, spot/twaalgos/randomize.cc, spot/twaalgos/reachiter.cc, spot/twaalgos/relabel.cc, spot/twaalgos/remfin.cc, spot/twaalgos/remprop.cc, spot/twaalgos/sbacc.cc, spot/twaalgos/sccfilter.cc, spot/twaalgos/sccinfo.cc, spot/twaalgos/se05.cc, spot/twaalgos/sepsets.cc, spot/twaalgos/simulation.cc, spot/twaalgos/split.cc, spot/twaalgos/stats.cc, spot/twaalgos/strength.cc, spot/twaalgos/stripacc.cc, spot/twaalgos/stutter.cc, spot/twaalgos/sum.cc, spot/twaalgos/tau03.cc, spot/twaalgos/tau03opt.cc, spot/twaalgos/totgba.cc, spot/twaalgos/toweak.cc, spot/twaalgos/translate.cc, spot/twaalgos/word.cc, tests/core/acc.cc, tests/core/bitvect.cc, tests/core/checkpsl.cc, tests/core/checkta.cc, tests/core/consterm.cc, tests/core/emptchk.cc, tests/core/equalsf.cc, tests/core/graph.cc, tests/core/ikwiad.cc, tests/core/intvcmp2.cc, tests/core/intvcomp.cc, tests/core/kind.cc, tests/core/kripkecat.cc, tests/core/length.cc, tests/core/ltlrel.cc, tests/core/ngraph.cc, tests/core/parity.cc, tests/core/randtgba.cc, tests/core/readltl.cc, tests/core/reduc.cc, tests/core/safra.cc, tests/core/sccif.cc, tests/core/syntimpl.cc, tests/core/taatgba.cc, tests/core/tostring.cc, tests/core/trival.cc, tests/core/twagraph.cc, tests/ltsmin/modelcheck.cc, spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Include config.h. * spot/gen/Makefile.am, spot/graph/Makefile.am, spot/kripke/Makefile.am, spot/ltsmin/Makefile.am, spot/parseaut/Makefile.am, spot/parsetl/Makefile.am, spot/priv/Makefile.am, spot/ta/Makefile.am, spot/taalgos/Makefile.am, spot/tl/Makefile.am, spot/twa/Makefile.am, spot/twaalgos/Makefile.am, spot/twaalgos/gtec/Makefile.am, tests/Makefile.am: Add the -I lib/ flags. * tests/sanity/includes.test: Catch missing config.h in *.cc, and diagnose config.h in *.hh. * tests/sanity/style.test: Better diagnostics.
353 lines
11 KiB
C++
353 lines
11 KiB
C++
// -*- coding: utf-8 -*-
|
|
// Copyright (C) 2012-2015, 2018 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 "config.h"
|
|
#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.emplace_back(c);
|
|
else if (oblig_ && c.is_syntactic_obligation())
|
|
oblig.emplace_back(c);
|
|
else if (c.is_eventual() && c.is_universal())
|
|
susp.emplace_back(c);
|
|
else
|
|
res.emplace_back(recurse(c));
|
|
}
|
|
if (!oblig.empty())
|
|
{
|
|
res.emplace_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.emplace_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);
|
|
{
|
|
// Copy all atomic propositions, except the one corresponding
|
|
// to the variable v used for synchronization.
|
|
int vn = bdd_var(v);
|
|
assert(dict->bdd_map[vn].type = bdd_dict::var);
|
|
formula vf = dict->bdd_map[vn].f;
|
|
for (auto a: left->ap())
|
|
if (a != vf)
|
|
res->register_ap(a);
|
|
for (auto a: right->ap())
|
|
if (a != vf)
|
|
res->register_ap(a);
|
|
}
|
|
|
|
unsigned lsets = left->num_sets();
|
|
res->set_generalized_buchi(lsets + right->num_sets());
|
|
|
|
acc_cond::mark_t radd = right->acc().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.emplace_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.emplace_back(d);
|
|
}
|
|
|
|
acc_cond::mark_t a = li->acc() | (racc << lsets);
|
|
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;
|
|
}
|
|
}
|