implement BA acceptance set reduction and enlargement

For issue #570.

* spot/twaalgos/cleanacc.hh,
spot/twaalgos/cleanacc.cc (reduce_buchi_acceptance_set_here,
enlarge_buchi_acceptance_set_here): New functions.
* bin/autfilt.cc: Add options --reduce-acceptance-set and
--enlarge-acceptance-set.
* tests/core/basetred.test: New file.
* tests/Makefile.am: Add it.
* NEWS: Mention it.
This commit is contained in:
Alexandre Duret-Lutz 2024-04-25 17:59:10 +02:00
parent ab7f4f51c4
commit be102e09d4
6 changed files with 311 additions and 3 deletions

View file

@ -18,6 +18,8 @@
#include "config.h"
#include <spot/twaalgos/cleanacc.hh>
#include <spot/twaalgos/sccinfo.hh>
#include <spot/twaalgos/genem.hh>
namespace spot
{
@ -668,4 +670,135 @@ namespace spot
{
return simplify_acceptance_here(make_twa_graph(aut, twa::prop_set::all()));
}
twa_graph_ptr
reduce_buchi_acceptance_set_here(twa_graph_ptr& aut, bool preserve_sbacc)
{
if (!aut->acc().is_buchi())
throw std::invalid_argument
("reduce_buchi_acceptance_set_here() expects a Büchi automaton");
if (!preserve_sbacc)
aut->prop_state_acc(trival::maybe());
aut->prop_weak(trival::maybe()); // issue #562
// For each accepting edge in the automaton, we will test if the
// acceptance mark can be removed. To test this, we have to make
// sure that no accepting cycle depends exclusively on this mark.
// We do so by temporary changing the mark of the current edge to
// {1}, and then using the following acceptance condition to
// ensure that there is no cycle that pass through {1} when we
// ignore all other edges with {0}.
acc_cond testacc = acc_cond(2, (acc_cond::acc_code::fin({0}) &
acc_cond::acc_code::inf({1})));
acc_cond::mark_t one{1};
acc_cond::mark_t zero{0};
acc_cond::mark_t none{};
scc_info si(aut, scc_info_options::TRACK_STATES);
if (!preserve_sbacc || !aut->prop_state_acc())
// transition-based version
for (auto& e: aut->edges())
{
if (e.acc == none) // nothing to remove
continue;
unsigned srcscc = si.scc_of(e.src);
if (srcscc != si.scc_of(e.dst)) // transient edge
{
e.acc = none;
}
else
{
e.acc = one;
if (generic_emptiness_check_for_scc(si, srcscc, testacc))
e.acc = none;
else
e.acc = zero;
}
}
else
// state-based version
for (unsigned s = 0, ns = aut->num_states(); s < ns; ++s)
{
acc_cond::mark_t acc = aut->state_acc_sets(s);
if (acc == none) // nothing to remove
continue;
for (auto& e: aut->out(s))
e.acc = one;
if (generic_emptiness_check_for_scc(si, si.scc_of(s), testacc))
acc = none;
for (auto& e: aut->out(s))
e.acc = acc;
}
return aut;
}
twa_graph_ptr
enlarge_buchi_acceptance_set_here(twa_graph_ptr& aut, bool preserve_sbacc)
{
if (!aut->acc().is_buchi())
throw std::invalid_argument
("enlarge_buchi_acceptance_set_here() expects a Büchi automaton");
if (!preserve_sbacc)
aut->prop_state_acc(trival::maybe());
aut->prop_weak(trival::maybe()); // issue #562
// For each edge not marked as accepting will test if an
// acceptance mark can be added. To test this, we have to make
// sure that no rejecting cycle goes through this edge.
// We do so my temporary changing the mark of the current edge to
// {1}, and then using the following acceptance condition to
// ensure that there is no accepting cycle that pass through {1}
// when we ignore all other edges with {0}.
acc_cond testacc =
acc_cond(2, acc_cond::acc_code::fin({0}) & acc_cond::acc_code::inf({1}));
acc_cond::mark_t one{1};
acc_cond::mark_t zero{0};
acc_cond::mark_t none{};
scc_info si(aut, scc_info_options::TRACK_STATES);
if (!preserve_sbacc || !aut->prop_state_acc())
// transition-based version
for (auto& e: aut->edges())
{
if (e.acc == zero) // nothing to add
continue;
unsigned srcscc = si.scc_of(e.src);
if (si.is_rejecting_scc(srcscc)) // nothing to add
continue;
if (srcscc != si.scc_of(e.dst)) // transient edge
{
e.acc = zero;
}
else
{
e.acc = one;
if (generic_emptiness_check_for_scc(si, srcscc, testacc))
e.acc = zero;
else
e.acc = none;
}
}
else
// state-based version
for (unsigned s = 0, ns = aut->num_states(); s < ns; ++s)
{
acc_cond::mark_t acc = aut->state_acc_sets(s);
if (acc == zero) // nothing to add
continue;
unsigned srcscc = si.scc_of(s);
if (si.is_rejecting_scc(srcscc)) // nothing to add
continue;
for (auto& e: aut->out(s))
e.acc = one;
if (generic_emptiness_check_for_scc(si, srcscc, testacc))
acc = zero;
for (auto& e: aut->out(s))
e.acc = acc;
}
return aut;
}
}

View file

@ -70,4 +70,32 @@ namespace spot
SPOT_API twa_graph_ptr
simplify_acceptance(const_twa_graph_ptr aut);
/// @}
/// \ingroup twa_acc_transform
/// \brief Reduce the acceptance set of a Büchi automaton
///
/// Iterate over all accepting transitions, and remove them from the
/// acceptance set if this does not change the language.
///
/// This modifies the automaton in place.
///
/// If the input has state-based acceptance, it might lose it,
/// unless \a preserve_sbacc is set.
SPOT_API twa_graph_ptr
reduce_buchi_acceptance_set_here(twa_graph_ptr& aut,
bool preserve_sbacc = false);
/// \ingroup twa_acc_transform
/// \brief Enlarge the acceptance set of a Büchi automaton
///
/// Iterate over all accepting transitions, and add them to the
/// acceptance set if this cannot change the language.
///
/// This modifies the automaton in place.
///
/// If the input has state-based acceptance, it might lose it,
/// unless \a preserve_sbacc is set.
SPOT_API twa_graph_ptr
enlarge_buchi_acceptance_set_here(twa_graph_ptr& aut,
bool preserve_sbacc = false);
}