autfilt add support for --partial-degeneralize
* bin/autfilt.cc: Add a --partial-degeneralize option. * NEWS: Mention it. * spot/twaalgos/degen.cc: Do not restrict partial_degeneralize() to deterministic automata. * spot/twaalgos/degen.hh: Adjust documentation. * tests/core/pdegen.test: New test case. * tests/Makefile.am: Add it. * tests/python/pdegen.py: Adjust.
This commit is contained in:
parent
bf42b19eff
commit
b5e464e05a
7 changed files with 94 additions and 45 deletions
9
NEWS
9
NEWS
|
|
@ -17,6 +17,10 @@ New in spot 2.8.5.dev (not yet released)
|
||||||
- When running translators ltlcross will now display {names} when
|
- When running translators ltlcross will now display {names} when
|
||||||
supplied.
|
supplied.
|
||||||
|
|
||||||
|
- autfilt learned the --partial-degeneralize option, to remove
|
||||||
|
conjunctions of Inf, or disjunction of Fin that appears in
|
||||||
|
arbitrary conditions.
|
||||||
|
|
||||||
Library:
|
Library:
|
||||||
|
|
||||||
- Historically, Spot only supports LTL with infinite semantics
|
- Historically, Spot only supports LTL with infinite semantics
|
||||||
|
|
@ -58,9 +62,8 @@ New in spot 2.8.5.dev (not yet released)
|
||||||
parity_min_even(n) = parity_min(false, n)
|
parity_min_even(n) = parity_min(false, n)
|
||||||
|
|
||||||
- partial_degeneralize() is a new function performing partial
|
- partial_degeneralize() is a new function performing partial
|
||||||
degeneralization to get rid of conjunctions of Inf terms in
|
degeneralization to get rid of conjunctions of Inf terms, or
|
||||||
acceptance conditions. This also works with disjunctions of
|
disjunctions of Fin terms in acceptance conditions.
|
||||||
Fin terms in deterministic automata.
|
|
||||||
|
|
||||||
- simplify_acceptance_here() and simplify_acceptance() learned to
|
- simplify_acceptance_here() and simplify_acceptance() learned to
|
||||||
simplify subformulas like Fin(i)&Fin(j) or Inf(i)|Inf(j), or some
|
simplify subformulas like Fin(i)&Fin(j) or Inf(i)|Inf(j), or some
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// -*- coding: utf-8 -*-
|
// -*- coding: utf-8 -*-
|
||||||
// Copyright (C) 2013-2019 Laboratoire de Recherche et Développement
|
// Copyright (C) 2013-2020 Laboratoire de Recherche et Développement
|
||||||
// de l'Epita (LRDE).
|
// de l'Epita (LRDE).
|
||||||
//
|
//
|
||||||
// This file is part of Spot, a model checking library.
|
// This file is part of Spot, a model checking library.
|
||||||
|
|
@ -133,6 +133,7 @@ enum {
|
||||||
OPT_MASK_ACC,
|
OPT_MASK_ACC,
|
||||||
OPT_MERGE,
|
OPT_MERGE,
|
||||||
OPT_NONDET_STATES,
|
OPT_NONDET_STATES,
|
||||||
|
OPT_PARTIAL_DEGEN,
|
||||||
OPT_PRODUCT_AND,
|
OPT_PRODUCT_AND,
|
||||||
OPT_PRODUCT_OR,
|
OPT_PRODUCT_OR,
|
||||||
OPT_RANDOMIZE,
|
OPT_RANDOMIZE,
|
||||||
|
|
@ -363,6 +364,10 @@ static const argp_option options[] =
|
||||||
{ "sum-and", OPT_SUM_AND, "FILENAME", 0,
|
{ "sum-and", OPT_SUM_AND, "FILENAME", 0,
|
||||||
"build the sum with the automaton in FILENAME "
|
"build the sum with the automaton in FILENAME "
|
||||||
"to intersect languages", 0 },
|
"to intersect languages", 0 },
|
||||||
|
{ "partial-degeneralize", OPT_PARTIAL_DEGEN, "NUM1,NUM2,...",
|
||||||
|
OPTION_ARG_OPTIONAL, "Degeneralize automata according to sets "
|
||||||
|
"NUM1,NUM2,... If no sets are given, partial degeneralization "
|
||||||
|
"is performed for all conjunctions of Inf and disjunctions of Fin.", 0 },
|
||||||
{ "separate-sets", OPT_SEP_SETS, nullptr, 0,
|
{ "separate-sets", OPT_SEP_SETS, nullptr, 0,
|
||||||
"if both Inf(x) and Fin(x) appear in the acceptance condition, replace "
|
"if both Inf(x) and Fin(x) appear in the acceptance condition, replace "
|
||||||
"Fin(x) by a new Fin(y) and adjust the automaton", 0 },
|
"Fin(x) by a new Fin(y) and adjust the automaton", 0 },
|
||||||
|
|
@ -619,6 +624,8 @@ static bool opt_complement = false;
|
||||||
static bool opt_complement_acc = false;
|
static bool opt_complement_acc = false;
|
||||||
static char* opt_decompose_scc = nullptr;
|
static char* opt_decompose_scc = nullptr;
|
||||||
static bool opt_dualize = false;
|
static bool opt_dualize = false;
|
||||||
|
static bool opt_partial_degen_set = false;
|
||||||
|
static spot::acc_cond::mark_t opt_partial_degen = {};
|
||||||
static spot::acc_cond::mark_t opt_mask_acc = {};
|
static spot::acc_cond::mark_t opt_mask_acc = {};
|
||||||
static std::vector<bool> opt_keep_states = {};
|
static std::vector<bool> opt_keep_states = {};
|
||||||
static unsigned int opt_keep_states_initial = 0;
|
static unsigned int opt_keep_states_initial = 0;
|
||||||
|
|
@ -1005,6 +1012,23 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
opt_nondet_states = parse_range(arg, 0, std::numeric_limits<int>::max());
|
opt_nondet_states = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||||
opt_nondet_states_set = true;
|
opt_nondet_states_set = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_PARTIAL_DEGEN:
|
||||||
|
{
|
||||||
|
opt_partial_degen_set = true;
|
||||||
|
if (arg)
|
||||||
|
for (auto res : to_longs(arg))
|
||||||
|
{
|
||||||
|
if (res < 0)
|
||||||
|
error(2, 0, "acceptance sets should be non-negative:"
|
||||||
|
" --partial-degeneralize=%ld", res);
|
||||||
|
if (static_cast<unsigned long>(res) >=
|
||||||
|
spot::acc_cond::mark_t::max_accsets())
|
||||||
|
error(2, 0, "this implementation does not support that many"
|
||||||
|
" acceptance sets: --partial-degeneralize=%ld", res);
|
||||||
|
opt_partial_degen.set(res);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OPT_PRODUCT_AND:
|
case OPT_PRODUCT_AND:
|
||||||
{
|
{
|
||||||
auto a = read_automaton(arg, opt->dict);
|
auto a = read_automaton(arg, opt->dict);
|
||||||
|
|
@ -1478,6 +1502,19 @@ namespace
|
||||||
else if (opt_rem_unreach)
|
else if (opt_rem_unreach)
|
||||||
aut->purge_unreachable_states();
|
aut->purge_unreachable_states();
|
||||||
|
|
||||||
|
if (opt_partial_degen_set)
|
||||||
|
{
|
||||||
|
if (opt_partial_degen)
|
||||||
|
{
|
||||||
|
auto sets = opt_partial_degen & aut->acc().all_sets();
|
||||||
|
aut = spot::partial_degeneralize(aut, sets);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aut = spot::partial_degeneralize(aut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opt->product_and)
|
if (opt->product_and)
|
||||||
aut = ::product(std::move(aut), opt->product_and);
|
aut = ::product(std::move(aut), opt->product_and);
|
||||||
if (opt->product_or)
|
if (opt->product_or)
|
||||||
|
|
|
||||||
|
|
@ -750,8 +750,7 @@ namespace spot
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
static acc_cond::mark_t
|
static acc_cond::mark_t
|
||||||
to_strip(const acc_cond::acc_code& code, acc_cond::mark_t todegen,
|
to_strip(const acc_cond::acc_code& code, acc_cond::mark_t todegen)
|
||||||
bool also_fin)
|
|
||||||
{
|
{
|
||||||
if (code.empty())
|
if (code.empty())
|
||||||
return todegen;
|
return todegen;
|
||||||
|
|
@ -768,14 +767,6 @@ namespace spot
|
||||||
break;
|
break;
|
||||||
case acc_cond::acc_op::Fin:
|
case acc_cond::acc_op::Fin:
|
||||||
case acc_cond::acc_op::FinNeg:
|
case acc_cond::acc_op::FinNeg:
|
||||||
if (!also_fin)
|
|
||||||
{
|
|
||||||
pos -= 2;
|
|
||||||
tostrip -= code[pos].mark;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Handle Fin and Inf in the same way
|
|
||||||
SPOT_FALLTHROUGH;
|
|
||||||
case acc_cond::acc_op::Inf:
|
case acc_cond::acc_op::Inf:
|
||||||
case acc_cond::acc_op::InfNeg:
|
case acc_cond::acc_op::InfNeg:
|
||||||
{
|
{
|
||||||
|
|
@ -796,8 +787,7 @@ namespace spot
|
||||||
update_acc_for_partial_degen(acc_cond::acc_code& code,
|
update_acc_for_partial_degen(acc_cond::acc_code& code,
|
||||||
acc_cond::mark_t todegen,
|
acc_cond::mark_t todegen,
|
||||||
acc_cond::mark_t tostrip,
|
acc_cond::mark_t tostrip,
|
||||||
acc_cond::mark_t accmark,
|
acc_cond::mark_t accmark)
|
||||||
bool also_fin)
|
|
||||||
{
|
{
|
||||||
if (!todegen || code.empty())
|
if (!todegen || code.empty())
|
||||||
{
|
{
|
||||||
|
|
@ -817,14 +807,6 @@ namespace spot
|
||||||
break;
|
break;
|
||||||
case acc_cond::acc_op::Fin:
|
case acc_cond::acc_op::Fin:
|
||||||
case acc_cond::acc_op::FinNeg:
|
case acc_cond::acc_op::FinNeg:
|
||||||
if (!also_fin)
|
|
||||||
{
|
|
||||||
pos -= 2;
|
|
||||||
code[pos].mark = code[pos].mark.strip(tostrip);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Handle Fin and Inf in the same way
|
|
||||||
SPOT_FALLTHROUGH;
|
|
||||||
case acc_cond::acc_op::Inf:
|
case acc_cond::acc_op::Inf:
|
||||||
case acc_cond::acc_op::InfNeg:
|
case acc_cond::acc_op::InfNeg:
|
||||||
{
|
{
|
||||||
|
|
@ -869,9 +851,6 @@ namespace spot
|
||||||
if (code.empty())
|
if (code.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (allow_fin)
|
|
||||||
allow_fin = is_deterministic(aut);
|
|
||||||
|
|
||||||
acc_cond::mark_t res = {};
|
acc_cond::mark_t res = {};
|
||||||
unsigned res_sz = -1U;
|
unsigned res_sz = -1U;
|
||||||
auto update = [&](const acc_cond::mark_t& m)
|
auto update = [&](const acc_cond::mark_t& m)
|
||||||
|
|
@ -926,16 +905,11 @@ namespace spot
|
||||||
res->copy_ap_of(a);
|
res->copy_ap_of(a);
|
||||||
acc_cond::acc_code acc = a->get_acceptance();
|
acc_cond::acc_code acc = a->get_acceptance();
|
||||||
|
|
||||||
// We can also degeneralize disjunctions of Fin if the input is
|
acc_cond::mark_t tostrip = to_strip(acc, todegen);
|
||||||
// deterministic.
|
|
||||||
bool also_fin = a->acc().uses_fin_acceptance() && is_deterministic(a);
|
|
||||||
|
|
||||||
acc_cond::mark_t tostrip = to_strip(acc, todegen, also_fin);
|
|
||||||
acc_cond::mark_t keep = a->acc().all_sets() - tostrip;
|
acc_cond::mark_t keep = a->acc().all_sets() - tostrip;
|
||||||
acc_cond::mark_t degenmark = {keep.count()};
|
acc_cond::mark_t degenmark = {keep.count()};
|
||||||
|
|
||||||
if (!update_acc_for_partial_degen(acc, todegen, tostrip, degenmark,
|
if (!update_acc_for_partial_degen(acc, todegen, tostrip, degenmark))
|
||||||
also_fin))
|
|
||||||
report_invalid_partial_degen_arg(todegen, acc);
|
report_invalid_partial_degen_arg(todegen, acc);
|
||||||
res->set_acceptance(acc);
|
res->set_acceptance(acc);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,12 @@ namespace spot
|
||||||
///
|
///
|
||||||
/// Cases where the sets listed in \a todegen also occur outside
|
/// Cases where the sets listed in \a todegen also occur outside
|
||||||
/// of the Inf-conjunction are also supported. Subformulas that
|
/// of the Inf-conjunction are also supported. Subformulas that
|
||||||
/// are disjunctions of Fin(.) terms (e.g., Fin(1)|Fin(2)) can
|
/// are disjunctions of Fin(.) terms (e.g., Fin(1)|Fin(2)) will
|
||||||
/// also be degeneralized if the input automaton is deterministic.
|
/// be degeneralized as well.
|
||||||
///
|
///
|
||||||
/// If this functions is called with a value of \a todegen that does
|
/// If this functions is called with a value of \a todegen that does
|
||||||
/// not match a conjunction of Inf(.), or in a deterministic
|
/// not match a conjunction of Inf(.), or a disjunction of Fin(.),
|
||||||
/// automaton a disjunction of Fin(.), an std::runtime_error
|
/// an std::runtime_error exception is thrown.
|
||||||
/// exception is thrown.
|
|
||||||
///
|
///
|
||||||
/// The version of the function that has no \a todegen argument will
|
/// The version of the function that has no \a todegen argument will
|
||||||
/// perform all possible partial degeneralizations, and may return
|
/// perform all possible partial degeneralizations, and may return
|
||||||
|
|
@ -132,10 +131,9 @@ namespace spot
|
||||||
|
|
||||||
/// \brief Is the automaton partially degeneralizable?
|
/// \brief Is the automaton partially degeneralizable?
|
||||||
///
|
///
|
||||||
/// Return a mark `M={m₁, m₂, ..., mₙ}` such that either (1)
|
/// Return a mark `M={m₁, m₂, ..., mₙ}` such that either
|
||||||
/// `Inf(m₁)&Inf(m₂)&...&Inf(mₙ)` appears in the acceptance
|
/// `Inf(m₁)&Inf(m₂)&...&Inf(mₙ)` or `Fin(m₁)|Fin(m₂)|...|Fin(mₙ)`
|
||||||
/// condition of \a aut, or (2) \a aut is deterministic and
|
/// appears in the acceptance condition of \a aut.
|
||||||
/// `Inf(m₁)|Inf(m₂)|...|Fin(mₙ)` appear in its conditions.
|
|
||||||
///
|
///
|
||||||
/// If multiple such marks exist the smallest such mark is returned.
|
/// If multiple such marks exist the smallest such mark is returned.
|
||||||
/// (This is important in case of overlapping options. E.g., in the
|
/// (This is important in case of overlapping options. E.g., in the
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@ TESTS_twa = \
|
||||||
core/ltl2dstar4.test \
|
core/ltl2dstar4.test \
|
||||||
core/ltl2ta.test \
|
core/ltl2ta.test \
|
||||||
core/ltl2ta2.test \
|
core/ltl2ta2.test \
|
||||||
|
core/pdegen.test \
|
||||||
core/randaut.test \
|
core/randaut.test \
|
||||||
core/randtgba.test \
|
core/randtgba.test \
|
||||||
core/isomorph.test \
|
core/isomorph.test \
|
||||||
|
|
|
||||||
32
tests/core/pdegen.test
Executable file
32
tests/core/pdegen.test
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2020 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
. ./defs
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
randaut -n10 -e.3 -Q7 -A 'generalized-co-Buchi 2..5' 2 |
|
||||||
|
autcross --verbose --language-preserved \
|
||||||
|
'autfilt --partial-degen' \
|
||||||
|
'autfilt --partial-degen=1,2,3'
|
||||||
|
|
||||||
|
randaut -n10 -e.3 -Q7 -A 'random 2..5' 2 |
|
||||||
|
autcross --verbose --language-preserved 'autfilt --partial-degen'
|
||||||
|
|
@ -144,10 +144,14 @@ State: 3 "2#0"
|
||||||
|
|
||||||
assert spot.is_partially_degeneralizable(de) == []
|
assert spot.is_partially_degeneralizable(de) == []
|
||||||
|
|
||||||
|
df = spot.partial_degeneralize(f, [0, 1])
|
||||||
|
df.equivalent_to(f)
|
||||||
|
assert str(df.acc()) == '(1, Fin(0))'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df = spot.partial_degeneralize(f, [0, 1])
|
df = spot.partial_degeneralize(f, [0, 1, 2])
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
assert 'partial_degeneralize(): {0,1} does not' in str(e)
|
assert 'partial_degeneralize(): {0,1,2} does not' in str(e)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("missing exception")
|
raise RuntimeError("missing exception")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue