diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index b7e44a200..c7b2a17d5 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -389,37 +391,12 @@ namespace spot bool will_use_labels = n_ap > 5; if (will_use_labels) { - std::set all_labels; + edge_separator es; // Gather all labels, but stop if we see too many. // The threshold below is arbitrary. - unsigned max_labels = 100 * n_ap; - for (auto& e: aut_->edges()) - { - if (all_labels.insert(e.cond).second) - if (all_labels.size() > max_labels) - { - will_use_labels = false; - break; - } - } + will_use_labels = es.add_to_basis(aut_, 256 * n_ap); if (will_use_labels) - { - separated_labels.reserve(all_labels.size()); - separated_labels.push_back(bddtrue); - for (auto& lab: all_labels) - { - // Do not use a range-based or iterator-based for loop - // here, as push_back invalidates the end iterator. - for (unsigned cur = 0, sz = separated_labels.size(); - cur < sz; ++cur) - if (bdd common = separated_labels[cur] & lab; - common != bddfalse && common != separated_labels[cur]) - { - separated_labels[cur] -= lab; - separated_labels.push_back(common); - } - } - } + separated_labels = es.basis(); } // We for easier computation of outgoing sets, we will @@ -490,6 +467,19 @@ namespace spot acc_cond::mark_t all_marks = res->acc().all_sets(); + // We use a cache to avoid the costly loop around + // separated_labels. + // + // Cache entries have the form (bdd, [begin, end]) where bdd + // what should be split, and begin/end denotes a range of + // existing transition numbers that cover the split. + // + // std::pair causes some noexcept warnings when used in + // robin_hood::unordered_map with GCC 9.4. Use robin_hood::pair + // instead. + typedef robin_hood::pair cached_t; + robin_hood::unordered_map split_cond; + state_set v; while (!todo.empty()) { @@ -547,12 +537,41 @@ namespace spot }; if (!will_use_labels) - // Loop over all possible valuations atomic properties. - for (bdd oneletter: minterms_of(all_letters, ap)) - create_edges(oneletter, bdd_restrict(bs, oneletter)); + { + // Loop over all possible valuations of atomic properties. + for (bdd oneletter: minterms_of(all_letters, ap)) + create_edges(oneletter, bdd_restrict(bs, oneletter)); + } else - for (bdd label: separated_labels) - create_edges(label, bdd_relprod(label, bs, res->ap_vars())); + { + auto& [begin, end] = split_cond[all_letters]; + if (begin == end) + { + begin = res->num_edges() + 1; + for (bdd label: separated_labels) + create_edges(label, bdd_relprod(label, bs, + res->ap_vars())); + end = res->num_edges() + 1; + } + else + { + // We have already split all_letters once, so we + // can simply reuse the set of labels we used + // then, avoiding the iteration on + // separated_labels. + auto& g = res->get_graph(); + bdd last = bddfalse; + for (unsigned i = begin; i < end; ++i) + { + bdd label = g.edge_storage(i).cond; + if (label == last) + continue; + last = label; + create_edges(label, bdd_relprod(label, bs, + res->ap_vars())); + } + } + } } res->merge_edges(); return res; diff --git a/spot/twaalgos/split.cc b/spot/twaalgos/split.cc index a4d973d58..93d08589d 100644 --- a/spot/twaalgos/split.cc +++ b/spot/twaalgos/split.cc @@ -118,6 +118,23 @@ namespace spot add_to_basis(lab); } + bool edge_separator::add_to_basis(const const_twa_graph_ptr& aut, + unsigned long max_label) + { + std::set all_labels; + for (auto& e: aut->edges()) + { + if (all_labels.insert(e.cond).second) + if (max_label-- == 0) + return false; + } + for (bdd lab: all_labels) + add_to_basis(lab); + for (formula f: aut->ap()) + aps_.insert(f); + return true; + } + twa_graph_ptr edge_separator::separate_implying(const const_twa_graph_ptr& aut) { diff --git a/spot/twaalgos/split.hh b/spot/twaalgos/split.hh index a30d8645e..cb66619f1 100644 --- a/spot/twaalgos/split.hh +++ b/spot/twaalgos/split.hh @@ -156,9 +156,15 @@ namespace spot /// add_to_basis() and add a new atomic proposition, you should /// remember to register it in the result of separate_implying() /// or separate_compat() yourself. + /// + /// If \a max_label is given, at most \a max_label unique labels + /// are added to the basis. False is returned iff the automaton + /// used more labels. /// @{ void add_to_basis(bdd label); void add_to_basis(const const_twa_graph_ptr& aut); + bool add_to_basis(const const_twa_graph_ptr& aut, + unsigned long max_label); /// @} /// \brief Separate an automaton /// @@ -200,10 +206,17 @@ namespace spot return {basis_, label}; } #endif + unsigned basis_size() const { return basis_.size(); } + + const std::vector& basis() const + { + return basis_; + } + private: std::vector basis_{bddtrue}; std::set aps_;