stats: add options to count unreachable states and transitions

Based on a request from Pierre Ganty.

* spot/twaalgos/stats.cc, spot/twaalgos/stats.hh,
bin/common_aoutput.cc, bin/common_aoutput.hh: Implement those
options.
* tests/core/format.test: Add test case.
* doc/org/autfilt.org: Update doc.
* NEWS: Mention them.
This commit is contained in:
Alexandre Duret-Lutz 2022-10-19 16:30:00 +02:00
parent 52ed3d1e8f
commit de29ba9e4c
7 changed files with 197 additions and 52 deletions

7
NEWS
View file

@ -1,5 +1,12 @@
New in spot 2.11.1.dev (not yet released) New in spot 2.11.1.dev (not yet released)
Command-line tools:
- The --stats specifications %s, %e, %t for printing the number of
(reachable) states, edges, and transitions, learned to support
options [r], [u], [a] to indicate if only reachable, unreachable,
or all elements should be counted.
Library: Library:
- spot::reduce_parity() now has a "layered" option to force all - spot::reduce_parity() now has a "layered" option to force all

View file

@ -203,12 +203,18 @@ static const argp_option io_options[] =
"to specify additional options as in --hoa=opt)", 0 }, "to specify additional options as in --hoa=opt)", 0 },
{ "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"name of the automaton", 0 }, "name of the automaton", 0 },
{ "%S, %s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%S, %s, %[LETTER]S, %[LETTER]s",
"number of reachable states", 0 }, 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
{ "%E, %e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "number of states (add one LETTER to select (r) reachable [default], "
"number of reachable edges", 0 }, "(u) unreachable, (a) all).", 0 },
{ "%T, %t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%E, %e, %[LETTER]E, %[LETTER]e",
"number of reachable transitions", 0 }, 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of edges (add one LETTER to select (r) reachable [default], "
"(u) unreachable, (a) all).", 0 },
{ "%T, %t, %[LETTER]E, %[LETTER]e",
0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of transitions (add one LETTER to select (r) reachable "
"[default], (u) unreachable, (a) all).", 0 },
{ "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of acceptance sets", 0 }, "number of acceptance sets", 0 },
{ "%G, %g, %[LETTERS]G, %[LETTERS]g", 0, nullptr, { "%G, %g, %[LETTERS]G, %[LETTERS]g", 0, nullptr,
@ -268,12 +274,15 @@ static const argp_option o_options[] =
"to specify additional options as in --hoa=opt)", 0 }, "to specify additional options as in --hoa=opt)", 0 },
{ "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"name of the automaton", 0 }, "name of the automaton", 0 },
{ "%s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%s, %[LETTER]s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of reachable states", 0 }, "number of states (add one LETTER to select (r) reachable [default], "
{ "%e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "(u) unreachable, (a) all).", 0 },
"number of reachable edges", 0 }, { "%e, %[LETTER]e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
{ "%t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "number of edges (add one LETTER to select (r) reachable [default], "
"number of reachable transitions", 0 }, "(u) unreachable, (a) all).", 0 },
{ "%t, %[LETTER]t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of transitions (add one LETTER to select (r) reachable "
"[default], (u) unreachable, (a) all).", 0 },
{ "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, { "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of acceptance sets", 0 }, "number of acceptance sets", 0 },
{ "%g, %[LETTERS]g", 0, nullptr, { "%g, %[LETTERS]g", 0, nullptr,
@ -472,15 +481,15 @@ hoa_stat_printer::print(const spot::const_parsed_aut_ptr& haut,
if (has('T')) if (has('T'))
{ {
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut); spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
haut_states_ = s.states; haut_states_.set(s.states, haut->aut->num_states());
haut_edges_ = s.edges; haut_edges_.set(s.edges, haut->aut->num_edges());
haut_trans_ = s.transitions; haut_trans_.set(s.transitions, count_all_transitions(haut->aut));
} }
else if (has('E') || has('S')) else if (has('E') || has('S'))
{ {
spot::twa_statistics s = stats_reachable(haut->aut); spot::twa_statistics s = stats_reachable(haut->aut);
haut_states_ = s.states; haut_states_.set(s.states, haut->aut->num_states());
haut_edges_ = s.edges; haut_edges_.set(s.edges, haut->aut->num_edges());
} }
if (has('M')) if (has('M'))
{ {

View file

@ -166,9 +166,9 @@ private:
spot::printable_value<std::string> aut_word_; spot::printable_value<std::string> aut_word_;
spot::printable_value<std::string> haut_word_; spot::printable_value<std::string> haut_word_;
spot::printable_acc_cond haut_gen_acc_; spot::printable_acc_cond haut_gen_acc_;
spot::printable_value<unsigned> haut_states_; spot::printable_size haut_states_;
spot::printable_value<unsigned> haut_edges_; spot::printable_size haut_edges_;
spot::printable_value<unsigned long long> haut_trans_; spot::printable_long_size haut_trans_;
spot::printable_value<unsigned> haut_acc_; spot::printable_value<unsigned> haut_acc_;
printable_varset haut_ap_; printable_varset haut_ap_;
printable_varset aut_ap_; printable_varset aut_ap_;

View file

@ -145,7 +145,8 @@ ltl2tgba --help | sed -n '/ sequences:/,/^$/p' | sed '1d;$d'
(iw) inherently weak. Use uppercase letters to (iw) inherently weak. Use uppercase letters to
negate them. negate them.
%d 1 if the output is deterministic, 0 otherwise %d 1 if the output is deterministic, 0 otherwise
%e number of reachable edges %e, %[LETTER]e number of edges (add one LETTER to select (r)
reachable [default], (u) unreachable, (a) all).
%f the formula, in Spot's syntax %f the formula, in Spot's syntax
%F name of the input file %F name of the input file
%g, %[LETTERS]g acceptance condition (in HOA syntax); add brackets %g, %[LETTERS]g acceptance condition (in HOA syntax); add brackets
@ -170,8 +171,11 @@ ltl2tgba --help | sed -n '/ sequences:/,/^$/p' | sed '1d;$d'
LETTERS to restrict to(u) user time, (s) system LETTERS to restrict to(u) user time, (s) system
time, (p) parent process, or (c) children time, (p) parent process, or (c) children
processes. processes.
%s number of reachable states %s, %[LETTER]s number of states (add one LETTER to select (r)
%t number of reachable transitions reachable [default], (u) unreachable, (a) all).
%t, %[LETTER]t number of transitions (add one LETTER to select
(r) reachable [default], (u) unreachable, (a)
all).
%u, %[e]u number of states (or [e]dges) with universal %u, %[e]u number of states (or [e]dges) with universal
branching branching
%u, %[LETTER]u 1 if the automaton contains some universal %u, %[LETTER]u 1 if the automaton contains some universal

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2008, 2011-2018, 2020 Laboratoire de Recherche et // Copyright (C) 2008, 2011-2018, 2020, 2022 Laboratoire de Recherche
// Développement de l'Epita (LRDE). // et Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -33,6 +33,16 @@
namespace spot namespace spot
{ {
unsigned long long
count_all_transitions(const const_twa_graph_ptr& g)
{
unsigned long long tr = 0;
bdd v = g->ap_vars();
for (auto& e: g->edges())
tr += bdd_satcountset(e.cond, v);
return tr;
}
namespace namespace
{ {
class stats_bfs: public twa_reachable_iterator_breadth_first class stats_bfs: public twa_reachable_iterator_breadth_first
@ -82,6 +92,7 @@ namespace spot
}; };
template<typename SU, typename EU> template<typename SU, typename EU>
void dfs(const const_twa_graph_ptr& ge, SU state_update, EU edge_update) void dfs(const const_twa_graph_ptr& ge, SU state_update, EU edge_update)
{ {
@ -344,10 +355,73 @@ namespace spot
<< std::string(beg, end + 2) << ", "; << std::string(beg, end + 2) << ", ";
tmp << e.what(); tmp << e.what();
throw std::runtime_error(tmp.str()); throw std::runtime_error(tmp.str());
} }
} }
void printable_size::print(std::ostream& os, const char* pos) const
{
char p = 'r';
if (*pos == '[')
{
p = pos[1];
if (pos[2] != ']' || !(p == 'r' || p == 'u' || p == 'a'))
{
const char* end = strchr(pos + 1, ']');
std::ostringstream tmp;
tmp << "while processing %"
<< std::string(pos, end + 2) << ", "
<< "only [a], [r], or [u] is supported.";
throw std::runtime_error(tmp.str());
}
}
switch (p)
{
case 'r':
os << reachable_;
return;
case 'a':
os << all_;
return;
case 'u':
os << all_ - reachable_;
return;
}
SPOT_UNREACHABLE();
return;
}
void printable_long_size::print(std::ostream& os, const char* pos) const
{
char p = 'r';
if (*pos == '[')
{
p = pos[1];
if (pos[2] != ']' || !(p == 'r' || p == 'u' || p == 'a'))
{
const char* end = strchr(pos + 1, ']');
std::ostringstream tmp;
tmp << "while processing %"
<< std::string(pos, end + 2) << ", "
<< "only [a], [r], or [u] is supported.";
throw std::runtime_error(tmp.str());
}
}
switch (p)
{
case 'r':
os << reachable_;
return;
case 'a':
os << all_;
return;
case 'u':
os << all_ - reachable_;
return;
}
SPOT_UNREACHABLE();
return;
}
stat_printer::stat_printer(std::ostream& os, const char* format) stat_printer::stat_printer(std::ostream& os, const char* format)
: format_(format) : format_(format)
@ -376,15 +450,15 @@ namespace spot
if (has('t')) if (has('t'))
{ {
twa_sub_statistics s = sub_stats_reachable(aut); twa_sub_statistics s = sub_stats_reachable(aut);
states_ = s.states; states_.set(s.states, aut->num_states());
edges_ = s.edges; edges_.set(s.edges, aut->num_edges());
trans_ = s.transitions; trans_.set(s.transitions, count_all_transitions(aut));
} }
else if (has('s') || has('e')) else if (has('s') || has('e'))
{ {
twa_statistics s = stats_reachable(aut); twa_statistics s = stats_reachable(aut);
states_ = s.states; states_.set(s.states, aut->num_states());
edges_ = s.edges; edges_.set(s.edges, aut->num_edges());
} }
if (has('a')) if (has('a'))

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2008, 2011-2017, 2020 Laboratoire de Recherche et // Copyright (C) 2008, 2011-2017, 2020, 2022 Laboratoire de Recherche
// Développement de l'Epita (LRDE). // et Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -55,6 +55,9 @@ namespace spot
/// \brief Compute sub statistics for an automaton. /// \brief Compute sub statistics for an automaton.
SPOT_API twa_sub_statistics sub_stats_reachable(const const_twa_ptr& g); SPOT_API twa_sub_statistics sub_stats_reachable(const const_twa_ptr& g);
/// \brief Count all transtitions, even unreachable ones.
SPOT_API unsigned long long
count_all_transitions(const const_twa_graph_ptr& g);
class SPOT_API printable_formula: public printable_value<formula> class SPOT_API printable_formula: public printable_value<formula>
{ {
@ -102,6 +105,36 @@ namespace spot
void print(std::ostream& os, const char* pos) const override; void print(std::ostream& os, const char* pos) const override;
}; };
class SPOT_API printable_size final:
public spot::printable
{
unsigned reachable_ = 0;
unsigned all_ = 0;
public:
void set(unsigned reachable, unsigned all)
{
reachable_ = reachable;
all_ = all;
}
void print(std::ostream& os, const char* pos) const override;
};
class SPOT_API printable_long_size final:
public spot::printable
{
unsigned long long reachable_ = 0;
unsigned long long all_ = 0;
public:
void set(unsigned long long reachable, unsigned long long all)
{
reachable_ = reachable;
all_ = all;
}
void print(std::ostream& os, const char* pos) const override;
};
/// \brief prints various statistics about a TGBA /// \brief prints various statistics about a TGBA
/// ///
/// This object can be configured to display various statistics /// This object can be configured to display various statistics
@ -123,9 +156,9 @@ namespace spot
const char* format_; const char* format_;
printable_formula form_; printable_formula form_;
printable_value<unsigned> states_; printable_size states_;
printable_value<unsigned> edges_; printable_size edges_;
printable_value<unsigned long long> trans_; printable_long_size trans_;
printable_value<unsigned> acc_; printable_value<unsigned> acc_;
printable_scc_info scc_; printable_scc_info scc_;
printable_value<unsigned> nondetstates_; printable_value<unsigned> nondetstates_;

View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2016, 2017 Laboratoire de Recherche et Développement de # Copyright (C) 2016, 2017, 2022 Laboratoire de Recherche et
# l'Epita (LRDE). # Développement de l'Epita (LRDE).
# #
# This file is part of Spot, a model checking library. # This file is part of Spot, a model checking library.
# #
@ -139,18 +139,36 @@ test 3,5 = `ltl2tgba --low --any --stats=%s,%e "$f"`
test 3,4 = `ltl2tgba --stats=%s,%e "$f"` test 3,4 = `ltl2tgba --stats=%s,%e "$f"`
cat >foo <<EOF cat >foo <<EOF
HOA: v1 HOA: v1 States: 7 Start: 5 AP: 1 "a" acc-name: Buchi
States: 3 Acceptance: 1 Inf(0) properties: trans-labels explicit-labels
Start: 0 state-acc deterministic properties: terminal --BODY-- State: 0 [0] 6
AP: 0 State: 1 [t] 0 State: 2 [t] 1 State: 3 [f] 2 State: 4 [t] 3 State: 5
Acceptance: 0 t [t] 4 State: 6 {0} [t] 6 --END--
--BODY--
State: 0
[t] 1
State: 1
[t] 0
State: 2
[t] 2
--END--
EOF EOF
test 2,2 = `autfilt --stats=%S,%s foo` (
autfilt --stats='%s,%[r]s,%[u]s,%[a]s' foo;
autfilt --remove-dead --stats='%s,%[r]s,%[u]s,%[a]s' foo;
autfilt --remove-dead --stats='%S,%[r]S,%[u]S,%[a]S' foo;
autfilt --stats='%e,%[r]e,%[u]e,%[a]e' foo;
autfilt --remove-dead --stats='%e,%[r]e,%[u]e,%[a]e' foo;
autfilt --remove-dead --stats='%E,%[r]E,%[u]E,%[a]E' foo;
autfilt --stats='%t,%[r]t,%[u]t,%[a]t' foo;
autfilt --remove-dead --stats='%t,%[r]t,%[u]t,%[a]t' foo;
autfilt --remove-dead --stats='%T,%[r]T,%[u]T,%[a]T' foo;
) > stats
cat >expected <<EOF
3,3,4,7
1,1,0,1
3,3,4,7
2,2,4,6
0,0,0,0
2,2,4,6
4,4,7,11
0,0,0,0
4,4,7,11
EOF
diff stats expected
autfilt --stats='%[x]T' foo 2>err && exit 1
grep 'only \[a\], \[r\], or \[u\] is supported' err