degen: learn to work on generalized-Co-Büchi as well
* spot/twaalgos/degen.hh, spot/twaalgos/degen.cc: Adjust degeneralize() and degeneralize_tba() to work on generalized-co-Büchi. * NEWS: Mention this. * spot/twaalgos/cobuchi.hh, spot/twaalgos/cobuchi.cc (to_nca): Use degeneralization on generalized-co-Büchi. * spot/twaalgos/postproc.cc: Use degeneralization for generalized co-Büchi as well. * bin/autfilt.cc: Improve chain products of co-Büchi automata by using generalization if too many colors are needed. * tests/core/prodchain.test, tests/python/pdegen.py: Add test cases.
This commit is contained in:
parent
fe3ebd370b
commit
bdac53511a
9 changed files with 169 additions and 81 deletions
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2017-2018, 2021 Laboratoire de Recherche et Développement
|
||||
// Copyright (C) 2017-2018, 2021, 2022 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#include <spot/twa/acc.hh>
|
||||
#include <spot/twa/bddprint.hh>
|
||||
#include <spot/twa/twagraph.hh>
|
||||
#include <spot/twaalgos/degen.hh>
|
||||
#include <spot/twaalgos/powerset.hh>
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
|
|
@ -338,23 +339,26 @@ namespace spot
|
|||
twa_graph_ptr
|
||||
to_nca(const_twa_graph_ptr aut, bool named_states)
|
||||
{
|
||||
if (aut->acc().is_co_buchi())
|
||||
const acc_cond& acc = aut->acc();
|
||||
if (acc.is_co_buchi())
|
||||
return make_twa_graph(aut, twa::prop_set::all());
|
||||
|
||||
if (auto weak = weak_to_cobuchi(aut))
|
||||
return weak;
|
||||
|
||||
if (acc.is_generalized_co_buchi())
|
||||
return degeneralize_tba(aut);
|
||||
|
||||
const acc_cond::acc_code& code = aut->get_acceptance();
|
||||
|
||||
std::vector<acc_cond::rs_pair> pairs;
|
||||
if (aut->acc().is_streett_like(pairs) || aut->acc().is_parity())
|
||||
if (acc.is_streett_like(pairs) || acc.is_parity())
|
||||
return nsa_to_nca(aut, named_states);
|
||||
else if (code.is_dnf())
|
||||
return dnf_to_nca(aut, named_states);
|
||||
|
||||
auto tmp = make_twa_graph(aut, twa::prop_set::all());
|
||||
tmp->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().to_dnf());
|
||||
tmp->set_acceptance(acc.num_sets(), code.to_dnf());
|
||||
return to_nca(tmp, named_states);
|
||||
}
|
||||
|
||||
|
|
@ -683,6 +687,8 @@ namespace spot
|
|||
return make_twa_graph(aut, twa::prop_set::all());
|
||||
if (auto weak = weak_to_cobuchi(aut))
|
||||
return weak;
|
||||
if (aut->acc().is_generalized_co_buchi())
|
||||
return degeneralize_tba(aut);
|
||||
}
|
||||
|
||||
const acc_cond::acc_code& code = aut->get_acceptance();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2017-2019 Laboratoire de Recherche et Développement
|
||||
// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
|
|
@ -92,8 +92,8 @@ namespace spot
|
|||
/// original language, and is a superset iff the original language
|
||||
/// can not be expressed using a co-Büchi acceptance condition.
|
||||
///
|
||||
/// The implementation dispatches between dnf_to_nca, nsa_to_nca,
|
||||
/// and a trivial implementation for weak automata.
|
||||
/// The implementation dispatches between dnf_to_nca(), nsa_to_nca(),
|
||||
/// degeneralize_tba(), and a trivial implementation for weak automata.
|
||||
SPOT_API twa_graph_ptr
|
||||
to_nca(const_twa_graph_ptr aut, bool named_states = false);
|
||||
|
||||
|
|
@ -126,7 +126,8 @@ namespace spot
|
|||
/// can not be expressed using a co-Büchi acceptance condition.
|
||||
///
|
||||
/// The implementation dispatches between dnf_to_dca, nsa_to_dca,
|
||||
/// and a trivial implementation for deterministic weak automata.
|
||||
/// degeneralize(), and a trivial implementation for deterministic
|
||||
/// weak automata.
|
||||
SPOT_API twa_graph_ptr
|
||||
to_dca(const_twa_graph_ptr aut, bool named_states = false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ namespace spot
|
|||
void fill_cache(unsigned s)
|
||||
{
|
||||
unsigned s1 = scc_of(s);
|
||||
acc_cond::mark_t common = a_->acc().all_sets();
|
||||
acc_cond::mark_t all_colors = a_->acc().all_sets();
|
||||
acc_cond::mark_t common = all_colors;
|
||||
acc_cond::mark_t union_ = {};
|
||||
bool has_acc_self_loop = false;
|
||||
bool is_true_state = false;
|
||||
|
|
@ -97,7 +98,7 @@ namespace spot
|
|||
std::get<2>(cache_[d]) &= t.acc;
|
||||
|
||||
// an accepting self-loop?
|
||||
if ((t.dst == s) && a_->acc().accepting(t.acc))
|
||||
if ((t.dst == s) && t.acc == all_colors)
|
||||
{
|
||||
has_acc_self_loop = true;
|
||||
if (t.cond == bddtrue)
|
||||
|
|
@ -330,9 +331,10 @@ namespace spot
|
|||
bool skip_levels, bool ignaccsl,
|
||||
bool remove_extra_scc)
|
||||
{
|
||||
if (!a->acc().is_generalized_buchi())
|
||||
bool input_is_gba = a->acc().is_generalized_buchi();
|
||||
if (!(input_is_gba || a->acc().is_generalized_co_buchi()))
|
||||
throw std::runtime_error
|
||||
("degeneralize() only works with generalized Büchi acceptance");
|
||||
("degeneralize() only works with generalized (co)Büchi acceptance");
|
||||
if (!a->is_existential())
|
||||
throw std::runtime_error
|
||||
("degeneralize() does not support alternation");
|
||||
|
|
@ -347,7 +349,11 @@ namespace spot
|
|||
// The result automaton is an SBA.
|
||||
auto res = make_twa_graph(dict);
|
||||
res->copy_ap_of(a);
|
||||
res->set_buchi();
|
||||
if (input_is_gba)
|
||||
res->set_buchi();
|
||||
else
|
||||
res->set_co_buchi();
|
||||
acc_cond::mark_t all_colors = a->get_acceptance().used_sets();
|
||||
if (want_sba)
|
||||
res->prop_state_acc(true);
|
||||
// Preserve determinism, weakness, and stutter-invariance
|
||||
|
|
@ -396,9 +402,32 @@ namespace spot
|
|||
std::vector<std::pair<unsigned, bool>> lvl_cache(a->num_states());
|
||||
|
||||
// Compute SCCs in order to use any optimization.
|
||||
std::unique_ptr<scc_info> m = use_scc
|
||||
? std::make_unique<scc_info>(a, scc_info_options::NONE)
|
||||
: nullptr;
|
||||
std::unique_ptr<scc_info> m = nullptr;
|
||||
if (use_scc)
|
||||
{
|
||||
if (!input_is_gba)
|
||||
{
|
||||
// If the input is gen-co-Büchi, temporary pretend its
|
||||
// generalized Büchi.
|
||||
unsigned n = a->num_sets();
|
||||
twa_graph_ptr amut = std::const_pointer_cast<twa_graph>(a);
|
||||
amut->set_generalized_buchi(n);
|
||||
try
|
||||
{
|
||||
m = std::make_unique<scc_info>(a, scc_info_options::NONE);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
amut->set_generalized_co_buchi(n);
|
||||
throw;
|
||||
}
|
||||
amut->set_generalized_co_buchi(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = std::make_unique<scc_info>(a, scc_info_options::NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize scc_orders
|
||||
std::unique_ptr<scc_orders> orders = use_cust_acc_orders
|
||||
|
|
@ -674,7 +703,7 @@ namespace spot
|
|||
{
|
||||
d.second = 0; // Make it go to the first level.
|
||||
// Skip as many levels as possible.
|
||||
if (!a->acc().accepting(acc) && skip_levels)
|
||||
if (acc != all_colors && skip_levels)
|
||||
{
|
||||
if (use_cust_acc_orders)
|
||||
{
|
||||
|
|
@ -723,9 +752,10 @@ namespace spot
|
|||
int use_lvl_cache, bool skip_levels, bool ignaccsl,
|
||||
bool remove_extra_scc)
|
||||
{
|
||||
// If this already a degeneralized digraph, there is nothing we
|
||||
// If this already a degeneralized twa, there is nothing we
|
||||
// can improve.
|
||||
if (a->is_sba())
|
||||
if (const acc_cond& acc = a->acc();
|
||||
a->prop_state_acc() && (acc.is_buchi() || acc.is_co_buchi()))
|
||||
return std::const_pointer_cast<twa_graph>(a);
|
||||
|
||||
return degeneralize_aux<true>(a, use_z_lvl, use_cust_acc_orders,
|
||||
|
|
@ -739,9 +769,9 @@ namespace spot
|
|||
int use_lvl_cache, bool skip_levels, bool ignaccsl,
|
||||
bool remove_extra_scc)
|
||||
{
|
||||
// If this already a degeneralized digraph, there is nothing we
|
||||
// If this already a degeneralized twa, there is nothing we
|
||||
// can improve.
|
||||
if (a->acc().is_buchi())
|
||||
if (a->acc().is_buchi() || a->acc().is_co_buchi())
|
||||
return std::const_pointer_cast<twa_graph>(a);
|
||||
|
||||
return degeneralize_aux<false>(a, use_z_lvl, use_cust_acc_orders,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012-2015, 2017-2020 Laboratoire de
|
||||
// Copyright (C) 2012-2015, 2017-2020, 2022 Laboratoire de
|
||||
// Recherche et Développement de l'Epita.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
|
|
@ -26,33 +26,36 @@ namespace spot
|
|||
class scc_info;
|
||||
|
||||
/// \ingroup twa_acc_transform
|
||||
/// \brief Degeneralize a spot::tgba into an equivalent sba with
|
||||
/// only one acceptance condition.
|
||||
/// \brief Degeneralize a generalized (co)Büchi automaton into an
|
||||
/// equivalent (co)Büchi automaton.
|
||||
///
|
||||
/// This algorithm will build a new explicit automaton that has
|
||||
/// at most (N+1) times the number of states of the original automaton.
|
||||
/// There are two variants of the function. If the generalizd
|
||||
/// (co)Büchi acceptance uses N colors, degeneralize() algorithm
|
||||
/// will builds a state-based (co)Büchi automaton that has at most
|
||||
/// (N+1) times the number of states of the original automaton.
|
||||
/// degeneralize_tba() builds a transition-based (co)Büchi automaton
|
||||
/// that has at most N times the number of states of the original
|
||||
/// automaton.
|
||||
///
|
||||
/// When \a use_z_lvl is set, the level of the degeneralized
|
||||
/// automaton is reset everytime an SCC is exited. If \a
|
||||
/// use_cust_acc_orders is set, the degeneralization will compute a
|
||||
/// custom acceptance order for each SCC (this option is disabled by
|
||||
/// default because our benchmarks show that it usually does more
|
||||
/// harm than good). If \a use_lvl_cache is set, everytime an SCC
|
||||
/// is entered on a state that as already been associated to some
|
||||
/// level elsewhere, reuse that level (set it to 2 to keep the
|
||||
/// smallest number, 3 to keep the largest level, and 1 to keep the
|
||||
/// first level found). If \a ignaccsl is set, we do not directly
|
||||
/// jump to the accepting level if the entering state has an
|
||||
/// accepting self-loop. If \a remove_extra_scc is set (the default)
|
||||
/// we ensure that the output automaton has as many SCCs as the input
|
||||
/// by removing superfluous SCCs.
|
||||
/// Additional options control optimizations described in
|
||||
/// \cite babiak.13.spin . When \a use_z_lvl is set, the level of
|
||||
/// the degeneralized automaton is reset everytime an SCC is exited.
|
||||
/// If \a use_cust_acc_orders is set, the degeneralization will
|
||||
/// compute a custom acceptance order for each SCC (this option is
|
||||
/// disabled by default because our benchmarks show that it usually
|
||||
/// does more harm than good). If \a use_lvl_cache is set,
|
||||
/// everytime an SCC is entered on a state that as already been
|
||||
/// associated to some level elsewhere, reuse that level (set it to
|
||||
/// 2 to keep the smallest number, 3 to keep the largest level, and
|
||||
/// 1 to keep the first level found). If \a ignaccsl is set, we do
|
||||
/// not directly jump to the accepting level if the entering state
|
||||
/// has an accepting self-loop. If \a remove_extra_scc is set (the
|
||||
/// default) we ensure that the output automaton has as many SCCs as
|
||||
/// the input by removing superfluous SCCs.
|
||||
///
|
||||
/// Any of these three options will cause the SCCs of the automaton
|
||||
/// \a a to be computed prior to its actual degeneralization.
|
||||
///
|
||||
/// The degeneralize_tba() variant produce a degeneralized automaton
|
||||
/// with transition-based acceptance.
|
||||
///
|
||||
/// The mapping between each state of the resulting automaton
|
||||
/// and the original state of the input automaton is stored in the
|
||||
/// "original-states" named property of the produced automaton. Call
|
||||
|
|
@ -70,6 +73,14 @@ namespace spot
|
|||
/// Similarly, the property "degen-levels" keeps track of the degeneralization
|
||||
/// levels. To retrieve it, call
|
||||
/// `aut->get_named_prop<std::vector<unsigned>>("degen-levels")`.
|
||||
///
|
||||
/// As an alternative method to degeneralization, one may also
|
||||
/// consider ACD transform. acd_transform() will never produce
|
||||
/// larger automata than degenaralize_tba(), and
|
||||
/// acd_transform_sbacc() produce smaller automata than
|
||||
/// degeneralize() on the average. See \cite casares.22.tacas for
|
||||
/// some comparisons.
|
||||
///
|
||||
/// \@{
|
||||
SPOT_API twa_graph_ptr
|
||||
degeneralize(const const_twa_graph_ptr& a, bool use_z_lvl = true,
|
||||
|
|
|
|||
|
|
@ -236,7 +236,8 @@ namespace spot
|
|||
if (COMP_)
|
||||
tmp = complete(tmp);
|
||||
bool want_parity = type_ & Parity;
|
||||
if (want_parity && tmp->acc().is_generalized_buchi())
|
||||
if (want_parity && (tmp->acc().is_generalized_buchi()
|
||||
|| tmp->acc().is_generalized_co_buchi()))
|
||||
tmp = choose_degen(tmp);
|
||||
assert(!!SBACC_ == state_based_);
|
||||
if (state_based_)
|
||||
|
|
@ -402,10 +403,19 @@ namespace spot
|
|||
|
||||
if (PREF_ == Any)
|
||||
{
|
||||
if (type_ == Buchi)
|
||||
a = choose_degen(a);
|
||||
if (type_ == Buchi
|
||||
|| (type_ == CoBuchi && a->acc().is_generalized_co_buchi()))
|
||||
{
|
||||
a = choose_degen(a);
|
||||
}
|
||||
else if (type_ == CoBuchi)
|
||||
a = to_nca(a);
|
||||
{
|
||||
a = to_nca(a);
|
||||
if (state_based_ && a->prop_state_acc().is_true())
|
||||
a = do_sba_simul(a, simul_);
|
||||
else
|
||||
a = do_simul(a, simul_);
|
||||
}
|
||||
return finalize(a);
|
||||
}
|
||||
|
||||
|
|
@ -699,6 +709,8 @@ namespace spot
|
|||
if (type_ == CoBuchi)
|
||||
{
|
||||
unsigned ns = sim->num_states();
|
||||
bool weak = sim->prop_weak().is_true();
|
||||
|
||||
if (PREF_ == Deterministic)
|
||||
sim = to_dca(sim);
|
||||
else
|
||||
|
|
@ -706,8 +718,13 @@ namespace spot
|
|||
|
||||
// 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_);
|
||||
if (!weak || (level_ != Low && ns < sim->num_states()))
|
||||
{
|
||||
if (state_based_ && sim->prop_state_acc().is_true())
|
||||
sim = do_sba_simul(sim, simul_);
|
||||
else
|
||||
sim = do_simul(sim, simul_);
|
||||
}
|
||||
}
|
||||
|
||||
return finalize(sim);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue