spot/spot/misc/game.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

342 lines
9.3 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 <cmath>
#include <spot/misc/game.hh>
namespace spot
{
void parity_game::print(std::ostream& os)
{
os << "parity " << num_states() - 1 << ";\n";
std::vector<bool> seen(num_states(), false);
std::vector<unsigned> todo({get_init_state_number()});
while (!todo.empty())
{
unsigned src = todo.back();
todo.pop_back();
seen[src] = true;
os << src << ' ';
os << out(src).begin()->acc.max_set() - 1 << ' ';
os << owner(src) << ' ';
bool first = true;
for (auto& e: out(src))
{
if (!first)
os << ',';
first = false;
os << e.dst;
if (!seen[e.dst])
todo.push_back(e.dst);
}
if (src == get_init_state_number())
os << " \"INIT\"";
os << ";\n";
}
}
std::pair<parity_game::region_t, parity_game::strategy_t>
parity_game::solve() const
{
region_t states_;
for (unsigned i = 0; i < num_states(); ++i)
states_.insert(i);
unsigned m = max_parity();
return solve_rec(states_, m);
}
bool parity_game::solve_qp() const
{
return reachability_game(*this).is_reachable();
}
parity_game::strategy_t
parity_game::attractor(const region_t& subgame, region_t& set,
unsigned max_parity, bool odd, bool attr_max) const
{
strategy_t strategy;
unsigned size;
std::unordered_set<unsigned> complement = subgame;
std::unordered_set<unsigned> delta = set;
do
{
size = set.size();
for (unsigned s: delta)
complement.erase(s);
for (unsigned s: complement)
{
bool any = false;
bool all = true;
unsigned i = 0;
for (auto& e: out(s))
{
if (e.acc.max_set() - 1 <= max_parity && subgame.count(e.dst))
{
if (set.count(e.dst)
|| (attr_max && e.acc.max_set() - 1 == max_parity))
{
if (!any && owner_[s] && odd)
strategy[s] = i;
any = true;
}
else
all = false;
}
++i;
}
bool owner_is_odd = !!owner_[s] == odd;
if ((owner_is_odd && any) || (!owner_is_odd && all))
{
set.insert(s);
delta.insert(s);
}
}
} while (set.size() != size);
return strategy;
}
auto parity_game::solve_rec(region_t& subgame, unsigned max_parity) const
-> std::pair<region_t, strategy_t>
{
// The algorithm works recursively on subgames. To avoid useless copies of
// the game at each call, subgame and max_parity are used to filter states
// and transitions.
if (max_parity == 0 || subgame.empty())
return {};
bool odd = max_parity % 2 == 1;
region_t w1;
strategy_t strategy;
// Recursion on max_parity.
region_t u;
auto strat_u = attractor(subgame, u, max_parity, odd, true);
for (unsigned s: u)
subgame.erase(s);
region_t w00; // Even's winning region in the first recursive call.
region_t w10; // Odd's winning region in the first recursive call.
strategy_t s10; // Odd's winning strategy in the first recursive call.
std::tie(w10, s10) = solve_rec(subgame, max_parity - 1);
if (odd && w10.size() != subgame.size())
for (unsigned s: subgame)
if (w10.find(s) == w10.end())
w00.insert(s);
// If !odd, w00 is not used, no need to compute it.
subgame.insert(u.begin(), u.end());
if (odd && w10.size() + u.size() == subgame.size())
{
strategy.insert(s10.begin(), s10.end());
strategy.insert(strat_u.begin(), strat_u.end());
w1.insert(subgame.begin(), subgame.end());
return {w1, strategy};
}
else if (!odd && w10.empty())
return {};
// Recursion on game size.
auto& wni = odd ? w00 : w10;
auto strat_wni = attractor(subgame, wni, max_parity, !odd);
if (!odd)
strat_wni.insert(s10.begin(), s10.end());
for (unsigned s: wni)
subgame.erase(s);
region_t w11; // Odd's winning region in the second recursive call.
strategy_t s11; // Odd's winning strategy in the second recursive call.
std::tie(w11, s11) = solve_rec(subgame, max_parity);
w1.insert(w11.begin(), w11.end());
strategy.insert(s11.begin(), s11.end());
if (!odd)
{
strategy.insert(strat_wni.begin(), strat_wni.end());
w1.insert(wni.begin(), wni.end());
}
subgame.insert(wni.begin(), wni.end());
return {w1, strategy};
}
int reachability_state::compare(const state* other) const
{
auto o = down_cast<const reachability_state*>(other);
assert(o);
if (num_ != o->num())
return num_ - o->num();
if (b_ < o->b())
return -1;
if (b_ > o->b())
return 1;
return 0;
}
bool reachability_state::operator<(const reachability_state& o) const
{
// Heuristic to process nodes with a higher chance of leading to a target
// node first.
assert(b_.size() == o.b().size());
for (unsigned i = b_.size(); i > 0; --i)
if (b_[i - 1] != o.b()[i - 1])
return b_[i - 1] > o.b()[i - 1];
return num_ < o.num();
}
const reachability_state* reachability_game_succ_iterator::dst() const
{
// NB: colors are indexed at 1 in Calude et al.'s paper and at 0 in spot
// All acceptance sets are therefore incremented (which is already done by
// max_set), so that 0 can be kept as a special value indicating that no
// i-sequence is tracked at this index. Hence the parity switch in the
// following implementation, compared to the paper.
std::vector<unsigned> b = state_.b();
unsigned a = it_->acc.max_set();
assert(a);
unsigned i = -1U;
bool all_even = a % 2 == 0;
for (unsigned j = 0; j < b.size(); ++j)
{
if ((b[j] % 2 == 1 || b[j] == 0) && all_even)
i = j;
else if (b[j] > 0 && a > b[j])
i = j;
all_even = all_even && b[j] > 0 && b[j] % 2 == 0;
}
if (i != -1U)
{
b[i] = a;
for (unsigned j = 0; j < i; ++j)
b[j] = 0;
}
return new reachability_state(it_->dst, b, !state_.anke());
}
const reachability_state* reachability_game::get_init_state() const
{
// b[ceil(log(n + 1))] != 0 implies there is an i-sequence of length
// 2^(ceil(log(n + 1))) >= 2^log(n + 1) = n + 1, so it has to contain a
// cycle.
unsigned i = std::ceil(std::log2(pg_.num_states() + 1));
return new reachability_state(pg_.get_init_state_number(),
std::vector<unsigned>(i + 1),
false);
}
reachability_game_succ_iterator*
reachability_game::succ_iter(const state* s) const
{
auto state = down_cast<const reachability_state*>(s);
return new reachability_game_succ_iterator(pg_, *state);
}
std::string reachability_game::format_state(const state* s) const
{
auto state = down_cast<const reachability_state*>(s);
std::ostringstream fmt;
bool first = true;
fmt << state->num() << ", ";
fmt << '[';
for (unsigned b : state->b())
{
if (!first)
fmt << ',';
else
first = false;
fmt << b;
}
fmt << ']';
return fmt.str();
}
bool reachability_game::is_reachable()
{
std::set<spot::reachability_state> todo{*init_state_};
while (!todo.empty())
{
spot::reachability_state v = *todo.begin();
todo.erase(todo.begin());
std::vector<spot::const_reachability_state_ptr> succs;
spot::reachability_game_succ_iterator* it = succ_iter(&v);
for (it->first(); !it->done(); it->next())
succs.push_back(spot::const_reachability_state_ptr(it->dst()));
if (is_target(v))
{
c_[v] = 1;
if (mark(v))
return true;
continue;
}
else if (v.anke())
c_[v] = 1;
else
c_[v] = succs.size();
for (auto succ: succs)
{
if (parents_[*succ].empty())
{
if (*succ != *init_state_)
{
todo.insert(*succ);
parents_[*succ] = { v };
c_[*succ] = -1U;
}
}
else
{
parents_[*succ].push_back(v);
if (c_[*succ] == 0 && mark(v))
return true;
}
}
}
return false;
}
bool reachability_game::mark(const spot::reachability_state& s)
{
if (c_[s] > 0)
{
--c_[s];
if (c_[s] == 0)
{
if (s == *init_state_)
return true;
for (auto& u: parents_[s])
if (mark(u))
return true;
}
}
return false;
}
bool reachability_game::is_target(const reachability_state& v)
{
return v.b().back();
}
}