hoa: add option 'b' to build an alias-based basis for all labels
Related to issue #563. * spot/twaalgos/hoa.hh (create_alias_basis): New function. * spot/twaalgos/hoa.cc (create_alias_basis): New function. (print_hoa): Add support for option 'b' and create_alias_basis in this case. * bin/common_aoutput.cc, NEWS: Document -Hb. * tests/core/readsave.test, tests/python/aliases.py: Add test cases.
This commit is contained in:
parent
03a4f01184
commit
7e228e86ee
6 changed files with 129 additions and 12 deletions
7
NEWS
7
NEWS
|
|
@ -12,6 +12,13 @@ New in spot 2.11.6.dev (not yet released)
|
||||||
|
|
||||||
autfilt input.hoa -o output-%l.hoa
|
autfilt input.hoa -o output-%l.hoa
|
||||||
|
|
||||||
|
- For tools that produce automata, using -Hb or --hoa=b will produce
|
||||||
|
an HOA file in which aliases are used to form a basis for the
|
||||||
|
whole set of labels. Those aliases are only used when more than
|
||||||
|
one atomic proposition is used (otherwise, the atomic proposition
|
||||||
|
and its negation is already a basis). This can help reducing the
|
||||||
|
size of large HOA files.
|
||||||
|
|
||||||
- ltlfilt has a new option --relabel-overlapping-bool=abc|pnn that
|
- ltlfilt has a new option --relabel-overlapping-bool=abc|pnn that
|
||||||
will replace boolean subformulas by fresh atomic propositions even
|
will replace boolean subformulas by fresh atomic propositions even
|
||||||
if those subformulas share atomic propositions.
|
if those subformulas share atomic propositions.
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ static const argp_option options[] =
|
||||||
{ "hoaf", 'H', "1.1|i|k|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
{ "hoaf", 'H', "1.1|i|k|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
||||||
"Output the automaton in HOA format (default). Add letters to select "
|
"Output the automaton in HOA format (default). Add letters to select "
|
||||||
"(1.1) version 1.1 of the format, "
|
"(1.1) version 1.1 of the format, "
|
||||||
|
"(b) create an alias basis if >=2 AP are used, "
|
||||||
"(i) use implicit labels for complete deterministic automata, "
|
"(i) use implicit labels for complete deterministic automata, "
|
||||||
"(s) prefer state-based acceptance when possible [default], "
|
"(s) prefer state-based acceptance when possible [default], "
|
||||||
"(t) force transition-based acceptance, "
|
"(t) force transition-based acceptance, "
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <spot/twa/twa.hh>
|
#include <spot/twa/twa.hh>
|
||||||
#include <spot/twa/twagraph.hh>
|
#include <spot/twa/twagraph.hh>
|
||||||
|
|
@ -30,6 +31,7 @@
|
||||||
#include <spot/tl/formula.hh>
|
#include <spot/tl/formula.hh>
|
||||||
#include <spot/kripke/fairkripke.hh>
|
#include <spot/kripke/fairkripke.hh>
|
||||||
#include <spot/kripke/kripkegraph.hh>
|
#include <spot/kripke/kripkegraph.hh>
|
||||||
|
#include <spot/twaalgos/split.hh>
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
|
@ -70,7 +72,8 @@ namespace spot
|
||||||
if (bdd_is_cube(a))
|
if (bdd_is_cube(a))
|
||||||
alias_cubes_.emplace_back(a, i);
|
alias_cubes_.emplace_back(a, i);
|
||||||
bdd neg = !a;
|
bdd neg = !a;
|
||||||
aliases_map_[neg.id()] = i;
|
// do not overwrite an existing alias with a negation
|
||||||
|
aliases_map_.emplace(neg.id(), i);
|
||||||
if (bdd_is_cube(neg))
|
if (bdd_is_cube(neg))
|
||||||
alias_cubes_.emplace_back(neg, i);
|
alias_cubes_.emplace_back(neg, i);
|
||||||
}
|
}
|
||||||
|
|
@ -464,6 +467,7 @@ namespace spot
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool state_labels = false;
|
bool state_labels = false;
|
||||||
bool v1_1 = false;
|
bool v1_1 = false;
|
||||||
|
bool alias_basis = false;
|
||||||
|
|
||||||
if (opt)
|
if (opt)
|
||||||
while (*opt)
|
while (*opt)
|
||||||
|
|
@ -486,6 +490,9 @@ namespace spot
|
||||||
v1_1 = false;
|
v1_1 = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
alias_basis = true;
|
||||||
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
implicit_labels = true;
|
implicit_labels = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -520,6 +527,27 @@ namespace spot
|
||||||
throw std::runtime_error("print_hoa(): automaton is declared not weak, "
|
throw std::runtime_error("print_hoa(): automaton is declared not weak, "
|
||||||
"but the acceptance makes this impossible");
|
"but the acceptance makes this impossible");
|
||||||
|
|
||||||
|
// If we were asked to create an alias basis, make sure we save
|
||||||
|
// existing aliases, so we can restore it before we exit this
|
||||||
|
// function.
|
||||||
|
std::vector<std::pair<std::string, bdd>> old_aliases;
|
||||||
|
if (aut->ap().size() <= 1)
|
||||||
|
alias_basis = false;
|
||||||
|
if (alias_basis)
|
||||||
|
{
|
||||||
|
if (auto* aliases = get_aliases(aut))
|
||||||
|
old_aliases = *aliases;
|
||||||
|
create_alias_basis(std::const_pointer_cast<twa_graph>(aut));
|
||||||
|
}
|
||||||
|
// restore the old aliases using a unique_ptr-based scope guard,
|
||||||
|
// because there are too many ways to exit this function.
|
||||||
|
auto restore_aliases = [&old_aliases, alias_basis, aut](void*) {
|
||||||
|
if (alias_basis)
|
||||||
|
set_aliases(std::const_pointer_cast<twa_graph>(aut), old_aliases);
|
||||||
|
};
|
||||||
|
std::unique_ptr<void, decltype(restore_aliases)>
|
||||||
|
restore_aliases_guard((void*)1, restore_aliases);
|
||||||
|
|
||||||
metadata md(aut, implicit_labels, state_labels);
|
metadata md(aut, implicit_labels, state_labels);
|
||||||
|
|
||||||
if (acceptance == Hoa_Acceptance_States && !md.has_state_acc)
|
if (acceptance == Hoa_Acceptance_States && !md.has_state_acc)
|
||||||
|
|
@ -1013,7 +1041,8 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
set_aliases(twa_ptr g, std::vector<std::pair<std::string, bdd>> aliases)
|
set_aliases(twa_ptr g,
|
||||||
|
const std::vector<std::pair<std::string, bdd>>& aliases)
|
||||||
{
|
{
|
||||||
if (aliases.empty())
|
if (aliases.empty())
|
||||||
{
|
{
|
||||||
|
|
@ -1027,4 +1056,17 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
create_alias_basis(const twa_graph_ptr& aut)
|
||||||
|
{
|
||||||
|
edge_separator es;
|
||||||
|
es.add_to_basis(aut);
|
||||||
|
std::vector<std::pair<std::string, bdd>> aliases;
|
||||||
|
unsigned n = 0;
|
||||||
|
for (bdd b: es.basis())
|
||||||
|
aliases.emplace_back(std::to_string(n++), b);
|
||||||
|
std::reverse(aliases.begin(), aliases.end());
|
||||||
|
set_aliases(aut, aliases);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ namespace spot
|
||||||
/// \param os The output stream to print on.
|
/// \param os The output stream to print on.
|
||||||
/// \param g The automaton to output.
|
/// \param g The automaton to output.
|
||||||
/// \param opt a set of characters each corresponding to a possible
|
/// \param opt a set of characters each corresponding to a possible
|
||||||
/// option: (i) implicit labels for complete and
|
/// option: (b) create an alias basis if more >=2 AP
|
||||||
|
/// are used, (i) implicit labels for complete and
|
||||||
/// deterministic automata, (k) state labels when possible,
|
/// deterministic automata, (k) state labels when possible,
|
||||||
/// (s) state-based acceptance when possible, (t)
|
/// (s) state-based acceptance when possible, (t)
|
||||||
/// transition-based acceptance, (m) mixed acceptance, (l)
|
/// transition-based acceptance, (m) mixed acceptance, (l)
|
||||||
|
|
@ -62,7 +63,8 @@ namespace spot
|
||||||
///
|
///
|
||||||
/// Pass an empty vector to remove existing aliases.
|
/// Pass an empty vector to remove existing aliases.
|
||||||
SPOT_API void
|
SPOT_API void
|
||||||
set_aliases(twa_ptr g, std::vector<std::pair<std::string, bdd>> aliases);
|
set_aliases(twa_ptr g,
|
||||||
|
const std::vector<std::pair<std::string, bdd>>& aliases);
|
||||||
|
|
||||||
/// \ingroup twa_io
|
/// \ingroup twa_io
|
||||||
/// \brief Help printing BDDs as text, using aliases.
|
/// \brief Help printing BDDs as text, using aliases.
|
||||||
|
|
@ -164,4 +166,18 @@ namespace spot
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \ingroup twa_io
|
||||||
|
/// \brief Create an alias basis
|
||||||
|
///
|
||||||
|
/// This use spot::edge_separator to build a set of alias that can
|
||||||
|
/// be used as a basis for all labels of the automaton.
|
||||||
|
///
|
||||||
|
/// Such a basis can be used to shorten the size of an output file
|
||||||
|
/// when printing in HOA format (actually, calling print_hoa() with
|
||||||
|
/// option 'b' will call this function). Such a basis may also be
|
||||||
|
/// useful to help visualize an automaton (using spot::print_dot's
|
||||||
|
/// `@` option) when its labels are too large.
|
||||||
|
SPOT_API void
|
||||||
|
create_alias_basis(const twa_graph_ptr& aut);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -685,7 +685,7 @@ EOF
|
||||||
|
|
||||||
diff output2 expect2
|
diff output2 expect2
|
||||||
|
|
||||||
SPOT_DEFAULT_FORMAT=hoa=k autfilt expect2 >output2b
|
SPOT_DEFAULT_FORMAT=hoa=kb autfilt expect2 >output2b
|
||||||
|
|
||||||
cat >expect2b <<EOF
|
cat >expect2b <<EOF
|
||||||
HOA: v1
|
HOA: v1
|
||||||
|
|
@ -695,12 +695,15 @@ AP: 2 "p0" "p1"
|
||||||
acc-name: all
|
acc-name: all
|
||||||
Acceptance: 0 t
|
Acceptance: 0 t
|
||||||
properties: state-labels explicit-labels state-acc deterministic weak
|
properties: state-labels explicit-labels state-acc deterministic weak
|
||||||
|
Alias: @0 !0&1 | 0&!1
|
||||||
|
Alias: @1 !0&!1
|
||||||
|
Alias: @2 !@1&!@0
|
||||||
--BODY--
|
--BODY--
|
||||||
State: [!0&!1] 0
|
State: [@1] 0
|
||||||
1
|
1
|
||||||
State: [0&1] 1
|
State: [@2] 1
|
||||||
2
|
2
|
||||||
State: [!0&!1] 2
|
State: [@1] 2
|
||||||
0
|
0
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -732,7 +735,7 @@ State: 5
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
autfilt -H --remove-unreach input >output3
|
autfilt -H --remove-unreach input >output3
|
||||||
autfilt -H --remove-dead input >>output3
|
autfilt -Hb --remove-dead input >>output3
|
||||||
|
|
||||||
cat >expect3 <<EOF
|
cat >expect3 <<EOF
|
||||||
HOA: v1
|
HOA: v1
|
||||||
|
|
@ -759,13 +762,16 @@ AP: 2 "p0" "p1"
|
||||||
acc-name: all
|
acc-name: all
|
||||||
Acceptance: 0 t
|
Acceptance: 0 t
|
||||||
properties: trans-labels explicit-labels state-acc deterministic weak
|
properties: trans-labels explicit-labels state-acc deterministic weak
|
||||||
|
Alias: @0 !0&1 | 0&!1
|
||||||
|
Alias: @1 !0&!1
|
||||||
|
Alias: @2 !@1&!@0
|
||||||
--BODY--
|
--BODY--
|
||||||
State: 0
|
State: 0
|
||||||
[!0&!1] 1
|
[@1] 1
|
||||||
State: 1
|
State: 1
|
||||||
[0&1] 2
|
[@2] 2
|
||||||
State: 2
|
State: 2
|
||||||
[!0&!1] 0
|
[@1] 0
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,51 @@ State: 0
|
||||||
[@p0&2 | @p1&2] 0
|
[@p0&2 | @p1&2] 0
|
||||||
--END--""")
|
--END--""")
|
||||||
|
|
||||||
|
s2b = aut.to_str('hoa', 'b')
|
||||||
|
tc.assertTrue(spot.are_equivalent(aut, spot.automaton(s2b)))
|
||||||
|
tc.assertEqual(s2b, """HOA: v1
|
||||||
|
States: 1
|
||||||
|
Start: 0
|
||||||
|
AP: 3 "x" "y" "z"
|
||||||
|
acc-name: all
|
||||||
|
Acceptance: 0 t
|
||||||
|
properties: trans-labels explicit-labels state-acc complete very-weak
|
||||||
|
Alias: @0 !0&!1&!2
|
||||||
|
Alias: @1 0&!1&!2
|
||||||
|
Alias: @2 !0&1&!2
|
||||||
|
Alias: @3 0&1&!2
|
||||||
|
Alias: @4 !0&!1&2
|
||||||
|
Alias: @5 0&1&2
|
||||||
|
Alias: @6 0&!1&2
|
||||||
|
Alias: @7 !@6&!@5&!@4&!@3&!@2&!@1&!@0
|
||||||
|
--BODY--
|
||||||
|
State: 0
|
||||||
|
[@6 | @5 | @3 | @1] 0
|
||||||
|
[@7 | @4 | @2 | @0] 0
|
||||||
|
[@7 | @5 | @3 | @2] 0
|
||||||
|
[@6 | @4 | @1 | @0] 0
|
||||||
|
[@4 | @0] 0
|
||||||
|
[@7 | @6 | @5 | @3 | @2 | @1] 0
|
||||||
|
[@7 | @2] 0
|
||||||
|
[@6 | @5 | @4 | @3 | @1 | @0] 0
|
||||||
|
[@6 | @1] 0
|
||||||
|
[@7 | @5 | @4 | @3 | @2 | @0] 0
|
||||||
|
[@7 | @4 | @2 | @0] 0
|
||||||
|
[@6 | @4 | @1 | @0] 0
|
||||||
|
[@7 | @6 | @2 | @1] 0
|
||||||
|
[@7 | @6 | @4 | @2 | @1 | @0] 0
|
||||||
|
[t] 0
|
||||||
|
[t] 0
|
||||||
|
[t] 0
|
||||||
|
[@5 | @3] 0
|
||||||
|
[@6 | @5 | @3 | @1] 0
|
||||||
|
[@7 | @5 | @3 | @2] 0
|
||||||
|
[@5 | @4 | @3 | @0] 0
|
||||||
|
[@5 | @4] 0
|
||||||
|
[@4] 0
|
||||||
|
[@7 | @6 | @5] 0
|
||||||
|
--END--""")
|
||||||
|
|
||||||
# Check what happens to aliases when an AP has been removed, but
|
# Check what happens to aliases when an AP has been removed, but
|
||||||
# the aliases have been preserved...
|
# the aliases have been preserved...
|
||||||
rem = spot.remove_ap()
|
rem = spot.remove_ap()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue