diff --git a/NEWS b/NEWS index db6a9bb9b..13f2ce84c 100644 --- a/NEWS +++ b/NEWS @@ -46,7 +46,7 @@ New in spot 2.2.2.dev (Not yet released) are automata-based and will also match pathological formulas. * The --format option of ltlfilt/genltl/randltl/ltlgrind learned to - print the class of a formula in the temporal hierachy of Manna & + print the class of a formula in the temporal hierarchy of Manna & Pnueli using %h. Try to classify the Dwyer & al. patterns with: genltl --dac --format='%[vw]h' | sort | uniq -c @@ -130,6 +130,15 @@ New in spot 2.2.2.dev (Not yet released) * The new function mp_class(f) returns the class of the formula f in the temporal hierarchy of Manna & Pnueli. +Bug fixed: + + * The minimize_wdba() function was not correctly minimizing automata + with useless SCCs. This was not an issue for the LTL translation + (where useless SCCs are always removed first), but it was an issue + when deciding if a formula was safety or guarantee. As a + consequence, some tricky safety or guarantee properties were only + recognized as obligations. + New in spot 2.2.2 (2016-12-16) Build: diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index d204f08aa..351b60aa4 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -734,8 +734,10 @@ namespace } else { - matched &= !guarantee || is_terminal_automaton(min); - matched &= !safety || is_safety_mwdba(min); + spot::scc_info si(min); + matched &= !guarantee + || is_terminal_automaton(min, &si, true); + matched &= !safety || is_safety_automaton(min, &si); } } // Recurrence properties are those that can be translated to diff --git a/python/ajax/spotcgi.in b/python/ajax/spotcgi.in index baec65f70..474d10edf 100755 --- a/python/ajax/spotcgi.in +++ b/python/ajax/spotcgi.in @@ -1,7 +1,7 @@ #!@PYTHON@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -511,8 +511,8 @@ if output_type == 'f': automaton = spot.ltl_to_tgba_fm(f, dict, False, True) minimized = spot.minimize_obligation(automaton, f) if minimized != automaton: - g = spot.is_terminal_automaton(minimized) - s = spot.is_safety_mwdba(minimized) + g = spot.is_terminal_automaton(minimized, None, True) + s = spot.is_safety_automaton(minimized) if s and not f.is_syntactic_safety(): unbufprint('
  • pathologic safety
  • ') if g and not f.is_syntactic_guarantee(): diff --git a/spot/tl/hierarchy.cc b/spot/tl/hierarchy.cc index 122665b5d..53c5531e3 100644 --- a/spot/tl/hierarchy.cc +++ b/spot/tl/hierarchy.cc @@ -66,8 +66,11 @@ namespace spot auto min = minimize_obligation(aut, f); if (aut != min) // An obligation. { - bool g = is_terminal_automaton(min); - bool s = is_safety_mwdba(min); + scc_info si(min); + // The minimba WDBA can have some trivial accepting SCCs + // that we should ignore in is_terminal_automaton(). + bool g = is_terminal_automaton(min, &si, true); + bool s = is_safety_automaton(min, &si); if (g) return s ? 'B' : 'G'; else diff --git a/spot/twaalgos/minimize.cc b/spot/twaalgos/minimize.cc index 4bb295624..39685caf3 100644 --- a/spot/twaalgos/minimize.cc +++ b/spot/twaalgos/minimize.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Laboratoire de +// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Laboratoire de // Recherche et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -520,7 +520,7 @@ namespace spot // (i.e., it is not the start of any accepting word). scc_info sm(det_a); - sm.determine_unknown_acceptance(); + unsigned scc_count = sm.scc_count(); // SCC that have been marked as useless. std::vector useless(scc_count); @@ -539,18 +539,13 @@ namespace spot bool transient = sm.is_trivial(m); auto& succ = sm.succ(m); - if (transient && succ.empty()) - { - // A trivial SCC without successor is useless. - useless[m] = true; - d[m] = k - 1; - continue; - } - - // Compute the minimum color l of the successors. - // Also SCCs are useless if all their successor are - // useless. - unsigned l = k; + // Compute the minimum color l of the successors. Also SCCs + // are useless if all their successor are useless. Note + // that Löding uses k-1 as level for non-final SCCs without + // successors but that seems bogus: using k+1 will make sure + // that a non-final SCCs without successor (i.e., a useless + // SCC) will be ignored in the computation of the level. + unsigned l = k + 1; for (unsigned j: succ) { is_useless &= useless[j]; @@ -575,6 +570,8 @@ namespace spot } else { + if (succ.empty()) + is_useless = true; d[m] = (l - 1) | 1; // largest odd number inferior or equal } } diff --git a/spot/twaalgos/strength.cc b/spot/twaalgos/strength.cc index ef161fbcf..926da171c 100644 --- a/spot/twaalgos/strength.cc +++ b/spot/twaalgos/strength.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016 Laboratoire de -// Recherche et Développement de l'Epita (LRDE) +// Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016, 2017 Laboratoire +// de Recherche et Développement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -28,7 +28,8 @@ namespace spot namespace { template - bool is_type_automaton(const twa_graph_ptr& aut, scc_info* si) + bool is_type_automaton(const twa_graph_ptr& aut, scc_info* si, + bool ignore_trivial_term = false) { // Create an scc_info if the user did not give one to us. bool need_si = !si; @@ -89,7 +90,7 @@ namespace spot // A terminal automaton should accept any word that as a prefix // leading to an accepting edge. In other words, we cannot have // an accepting edge that goes into a rejecting SCC. - if (terminal && is_term) + if (terminal && is_term && !ignore_trivial_term) for (auto& e: aut->edges()) if (si->is_rejecting_scc(si->scc_of(e.dst)) && aut->acc().accepting(e.acc)) @@ -103,7 +104,12 @@ namespace spot if (set) { if (terminal) - aut->prop_terminal(is_term && is_weak); + { + if (!ignore_trivial_term) + aut->prop_terminal(is_term && is_weak); + else if (is_term && is_weak) + aut->prop_terminal(true); + } aut->prop_weak(is_weak); aut->prop_very_weak(is_single_state_scc && is_weak); if (inweak) @@ -116,13 +122,15 @@ namespace spot } bool - is_terminal_automaton(const const_twa_graph_ptr& aut, scc_info* si) + is_terminal_automaton(const const_twa_graph_ptr& aut, scc_info* si, + bool ignore_trivial_term) { trival v = aut->prop_terminal(); if (v.is_known()) return v.is_true(); bool res = - is_type_automaton(std::const_pointer_cast(aut), si); + is_type_automaton(std::const_pointer_cast(aut), si, + ignore_trivial_term); std::const_pointer_cast(aut)->prop_terminal(res); return res; } @@ -170,16 +178,27 @@ namespace spot is_type_automaton(aut, si); } - bool is_safety_mwdba(const const_twa_graph_ptr& aut) + bool is_safety_automaton(const const_twa_graph_ptr& aut, scc_info* si) { - if (!(aut->acc().is_buchi() || aut->acc().is_all())) - throw std::runtime_error - ("is_safety_mwdba() should be called on a Buchi automaton"); + if (aut->acc().is_t()) + return true; - for (auto& t: aut->edges()) - if (!aut->acc().accepting(t.acc)) - return false; - return true; + bool need_si = !si; + if (need_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; + break; + } + + if (need_si) + delete si; + return res; } diff --git a/spot/twaalgos/strength.hh b/spot/twaalgos/strength.hh index 66cea4db2..550c1815e 100644 --- a/spot/twaalgos/strength.hh +++ b/spot/twaalgos/strength.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016 Laboratoire de -// Recherche et Développement de l'Epita (LRDE) +// Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016, 2017 Laboratoire +// de Recherche et Développement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -29,6 +29,9 @@ namespace spot /// are complete, and no accepting transitions lead to a /// non-accepting SCC. /// + /// If ignore_trivial_scc is set, accepting transitions from trivial + /// SCCs are ignored. + /// /// This property guarantees that a word is accepted if it has some /// prefix that reaches an accepting transition. /// @@ -41,8 +44,8 @@ namespace spot /// the prop_terminal() property of the automaton as a side-effect, /// so further calls will return in constant-time. SPOT_API bool - is_terminal_automaton(const const_twa_graph_ptr& aut, scc_info* sm = nullptr); - + is_terminal_automaton(const const_twa_graph_ptr& aut, scc_info* sm = nullptr, + bool ignore_trivial_scc = false); /// \brief Check whether an automaton is weak. /// @@ -95,16 +98,22 @@ namespace spot is_inherently_weak_automaton(const const_twa_graph_ptr& aut, scc_info* sm = nullptr); - /// \brief Check whether a minimized WDBA represents a safety - /// property. + /// \brief Check whether an automaton is a safety automaton. + /// + /// A safety automaton has only accepting SCCs (or trivial + /// SCCs). /// /// A minimized WDBA (as returned by a successful run of - /// minimize_obligation()) represent safety property if it contains - /// only accepting transitions. + /// minimize_obligation()) represent safety property if it is a + /// safety automaton. /// /// \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_safety_mwdba(const const_twa_graph_ptr& aut); + is_safety_automaton(const const_twa_graph_ptr& aut, + scc_info* sm = nullptr); /// \brief Check whether an automaton is weak or terminal. /// diff --git a/tests/core/hierarchy.test b/tests/core/hierarchy.test index 9515d9011..c44f28f37 100755 --- a/tests/core/hierarchy.test +++ b/tests/core/hierarchy.test @@ -62,3 +62,7 @@ cat >expected <