From 64b27a9a26920c65e3f252c1a70012fc9b2aa6b3 Mon Sep 17 00:00:00 2001 From: Alexandre Lewkowicz Date: Sat, 30 May 2015 12:41:35 +0200 Subject: [PATCH] safra: Add pretty printer for states * src/tests/safra.cc, src/tests/safra.test: Add options and test. * src/twaalgos/safra.cc, src/twaalgos/safra.hh: Here. --- src/tests/safra.cc | 62 +++++++++++++++++--- src/tests/safra.test | 21 +++++++ src/twaalgos/safra.cc | 132 ++++++++++++++++++++++++++++++++++++------ src/twaalgos/safra.hh | 10 ++-- 4 files changed, 194 insertions(+), 31 deletions(-) diff --git a/src/tests/safra.cc b/src/tests/safra.cc index 6faf8b9f1..0e13ab9c2 100644 --- a/src/tests/safra.cc +++ b/src/tests/safra.cc @@ -30,14 +30,58 @@ #include "twaalgos/degen.hh" +int help() +{ + std::cerr << "safra [OPTIONS]\n"; + std::cerr << "\t-f ltl_formula\tinput string is an ltl formulae\n"; + std::cerr << "\t--hoa file.hoa\tinput file has hoa format\n"; + std::cerr << "\t-p\tpretty print states\n"; + std::cerr << "\t-H\toutput hoa format\n"; + return 1; +} + int main(int argc, char* argv[]) { + bool in_hoa = false; + bool in_ltl = false; + bool out_dot = true; + bool out_hoa = false; + bool pretty_print = false; + + char* input = nullptr; if (argc <= 2) - return 1; - char* input = argv[2]; + return help(); + for (int i = 1; i < argc; ++i) + { + if (!strncmp(argv[i], "--hoa", 5)) + { + in_hoa = true; + if (i + 1 >= argc) + return help(); + input = argv[++i]; + } + else if (!strncmp(argv[i], "-f", 2)) + { + in_ltl = true; + if (i + 1 >= argc) + return help(); + input = argv[++i]; + } + else if (!strncmp(argv[i], "-H", 2)) + { + out_dot = false; + out_hoa = true; + } + else if (!strncmp(argv[i], "-p", 2)) + pretty_print = true; + } + + if (!input) + help(); + auto dict = spot::make_bdd_dict(); spot::twa_graph_ptr res; - if (!strncmp(argv[1], "-f", 2)) + if (in_ltl) { spot::ltl::parse_error_list pel; const spot::ltl::formula* f = @@ -48,24 +92,26 @@ int main(int argc, char* argv[]) spot::translator trans(dict); trans.set_pref(spot::postprocessor::Deterministic); auto tmp = trans.run(f); - res = spot::tgba_determinisation(tmp); + res = spot::tgba_determinisation(tmp, pretty_print); f->destroy(); } - else if (!strncmp(argv[1], "--hoa", 5)) + else if (in_hoa) { spot::hoa_parse_error_list pel; auto aut = spot::hoa_parse(input, pel, dict); if (spot::format_hoa_parse_errors(std::cerr, input, pel)) return 2; - res = tgba_determinisation(aut->aut); + res = tgba_determinisation(aut->aut, pretty_print); } res->merge_transitions(); - if (argc >= 4 && !strncmp(argv[3], "-H", 2)) + if (out_hoa) { spot::hoa_reachable(std::cout, res, "t"); std::cout << std::endl; } - else + else if (out_dot) spot::dotty_reachable(std::cout, res); + else + assert(false); } diff --git a/src/tests/safra.test b/src/tests/safra.test index 5aeb2a2f7..9b051b68e 100755 --- a/src/tests/safra.test +++ b/src/tests/safra.test @@ -119,3 +119,24 @@ EOF run 0 ../safra --hoa double_b.hoa -H > out.hoa diff out.hoa out.exp + +# Formulas from bench/dtgbasat/formulas +cat >formulae < out.hoa +ltl2tgba=../../bin/ltl2tgba +../../bin/ltlcross -F formulae \ + "../safra -f %f -H > %O" \ + "$ltl2tgba -f %f -H > %O" diff --git a/src/twaalgos/safra.cc b/src/twaalgos/safra.cc index 95d72f89f..c1eafbbbd 100644 --- a/src/twaalgos/safra.cc +++ b/src/twaalgos/safra.cc @@ -17,6 +17,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include #include #include @@ -28,6 +29,97 @@ namespace spot { namespace { + using power_set = std::map; + const char* const sub[10] = + { + "\u2080", + "\u2081", + "\u2082", + "\u2083", + "\u2084", + "\u2085", + "\u2086", + "\u2087", + "\u2088", + "\u2089", + }; + + std::string subscript(unsigned start) + { + std::string res; + do + { + res = sub[start % 10] + res; + start /= 10; + } + while (start); + return res; + } + + void print(unsigned start, const safra_state::nodes_t& nodes, + std::ostringstream& os, std::vector& idx) + { + std::string s = subscript(start); + os << '[' << s; + std::vector new_idx; + std::vector todo; + unsigned next = -1U; + bool first = true; + for (auto& i: idx) + { + auto it = std::lower_bound(nodes.at(i).cbegin(), nodes.at(i).cend(), + start + 1); + if (it == nodes.at(i).cend()) + { + if (first) + { + os << i; + first = false; + } + else + os << ' ' << i; + } + else if (*it == (start + 1)) + new_idx.push_back(i); + else + { + todo.push_back(i); + next = std::min(next, *it); + } + } + if (!new_idx.empty()) + print(start + 1, nodes, os, new_idx); + if (next != -1U) + { + std::vector todo2; + std::vector todo_next; + unsigned new_next = -1U; + while (!todo.empty()) + { + for (auto& i: todo) + { + auto it = std::lower_bound(nodes.at(i).cbegin(), + nodes.at(i).cend(), next); + if (*it == next) + todo_next.push_back(i); + else + { + todo2.push_back(i); + next = std::min(new_next, *it); + } + } + print(next, nodes, os, todo_next); + + next = new_next; + new_next = -1; + todo = todo2; + todo2.clear(); + todo_next.clear(); + } + } + os << s << ']'; + } + // Returns true if lhs has a smaller nesting pattern than rhs // If lhs and rhs are the same, return false. bool nesting_cmp(const std::vector& lhs, @@ -53,6 +145,25 @@ namespace spot t.acc &= mask; } } + + std::vector* + print_debug(std::map& states) + { + std::vector* res = nullptr; + res = new std::vector(states.size()); + std::vector idx; + for (auto& p: states) + { + std::ostringstream os; + for (auto& n: p.first.nodes_) + idx.push_back(n.first); + print(0, p.first.nodes_, os, idx); + (*res)[p.second] = os.str(); + idx.clear(); + } + return res; + } + } auto safra_state::compute_succs(const const_twa_graph_ptr& aut, @@ -255,23 +366,8 @@ namespace spot return nodes_ < other.nodes_; } - void safra_state::print_debug(unsigned state_id) const - { - std::cerr << "State: " << state_id << "{ "; - for (auto& n: nodes_) - { - std::cerr << n.first << " ["; - for (auto& b: n.second) - { - std::cerr << b << ' '; - } - std::cerr << "], "; - } - std::cerr << "}\n"; - } - twa_graph_ptr - tgba_determinisation(const const_twa_graph_ptr& a) + tgba_determinisation(const const_twa_graph_ptr& a, bool pretty_print) { // Degeneralize const_twa_graph_ptr aut; @@ -331,7 +427,6 @@ namespace spot // Given a safra_state get its associated state in output automata. // Required to create new transitions from 2 safra-state - typedef std::map power_set; power_set seen; safra_state init(aut->get_init_state_number(), true); unsigned num = res->new_state(); @@ -374,11 +469,12 @@ namespace spot res->new_transition(src_num, dst_num, num2bdd[s.second]); } } - // remove_dead_acc(res, sets); res->set_acceptance(sets, acc_cond::acc_code::parity(false, false, sets)); res->prop_deterministic(true); res->prop_state_based_acc(false); + if (pretty_print) + res->set_named_prop("state-names", print_debug(seen)); return res; } } diff --git a/src/twaalgos/safra.hh b/src/twaalgos/safra.hh index 09078a22c..c13f35275 100644 --- a/src/twaalgos/safra.hh +++ b/src/twaalgos/safra.hh @@ -40,10 +40,9 @@ namespace spot class safra_state { public: - typedef std::vector> succs_t; + using nodes_t = std::map>; + using succs_t = std::vector>; bool operator<(const safra_state& other) const; - // Print each sub-state with their associated braces of a safra state - void print_debug(unsigned state_id) const; // Printh the number of states in each brace safra_state(unsigned state_number, bool init_state = false); // Given a certain transition_label, compute all the successors of that @@ -63,7 +62,7 @@ namespace spot // A list of nodes similar to the ones of a // safra tree. These are constructed in the same way as the powerset // algorithm. - std::map> nodes_; + nodes_t nodes_; // A counter that indicates the nomber of states within a brace. // This enables us to compute the red value std::vector nb_braces_; @@ -73,5 +72,6 @@ namespace spot }; SPOT_API twa_graph_ptr - tgba_determinisation(const const_twa_graph_ptr& aut); + tgba_determinisation(const const_twa_graph_ptr& aut, + bool pretty_print = false); }