alternation: speed up remove_alternation when few labels are used
Related to issue #566. * spot/twaalgos/alternation.cc (alternation_remover::run): Here. * tests/core/566.test: Augment test case. * NEWS: Mention the change.
This commit is contained in:
parent
1e512d422b
commit
f26f3243dd
3 changed files with 102 additions and 38 deletions
5
NEWS
5
NEWS
|
|
@ -150,7 +150,10 @@ New in spot 2.11.6.dev (not yet released)
|
||||||
|
|
||||||
- spot::dualize() learned a trick to be faster on states that have
|
- spot::dualize() learned a trick to be faster on states that have
|
||||||
less outgoing edges than atomic proposition declared on the
|
less outgoing edges than atomic proposition declared on the
|
||||||
automaton. (Issue #566.)
|
automaton. spot::remove_alternation() learned a similar trick,
|
||||||
|
except it isn't applied at the state level but of the entire
|
||||||
|
alternating use few distinct labels. These two changes speed up
|
||||||
|
the complementation of very weak automata. (Issue #566.)
|
||||||
|
|
||||||
- [Potential backward incompatibility] spot::dualize() does not call
|
- [Potential backward incompatibility] spot::dualize() does not call
|
||||||
cleanup_acceptance() anymore. This change ensures that the dual
|
cleanup_acceptance() anymore. This change ensures that the dual
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <spot/twaalgos/alternation.hh>
|
#include <spot/twaalgos/alternation.hh>
|
||||||
#include <spot/twaalgos/sccinfo.hh>
|
#include <spot/twaalgos/sccinfo.hh>
|
||||||
#include <spot/misc/minato.hh>
|
#include <spot/misc/minato.hh>
|
||||||
|
#include <spot/misc/bddlt.hh>
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
@ -361,7 +362,7 @@ namespace spot
|
||||||
(has_reject_more_ + reject_1_count_) > SPOT_MAX_ACCSETS)
|
(has_reject_more_ + reject_1_count_) > SPOT_MAX_ACCSETS)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Rejecting SCCs of size 1 can be handled using genralized
|
// Rejecting SCCs of size 1 can be handled using generalized
|
||||||
// Büchi acceptance, using one set per SCC, as in Gastin &
|
// Büchi acceptance, using one set per SCC, as in Gastin &
|
||||||
// Oddoux CAV'01. See also Boker & et al. ICALP'10. Larger
|
// Oddoux CAV'01. See also Boker & et al. ICALP'10. Larger
|
||||||
// rejecting SCCs require a more expensive procedure known as
|
// rejecting SCCs require a more expensive procedure known as
|
||||||
|
|
@ -375,6 +376,54 @@ namespace spot
|
||||||
// This will raise an exception if we request too many sets.
|
// This will raise an exception if we request too many sets.
|
||||||
res->set_generalized_buchi(has_reject_more_ + reject_1_count_);
|
res->set_generalized_buchi(has_reject_more_ + reject_1_count_);
|
||||||
|
|
||||||
|
// Before we start, let's decide how we will iterate on the
|
||||||
|
// BDD that we use to encode the transition function. We have
|
||||||
|
// two way of doing so: iterating over 2^AP, or precomputing a
|
||||||
|
// set of "separated labels" that cover all labels. The
|
||||||
|
// latter is a good idea if that set is much smaller than
|
||||||
|
// 2^AP, but we cannot always know that before hand.
|
||||||
|
std::vector<bdd> separated_labels;
|
||||||
|
unsigned n_ap = aut_->ap().size();
|
||||||
|
// If |AP| is small, don't bother with the computation of
|
||||||
|
// separated labels.
|
||||||
|
bool will_use_labels = n_ap > 5;
|
||||||
|
if (will_use_labels)
|
||||||
|
{
|
||||||
|
std::set<bdd, bdd_less_than> all_labels;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (will_use_labels)
|
||||||
|
{
|
||||||
|
separated_labels.reserve(all_labels.size());
|
||||||
|
separated_labels.push_back(bddtrue);
|
||||||
|
for (auto& lab: all_labels)
|
||||||
|
{
|
||||||
|
// make sure don't realloc during the loop
|
||||||
|
separated_labels.reserve(separated_labels.size() * 2);
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
separated_labels[cur] -= lab;
|
||||||
|
separated_labels.push_back(common);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We for easier computation of outgoing sets, we will
|
// We for easier computation of outgoing sets, we will
|
||||||
// represent states using BDD variables.
|
// represent states using BDD variables.
|
||||||
allocate_state_vars();
|
allocate_state_vars();
|
||||||
|
|
@ -459,44 +508,53 @@ namespace spot
|
||||||
bdd ap = bdd_exist(bdd_support(bs), all_vars_);
|
bdd ap = bdd_exist(bdd_support(bs), all_vars_);
|
||||||
bdd all_letters = bdd_exist(bs, all_vars_);
|
bdd all_letters = bdd_exist(bs, all_vars_);
|
||||||
|
|
||||||
// First loop over all possible valuations atomic properties.
|
// Given a label, and BDD expression representing
|
||||||
for (bdd oneletter: minterms_of(all_letters, ap))
|
// the combination of destinations, create the edges.
|
||||||
{
|
auto create_edges = [&](bdd label, bdd dest_formula)
|
||||||
minato_isop isop(bdd_restrict(bs, oneletter));
|
{
|
||||||
bdd dest;
|
minato_isop isop(dest_formula);
|
||||||
while ((dest = isop.next()) != bddfalse)
|
bdd dest;
|
||||||
{
|
while ((dest = isop.next()) != bddfalse)
|
||||||
v.clear();
|
{
|
||||||
acc_cond::mark_t m = bdd_to_state(dest, v);
|
v.clear();
|
||||||
|
acc_cond::mark_t m = bdd_to_state(dest, v);
|
||||||
|
|
||||||
// if there is no promise "f" between a state
|
// if there is no promise "f" between a state
|
||||||
// that does not have f, and a state that have
|
// that does not have f, and a state that have
|
||||||
// "f", we can add one. Doing so will help later
|
// "f", we can add one. Doing so will help later
|
||||||
// simplifications performed by postprocessor. An
|
// simplifications performed by postprocessor. An
|
||||||
// example where this is needed is the VWAA
|
// example where this is needed is the VWAA
|
||||||
// generated by ltl[23]ba for GFa. Without the
|
// generated by ltl[23]ba for GFa. Without the
|
||||||
// next loop, the final TGBA has 2 states instead
|
// next loop, the final TGBA has 2 states instead
|
||||||
// of 1.
|
// of 1.
|
||||||
for (unsigned m1: (all_marks - m).sets())
|
for (unsigned m1: (all_marks - m).sets())
|
||||||
{
|
{
|
||||||
if (has_reject_more_ && m1 == 0)
|
if (has_reject_more_ && m1 == 0)
|
||||||
continue;
|
continue;
|
||||||
auto& sv = s_to_ss[s];
|
auto& sv = s_to_ss[s];
|
||||||
unsigned ms = mark_to_state_[m1];
|
unsigned ms = mark_to_state_[m1];
|
||||||
if (std::find(v.begin(), v.end(), ms) != v.end())
|
if (std::find(v.begin(), v.end(), ms) != v.end())
|
||||||
{
|
{
|
||||||
unsigned ms = mark_to_state_[m1];
|
unsigned ms = mark_to_state_[m1];
|
||||||
if (std::find(sv.begin(), sv.end(), ms) == sv.end())
|
if (std::find(sv.begin(), sv.end(), ms) == sv.end())
|
||||||
m.set(m1);
|
m.set(m1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned d = new_state(v, has_mark);
|
unsigned d = new_state(v, has_mark);
|
||||||
if (has_mark)
|
if (has_mark)
|
||||||
m.set(0);
|
m.set(0);
|
||||||
res->new_edge(s, d, oneletter, all_marks - m);
|
res->new_edge(s, d, label, all_marks - m);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
else
|
||||||
|
for (bdd label: separated_labels)
|
||||||
|
create_edges(label, bdd_relprod(label, bs, res->ap_vars()));
|
||||||
}
|
}
|
||||||
res->merge_edges();
|
res->merge_edges();
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -135,3 +135,6 @@ EOF
|
||||||
# but it is difficult to test so in the test suite.
|
# but it is difficult to test so in the test suite.
|
||||||
res=`autfilt --dualize 21.hoa --stats='%S %E %T %s %e %t'`
|
res=`autfilt --dualize 21.hoa --stats='%S %E %T %s %e %t'`
|
||||||
test "$res" = "5 13 85 6 13 12582912"
|
test "$res" = "5 13 85 6 13 12582912"
|
||||||
|
|
||||||
|
res=`autfilt --complement 21.hoa --stats='%S %E %T %s %e %t'`
|
||||||
|
test "$res" = "5 13 85 5 11 10485760"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue