strength: generalize is_safety_automaton to any type of automata

Reported by Samuel Judson.

* spot/twaalgos/strength.cc (is_safety_automaton): Reimplement it.
* spot/twaalgos/strength.hh (is_safety_automaton): Update
documentation.
* tests/python/safety.py: New file.
* tests/Makefile.am: Add it.
* NEWS: Mention this change.
* THANKS: Add Samuel.
This commit is contained in:
Alexandre Duret-Lutz 2023-12-16 00:09:24 +01:00
parent e8c2b27ad2
commit 983964d037
6 changed files with 122 additions and 16 deletions

View file

@ -23,6 +23,8 @@
#include <spot/twaalgos/minimize.hh>
#include <spot/twaalgos/isdet.hh>
#include <spot/twaalgos/sccfilter.hh>
#include <spot/twaalgos/contains.hh>
#include <spot/twaalgos/stripacc.hh>
using namespace std::string_literals;
@ -182,23 +184,55 @@ namespace spot
{
if (aut->acc().is_t())
return true;
if (!aut->is_existential())
throw std::runtime_error
("is_safety_automaton() does not support alternation");
bool need_si = !si;
if (need_si)
si = new scc_info(aut);
std::unique_ptr<scc_info> localsi;
if (!si)
{
localsi = std::make_unique<scc_info>(aut);
si = localsi.get();
}
si->determine_unknown_acceptance();
bool res = true;
unsigned scount = si->scc_count();
for (unsigned scc = 0; scc < scount; ++scc)
if (!si->is_trivial(scc) && si->is_rejecting_scc(scc))
// a trim automaton without rejecting cycle is a safety automaton
bool has_rejecting_cycle = false;
// first, look for rejecting SCCs.
unsigned scccount = si->scc_count();
for (unsigned scc = 0; scc < scccount; ++scc)
if (si->is_useful_scc(scc)
&& !si->is_trivial(scc)
&& si->is_rejecting_scc(scc))
{
res = false;
has_rejecting_cycle = true;
break;
}
if (!has_rejecting_cycle && !aut->prop_inherently_weak().is_true())
{
// maybe we have rejecting cycles inside accepting SCCs?
for (unsigned scc = 0; scc < scccount; ++scc)
if (si->is_useful_scc(scc)
&& !si->is_trivial(scc)
&& si->is_accepting_scc(scc)
&& scc_has_rejecting_cycle(*si, scc))
{
has_rejecting_cycle = true;
break;
}
}
if (!has_rejecting_cycle)
return true;
if (need_si)
delete si;
return res;
// If the automaton has a rejecting loop and is deterministic, it
// cannot be a safety automaton.
if (is_universal(aut))
return false;
twa_graph_ptr b = make_twa_graph(aut, twa::prop_set::all());
strip_acceptance_here(b);
return spot::contains(aut, b);
}

View file

@ -104,12 +104,24 @@ namespace spot
/// \brief Check whether an automaton is a safety automaton.
///
/// A safety automaton has only accepting SCCs (or trivial
/// SCCs).
/// An automaton is a safety automaton if its acceptance condition
/// can be changed to "true" without changing its language.
///
/// A minimized WDBA (as returned by a successful run of
/// minimize_obligation()) represents safety property if it is a
/// safety automaton.
/// The test performed by this function differs depending on
/// the nature of the input \a aut.
///
/// If \a aut is an automaton with `t` acceptance, it is necessarily
/// a safety automaton.
///
/// Else we check for the absence of rejecting cycle in the
/// useful part of the automaton. This absence is only a sufficient
/// condition in the non-deterministic case, because a rejecting
/// run might correspond to a word that is accepted by another run.
///
/// If the previous test could not conclude, we build the automaton
/// B that is a copy of \a aut with acceptance set to true, and we
/// check that \a aut contains all words of B. This last test
/// requires complementing \a aut.
///
/// \param aut the automaton to check
///