acd: add ORDER_HEURISTIC for state-based ACD-transform

* spot/twaalgos/zlktree.cc, spot/twaalgos/zlktree.hh: Add the
acd_options::ORDER_HEURISTIC and use it by default in
acd_transform_sbacc().
* spot/misc/bitvect.hh (bitvect::count, bitvect::add_common): New
methods.
* tests/python/zlktree.ipynb: Adjust examples and discuss this
heuristic.
This commit is contained in:
Alexandre Duret-Lutz 2021-09-24 20:39:47 +02:00
parent 70ede35702
commit fea0be96c1
4 changed files with 1655 additions and 554 deletions

View file

@ -255,6 +255,16 @@ namespace spot
return *this; return *this;
} }
bitvect& add_common(const bitvect& other1, const bitvect& other2)
{
SPOT_ASSERT(other1.size_ <= size_ && other2.size_ <= size_);
unsigned m = std::min(other2.block_count_,
std::min(other1.block_count_, block_count_));
for (size_t i = 0; i < m; ++i)
storage_[i] |= other1.storage_[i] & other2.storage_[i];
return *this;
}
bool intersects(const bitvect& other) bool intersects(const bitvect& other)
{ {
SPOT_ASSERT(other.size_ <= size_); SPOT_ASSERT(other.size_ <= size_);
@ -302,6 +312,32 @@ namespace spot
== (storage_[i] & mask)); == (storage_[i] & mask));
} }
unsigned count() const
{
size_t i;
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
size_t c = 0;
for (i = 0; i < block_count_; ++i)
{
block_t v = storage_[i];
if ((i == block_count_ - 1) && rest)
// The last block might not be fully used, scan only the
// relevant portion.
v &= (1UL << rest) - 1;
#ifdef __GNUC__
c += __builtin_popcountl(v);
#else
while (v)
{
++c;
v &= v - 1;
}
#endif
}
return c;
}
bool operator==(const bitvect& other) const bool operator==(const bitvect& other) const
{ {
if (other.size_ != size_) if (other.size_ != size_)

View file

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include <iostream> #include <iostream>
#include <deque> #include <deque>
#include <algorithm>
#include <spot/twaalgos/zlktree.hh> #include <spot/twaalgos/zlktree.hh>
#include <spot/twaalgos/genem.hh> #include <spot/twaalgos/genem.hh>
#include <spot/misc/escape.hh> #include <spot/misc/escape.hh>
@ -439,6 +440,15 @@ namespace spot
}; };
std::vector<size_model> out; std::vector<size_model> out;
std::unique_ptr<bitvect> seen_src;
std::unique_ptr<bitvect> seen_dst;
std::unique_ptr<bitvect> seen_dup;
if (!!(opt_ & acd_options::ORDER_HEURISTIC))
{
seen_src.reset(make_bitvect(nstates));
seen_dst.reset(make_bitvect(nstates));
seen_dup.reset(make_bitvect(nstates));
}
// This loop is a BFS over the increasing set of nodes. // This loop is a BFS over the increasing set of nodes.
for (unsigned node = 0; node < nodes_.size(); ++node) for (unsigned node = 0; node < nodes_.size(); ++node)
{ {
@ -483,9 +493,55 @@ namespace spot
accepting_node ? negacc : posacc, accepting_node ? negacc : posacc,
nodes_[node].edges, callback); nodes_[node].edges, callback);
if (!!(opt_ & acd_options::ORDER_HEURISTIC))
{
// Find states that appear in multiple children
seen_dup->clear_all(); // duplicate states
seen_src->clear_all(); // union of all children states
for (auto& [sz, bv]: out)
{
seen_dst->clear_all(); // local states of the node
bv->foreach_set_index([&aut, &seen_dst](unsigned e)
{
seen_dst->set(aut->edge_storage(e).src);
});
seen_dup->add_common(*seen_src, *seen_dst);
*seen_src |= *seen_dst;
}
// Now the union in seen_src is not useful anymore. Process
// each node again, but consider only the states that are in
// seen_dup.
for (auto& [sz, bv]: out)
{
seen_src->clear_all(); // local source of the node
bv->foreach_set_index([&aut, &seen_src, &seen_dup](unsigned e)
{
unsigned idx = aut->edge_storage(e).src;
if (seen_dup->get(idx))
seen_src->set(idx); // store duplicates
});
seen_dst->clear_all();
// Count the number of states reached by leaving this node.
seen_src->foreach_set_index([&aut, bv=bv.get(),
&seen_dst](unsigned s)
{
for (auto& e: aut->out(s))
if (!bv->get(aut->edge_number(e)))
seen_dst->set(e.dst);
});
sz = seen_dst->count();
}
// reorder by decreasing number of new states reached
std::stable_sort(out.begin(), out.end(),
[&](auto& p1, auto& p2) {
return p1.size > p2.size;
});
}
unsigned before_size = nodes_.size(); unsigned before_size = nodes_.size();
for (const auto& [sz, bv]: out) for (const auto& [sz, bv]: out)
{ {
(void)sz;
unsigned vnum = trees_[scc].num_nodes++; unsigned vnum = trees_[scc].num_nodes++;
allocate_vectors_maybe(vnum); allocate_vectors_maybe(vnum);
nodes_.emplace_back(edge_vector(vnum), state_vector(vnum)); nodes_.emplace_back(edge_vector(vnum), state_vector(vnum));
@ -877,12 +933,13 @@ namespace spot
// ACD, to // ACD, to
template<bool sbacc> template<bool sbacc>
twa_graph_ptr twa_graph_ptr
acd_transform_(const const_twa_graph_ptr& a, bool colored) acd_transform_(const const_twa_graph_ptr& a, bool colored,
acd_options options = acd_options::NONE)
{ {
auto res = make_twa_graph(a->get_dict()); auto res = make_twa_graph(a->get_dict());
res->copy_ap_of(a); res->copy_ap_of(a);
scc_info si(a, scc_info_options::TRACK_STATES); scc_info si(a, scc_info_options::TRACK_STATES);
acd theacd(si); acd theacd(si, options);
// If we desire non-colored output, we can omit the maximal // If we desire non-colored output, we can omit the maximal
// color of each SCC if it has the same parity as max_level. // color of each SCC if it has the same parity as max_level.
@ -1022,9 +1079,12 @@ namespace spot
} }
twa_graph_ptr twa_graph_ptr
acd_transform_sbacc(const const_twa_graph_ptr& a, bool colored) acd_transform_sbacc(const const_twa_graph_ptr& a, bool colored,
bool order_heuristic)
{ {
return acd_transform_<true>(a, colored); return acd_transform_<true>(a, colored, order_heuristic
? acd_options::ORDER_HEURISTIC
: acd_options::NONE);
} }
} }

