Commit 4a5d7a39 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

rename is_deterministic to is_universal

For #212.

* spot/twa/twa.hh: Rename prop_deterministic() as prop_universal(),
and keep the old name as deprecated.
* spot/twaalgos/isdet.cc, spot/twaalgos/isdet.hh: Rename
is_deterministic() as is_universal(), and add a new function
for is_deterministic().
* doc/org/concepts.org, doc/org/hoa.org, doc/org/tut21.org,
spot/tl/hierarchy.cc, spot/twa/twagraph.cc,
spot/twaalgos/are_isomorphic.cc, spot/twaalgos/determinize.cc,
spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc,
spot/twaalgos/hoa.cc, spot/twaalgos/minimize.cc,
spot/twaalgos/postproc.cc, spot/twaalgos/product.cc,
spot/twaalgos/randomgraph.cc, spot/twaalgos/remfin.cc,
spot/twaalgos/simulation.cc, spot/twaalgos/totgba.cc,
spot/twaalgos/word.cc, tests/python/product.ipynb,
tests/python/remfin.py: Adjust.
* NEWS: Mention the change.
parent 4518724a
......@@ -36,6 +36,18 @@ New in spot 2.3.2.dev (not yet released)
It was never used. You always want to use
spot::twa_graph::set_init_state(unsigned) in practice.
- The previous implementation of spot::is_deterministic() has been
renamed to spot::is_universal(). The new version of
spot::is_deterministic() requires the automaton to be both
universal and existential. This should not make any difference in
existing code unless you work with the recently added support for
alternating automata.
- The spot::twa::prop_deterministic() methods have been renamed to
spot::twa::prop_universal() for consistency with the above change.
We have kept spot::twa::prop_deterministic() as a deprecated
synonym for spot::twa::prop_universal() to help backward
compatibility.
New in spot 2.3.2 (2017-03-15)
......
......@@ -1054,18 +1054,18 @@ better choices.
There are actually several property flags that are stored into each
automaton, and that can be queried or set by algorithms:
| flag name | meaning when =true= |
|----------------------+----------------------------------------------------------------------------------------------|
| =state_acc= | automaton should be considered as having state-based acceptance |
| =inherently_weak= | accepting and rejecting cycles cannot be mixed in the same SCC |
| =weak= | transitions of an SCC all belong to the same acceptance sets |
| =very-weak= | weak automaton where all SCCs have size 1 |
| =terminal= | automaton is weak, accepting SCCs are complete, accepting edges may not go to rejecting SCCs |
| =complete= | it is always possible to move the automaton forward, using any letter |
| =deterministic= | there is at most one run *recognizing* a word, but not necessarily accepting it |
| =semi-deterministic= | any nondeterminism occurs before entering an accepting SCC |
| =unambiguous= | there is at most one run *accepting* a word (but it might be recognized several time) |
| =stutter_invariant= | the property recognized by the automaton is [[https://www.lrde.epita.fr/~adl/dl/adl/michaud.15.spin.pdf][stutter-invariant]] |
| flag name | meaning when =true= |
|----------------------+-------------------------------------------------------------------------------------------------------------------------------------------|
| =state_acc= | automaton should be considered as having state-based acceptance |
| =inherently_weak= | accepting and rejecting cycles cannot be mixed in the same SCC |
| =weak= | transitions of an SCC all belong to the same acceptance sets |
| =very-weak= | weak automaton where all SCCs have size 1 |
| =terminal= | automaton is weak, accepting SCCs are complete, accepting edges may not go to rejecting SCCs |
| =complete= | it is always possible to move the automaton forward, using any letter |
| =universal= | there is no non-determinism branching in the automaton (hence each word is *recognized* by at most one run, but not necessarily accepted) |
| =semi-deterministic= | any nondeterminism occurs before entering an accepting SCC |
| =unambiguous= | there is at most one run *accepting* a word (but it might be recognized several time) |
| =stutter_invariant= | the property recognized by the automaton is [[https://www.lrde.epita.fr/~adl/dl/adl/michaud.15.spin.pdf][stutter-invariant]] |
For each flag =flagname=, the =twa= class has a method
=prop_flagname()= that returns the value of the flag as an instance of
......@@ -1083,12 +1083,14 @@ operator =X= does not prevent the formula from being
stutter-invariant, but it would require additional work to check.
As another example, if you write an algorithm that must check whether
an automaton is deterministic, do not call the
=twa::prop_deterministic()= method, because that might return
=trival::maybe=. Instead, call =spot::is_deterministic(...)=: that
will respond in constant time if the =deterministic= property flag was
either =true= or =false=, otherwise it will actually explore the
automaton to decide its determinism.
an automaton is universal, do not call the =twa::prop_universal()=
method, because that might return =trival::maybe=. Instead, call
=spot::is_universal(...)=: that will respond in constant time if the
=universal= property flag was either =true= or =false=, otherwise it
will actually explore the automaton to decide its determinism. Note
that there is also a =spot::is_deterministic(...)= function, which is
equivalent to testing that the automaton is both universal and
existential.
These automata properties are encoded into the [[file:hoa.org::#property-bits][HOA format]], so they can
be preserved when building a processing pipeline using the shell.
......
......@@ -609,11 +609,11 @@ double-checked by the parser.
It should be noted that each property can take three values: true,
false, or maybe. So actually two bits are used per property. For
instance if in some algorithm you want to know whether an automaton is
deterministic (the equivalent of calling =autfilt -q
--is-deterministic aut.hoa= from the command-line), you should not
call the method =aut->prop_deterministic()= because that only checks
complete (the equivalent of calling =autfilt -q
--is-complete aut.hoa= from the command-line), you should not
call the method =aut->prop_complete()= because that only checks
the property bits, and it might return =maybe= even if =aut= is
deterministic. Instead, call the function =is_deterministic(aut)=.
deterministic. Instead, call the function =is_complete(aut)=.
This function will first test the property bits, and do the actual
check in case it is unknown.
......@@ -639,12 +639,12 @@ this requests "verbose" properties.
The following table summarizes how supported properties are handled. In
particular:
- for the parser =checked= means that the property is always inferred
- For the parser, =checked= means that the property is always inferred
and checked against any declaration (if present), =trusted= means
that the property will be stored without being checked (unless
=--trust-hoa=no= is specified).
- Stored properties are those represented as bits in the automaton.
- the printer will sometime check some properties when it can do
- The printer will sometime check some properties when it can do
it as part of its initial "survey scan" of the automaton; in that
case the stored property is not used. This makes it possible
to detect deterministic automata that have been output by algorithms
......
......@@ -132,7 +132,8 @@ corresponding BDD variable number, and then use for instance
// example, the properties that are set come from the "properties:"
// line of the input file.
out << "Complete: " << aut->prop_complete() << '\n';
out << "Deterministic: " << aut->prop_deterministic() << '\n';
out << "Deterministic: " << (aut->prop_universal()
&& aut->is_existential()) << '\n';
out << "Unambiguous: " << aut->prop_unambiguous() << '\n';
out << "State-Based Acc: " << aut->prop_state_acc() << '\n';
out << "Terminal: " << aut->prop_terminal() << '\n';
......@@ -162,57 +163,6 @@ corresponding BDD variable number, and then use for instance
#+END_SRC
#+RESULTS:
#+begin_example
Acceptance: Inf(0)&Inf(1)
Number of sets: 2
Number of states: 4
Number of edges: 10
Initial state: 0
Atomic propositions: a (=0) b (=1) c (=2)
Name: Fa | G(Fb & Fc)
Complete: no
Deterministic: no
Unambiguous: yes
State-Based Acc: maybe
Terminal: maybe
Weak: maybe
Inherently Weak: maybe
Stutter Invariant: yes
State 0:
edge(0 -> 1)
label = a
acc sets = {}
edge(0 -> 2)
label = !a
acc sets = {}
edge(0 -> 3)
label = !a
acc sets = {}
State 1:
edge(1 -> 1)
label = 1
acc sets = {0,1}
State 2:
edge(2 -> 1)
label = a
acc sets = {}
edge(2 -> 2)
label = !a
acc sets = {}
State 3:
edge(3 -> 3)
label = !a & b & c
acc sets = {0,1}
edge(3 -> 3)
label = !a & !b & c
acc sets = {1}
edge(3 -> 3)
label = !a & b & !c
acc sets = {0}
edge(3 -> 3)
label = !a & !b & !c
acc sets = {}
#+end_example
* Python
......@@ -239,7 +189,7 @@ Here is the very same example, but written in Python:
name = aut.get_name()
if name:
print("Name: ", name)
print("Deterministic:", aut.prop_deterministic())
print("Deterministic:", aut.prop_universal() and aut.is_existential())
print("Unambiguous:", aut.prop_unambiguous())
print("State-Based Acc:", aut.prop_state_acc())
print("Terminal:", aut.prop_terminal())
......
......@@ -34,7 +34,7 @@ namespace spot
{
static bool is_recurrence(formula f, const twa_graph_ptr& aut)
{
if (f.is_syntactic_recurrence() || is_deterministic(aut))
if (f.is_syntactic_recurrence() || is_universal(aut))
return true;
// If aut is a non-deterministic TGBA, we do
// TGBA->DPA->DRA->(D?)BA. The conversion from DRA to
......
......@@ -987,7 +987,7 @@ namespace spot
trival::repr_t inherently_weak:2; // Inherently Weak automaton.
trival::repr_t weak:2; // Weak automaton.
trival::repr_t terminal:2; // Terminal automaton.
trival::repr_t deterministic:2; // Deterministic automaton.
trival::repr_t universal:2; // Universal automaton.
trival::repr_t unambiguous:2; // Unambiguous automaton.
trival::repr_t stutter_invariant:2; // Stutter invariant language.
trival::repr_t very_weak:2; // very-weak, or 1-weak
......@@ -1292,37 +1292,55 @@ namespace spot
is.complete = val.val();
}
/// \brief Whether the automaton is deterministic.
/// \brief Whether the automaton is universal.
///
/// An automaton is deterministic if the conjunction between the
/// An automaton is universal if the conjunction between the
/// labels of two transitions leaving a state is always false.
///
/// Note that this method may return trival::maybe() when it is
/// unknown whether the automaton is deterministic or not. If you
/// need a true/false answer, prefer the is_deterministic() function.
/// unknown whether the automaton is universal or not. If you
/// need a true/false answer, prefer the is_universal() function.
///
/// \see prop_unambiguous()
/// \see is_deterministic()
trival prop_deterministic() const
/// \see is_universal()
trival prop_universal() const
{
return is.deterministic;
return is.universal;
}
/// \brief Set the deterministic property.
/// \brief Set the universal property.
///
/// Setting the "deterministic" property automatically sets the
/// Setting the "universal" property automatically sets the
/// "unambiguous" and "semi-deterministic" properties.
///
/// \see prop_unambiguous()
/// \see prop_semi_deterministic()
void prop_deterministic(trival val)
void prop_universal(trival val)
{
is.deterministic = val.val();
is.universal = val.val();
if (val)
// deterministic implies unambiguous and semi-deterministic
// universal implies unambiguous and semi-deterministic
is.unambiguous = is.semi_deterministic = val.val();
}
// Starting with Spot 2.4, and automaton is deterministic if it is
// both universal and existential, but as we already have
// twa::is_existential(), we only need to additionally record the
// universal property. Before that, the deterministic property
// was just a synonym for universal, hence we keep the deprecated
// function prop_deterministic() with this meaning.
SPOT_DEPRECATED("use prop_universal() instead")
void prop_deterministic(trival val)
{
prop_universal(val);
}
SPOT_DEPRECATED("use prop_universal() instead")
trival prop_deterministic() const
{
return prop_universal();
}
/// \brief Whether the automaton is unambiguous
///
/// An automaton is unambiguous if any accepted word is recognized
......@@ -1334,7 +1352,7 @@ namespace spot
/// unknown whether the automaton is unambiguous or not. If you
/// need a true/false answer, prefer the is_unambiguous() function.
///
/// \see prop_deterministic()
/// \see prop_universal()
/// \see is_unambiguous()
trival prop_unambiguous() const
{
......@@ -1344,27 +1362,27 @@ namespace spot
/// \brief Sets the unambiguous property
///
/// Marking an automaton as "non unambiguous" automatically
/// marks it as "non deterministic".
/// marks it as "non universal".
///
/// \see prop_deterministic()
void prop_unambiguous(trival val)
{
is.unambiguous = val.val();
if (!val)
is.deterministic = val.val();
is.universal = val.val();
}
/// \brief Whether the automaton is semi-deterministic
///
/// An automaton is semi-deterministic if the sub-automaton
/// reachable from any accepting SCC is deterministic.
/// reachable from any accepting SCC is universal.
///
/// Note that this method may return trival::maybe() when it is
/// unknown whether the automaton is semi-deterministic or not.
/// If you need a true/false answer, prefer the
/// is_semi_deterministic() function.
///
/// \see prop_deterministic()
/// \see prop_universal()
/// \see is_semi_deterministic()
trival prop_semi_deterministic() const
{
......@@ -1374,14 +1392,14 @@ namespace spot
/// \brief Sets the semi-deterministic property
///
/// Marking an automaton as "non semi-deterministic" automatically
/// marks it as "non deterministic".
/// marks it as "non universal".
///
/// \see prop_deterministic()
/// \see prop_universal()
void prop_semi_deterministic(trival val)
{
is.semi_deterministic = val.val();
if (!val)
is.deterministic = val.val();
is.universal = val.val();
}
/// \brief Whether the automaton is stutter-invariant.
......@@ -1434,7 +1452,7 @@ namespace spot
/// "stutter invariant" properties from \c other_aut to \c code.
///
/// There are two flags for the determinism. If \code
/// deterministic is set, the deterministic, semi-deterministic,
/// deterministic is set, the universal, semi-deterministic,
/// and unambiguous properties are copied as-is. If deterministic
/// is unset but improve_det is set, then those properties are
/// only copied if they are positive.
......@@ -1542,15 +1560,15 @@ namespace spot
}
if (p.deterministic)
{
prop_deterministic(other->prop_deterministic());
prop_universal(other->prop_universal());
prop_semi_deterministic(other->prop_semi_deterministic());
prop_unambiguous(other->prop_unambiguous());
}
else if (p.improve_det)
{
if (other->prop_deterministic().is_true())
if (other->prop_universal().is_true())
{
prop_deterministic(true);
prop_universal(true);
}
else
{
......@@ -1584,8 +1602,8 @@ namespace spot
}
if (!p.deterministic)
{
if (!(p.improve_det && prop_deterministic().is_true()))
prop_deterministic(trival::maybe());
if (!(p.improve_det && prop_universal().is_true()))
prop_universal(trival::maybe());
if (!(p.improve_det && prop_semi_deterministic().is_true()))
prop_semi_deterministic(trival::maybe());
if (!(p.improve_det && prop_unambiguous().is_true()))
......
......@@ -235,9 +235,9 @@ namespace spot
return; // No unreachable state.
// Removing some non-deterministic dead state could make the
// automaton deterministic.
if (prop_deterministic().is_false())
prop_deterministic(trival::maybe());
// automaton universal.
if (prop_universal().is_false())
prop_universal(trival::maybe());
if (prop_complete().is_false())
prop_complete(trival::maybe());
......@@ -403,9 +403,9 @@ namespace spot
return; // No useless state.
// Removing some non-deterministic dead state could make the
// automaton deterministic. Likewise for non-complete.
if (prop_deterministic().is_false())
prop_deterministic(trival::maybe());
// automaton universal. Likewise for non-complete.
if (prop_universal().is_false())
prop_universal(trival::maybe());
if (prop_complete().is_false())
prop_complete(trival::maybe());
......
......@@ -113,7 +113,7 @@ namespace spot
isomorphism_checker::isomorphism_checker(const const_twa_graph_ptr ref)
{
ref_ = make_twa_graph(ref, twa::prop_set::all());
trival prop_det = ref_->prop_deterministic();
trival prop_det = ref_->prop_universal();
if (prop_det)
{
ref_deterministic_ = true;
......@@ -135,10 +135,10 @@ namespace spot
if (!aut->is_existential())
throw std::runtime_error
("isomorphism_checker does not yet support alternation");
trival autdet = aut->prop_deterministic();
trival autdet = aut->prop_universal();
if (ref_deterministic_)
{
if (!spot::is_deterministic(aut))
if (!spot::is_universal(aut))
return false;
return are_isomorphic_det(ref_, aut);
}
......
......@@ -32,6 +32,7 @@
#include <spot/twaalgos/degen.hh>
#include <spot/twaalgos/sccfilter.hh>
#include <spot/twaalgos/simulation.hh>
#include <spot/twaalgos/isdet.hh>
namespace spot
......@@ -582,7 +583,7 @@ namespace spot
if (!a->is_existential())
throw std::runtime_error
("tgba_determinize() does not support alternation");
if (a->prop_deterministic())
if (is_universal(a))
return std::const_pointer_cast<twa_graph>(a);
// Degeneralize
......@@ -701,7 +702,7 @@ namespace spot
remove_dead_acc(res, sets);
// Acceptance is now min(odd) since we con emit Red on paths 0 with new opti
res->set_acceptance(sets, acc_cond::acc_code::parity(false, true, sets));
res->prop_deterministic(true);
res->prop_universal(true);
res->prop_state_acc(false);
if (pretty_print)
......
......@@ -599,7 +599,7 @@ namespace spot
a->set_buchi();
if (state_based)
a->prop_state_acc(true);
a->prop_deterministic(true);
a->prop_universal(true);
a->new_states(satdict.cand_size);
#if DEBUG
......
......@@ -879,7 +879,7 @@ namespace spot
a->copy_ap_of(aut);
if (state_based)
a->prop_state_acc(true);
a->prop_deterministic(true);
a->prop_universal(true);
a->set_acceptance(satdict.cand_nacc, satdict.cand_acc);
a->new_states(satdict.cand_size);
......@@ -1474,7 +1474,7 @@ namespace spot
// mode. If the desired output is a Büchi automaton, or not
// desired acceptance was specified, stop here. There is not
// point in minimizing a minimal automaton.
if (a->prop_inherently_weak() && a->prop_deterministic()
if (a->prop_weak() && a->prop_universal()
&& (target_is_buchi || !user_supplied_acc))
return a;
}
......
......@@ -158,10 +158,10 @@ namespace spot
// some states without successors do not declare it as
// colored.
is_colored = colored && (!has_state_acc || nodeadend);
// If the automaton declares that it is deterministic or
// If the automaton declares that it is universal or
// state-based, make sure that it really is.
assert(!aut->prop_deterministic().is_known() ||
deterministic == aut->prop_deterministic().is_true());
assert(!aut->prop_universal().is_known() ||
deterministic == aut->prop_universal().is_true());
assert(!aut->prop_complete().is_known() ||
complete == aut->prop_complete().is_true());
assert(state_acc || !aut->prop_state_acc().is_true());
......
......@@ -50,7 +50,7 @@ namespace spot
break;
}
std::const_pointer_cast<twa_graph>(aut)
->prop_deterministic(!nondet_states);
->prop_universal(!nondet_states);
return nondet_states;
}
}
......@@ -58,29 +58,35 @@ namespace spot
unsigned
count_nondet_states(const const_twa_graph_ptr& aut)
{
if (aut->prop_deterministic())
if (aut->prop_universal())
return 0;
return count_nondet_states_aux<true>(aut);
}
bool
is_deterministic(const const_twa_graph_ptr& aut)
is_universal(const const_twa_graph_ptr& aut)
{
trival d = aut->prop_deterministic();
trival d = aut->prop_universal();
if (d.is_known())
return d.is_true();
return !count_nondet_states_aux<false>(aut);
}
bool
is_deterministic(const const_twa_graph_ptr& aut)
{
return aut->is_existential() && is_universal(aut);
}
void
highlight_nondet_states(twa_graph_ptr& aut, unsigned color)
{
if (aut->prop_deterministic())
if (aut->prop_universal())
return;
unsigned ns = aut->num_states();
auto* highlight = aut->get_or_set_named_prop<std::map<unsigned, unsigned>>
("highlight-states");
bool deterministic = true;
bool universal = true;
for (unsigned src = 0; src < ns; ++src)
{
bdd available = bddtrue;
......@@ -88,25 +94,25 @@ namespace spot
if (!bdd_implies(t.cond, available))
{
(*highlight)[src] = color;
deterministic = false;
universal = false;
}
else
{
available -= t.cond;
}
}
aut->prop_deterministic(deterministic);
aut->prop_universal(universal);
}
void
highlight_nondet_edges(twa_graph_ptr& aut, unsigned color)
{
if (aut->prop_deterministic())
if (aut->prop_universal())
return;
unsigned ns = aut->num_states();
auto* highlight = aut->get_or_set_named_prop<std::map<unsigned, unsigned>>
("highlight-edges");
bool deterministic = true;
bool universal = true;
for (unsigned src = 0; src < ns; ++src)
{
// Make a first pass to gather non-deterministic labels
......@@ -116,19 +122,19 @@ namespace spot
if (!bdd_implies(t.cond, available))
{
extra |= (t.cond - available);
deterministic = false;
universal = false;
}
else
{
available -= t.cond;
}
// Second pass to gather the relevant edges.
if (!deterministic)
if (!universal)
for (auto& t: aut->out(src))
if ((t.cond & extra) != bddfalse)
(*highlight)[aut->get_graph().index_of_edge(t)] = color;
}
aut->prop_deterministic(deterministic);
aut->prop_universal(universal);
}
bool
......
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014, 2015, 2016 Laboratoire de Recherche
// Copyright (C) 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.
......@@ -26,24 +26,33 @@ namespace spot
/// \addtogroup twa_misc
/// @{
/// \brief Count the number of non-deterministic states in \a aut.
/// \brief Count the number of states with non-deterministic
/// branching in \a aut.
///
/// The automaton is deterministic if it has 0 nondeterministic states,
/// but it is more efficient to call is_deterministic() if you do not
/// care about the number of nondeterministic states.
/// The automaton is universal if it has 0 states with
/// non-deterministic branching but it is more efficient to call
/// is_universal() if you do not care about the number of
/// non-deterministic states.
SPOT_API unsigned
count_nondet_states(const const_twa_graph_ptr& aut);
/// \brief Return true iff \a aut is deterministic.
/// \brief Return true iff \a aut is universal.
///
/// This function is more efficient than count_nondet_states() when
/// the automaton is nondeterministic, because it can return before
/// the entire automaton has been explored.
///
/// In addition to returning the result as a Boolean, this will set
/// the prop_deterministic() property of the automaton as a
/// the prop_universal() property of the automaton as a
/// side-effect, so further calls will return in constant-time.
SPOT_API bool
is_universal(const const_twa_graph_ptr& aut);
/// \brief Return true iff \a aut is deterministic.
///
/// An automaton is called deterministic if it is both universal and
/// existential.
SPOT_API bool
is_deterministic(const const_twa_graph_ptr& aut);
/// \brief Highlight nondeterministic states
......
......@@ -487,7 +487,7 @@ namespace spot
build_state_set(det_a, non_final);
auto res = minimize_dfa(det_a, final, non_final);
res->prop_copy(a, { false, false, false, false, true, true });
res->prop_deterministic(true);
res->prop_universal(true);
res->prop_weak(true);
res->prop_state_acc(true);
// Quickly check if this is a terminal automaton
......@@ -596,7 +596,7 @@ namespace spot
auto res = minimize_dfa(det_a, final, non_final);
res->prop_copy(a, { false, false, false, false, false, true });
res->prop_deterministic(true);
res->prop_universal(true);
res->prop_weak(true);
// If the input was terminal, then the output is also terminal.
// FIXME:
......@@ -633,7 +633,7 @@ namespace spot
// If the input automaton was already weak and deterministic, the
// output is necessary correct.