spot/spot/tl/hierarchy.cc
Alexandre Duret-Lutz ac6b0c9432 include config.h in all *.cc files
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.
2018-02-21 17:59:09 +01:00

471 lines
13 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2017, 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 <sstream>
#include <spot/tl/formula.hh>
#include <spot/tl/hierarchy.hh>
#include <spot/tl/nenoform.hh>
#include <spot/twaalgos/isdet.hh>
#include <spot/twaalgos/ltl2tgba_fm.hh>
#include <spot/twaalgos/minimize.hh>
#include <spot/twaalgos/postproc.hh>
#include <spot/twaalgos/remfin.hh>
#include <spot/twaalgos/sccfilter.hh>
#include <spot/twaalgos/strength.hh>
#include <spot/twaalgos/totgba.hh>
#include <spot/twaalgos/cobuchi.hh>
namespace spot
{
namespace
{
static bool
cobuchi_realizable(spot::formula f,
const const_twa_graph_ptr& aut)
{
// Find which algorithm must be performed between nsa_to_nca() and
// dnf_to_nca(). Throw an exception if none of them can be performed.
std::vector<acc_cond::rs_pair> pairs;
bool street_or_parity = aut->acc().is_streett_like(pairs)
|| aut->acc().is_parity();
if (!street_or_parity && !aut->get_acceptance().is_dnf())
throw std::runtime_error("cobuchi_realizable() only works with "
"Streett-like, Parity or any "
"acceptance condition in DNF");
// If !f is a DBA, it belongs to the recurrence class, which means
// f belongs to the persistence class (is cobuchi_realizable).
twa_graph_ptr not_aut = ltl_to_tgba_fm(formula::Not(f), aut->get_dict());
not_aut = scc_filter(not_aut);
if (is_universal(not_aut))
return true;
// Checks if f is cobuchi_realizable.
twa_graph_ptr cobuchi = street_or_parity ? nsa_to_nca(aut)
: dnf_to_nca(aut);
return !cobuchi->intersects(not_aut);
}
static bool
detbuchi_realizable(const twa_graph_ptr& aut)
{
if (is_universal(aut))
return true;
// if aut is a non-deterministic TGBA, we do
// TGBA->DPA->DRA->(D?)BA. The conversion from DRA to
// BA will preserve determinism if possible.
spot::postprocessor p;
p.set_type(spot::postprocessor::Generic);
p.set_pref(spot::postprocessor::Deterministic);
p.set_level(spot::postprocessor::Low);
auto dpa = p.run(aut);
if (dpa->acc().is_generalized_buchi())
{
assert(is_deterministic(dpa));
return true;
}
else
{
auto dra = to_generalized_rabin(dpa);
return rabin_is_buchi_realizable(dra);
}
}
}
[[noreturn]] static void invalid_spot_pr_check()
{
throw std::runtime_error("invalid value for SPOT_PR_CHECK "
"(should be 1 or 2)");
}
static prcheck
algo_to_perform(bool is_persistence, bool aut_given)
{
static prcheck env_algo = [&]()
{
int val;
try
{
auto s = getenv("SPOT_PR_CHECK");
val = s ? std::stoi(s) : 0;
}
catch (const std::exception& e)
{
invalid_spot_pr_check();
}
if (val == 0)
{
if (aut_given && !is_persistence)
return prcheck::via_Rabin;
else if (is_persistence || !aut_given)
return prcheck::via_CoBuchi;
else
SPOT_UNREACHABLE();
}
else if (val == 1)
{
return prcheck::via_CoBuchi;
}
else if (val == 2)
{
return prcheck::via_Rabin;
}
else
{
invalid_spot_pr_check();
}
}();
return env_algo;
}
bool
is_persistence(formula f, twa_graph_ptr aut, prcheck algo)
{
if (f.is_syntactic_persistence())
return true;
// Perform a quick simplification of the formula taking into account the
// following simplification's parameters: basics, synt_impl, event_univ.
spot::tl_simplifier simpl(spot::tl_simplifier_options(true, true, true));
f = simpl.simplify(f);
if (f.is_syntactic_persistence())
return true;
if (algo == prcheck::Auto)
algo = algo_to_perform(true, aut != nullptr);
switch (algo)
{
case prcheck::via_CoBuchi:
return cobuchi_realizable(f, aut ? aut :
ltl_to_tgba_fm(f, make_bdd_dict(), true));
case prcheck::via_Rabin:
return detbuchi_realizable(ltl_to_tgba_fm(formula::Not(f),
make_bdd_dict(), true));
case prcheck::Auto:
SPOT_UNREACHABLE();
}
SPOT_UNREACHABLE();
}
bool
is_recurrence(formula f, twa_graph_ptr aut, prcheck algo)
{
if (f.is_syntactic_recurrence())
return true;
// Perform a quick simplification of the formula taking into account the
// following simplification's parameters: basics, synt_impl, event_univ.
spot::tl_simplifier simpl(spot::tl_simplifier_options(true, true, true));
f = simpl.simplify(f);
if (f.is_syntactic_recurrence())
return true;
if (algo == prcheck::Auto)
algo = algo_to_perform(true, aut != nullptr);
switch (algo)
{
case prcheck::via_CoBuchi:
return cobuchi_realizable(formula::Not(f),
ltl_to_tgba_fm(formula::Not(f),
make_bdd_dict(), true));
case prcheck::via_Rabin:
return detbuchi_realizable(aut ? aut :
ltl_to_tgba_fm(f, make_bdd_dict(), true));
case prcheck::Auto:
SPOT_UNREACHABLE();
}
SPOT_UNREACHABLE();
}
[[noreturn]] static void invalid_spot_o_check()
{
throw std::runtime_error("invalid value for SPOT_O_CHECK "
"(should be 1, 2, or 3)");
}
// This private function is defined in minimize.cc for technical
// reasons.
SPOT_LOCAL bool is_wdba_realizable(formula f, twa_graph_ptr aut = nullptr);
bool
is_obligation(formula f, twa_graph_ptr aut, ocheck algo)
{
if (algo == ocheck::Auto)
{
static ocheck env_algo = []()
{
int val;
try
{
auto s = getenv("SPOT_O_CHECK");
val = s ? std::stoi(s) : 0;
}
catch (const std::exception& e)
{
invalid_spot_o_check();
}
if (val == 0)
return ocheck::via_WDBA;
else if (val == 1)
return ocheck::via_CoBuchi;
else if (val == 2)
return ocheck::via_Rabin;
else if (val == 3)
return ocheck::via_WDBA;
else
invalid_spot_o_check();
}();
algo = env_algo;
}
switch (algo)
{
case ocheck::via_WDBA:
return is_wdba_realizable(f, aut);
case ocheck::via_CoBuchi:
return (is_persistence(f, aut, prcheck::via_CoBuchi)
&& is_recurrence(f, aut, prcheck::via_CoBuchi));
case ocheck::via_Rabin:
return (is_persistence(f, aut, prcheck::via_Rabin)
&& is_recurrence(f, aut, prcheck::via_Rabin));
case ocheck::Auto:
SPOT_UNREACHABLE();
}
SPOT_UNREACHABLE();
}
char mp_class(formula f)
{
if (f.is_syntactic_safety() && f.is_syntactic_guarantee())
return 'B';
auto dict = make_bdd_dict();
auto aut = ltl_to_tgba_fm(f, dict, true);
auto min = minimize_obligation(aut, f);
if (aut != min) // An obligation.
{
scc_info si(min);
// The minimba WDBA can have some trivial accepting SCCs
// that we should ignore in is_terminal_automaton().
bool g = is_terminal_automaton(min, &si, true);
bool s = is_safety_automaton(min, &si);
if (g)
return s ? 'B' : 'G';
else
return s ? 'S' : 'O';
}
// Not an obligation. Could by 'P', 'R', or 'T'.
if (is_recurrence(f, aut))
return 'R';
if (is_persistence(f, aut))
return 'P';
return 'T';
}
std::string mp_class(formula f, const char* opt)
{
return mp_class(mp_class(f), opt);
}
std::string mp_class(char mpc, const char* opt)
{
bool verbose = false;
bool wide = false;
if (opt)
for (;;)
switch (int o = *opt++)
{
case 'v':
verbose = true;
break;
case 'w':
wide = true;
break;
case ' ':
case '\t':
case '\n':
case ',':
break;
case '\0':
case ']':
goto break2;
default:
{
std::ostringstream err;
err << "unknown option '" << o << "' for mp_class()";
throw std::runtime_error(err.str());
}
}
break2:
std::string c(1, mpc);
if (wide)
{
switch (mpc)
{
case 'B':
c = "GSOPRT";
break;
case 'G':
c = "GOPRT";
break;
case 'S':
c = "SOPRT";
break;
case 'O':
c = "OPRT";
break;
case 'P':
c = "PT";
break;
case 'R':
c = "RT";
break;
case 'T':
break;
default:
throw std::runtime_error("mp_class() called with unknown class");
}
}
if (!verbose)
return c;
std::ostringstream os;
bool first = true;
for (char ch: c)
{
if (first)
first = false;
else
os << ' ';
switch (ch)
{
case 'B':
os << "guarantee safety";
break;
case 'G':
os << "guarantee";
break;
case 'S':
os << "safety";
break;
case 'O':
os << "obligation";
break;
case 'P':
os << "persistence";
break;
case 'R':
os << "recurrence";
break;
case 'T':
os << "reactivity";
break;
}
}
return os.str();
}
unsigned nesting_depth(formula f, op oper)
{
unsigned max_depth = 0;
for (formula child: f)
max_depth = std::max(max_depth, nesting_depth(child, oper));
return max_depth + f.is(oper);
}
unsigned nesting_depth(formula f, const op* begin, const op* end)
{
unsigned max_depth = 0;
for (formula child: f)
max_depth = std::max(max_depth, nesting_depth(child, begin, end));
bool matched = std::find(begin, end, f.kind()) != end;
return max_depth + matched;
}
unsigned nesting_depth(formula f, const char* opers)
{
bool want_nnf = false;
std::vector<op> v;
for (;;)
switch (char c = *opers++)
{
case '~':
want_nnf = true;
break;
case '!':
v.push_back(op::Not);
break;
case '&':
v.push_back(op::And);
break;
case '|':
v.push_back(op::Or);
break;
case 'e':
v.push_back(op::Equiv);
break;
case 'F':
v.push_back(op::F);
break;
case 'G':
v.push_back(op::G);
break;
case 'i':
v.push_back(op::Implies);
break;
case 'M':
v.push_back(op::M);
break;
case 'R':
v.push_back(op::R);
break;
case 'U':
v.push_back(op::U);
break;
case 'W':
v.push_back(op::W);
break;
case 'X':
v.push_back(op::X);
break;
case '\0':
case ']':
goto break2;
default:
throw std::runtime_error
(std::string("nesting_depth(): unknown operator '") + c + '\'');
}
break2:
if (want_nnf)
f = negative_normal_form(f);
const op* vd = v.data();
return nesting_depth(f, vd, vd + v.size());
}
}