View file

@ -167,6 +167,11 @@ namespace spot
/// shape that is tested. When that happens, node_count() is set /// shape that is tested. When that happens, node_count() is set
/// to 0. /// to 0.
ABORT_WRONG_SHAPE = 4, ABORT_WRONG_SHAPE = 4,
/// Order the children of a node by decreasing size of the number
/// of states they would introduce if that child appears as the
/// last child of an "ACD" round in the state-based version of the
/// ACD output.
ORDER_HEURISTIC = 8,
}; };
#ifndef SWIG #ifndef SWIG
@ -407,13 +412,15 @@ namespace spot
/// optimal transition-based output (optimal in the sense of least /// optimal transition-based output (optimal in the sense of least
/// number of duplicated states), while the acd_tansform_sbacc() variant /// number of duplicated states), while the acd_tansform_sbacc() variant
/// produces state-based output from transition-based input and without /// produces state-based output from transition-based input and without
/// any optimality claim. /// any optimality claim. The \a order_heuristics argument, enabled
/// by default activates the ORDER_HEURISTICS option of the ACD.
/// @{ /// @{
SPOT_API SPOT_API
twa_graph_ptr acd_transform(const const_twa_graph_ptr& aut, twa_graph_ptr acd_transform(const const_twa_graph_ptr& aut,
bool colored = false); bool colored = false);
SPOT_API SPOT_API
twa_graph_ptr acd_transform_sbacc(const const_twa_graph_ptr& aut, twa_graph_ptr acd_transform_sbacc(const const_twa_graph_ptr& aut,
bool colored = false); bool colored = false,
bool order_heuristic = true);
/// @} /// @}
} }

File diff suppressed because one or more lines are too long