introduce is_inherently_weak_automaton()
* spot/twaalgos/strength.cc, spot/twaalgos/strength.hh (is_inherently_weak_automaton): New function. (is_type_automaton): Adjust to implement the above and set prop_inherently_weak(). * spot/twaalgos/isweakscc.cc, spot/twaalgos/isweakscc.hh: Rewrite is_inherently_weak_scc() to not enumerate cycles. * spot/bin/autfilt.cc: Add a --is-inherently-weak option. * spot/tests/readsave.test: More tests. * spot/tests/strength.test: Adjust expected output. * doc/org/hoa.org: Adjust documentation of --check. * NEWS: Mention those changes.
This commit is contained in:
parent
0edb2ad066
commit
1f2260f971
9 changed files with 154 additions and 70 deletions
6
NEWS
6
NEWS
|
|
@ -2,6 +2,8 @@ New in spot 1.99.6a (not yet released)
|
||||||
|
|
||||||
Command-line tools:
|
Command-line tools:
|
||||||
|
|
||||||
|
* autfilt has a new option: --is-inherently-weak.
|
||||||
|
|
||||||
Library:
|
Library:
|
||||||
|
|
||||||
* Installed headers now assume that they will be included as
|
* Installed headers now assume that they will be included as
|
||||||
|
|
@ -23,6 +25,10 @@ New in spot 1.99.6a (not yet released)
|
||||||
of $includedir/spot/iface/, so that installed and non-installed
|
of $includedir/spot/iface/, so that installed and non-installed
|
||||||
directories can be used similarly.
|
directories can be used similarly.
|
||||||
|
|
||||||
|
* is_inherently_weak_automaton() is a new function, and
|
||||||
|
check_strength() has been modified to also check inherently weak
|
||||||
|
automata.
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
Documentation:
|
Documentation:
|
||||||
|
|
|
||||||
|
|
@ -666,7 +666,7 @@ particular:
|
||||||
| =stutter-sensitive= | trusted | yes | as stored | can be checked with =--check=stuttering= |
|
| =stutter-sensitive= | trusted | yes | as stored | can be checked with =--check=stuttering= |
|
||||||
| =terminal= | trusted | yes | as stored | can be checked with =--check=strength= |
|
| =terminal= | trusted | yes | as stored | can be checked with =--check=strength= |
|
||||||
| =weak= | trusted | yes | as stored if (=-Hv= or not =terminal=) | can be checked with =--check=strength= |
|
| =weak= | trusted | yes | as stored if (=-Hv= or not =terminal=) | can be checked with =--check=strength= |
|
||||||
| =inherently-weak= | trusted | yes | as stored if (=-Hv= or not =weak=) | |
|
| =inherently-weak= | trusted | yes | as stored if (=-Hv= or not =weak=) | can be checked with =--check=strength= |
|
||||||
| =colored= | ignored | no | checked | |
|
| =colored= | ignored | no | checked | |
|
||||||
|
|
||||||
** Named properties
|
** Named properties
|
||||||
|
|
@ -942,4 +942,5 @@ bits section above), and property that are trivial to compute.
|
||||||
|
|
||||||
Command-line tools with a HOA output all have a =--check= option that
|
Command-line tools with a HOA output all have a =--check= option that
|
||||||
can be used to request additional checks such as testing whether the
|
can be used to request additional checks such as testing whether the
|
||||||
automaton is stutter-invariant, unambiguous, weak, and terminal.
|
automaton is stutter-invariant, unambiguous, (inherently) weak, and
|
||||||
|
terminal.
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ enum {
|
||||||
OPT_IS_UNAMBIGUOUS,
|
OPT_IS_UNAMBIGUOUS,
|
||||||
OPT_IS_TERMINAL,
|
OPT_IS_TERMINAL,
|
||||||
OPT_IS_WEAK,
|
OPT_IS_WEAK,
|
||||||
|
OPT_IS_INHERENTLY_WEAK,
|
||||||
OPT_KEEP_STATES,
|
OPT_KEEP_STATES,
|
||||||
OPT_MASK_ACC,
|
OPT_MASK_ACC,
|
||||||
OPT_MERGE,
|
OPT_MERGE,
|
||||||
|
|
@ -200,6 +201,8 @@ static const argp_option options[] =
|
||||||
"keep only terminal automata", 0 },
|
"keep only terminal automata", 0 },
|
||||||
{ "is-weak", OPT_IS_WEAK, nullptr, 0,
|
{ "is-weak", OPT_IS_WEAK, nullptr, 0,
|
||||||
"keep only weak automata", 0 },
|
"keep only weak automata", 0 },
|
||||||
|
{ "is-inherently-weak", OPT_IS_INHERENTLY_WEAK, nullptr, 0,
|
||||||
|
"keep only inherently weak automata", 0 },
|
||||||
{ "intersect", OPT_INTERSECT, "FILENAME", 0,
|
{ "intersect", OPT_INTERSECT, "FILENAME", 0,
|
||||||
"keep automata whose languages have an non-empty intersection with"
|
"keep automata whose languages have an non-empty intersection with"
|
||||||
" the automaton from FILENAME", 0 },
|
" the automaton from FILENAME", 0 },
|
||||||
|
|
@ -268,6 +271,7 @@ static bool opt_is_deterministic = false;
|
||||||
static bool opt_is_unambiguous = false;
|
static bool opt_is_unambiguous = false;
|
||||||
static bool opt_is_terminal = false;
|
static bool opt_is_terminal = false;
|
||||||
static bool opt_is_weak = false;
|
static bool opt_is_weak = false;
|
||||||
|
static bool opt_is_inherently_weak = false;
|
||||||
static bool opt_invert = false;
|
static bool opt_invert = false;
|
||||||
static range opt_states = { 0, std::numeric_limits<int>::max() };
|
static range opt_states = { 0, std::numeric_limits<int>::max() };
|
||||||
static range opt_edges = { 0, std::numeric_limits<int>::max() };
|
static range opt_edges = { 0, std::numeric_limits<int>::max() };
|
||||||
|
|
@ -379,6 +383,9 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
case OPT_IS_EMPTY:
|
case OPT_IS_EMPTY:
|
||||||
opt_is_empty = true;
|
opt_is_empty = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_IS_INHERENTLY_WEAK:
|
||||||
|
opt_is_inherently_weak = true;
|
||||||
|
break;
|
||||||
case OPT_IS_UNAMBIGUOUS:
|
case OPT_IS_UNAMBIGUOUS:
|
||||||
opt_is_unambiguous = true;
|
opt_is_unambiguous = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -594,6 +601,8 @@ namespace
|
||||||
matched &= is_terminal_automaton(aut);
|
matched &= is_terminal_automaton(aut);
|
||||||
else if (opt_is_weak)
|
else if (opt_is_weak)
|
||||||
matched &= is_weak_automaton(aut);
|
matched &= is_weak_automaton(aut);
|
||||||
|
else if (opt_is_inherently_weak)
|
||||||
|
matched &= is_inherently_weak_automaton(aut);
|
||||||
if (opt->are_isomorphic)
|
if (opt->are_isomorphic)
|
||||||
matched &= opt->isomorphism_checker->is_isomorphic(aut);
|
matched &= opt->isomorphism_checker->is_isomorphic(aut);
|
||||||
if (opt_is_empty)
|
if (opt_is_empty)
|
||||||
|
|
|
||||||
|
|
@ -737,6 +737,7 @@ State: 2 {0 1} [0] 2
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
test `$autfilt --is-weak -c input4` = 1
|
test `$autfilt --is-weak -c input4` = 1
|
||||||
|
test `$autfilt --is-inherently-weak -c input4` = 1
|
||||||
test `$autfilt --is-terminal -c input4` = 0
|
test `$autfilt --is-terminal -c input4` = 0
|
||||||
|
|
||||||
$autfilt -H --small --high input4 >output4
|
$autfilt -H --small --high input4 >output4
|
||||||
|
|
@ -837,3 +838,62 @@ State: [0] 2 {0}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
diff output6 expect6
|
diff output6 expect6
|
||||||
|
|
||||||
|
|
||||||
|
cat >input7 <<EOF
|
||||||
|
HOA: v1
|
||||||
|
States: 3
|
||||||
|
Start: 1
|
||||||
|
AP: 0
|
||||||
|
acc-name: Buchi
|
||||||
|
Acceptance: 2 Inf(0) & Inf(1)
|
||||||
|
--BODY--
|
||||||
|
State: 0
|
||||||
|
[t] 1 {0}
|
||||||
|
[t] 0 {0 1}
|
||||||
|
State: 1
|
||||||
|
[t] 0 {1}
|
||||||
|
[t] 1 {0 1}
|
||||||
|
--END--
|
||||||
|
EOF
|
||||||
|
test `$autfilt -c --is-inherently-weak input7` = 1
|
||||||
|
test `$autfilt -c --is-weak input7` = 0
|
||||||
|
$autfilt --check input7 -H >output7
|
||||||
|
cat >expected7 <<EOF
|
||||||
|
HOA: v1
|
||||||
|
States: 3
|
||||||
|
Start: 1
|
||||||
|
AP: 0
|
||||||
|
acc-name: generalized-Buchi 2
|
||||||
|
Acceptance: 2 Inf(0)&Inf(1)
|
||||||
|
properties: trans-labels explicit-labels trans-acc inherently-weak
|
||||||
|
--BODY--
|
||||||
|
State: 0
|
||||||
|
[t] 1 {0}
|
||||||
|
[t] 0 {0 1}
|
||||||
|
State: 1
|
||||||
|
[t] 0 {1}
|
||||||
|
[t] 1 {0 1}
|
||||||
|
State: 2
|
||||||
|
--END--
|
||||||
|
EOF
|
||||||
|
diff output7 expected7
|
||||||
|
|
||||||
|
cat >input8 <<EOF
|
||||||
|
HOA: v1
|
||||||
|
States: 3
|
||||||
|
Start: 1
|
||||||
|
AP: 0
|
||||||
|
acc-name: Buchi
|
||||||
|
Acceptance: 2 Inf(0) & Inf(1)
|
||||||
|
--BODY--
|
||||||
|
State: 0
|
||||||
|
[t] 1 {0}
|
||||||
|
[t] 0 {0 1}
|
||||||
|
State: 1
|
||||||
|
[t] 0 {1}
|
||||||
|
[t] 1 {0}
|
||||||
|
--END--
|
||||||
|
EOF
|
||||||
|
test `$autfilt -c --is-inherently-weak input8` = 0
|
||||||
|
test `$autfilt -c --is-weak input8` = 0
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ Start: 1
|
||||||
AP: 1 "a"
|
AP: 1 "a"
|
||||||
Acceptance: 2 Inf(0) | Inf(1)
|
Acceptance: 2 Inf(0) | Inf(1)
|
||||||
properties: trans-labels explicit-labels trans-acc colored complete
|
properties: trans-labels explicit-labels trans-acc colored complete
|
||||||
properties: deterministic
|
properties: deterministic inherently-weak
|
||||||
--BODY--
|
--BODY--
|
||||||
State: 0
|
State: 0
|
||||||
[t] 1 {0}
|
[t] 1 {0}
|
||||||
|
|
|
||||||
|
|
@ -17,51 +17,26 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <spot/twaalgos/cycles.hh>
|
|
||||||
#include <spot/tl/formula.hh>
|
#include <spot/tl/formula.hh>
|
||||||
#include <spot/twaalgos/isweakscc.hh>
|
#include <spot/twaalgos/isweakscc.hh>
|
||||||
|
#include <spot/twaalgos/mask.hh>
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
namespace
|
bool
|
||||||
|
scc_has_rejecting_cycle(scc_info& map, unsigned scc)
|
||||||
{
|
{
|
||||||
// Look for a non-accepting cycle.
|
// We check that by cloning the SCC and complementing its
|
||||||
class weak_checker final : public enumerate_cycles
|
// acceptance condition.
|
||||||
{
|
auto aut = map.get_aut();
|
||||||
public:
|
std::vector<bool> keep(aut->num_states(), false);
|
||||||
bool result;
|
auto& states = map.states_of(scc);
|
||||||
|
for (auto s: states)
|
||||||
weak_checker(const scc_info& map)
|
keep[s] = true;
|
||||||
: enumerate_cycles(map), result(true)
|
auto sccaut = mask_keep_states(aut, keep, states.front());
|
||||||
{
|
sccaut->set_acceptance(sccaut->acc().num_sets(),
|
||||||
}
|
sccaut->get_acceptance().complement());
|
||||||
|
return !sccaut->is_empty();
|
||||||
virtual bool
|
|
||||||
cycle_found(unsigned start) override
|
|
||||||
{
|
|
||||||
dfs_stack::const_reverse_iterator i = dfs_.rbegin();
|
|
||||||
acc_cond::mark_t acc = 0U;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
acc |= aut_->edge_storage(i->succ).acc;
|
|
||||||
if (i->s == start)
|
|
||||||
break;
|
|
||||||
++i;
|
|
||||||
// The const cast is here to please old g++ versions.
|
|
||||||
// At least version 4.0 needs it.
|
|
||||||
assert(i != const_cast<const dfs_stack&>(dfs_).rend());
|
|
||||||
}
|
|
||||||
if (!aut_->acc().accepting(acc))
|
|
||||||
{
|
|
||||||
// We have found an non-accepting cycle, so the SCC is not
|
|
||||||
// weak.
|
|
||||||
result = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -70,11 +45,10 @@ namespace spot
|
||||||
// Weak SCCs are inherently weak.
|
// Weak SCCs are inherently weak.
|
||||||
if (is_weak_scc(map, scc))
|
if (is_weak_scc(map, scc))
|
||||||
return true;
|
return true;
|
||||||
// If the SCC is accepting, but one cycle is not, the SCC is not
|
// If we reach this place, we now the SCC has an accepting cycle.
|
||||||
// weak.
|
// The question is now to find whether is also contains a
|
||||||
weak_checker w(map);
|
// rejecting cycle.
|
||||||
w.run(scc);
|
return !scc_has_rejecting_cycle(map, scc);
|
||||||
return w.result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ namespace spot
|
||||||
/// \addtogroup twa_misc
|
/// \addtogroup twa_misc
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// \brief Whether the SCC number \a scc in \a map has a rejecting
|
||||||
|
/// cycle.
|
||||||
|
SPOT_API bool
|
||||||
|
scc_has_rejecting_cycle(scc_info& map, unsigned scc);
|
||||||
|
|
||||||
/// \brief Whether the SCC number \a scc in \a map is inherently
|
/// \brief Whether the SCC number \a scc in \a map is inherently
|
||||||
/// weak.
|
/// weak.
|
||||||
///
|
///
|
||||||
|
|
@ -34,13 +39,6 @@ namespace spot
|
||||||
///
|
///
|
||||||
/// Note the terminal SCCs are also inherently weak with that
|
/// Note the terminal SCCs are also inherently weak with that
|
||||||
/// definition.
|
/// definition.
|
||||||
///
|
|
||||||
/// The absence of accepting cycle is easy to check (the scc_info
|
|
||||||
/// object can tell whether the SCC is non-accepting already).
|
|
||||||
/// Similarly, an SCC in which all transitions belong to all
|
|
||||||
/// acceptance sets is necessarily weak. For other accepting SCCs,
|
|
||||||
/// this function enumerates all cycles in the given SCC (it stops
|
|
||||||
/// if it find a non-accepting cycle).
|
|
||||||
SPOT_API bool
|
SPOT_API bool
|
||||||
is_inherently_weak_scc(scc_info& map, unsigned scc);
|
is_inherently_weak_scc(scc_info& map, unsigned scc);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template <bool terminal, bool set = false>
|
template <bool terminal, bool inweak = false, bool set = false>
|
||||||
bool is_type_automaton(const twa_graph_ptr& aut, scc_info* si)
|
bool is_type_automaton(const twa_graph_ptr& aut, scc_info* si)
|
||||||
{
|
{
|
||||||
// Create an scc_info if the user did not give one to us.
|
// Create an scc_info if the user did not give one to us.
|
||||||
|
|
@ -36,6 +36,7 @@ namespace spot
|
||||||
si = new scc_info(aut);
|
si = new scc_info(aut);
|
||||||
si->determine_unknown_acceptance();
|
si->determine_unknown_acceptance();
|
||||||
|
|
||||||
|
bool is_inweak = true;
|
||||||
bool is_weak = true;
|
bool is_weak = true;
|
||||||
bool is_term = true;
|
bool is_term = true;
|
||||||
unsigned n = si->scc_count();
|
unsigned n = si->scc_count();
|
||||||
|
|
@ -45,6 +46,7 @@ namespace spot
|
||||||
continue;
|
continue;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
acc_cond::mark_t m = 0U;
|
acc_cond::mark_t m = 0U;
|
||||||
|
if (is_weak)
|
||||||
for (auto src: si->states_of(i))
|
for (auto src: si->states_of(i))
|
||||||
for (auto& t: aut->out(src))
|
for (auto& t: aut->out(src))
|
||||||
if (si->scc_of(t.dst) == i)
|
if (si->scc_of(t.dst) == i)
|
||||||
|
|
@ -57,9 +59,19 @@ namespace spot
|
||||||
else if (m != t.acc)
|
else if (m != t.acc)
|
||||||
{
|
{
|
||||||
is_weak = false;
|
is_weak = false;
|
||||||
|
if (!inweak)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!is_weak && si->is_accepting_scc(i))
|
||||||
|
{
|
||||||
|
assert(inweak);
|
||||||
|
if (scc_has_rejecting_cycle(*si, i))
|
||||||
|
{
|
||||||
|
is_inweak = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (terminal && si->is_accepting_scc(i) && !is_complete_scc(*si, i))
|
if (terminal && si->is_accepting_scc(i) && !is_complete_scc(*si, i))
|
||||||
{
|
{
|
||||||
is_term = false;
|
is_term = false;
|
||||||
|
|
@ -75,7 +87,10 @@ namespace spot
|
||||||
if (terminal)
|
if (terminal)
|
||||||
aut->prop_terminal(is_term && is_weak);
|
aut->prop_terminal(is_term && is_weak);
|
||||||
aut->prop_weak(is_weak);
|
aut->prop_weak(is_weak);
|
||||||
|
aut->prop_inherently_weak(is_inweak);
|
||||||
}
|
}
|
||||||
|
if (inweak)
|
||||||
|
return is_inweak;
|
||||||
return is_weak && is_term;
|
return is_weak && is_term;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,9 +111,17 @@ namespace spot
|
||||||
si));
|
si));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_inherently_weak_automaton(const const_twa_graph_ptr& aut, scc_info* si)
|
||||||
|
{
|
||||||
|
return (aut->prop_inherently_weak() ||
|
||||||
|
is_type_automaton<false, true>
|
||||||
|
(std::const_pointer_cast<twa_graph>(aut), si));
|
||||||
|
}
|
||||||
|
|
||||||
void check_strength(const twa_graph_ptr& aut, scc_info* si)
|
void check_strength(const twa_graph_ptr& aut, scc_info* si)
|
||||||
{
|
{
|
||||||
is_type_automaton<true, true>(aut, si);
|
is_type_automaton<true, true, true>(aut, si);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_safety_mwdba(const const_twa_graph_ptr& aut)
|
bool is_safety_mwdba(const const_twa_graph_ptr& aut)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace spot
|
||||||
|
|
||||||
/// \brief Whether an automaton is weak.
|
/// \brief Whether an automaton is weak.
|
||||||
///
|
///
|
||||||
/// An automaton is weak if if any given SCC, all transitions belong
|
/// An automaton is weak if in any given SCC, all transitions belong
|
||||||
/// to the same acceptance sets.
|
/// to the same acceptance sets.
|
||||||
///
|
///
|
||||||
/// \param aut the automaton to check
|
/// \param aut the automaton to check
|
||||||
|
|
@ -48,6 +48,19 @@ namespace spot
|
||||||
SPOT_API bool
|
SPOT_API bool
|
||||||
is_weak_automaton(const const_twa_graph_ptr& aut, scc_info* sm = nullptr);
|
is_weak_automaton(const const_twa_graph_ptr& aut, scc_info* sm = nullptr);
|
||||||
|
|
||||||
|
/// \brief Whether an automaton is inherently weak.
|
||||||
|
///
|
||||||
|
/// An automaton is inherently weak if in any given SCC, there
|
||||||
|
/// are only accepting cycles, or only rejecting cycles.
|
||||||
|
///
|
||||||
|
/// \param aut the automaton to check
|
||||||
|
///
|
||||||
|
/// \param sm an scc_info object for the automaton if available (it
|
||||||
|
/// will be built otherwise).
|
||||||
|
SPOT_API bool
|
||||||
|
is_inherently_weak_automaton(const const_twa_graph_ptr& aut,
|
||||||
|
scc_info* sm = nullptr);
|
||||||
|
|
||||||
/// \brief Whether a minimized WDBA represents a safety property.
|
/// \brief Whether a minimized WDBA represents a safety property.
|
||||||
///
|
///
|
||||||
/// A minimized WDBA (as returned by a successful run of
|
/// A minimized WDBA (as returned by a successful run of
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue