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:
Alexandre Duret-Lutz 2024-03-22 14:41:42 +01:00
parent 03a4f01184
commit 7e228e86ee
6 changed files with 129 additions and 12 deletions

View file

@ -20,6 +20,7 @@
#include <ostream>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <map>
#include <spot/twa/twa.hh>
#include <spot/twa/twagraph.hh>
@ -30,6 +31,7 @@
#include <spot/tl/formula.hh>
#include <spot/kripke/fairkripke.hh>
#include <spot/kripke/kripkegraph.hh>
#include <spot/twaalgos/split.hh>
using namespace std::string_literals;
@ -70,7 +72,8 @@ namespace spot
if (bdd_is_cube(a))
alias_cubes_.emplace_back(a, i);
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))
alias_cubes_.emplace_back(neg, i);
}
@ -464,6 +467,7 @@ namespace spot
bool verbose = false;
bool state_labels = false;
bool v1_1 = false;
bool alias_basis = false;
if (opt)
while (*opt)
@ -486,6 +490,9 @@ namespace spot
v1_1 = false;
}
break;
case 'b':
alias_basis = true;
break;
case 'i':
implicit_labels = true;
break;
@ -520,6 +527,27 @@ namespace spot
throw std::runtime_error("print_hoa(): automaton is declared not weak, "
"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);
if (acceptance == Hoa_Acceptance_States && !md.has_state_acc)
@ -1013,7 +1041,8 @@ namespace spot
}
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())
{
@ -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);
}
}

View file

@ -35,7 +35,8 @@ namespace spot
/// \param os The output stream to print on.
/// \param g The automaton to output.
/// \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,
/// (s) state-based acceptance when possible, (t)
/// transition-based acceptance, (m) mixed acceptance, (l)
@ -62,7 +63,8 @@ namespace spot
///
/// Pass an empty vector to remove existing aliases.
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
/// \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);
}