Commit a8504509 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

introduce output_aborter, and use it in ltlcross

* spot/twaalgos/alternation.cc, spot/twaalgos/alternation.hh,
spot/twaalgos/complement.cc, spot/twaalgos/complement.hh,
spot/twaalgos/determinize.cc, spot/twaalgos/determinize.hh,
spot/twaalgos/minimize.cc, spot/twaalgos/minimize.hh,
spot/twaalgos/postproc.cc, spot/twaalgos/postproc.hh,
spot/twaalgos/powerset.cc, spot/twaalgos/powerset.hh,
spot/twaalgos/product.cc, spot/twaalgos/product.hh: Use an
output_aborter argument to abort if the output is too large.
* bin/ltlcross.cc: Use complement() with an output_aborter
so that ltlcross will not attempt to build complement larger
than 500 states or 5000 edges.  Add --determinize-max-states
and --determinize-max-edges options.
* tests/core/ltlcross3.test, tests/core/ltlcrossce2.test,
tests/core/sccsimpl.test, tests/core/wdba2.test,
tests/python/stutter-inv.ipynb: Adjust test cases.
* NEWS: Document this.
* bin/spot-x.cc: Add documentation for postprocessor's
det-max-states and det-max-edges arguments.
* doc/org/ltlcross.org: Update description.
parent 5c3a33f7
Pipeline #9527 passed with stages
in 158 minutes and 17 seconds
......@@ -8,6 +8,19 @@ New in spot 2.7.4.dev (not yet released)
- ltldo, ltlcross, and autcross are now preferring posix_spawn()
over fork()+exec() when available.
- ltlcross has new options --determinize-max-states=N and
--determinize-max-edges=M to restrict the use of
determinization-based complementation to cases where it produces
automata with at most N states and M edges. By default
determinization is now attempted up to 500 states and 5000
edges. This is an improvement over the previous default where
determinization-based complementation was not performed at all,
unless -D was specified.
- ltlcross will no skip unnecessary cross-checks and
consistency-checks (they are unnecessary when all automata
could be complemented and statistics were not required).
Library:
- Add generic_accepting_run() as a variant of generic_emptiness_check() that
......@@ -32,7 +45,7 @@ New in spot 2.7.4.dev (not yet released)
allows "autfilt [-D] --small" to minimize very-weak automata
whenever they are found to represent obligation properties.)
- There is a new spot::scc_and_mark_filter objet that simplify the
- There is a new spot::scc_and_mark_filter objet that simplifies the
creation of filters to restrict spot::scc_info to some particular
SCC while cutting new SCCs on given acceptance sets. This is used
by spot::generic_emptiness_check() when processing SCCs
......@@ -51,6 +64,20 @@ New in spot 2.7.4.dev (not yet released)
acceptance condition. The output can be alternating only if the
input was alternating.
- There is a new class output_aborter that is used to specify
upper bounds on the size of automata produced by some algorithms.
Several functions have been changed to accept an output_aborter.
This includes:
* tgba_determinize()
* tgba_powerset()
* minimize_obligation()
* minimize_wdba()
* remove_alternation()
* product()
* the new complement()
* the postprocessor class, via the "det-max-state" and
"det-max-edges" options.
- SVA's first_match operator can now be used in SERE formulas and
that is supported by the ltl_to_tgba_fm() translation. See
doc/tl/tl.pdf for the semantics. *WARNING* Because this adds a
......@@ -66,7 +93,7 @@ New in spot 2.7.4.dev (not yet released)
terms of the existing PSL operators. ##[+] and ##[*] are sugar
for ##[1:$] and ##[0:$].
- spot::relabel_apply() make it easier to reverse the effect
- spot::relabel_apply() makes it easier to reverse the effect
of spot::relabel() or spot::relabel_bse() on formula.
- The LTL simplifier learned the following optional rules:
......
This diff is collapsed.
......@@ -103,6 +103,14 @@ for the presence of an accepting self-loop.") },
{ DOC("degen-remscc", "If non-zero (the default), make sure the output \
of the degenalization has as many SCCs as the input, by removing superfluous \
ones.") },
{ DOC("det-max-states", "When defined to a positive integer N, \
determinizations will be aborted whenever the number of generated \
states would exceed N. In this case a non-deterministic automaton \
will be returned.")},
{ DOC("det-max-edges", "When defined to a positive integer N, \
determinizations will be aborted whenever the number of generated \
edges would exceed N. In this case a non-deterministic automaton \
will be returned.")},
{ DOC("det-scc", "Set to 0 to disable scc-based optimizations in \
the determinization algorithm.") },
{ DOC("det-simul", "Set to 0 to disable simulation-based optimizations in \
......
This diff is collapsed.
// -*- coding: utf-8 -*-
// Copyright (C) 2016-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2016-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -349,7 +349,7 @@ namespace spot
}
twa_graph_ptr run(bool named_states)
twa_graph_ptr run(bool named_states, const output_aborter* aborter)
{
// First, we classify each SCC into three possible classes:
//
......@@ -442,6 +442,9 @@ namespace spot
state_set v;
while (!todo.empty())
{
if (aborter && aborter->too_large(res))
return nullptr;
unsigned s = todo.top();
todo.pop();
......@@ -505,14 +508,15 @@ namespace spot
twa_graph_ptr remove_alternation(const const_twa_graph_ptr& aut,
bool named_states)
bool named_states,
const output_aborter* aborter)
{
if (aut->is_existential())
// Nothing to do, why was this function called at all?
return std::const_pointer_cast<twa_graph>(aut);
alternation_remover ar(aut);
return ar.run(named_states);
return ar.run(named_states, aborter);
}
......
// -*- coding: utf-8 -*-
// Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement
// Copyright (C) 2016, 2018, 2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
#include <utility>
namespace spot
......@@ -98,10 +99,14 @@ namespace spot
/// acceptance is only used in presence of size-1 rejecting-SCCs.)
///
/// \param named_states name each state for easier debugging
///
/// \param aborter Return nullptr if the built automaton would
/// be larger than the size specified by the \a aborter.
/// @}
SPOT_API
twa_graph_ptr remove_alternation(const const_twa_graph_ptr& aut,
bool named_states = false);
bool named_states = false,
const output_aborter* aborter = nullptr);
// Remove universal edges on the fly.
......
......@@ -511,17 +511,31 @@ namespace spot
}
twa_graph_ptr
complement(const const_twa_graph_ptr& aut)
complement(const const_twa_graph_ptr& aut, const output_aborter* aborter)
{
if (!aut->is_existential() || is_universal(aut))
return dualize(aut);
if (is_very_weak_automaton(aut))
return remove_alternation(dualize(aut));
return remove_alternation(dualize(aut), aborter);
// Determinize
spot::postprocessor p;
spot::option_map m;
if (aborter)
{
m.set("det-max-states", aborter->max_states());
m.set("det-max-edges", aborter->max_edges());
}
if (aut->num_states() > 32)
{
m.set("ba-simul", 0);
m.set("simul", 0);
}
spot::postprocessor p(&m);
p.set_type(spot::postprocessor::Generic);
p.set_pref(spot::postprocessor::Deterministic);
p.set_level(spot::postprocessor::Low);
return dualize(p.run(std::const_pointer_cast<twa_graph>(aut)));
auto det = p.run(std::const_pointer_cast<twa_graph>(aut));
if (!det)
return nullptr;
return dualize(det);
}
}
......@@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
namespace spot
{
......@@ -70,8 +71,12 @@ namespace spot
/// - any other type of input is determized before
/// complementation.
///
/// If an output_aborter is supplied, it is used to
/// abort the construction of larger automata.
///
/// complement_semidet() is not yet used.
SPOT_API twa_graph_ptr
complement(const const_twa_graph_ptr& aut);
complement(const const_twa_graph_ptr& aut,
const output_aborter* aborter = nullptr);
}
// -*- coding: utf-8 -*-
// Copyright (C) 2015-2018 Laboratoire de Recherche et
// Copyright (C) 2015-2019 Laboratoire de Recherche et
// Développement de l'Epita.
//
// This file is part of Spot, a model checking library.
......@@ -804,7 +804,8 @@ namespace spot
twa_graph_ptr
tgba_determinize(const const_twa_graph_ptr& a,
bool pretty_print, bool use_scc,
bool use_simulation, bool use_stutter)
bool use_simulation, bool use_stutter,
const output_aborter* aborter)
{
if (!a->is_existential())
throw std::runtime_error
......@@ -938,6 +939,8 @@ namespace spot
// The main loop
while (!todo.empty())
{
if (aborter && aborter->too_large(res))
return nullptr;
const safra_state& curr = todo.front().get().first;
unsigned src_num = todo.front().get().second;
todo.pop_front();
......
// -*- coding: utf-8 -*-
// Copyright (C) 2015, 2016 Laboratoire de Recherche et Développement
// de l'Epita.
// Copyright (C) 2015, 2016, 2019 Laboratoire de Recherche et
// Développement de l'Epita.
//
// This file is part of Spot, a model checking library.
//
......@@ -19,6 +19,7 @@
#pragma once
#include <spot/twaalgos/powerset.hh>
#include <spot/twa/twagraph.hh>
namespace spot
......@@ -72,10 +73,15 @@ namespace spot
/// might be worth to call
/// spot::check_stutter_invariance() first if
/// possible.)
///
/// \param aborter abort the construction if the constructed
/// automaton would be too large. Return nullptr
/// in this case.
SPOT_API twa_graph_ptr
tgba_determinize(const const_twa_graph_ptr& aut,
bool pretty_print = false,
bool use_scc = true,
bool use_simulation = true,
bool use_stutter = true);
bool use_stutter = true,
const output_aborter* aborter = nullptr);
}
......@@ -36,7 +36,6 @@
#include <spot/misc/hash.hh>
#include <spot/misc/bddlt.hh>
#include <spot/twaalgos/product.hh>
#include <spot/twaalgos/powerset.hh>
#include <spot/twaalgos/gtec/gtec.hh>
#include <spot/twaalgos/strength.hh>
#include <spot/twaalgos/sccfilter.hh>
......@@ -374,7 +373,8 @@ namespace spot
return res;
}
twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a)
twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a,
const output_aborter* aborter)
{
if (!a->is_existential())
throw std::runtime_error
......@@ -389,9 +389,15 @@ namespace spot
power_map pm;
bool input_is_det = is_deterministic(a);
if (input_is_det)
det_a = std::const_pointer_cast<twa_graph>(a);
{
det_a = std::const_pointer_cast<twa_graph>(a);
}
else
det_a = tgba_powerset(a, pm);
{
det_a = tgba_powerset(a, pm, aborter);
if (!det_a)
return nullptr;
}
// For each SCC of the deterministic automaton, determine if it
// is accepting or not.
......@@ -420,8 +426,10 @@ namespace spot
}
else
{
twa_graph_ptr prod = spot::product(a, det_a);
//print_dot(std::cerr, prod, "s");
twa_graph_ptr prod = spot::product(a, det_a, aborter);
if (!prod)
return nullptr;
const product_states* pmap =
prod->get_named_prop<product_states>("product-states");
assert(pmap);
......@@ -564,21 +572,38 @@ namespace spot
minimize_obligation(const const_twa_graph_ptr& aut_f,
formula f,
const_twa_graph_ptr aut_neg_f,
bool reject_bigger)
bool reject_bigger,
const output_aborter* aborter)
{
if (!aut_f->is_existential())
throw std::runtime_error
("minimize_obligation() does not support alternation");
// FIXME: We should build scc_info once, pass it to minimize_wdba
// and reuse it for is_terminal_automaton(),
// is_weak_automaton(), and is_very_weak_automaton().
// FIXME: we should postpone the call to minimize_wdba() until
// we know for sure that we can verify (or that we do not need
// to verify) its output, rather than computing in cases where
// we may discard it.
auto min_aut_f = minimize_wdba(aut_f);
bool minimization_will_be_correct = false;
// WDBA-minimization necessarily work for obligations
if ((f && f.is_syntactic_obligation())
// Weak deterministic automata are obligations
|| (aut_f->prop_weak() && is_deterministic(aut_f))
// Guarantee automata are obligations as well.
|| is_terminal_automaton(aut_f))
{
minimization_will_be_correct = true;
}
else if (!aut_neg_f)
{
// The minimization might not be correct and will need to
// be checked. Are we able to build aut_neg_f?
if (!(is_deterministic(aut_f) || f || is_very_weak_automaton(aut_f)))
return nullptr;
}
// FIXME: We should build scc_info once, and reuse it between
// minimize_wdba is_terminal_automaton(), is_weak_automaton(),
// and is_very_weak_automaton().
auto min_aut_f = minimize_wdba(aut_f, aborter);
if (!min_aut_f)
return std::const_pointer_cast<twa_graph>(aut_f);
if (reject_bigger)
{
// Abort if min_aut_f has more states than aut_f.
......@@ -587,29 +612,18 @@ namespace spot
return std::const_pointer_cast<twa_graph>(aut_f);
}
// if f is a syntactic obligation formula, the WDBA minimization
// must be correct.
if (f && f.is_syntactic_obligation())
if (minimization_will_be_correct)
return min_aut_f;
// If the input automaton was already weak and deterministic, the
// output is necessary correct.
if (aut_f->prop_weak() && is_deterministic(aut_f))
return min_aut_f;
// If aut_f is a guarantee automaton, the WDBA minimization must be
// correct.
if (is_terminal_automaton(aut_f))
return min_aut_f;
// Build negation automaton if not supplied.
// The minimization might not be correct and will need to
// be checked. Build negation automaton if not supplied.
if (!aut_neg_f)
{
if (is_deterministic(aut_f))
{
// If the automaton is deterministic, complementing is
// easy.
aut_neg_f = remove_fin(dualize(aut_f));
aut_neg_f = dualize(aut_f);
}
else if (f)
{
......@@ -631,15 +645,10 @@ namespace spot
return nullptr;
}
}
// If the negation is a guarantee automaton, then the
// minimization is correct.
if (is_terminal_automaton(aut_neg_f))
return min_aut_f;
// Make sure the minimized WDBA does not accept more words than
// the input.
if (product(min_aut_f, aut_neg_f)->is_empty())
auto prod = product(min_aut_f, aut_neg_f, aborter);
if (prod && prod->is_empty())
{
assert((bool)min_aut_f->prop_weak());
return min_aut_f;
......
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2019
// Laboratoire de Recherche et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
#include <spot/tl/formula.hh>
namespace spot
......@@ -92,7 +93,12 @@ namespace spot
month = oct
}
\endverbatim */
SPOT_API twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a);
///
/// If an \a output_aborter is given, the determinization is aborted
/// whenever it would produce an automaton that is too large. In
/// that case, a nullptr is returned.
SPOT_API twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a,
const output_aborter* aborter = nullptr);
/// \brief Minimize an automaton if it represents an obligation property.
///
......@@ -149,10 +155,15 @@ namespace spot
/// determinization step during minimize_wdba().) Note that
/// checking the size of the minimized WDBA occurs before ensuring
/// that the minimized WDBA is correct.
///
/// If an \a output_aborter is given, the determinization is aborted
/// whenever it would produce an automaton that is too large. In
/// this case, aut_f is returned unchanged.
SPOT_API twa_graph_ptr
minimize_obligation(const const_twa_graph_ptr& aut_f,
formula f = nullptr,
const_twa_graph_ptr aut_neg_f = nullptr,
bool reject_bigger = false);
bool reject_bigger = false,
const output_aborter* aborter = nullptr);
/// @}
}
// -*- coding: utf-8 -*-
// Copyright (C) 2012-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -71,6 +71,8 @@ namespace spot
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);
......@@ -337,12 +339,19 @@ namespace spot
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_)
{
// FIXME: This should be level_ <= Medium I believe.
bool reject_bigger = (PREF_ == Small) && (level_ == Medium);
dba = minimize_obligation(a, f, nullptr, reject_bigger);
dba = minimize_obligation(a, f, nullptr, reject_bigger, aborter);
if (dba
&& dba->prop_inherently_weak().is_true()
&& dba->prop_universal().is_true())
......@@ -459,11 +468,17 @@ namespace spot
if ((PREF_ == Deterministic && (type_ == Generic || want_parity)) && !dba)
{
dba = tgba_determinize(to_generalized_buchi(sim),
false, det_scc_, det_simul_, det_stutter_);
dba = simplify_acc(dba);
if (level_ != Low)
dba = simulation(dba);
sim = nullptr;
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
......
// -*- coding: utf-8 -*-
// Copyright (C) 2012-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -240,6 +240,8 @@ namespace spot
bool det_scc_ = true;
bool det_simul_ = true;
bool det_stutter_ = true;
int det_max_states_ = -1;
int det_max_edges_ = -1;
int simul_ = -1;
int scc_filter_ = -1;
int ba_simul_ = -1;
......
......@@ -70,8 +70,19 @@ namespace spot
};
}
std::ostream& output_aborter::print_reason(std::ostream& os) const
{
os << "more than ";
if (reason_is_states_)
os << max_states_ << " states required";
else
os << max_edges_ << " edges required";
return os;
}
twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut, power_map& pm, bool merge)
tgba_powerset(const const_twa_graph_ptr& aut, power_map& pm, bool merge,
const output_aborter* aborter)
{
unsigned ns = aut->num_states();
unsigned nap = aut->ap().size();
......@@ -245,6 +256,12 @@ namespace spot
pm.map_.emplace_back(std::move(ps));
}
res->new_edge(src_num, dst_num, num2bdd[c]);
if (aborter && aborter->too_large(res))
{
for (auto v: toclean)
delete v;
return nullptr;
}
}
}
......@@ -256,10 +273,11 @@ namespace spot
}
twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut)
tgba_powerset(const const_twa_graph_ptr& aut,
const output_aborter* aborter)
{
power_map pm;
return tgba_powerset(aut, pm);
return tgba_powerset(aut, pm, true, aborter);
}
......@@ -428,10 +446,13 @@ namespace spot
// Do not merge edges in the deterministic automaton. If we
// add two self-loops labeled by "a" and "!a", we do not want
// these to be merged as "1" before the acceptance has been fixed.
auto det = tgba_powerset(aut, pm, false);
if ((threshold_states > 0)
&& (pm.map_.size() > aut->num_states() * threshold_states))
unsigned max_states = aut->num_states() * threshold_states;
if (max_states == 0)
max_states = ~0U;
output_aborter aborter(max_states);
auto det = tgba_powerset(aut, pm, false, &aborter);
if (!det)
return nullptr;
if (fix_dba_acceptance(det, aut, pm, threshold_cycles))
return nullptr;
......
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et
// Copyright (C) 2011, 2013-2015, 2019 Laboratoire de Recherche et
// Développement de l'Epita.
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
......@@ -24,6 +24,7 @@
#include <set>
#include <vector>
#include <iosfwd>
#include <spot/twa/twagraph.hh>
namespace spot
......@@ -41,6 +42,43 @@ namespace spot
}
};
/// \brief Helper object to specify when an algorithm
/// should abort its construction.
class SPOT_API output_aborter
{
unsigned max_states_;
unsigned max_edges_;
mutable bool reason_is_states_;
public:
output_aborter(unsigned max_states,
unsigned max_edges = ~0U)
: max_states_(max_states), max_edges_(max_edges)
{
}
unsigned max_states() const
{
return max_states_;
}
unsigned max_edges() const
{
return max_edges_;
}
bool too_large(const const_twa_graph_ptr& aut) const
{
bool too_many_states = aut->num_states() > max_states_;
if (!too_many_states && (aut->num_edges() <= max_edges_))
return false;
// Only update the reason if we return true;
reason_is_states_ = too_many_states;
return true;
}
std::ostream& print_reason(std::ostream&) const;
};
/// \ingroup twa_misc
/// \brief Build a deterministic automaton, ignoring acceptance conditions.
......@@ -53,12 +91,17 @@ namespace spot
/// associated to each state of the deterministic automaton.