spot/spot/twaalgos/postproc.cc
Alexandre Duret-Lutz cfe3ed707a fix ltl2tgba -B not always returning Inf(0)
Reported by František Blahoudek.

* spot/twaalgos/postproc.cc: Turn "t" into "Inf(0)" for BA.
* tests/core/ltl2tgba.test: Add test case.
* NEWS: Mention the bug.
2019-12-26 23:05:58 +01:00

638 lines
20 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2012-2019 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/postproc.hh>
#include <spot/twaalgos/minimize.hh>
#include <spot/twaalgos/simulation.hh>
#include <spot/twaalgos/sccfilter.hh>
#include <spot/twaalgos/degen.hh>
#include <spot/twaalgos/stripacc.hh>
#include <cstdlib>
#include <spot/misc/optionmap.hh>
#include <spot/twaalgos/powerset.hh>
#include <spot/twaalgos/isdet.hh>
#include <spot/twaalgos/dtbasat.hh>
#include <spot/twaalgos/dtwasat.hh>
#include <spot/twaalgos/complete.hh>
#include <spot/twaalgos/totgba.hh>
#include <spot/twaalgos/sbacc.hh>
#include <spot/twaalgos/sepsets.hh>
#include <spot/twaalgos/determinize.hh>
#include <spot/twaalgos/alternation.hh>
#include <spot/twaalgos/parity.hh>
#include <spot/twaalgos/cobuchi.hh>
#include <spot/twaalgos/rabin2parity.hh>
#include <spot/twaalgos/cleanacc.hh>
namespace spot
{
namespace
{
static twa_graph_ptr
ensure_ba(twa_graph_ptr& a)
{
if (a->num_sets() == 0)
{
auto m = a->set_buchi();
for (auto& t: a->edges())
t.acc = m;
}
return a;
}
}
postprocessor::postprocessor(const option_map* opt)
{
if (opt)
{
degen_order_ = opt->get("degen-order", 0);
degen_reset_ = opt->get("degen-reset", 1);
degen_cache_ = opt->get("degen-lcache", 1);
degen_lskip_ = opt->get("degen-lskip", 1);
degen_lowinit_ = opt->get("degen-lowinit", 0);
degen_remscc_ = opt->get("degen-remscc", 1);
det_scc_ = opt->get("det-scc", 1);
det_simul_ = opt->get("det-simul", 1);
det_stutter_ = opt->get("det-stutter", 1);
det_max_states_ = opt->get("det-max-states", -1);
det_max_edges_ = opt->get("det-max-edges", -1);
simul_ = opt->get("simul", -1);
scc_filter_ = opt->get("scc-filter", -1);
ba_simul_ = opt->get("ba-simul", -1);
tba_determinisation_ = opt->get("tba-det", 0);
sat_minimize_ = opt->get("sat-minimize", 0);
sat_incr_steps_ = opt->get("sat-incr-steps", -2); // -2 or any num < -1
sat_langmap_ = opt->get("sat-langmap", 0);
sat_acc_ = opt->get("sat-acc", 0);
sat_states_ = opt->get("sat-states", 0);
state_based_ = opt->get("state-based", 0);
wdba_minimize_ = opt->get("wdba-minimize", 1);
if (sat_acc_ && sat_minimize_ == 0)
sat_minimize_ = 1; // Dicho.
if (sat_states_ && sat_minimize_ == 0)
sat_minimize_ = 1;
if (sat_minimize_)
{
tba_determinisation_ = 1;
if (sat_acc_ <= 0)
sat_acc_ = -1;
if (sat_states_ <= 0)
sat_states_ = -1;
}
// Set default param value if not provided and used.
if (sat_minimize_ == 2 && sat_incr_steps_ < 0) // Assume algorithm.
sat_incr_steps_ = 6;
else if (sat_minimize_ == 3 && sat_incr_steps_ < -1) // Incr algorithm.
sat_incr_steps_ = 2;
}
}
twa_graph_ptr
postprocessor::do_simul(const twa_graph_ptr& a, int opt) const
{
if (!has_separate_sets(a))
return a;
switch (opt)
{
case 0:
return a;
case 1:
return simulation(a);
case 2:
return cosimulation(a);
case 3:
default:
return iterated_simulations(a);
}
}
twa_graph_ptr
postprocessor::do_sba_simul(const twa_graph_ptr& a, int opt) const
{
if (ba_simul_ <= 0)
return a;
switch (opt)
{
case 0:
return a;
case 1:
return simulation_sba(a);
case 2:
return cosimulation_sba(a);
case 3:
default:
return iterated_simulations_sba(a);
}
}
twa_graph_ptr
postprocessor::do_degen(const twa_graph_ptr& a) const
{
auto d = degeneralize(a,
degen_reset_, degen_order_,
degen_cache_, degen_lskip_,
degen_lowinit_, degen_remscc_);
return do_sba_simul(d, ba_simul_);
}
twa_graph_ptr
postprocessor::do_degen_tba(const twa_graph_ptr& a) const
{
return degeneralize_tba(a,
degen_reset_, degen_order_,
degen_cache_, degen_lskip_,
degen_lowinit_, degen_remscc_);
}
static void
force_buchi(twa_graph_ptr& a)
{
assert(a->acc().is_t());
acc_cond::mark_t m = a->set_buchi();
for (auto& e: a->edges())
e.acc = m;
}
twa_graph_ptr
postprocessor::do_scc_filter(const twa_graph_ptr& a, bool arg) const
{
if (scc_filter_ == 0)
return a;
if (state_based_ && a->prop_state_acc().is_true())
return scc_filter_states(a, arg);
else
return scc_filter(a, arg);
}
twa_graph_ptr
postprocessor::do_scc_filter(const twa_graph_ptr& a) const
{
return do_scc_filter(a, scc_filter_ > 1);
}
#define PREF_ (pref_ & (Small | Deterministic))
#define COMP_ (pref_ & Complete)
#define SBACC_ (pref_ & SBAcc)
#define COLORED_ (pref_ & Colored)
twa_graph_ptr
postprocessor::finalize(twa_graph_ptr tmp) const
{
if (COMP_)
tmp = complete(tmp);
bool want_parity = type_ & Parity;
if (want_parity && tmp->acc().is_generalized_buchi())
tmp = SBACC_ ? do_degen(tmp) : do_degen_tba(tmp);
if (SBACC_)
tmp = sbacc(tmp);
if (type_ == BA && tmp->acc().is_t())
force_buchi(tmp);
if (want_parity)
{
reduce_parity_here(tmp, COLORED_);
parity_kind kind = parity_kind_any;
parity_style style = parity_style_any;
if ((type_ & ParityMin) == ParityMin)
kind = parity_kind_min;
else if ((type_ & ParityMax) == ParityMax)
kind = parity_kind_max;
if ((type_ & ParityOdd) == ParityOdd)
style = parity_style_odd;
else if ((type_ & ParityEven) == ParityEven)
style = parity_style_even;
change_parity_here(tmp, kind, style);
}
return tmp;
}
twa_graph_ptr
postprocessor::run(twa_graph_ptr a, formula f)
{
if (simul_ < 0)
simul_ = (level_ == Low) ? 1 : 3;
if (ba_simul_ < 0)
ba_simul_ = (level_ == High) ? 3 : 0;
if (scc_filter_ < 0)
scc_filter_ = 1;
if (type_ == BA || SBACC_)
state_based_ = true;
bool via_gba = (type_ == BA) || (type_ == TGBA) || (type_ == Monitor);
bool want_parity = type_ & Parity;
if (COLORED_ && !want_parity)
throw std::runtime_error("postprocessor: the Colored setting only works "
"for parity acceptance");
// Attempt to simplify the acceptance condition, unless this is a
// parity automaton and we want parity acceptance in the output.
auto simplify_acc = [&](twa_graph_ptr in)
{
if (PREF_ == Any && level_ == Low)
return in;
bool isparity = in->acc().is_parity();
if (isparity && in->is_existential()
&& (type_ == Generic || want_parity))
return reduce_parity(in);
if (!(want_parity && isparity))
{
if (level_ == High)
return simplify_acceptance(in);
else
return cleanup_acceptance(in);
}
return cleanup_parity(in);
};
a = simplify_acc(a);
if (!a->is_existential() &&
// We will probably have to revisit this condition later.
// Currently, the intent is that postprocessor should never
// return an alternating automaton, unless it is called with
// its laxest settings.
!(type_ == Generic && PREF_ == Any && level_ == Low))
a = remove_alternation(a);
if ((via_gba && !a->acc().is_generalized_buchi())
|| (want_parity && !a->acc().is_parity()))
{
twa_graph_ptr b = nullptr;
if (want_parity && is_deterministic(a) &&
!a->acc().is_generalized_buchi())
b = iar_maybe(a);
// possible only if a was deterministic and (Rabin-like or Streett-like)
// and we want parity and a is not a TGBA
// NB: on a TGBA, degeneralization is better than IAR
if (b)
a = b;
else
a = to_generalized_buchi(a);
if (PREF_ == Any && level_ == Low)
a = do_scc_filter(a, true);
}
if (PREF_ == Any && level_ == Low
&& (type_ == Generic
|| type_ == TGBA
|| (type_ == BA && a->is_sba())
|| (type_ == Monitor && a->num_sets() == 0)
|| (want_parity && a->acc().is_parity())
|| (type_ == CoBuchi && a->acc().is_co_buchi())))
return finalize(a);
int original_acc = a->num_sets();
// Remove useless SCCs.
if (type_ == Monitor)
// Do not bother about acceptance conditions, they will be
// ignored.
a = scc_filter_states(a);
else
a = do_scc_filter(a, (PREF_ == Any));
if (type_ == Monitor)
{
if (PREF_ == Deterministic)
a = minimize_monitor(a);
else
strip_acceptance_here(a);
if (PREF_ != Any)
{
if (PREF_ != Deterministic)
a = do_simul(a, simul_);
// For Small,High we return the smallest between the output of
// the simulation, and that of the deterministic minimization.
// Prefer the deterministic automaton in case of equality.
if (PREF_ == Small && level_ == High && simul_)
{
auto m = minimize_monitor(a);
if (m->num_states() <= a->num_states())
a = m;
}
a->remove_unused_ap();
}
return finalize(a);
}
if (PREF_ == Any)
{
if (type_ == BA)
a = do_degen(a);
else if (type_ == CoBuchi)
a = to_nca(a);
return finalize(a);
}
bool dba_is_wdba = false;
bool dba_is_minimal = false;
twa_graph_ptr dba = nullptr;
twa_graph_ptr sim = nullptr;
output_aborter aborter_
(det_max_states_ >= 0 ? static_cast<unsigned>(det_max_states_) : -1U,
det_max_edges_ >= 0 ? static_cast<unsigned>(det_max_edges_) : -1U);
output_aborter* aborter =
(det_max_states_ >= 0 || det_max_edges_ >= 0) ? &aborter_ : nullptr;
// (Small,Low) is the only configuration where we do not run
// WDBA-minimization.
if ((PREF_ != Small || level_ != Low) && wdba_minimize_)
{
bool reject_bigger = (PREF_ == Small) && (level_ <= Medium);
dba = minimize_obligation(a, f, nullptr, reject_bigger, aborter);
if (dba
&& dba->prop_inherently_weak().is_true()
&& dba->prop_universal().is_true())
{
// The WDBA is a BA, so no degeneralization is required.
// We just need to add an acceptance set if there is none.
dba_is_minimal = dba_is_wdba = true;
if (type_ == BA)
ensure_ba(dba);
}
else
{
// Minimization failed.
dba = nullptr;
}
}
// Run a simulation when wdba failed (or was not run), or
// at hard levels if we want a small output.
if (!dba || (level_ == High && PREF_ == Small))
{
if (((SBACC_ && a->prop_state_acc().is_true())
|| (type_ == BA && a->is_sba()))
&& !tba_determinisation_)
{
sim = do_sba_simul(a, ba_simul_);
}
else
{
sim = do_simul(a, simul_);
// Degeneralize the result of the simulation if needed.
// No need to do that if tba_determinisation_ will be used.
if (type_ == BA && !tba_determinisation_)
sim = do_degen(sim);
else if (want_parity && !sim->acc().is_parity())
sim = do_degen_tba(sim);
else if (SBACC_ && !tba_determinisation_)
sim = sbacc(sim);
}
}
// If WDBA failed, but the simulation returned a deterministic
// automaton, use it as dba.
assert(dba || sim);
if (!dba && is_deterministic(sim))
{
std::swap(sim, dba);
// We postponed degeneralization above i case we would need
// to perform TBA-determinisation, but now it is clear
// that we won't perform it. So do degeneralize.
if (tba_determinisation_)
{
if (type_ == BA)
{
dba = do_degen(dba);
assert(is_deterministic(dba));
}
else if (SBACC_)
{
dba = sbacc(dba);
assert(is_deterministic(dba));
}
}
}
// If we don't have a DBA, attempt tba-determinization if requested.
if (tba_determinisation_ && !dba)
{
twa_graph_ptr tmpd = nullptr;
if (PREF_ == Deterministic
&& f
&& f.is_syntactic_recurrence()
&& sim->num_sets() > 1)
tmpd = degeneralize_tba(sim);
auto in = tmpd ? tmpd : sim;
// These thresholds are arbitrary.
//
// For producing Small automata, we assume that a
// deterministic automaton that is twice the size of the
// original will never get reduced to a smaller one. We also
// do not want more than 2^13 cycles in an SCC.
//
// For Deterministic automata, we accept automata that
// are 8 times bigger, with no more that 2^15 cycle per SCC.
// The cycle threshold is the most important limit here. You
// may up it if you want to try producing larger automata.
auto tmp =
tba_determinize_check(in,
(PREF_ == Small) ? 2 : 8,
1 << ((PREF_ == Small) ? 13 : 15),
f);
if (tmp && tmp != in)
{
// There is no point in running the reverse simulation on
// a deterministic automaton, since all prefixes are
// unique.
dba = simulation(tmp);
}
if (dba && PREF_ == Deterministic)
{
// disregard the result of the simulation.
sim = nullptr;
}
else
{
// degeneralize sim, because we did not do it earlier
if (type_ == BA)
sim = do_degen(sim);
}
}
if ((PREF_ == Deterministic && (type_ == Generic || want_parity)) && !dba)
{
dba = tgba_determinize(to_generalized_buchi(sim),
false, det_scc_, det_simul_, det_stutter_,
aborter);
// Setting det-max-states or det-max-edges may cause tgba_determinize
// to fail.
if (dba)
{
dba = simplify_acc(dba);
if (level_ != Low)
dba = simulation(dba);
sim = nullptr;
}
}
// Now dba contains either the result of WDBA-minimization (in
// that case dba_is_wdba=true), or some deterministic automaton
// that is either the result of the simulation or of the
// TBA-determinization (dba_is_wdba=false in both cases), or a
// parity automaton coming from tgba_determinize(). If the dba is
// a WDBA, we do not have to run SAT-minimization. A negative
// value in sat_minimize_ can force its use for debugging.
if (sat_minimize_ && dba && (!dba_is_wdba || sat_minimize_ < 0))
{
if (type_ == Generic)
throw std::runtime_error
("postproc() not yet updated to mix sat-minimize and Generic");
unsigned target_acc;
if (type_ == BA)
target_acc = 1;
else if (sat_acc_ != -1)
target_acc = sat_acc_;
else
// Take the number of acceptance conditions from the input
// automaton, not from dba, because dba often has been
// degeneralized by tba_determinize_check(). Make sure it
// is at least 1.
target_acc = original_acc > 0 ? original_acc : 1;
const_twa_graph_ptr in = nullptr;
if (target_acc == 1)
{
// If we are seeking a minimal DBA with unknown number of
// states, then we should start from the degeneralized,
// because the input TBA might be smaller.
if (state_based_)
in = degeneralize(dba);
else
in = degeneralize_tba(dba);
}
else
{
in = dba;
}
twa_graph_ptr res = complete(in);
if (target_acc == 1)
{
if (sat_states_ != -1)
res = dtba_sat_synthetize(res, sat_states_, state_based_);
else if (sat_minimize_ == 1)
res = dtba_sat_minimize_dichotomy
(res, state_based_, sat_langmap_);
else if (sat_minimize_ == 2)
res = dtba_sat_minimize_assume(res, state_based_, -1,
sat_incr_steps_);
else if (sat_minimize_ == 3)
res = dtba_sat_minimize_incr(res, state_based_, -1,
sat_incr_steps_);
else // if (sat_minimize == 4 || sat_minimize == -1)
res = dtba_sat_minimize(res, state_based_);
}
else
{
if (sat_states_ != -1)
res = dtwa_sat_synthetize
(res, target_acc,
acc_cond::acc_code::generalized_buchi(target_acc),
sat_states_, state_based_);
else if (sat_minimize_ == 1)
res = dtwa_sat_minimize_dichotomy
(res, target_acc,
acc_cond::acc_code::generalized_buchi(target_acc),
state_based_, sat_langmap_);
else if (sat_minimize_ == 2)
res = dtwa_sat_minimize_assume
(res, target_acc,
acc_cond::acc_code::generalized_buchi(target_acc),
state_based_, -1, false, sat_incr_steps_);
else if (sat_minimize_ == 3)
res = dtwa_sat_minimize_incr
(res, target_acc,
acc_cond::acc_code::generalized_buchi(target_acc),
state_based_, -1, false, sat_incr_steps_);
else // if (sat_minimize_ == 4 || sat_minimize_ == -1)
res = dtwa_sat_minimize
(res, target_acc,
acc_cond::acc_code::generalized_buchi(target_acc),
state_based_);
}
if (res)
{
dba = do_scc_filter(res, true);
dba_is_minimal = true;
}
}
// Degeneralize the dba resulting from tba-determinization or
// sat-minimization (which is a TBA) if requested and needed.
if (dba && !dba_is_wdba && type_ == BA
&& !(dba_is_minimal && state_based_ && dba->num_sets() == 1))
dba = degeneralize(dba);
if (dba && sim)
{
if (dba->num_states() > sim->num_states())
dba = nullptr;
else
sim = nullptr;
}
if (level_ == High && scc_filter_ != 0)
{
if (dba)
{
// Do that even for WDBA, to remove marks from transitions
// leaving trivial SCCs.
dba = do_scc_filter(dba, true);
assert(!sim);
}
else if (sim)
{
sim = do_scc_filter(sim, true);
assert(!dba);
}
}
sim = dba ? dba : sim;
sim->remove_unused_ap();
if (type_ == CoBuchi)
{
unsigned ns = sim->num_states();
if (PREF_ == Deterministic)
sim = to_dca(sim);
else
sim = to_nca(sim);
// if the input of to_dca/to_nca was weak, the number of
// states has not changed, and running simulation is useless.
if (level_ != Low && ns < sim->num_states())
sim = do_simul(sim, simul_);
}
return finalize(sim);
}
}