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:
parent
70ede35702
commit
fea0be96c1
4 changed files with 1655 additions and 554 deletions
|
|
@ -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_)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue