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:
parent
e8c2b27ad2
commit
983964d037
6 changed files with 122 additions and 16 deletions
6
NEWS
6
NEWS
|
|
@ -120,6 +120,12 @@ New in spot 2.11.6.dev (not yet released)
|
||||||
- ltsmin's interface will now point to README.ltsmin in case an
|
- ltsmin's interface will now point to README.ltsmin in case an
|
||||||
error is found while running divine or spins.
|
error is found while running divine or spins.
|
||||||
|
|
||||||
|
- spot::is_safety_automaton() was generalized to detect any
|
||||||
|
automaton for which the acceptance could be changed to "t" without
|
||||||
|
changing the language. In previous versions this function assumed
|
||||||
|
weak automata as input, but the documentation did not reflect
|
||||||
|
this.
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
- The spot.automata() and spot.automaton() functions now accept a
|
- The spot.automata() and spot.automaton() functions now accept a
|
||||||
|
|
|
||||||
1
THANKS
1
THANKS
|
|
@ -56,6 +56,7 @@ Raven Beutner
|
||||||
Reuben Rowe
|
Reuben Rowe
|
||||||
Roei Nahum
|
Roei Nahum
|
||||||
Rüdiger Ehlers
|
Rüdiger Ehlers
|
||||||
|
Samuel Judson
|
||||||
Shachar Itzhaky
|
Shachar Itzhaky
|
||||||
Shengping Shaw
|
Shengping Shaw
|
||||||
Shufang Zhu
|
Shufang Zhu
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
#include <spot/twaalgos/minimize.hh>
|
#include <spot/twaalgos/minimize.hh>
|
||||||
#include <spot/twaalgos/isdet.hh>
|
#include <spot/twaalgos/isdet.hh>
|
||||||
#include <spot/twaalgos/sccfilter.hh>
|
#include <spot/twaalgos/sccfilter.hh>
|
||||||
|
#include <spot/twaalgos/contains.hh>
|
||||||
|
#include <spot/twaalgos/stripacc.hh>
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
|
@ -182,23 +184,55 @@ namespace spot
|
||||||
{
|
{
|
||||||
if (aut->acc().is_t())
|
if (aut->acc().is_t())
|
||||||
return true;
|
return true;
|
||||||
|
if (!aut->is_existential())
|
||||||
|
throw std::runtime_error
|
||||||
|
("is_safety_automaton() does not support alternation");
|
||||||
|
|
||||||
bool need_si = !si;
|
std::unique_ptr<scc_info> localsi;
|
||||||
if (need_si)
|
if (!si)
|
||||||
si = new scc_info(aut);
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
res = false;
|
localsi = std::make_unique<scc_info>(aut);
|
||||||
|
si = localsi.get();
|
||||||
|
}
|
||||||
|
si->determine_unknown_acceptance();
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
{
|
||||||
|
has_rejecting_cycle = true;
|
||||||
break;
|
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)
|
// If the automaton has a rejecting loop and is deterministic, it
|
||||||
delete si;
|
// cannot be a safety automaton.
|
||||||
return res;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,12 +104,24 @@ namespace spot
|
||||||
|
|
||||||
/// \brief Check whether an automaton is a safety automaton.
|
/// \brief Check whether an automaton is a safety automaton.
|
||||||
///
|
///
|
||||||
/// A safety automaton has only accepting SCCs (or trivial
|
/// An automaton is a safety automaton if its acceptance condition
|
||||||
/// SCCs).
|
/// can be changed to "true" without changing its language.
|
||||||
///
|
///
|
||||||
/// A minimized WDBA (as returned by a successful run of
|
/// The test performed by this function differs depending on
|
||||||
/// minimize_obligation()) represents safety property if it is a
|
/// the nature of the input \a aut.
|
||||||
/// safety automaton.
|
///
|
||||||
|
/// 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
|
/// \param aut the automaton to check
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -446,6 +446,7 @@ TESTS_python = \
|
||||||
python/relabel.py \
|
python/relabel.py \
|
||||||
python/remfin.py \
|
python/remfin.py \
|
||||||
python/removeap.py \
|
python/removeap.py \
|
||||||
|
python/safety.py \
|
||||||
python/satmin.py \
|
python/satmin.py \
|
||||||
python/sbacc.py \
|
python/sbacc.py \
|
||||||
python/sccfilter.py \
|
python/sccfilter.py \
|
||||||
|
|
|
||||||
52
tests/python/safety.py
Normal file
52
tests/python/safety.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) by the Spot authors, see the AUTHORS file for details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
import spot
|
||||||
|
from unittest import TestCase
|
||||||
|
tc = TestCase()
|
||||||
|
|
||||||
|
|
||||||
|
for f in ['Fb', 'GFa & GFb',
|
||||||
|
'(p0 W Fp0) R ((Gp1 & Fp1) | (F!p1 & G!p1)) & GFp0',
|
||||||
|
'GFp0 xor GFp1']:
|
||||||
|
aut = spot.translate(f, 'BA')
|
||||||
|
tc.assertFalse(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
aut = spot.translate(f, 'BA')
|
||||||
|
tc.assertFalse(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
aut = spot.translate(f, 'deterministic', 'complete')
|
||||||
|
tc.assertFalse(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
aut = spot.translate(f, 'generic', 'sbacc')
|
||||||
|
tc.assertFalse(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
for f in ['Gb', 'Ga|Gb|Gc', 'Fr->(!p U r)', 'p1 M F(p1 U (Gp0 U X(0)))',
|
||||||
|
'((p1 U !p0) M !FXp1) W p0', 'p0 & ((!p1 | (p1 W X!p1)) M p1)',
|
||||||
|
'(p0 W Fp0) R ((Gp1 & Fp1) | (F!p1 & G!p1))']:
|
||||||
|
aut = spot.translate(f, 'BA')
|
||||||
|
tc.assertTrue(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
ba = spot.translate(f, 'BA', 'complete')
|
||||||
|
tc.assertTrue(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
ba = spot.translate(f, 'deterministic', 'complete')
|
||||||
|
tc.assertTrue(spot.is_safety_automaton(aut))
|
||||||
|
|
||||||
|
ba = spot.translate(f, 'generic', 'sbacc')
|
||||||
|
tc.assertTrue(spot.is_safety_automaton(aut))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue