acd: do not recompute identical subtrees
* spot/twaalgos/zlktree.cc, spot/twaalgos/zlktree.hh (acd::build_): When processing a node identical to a node previously seen, simply copy the children of that other node, and share its vectors.
This commit is contained in:
parent
2c435c6c11
commit
60225fd139
2 changed files with 125 additions and 33 deletions
|
|
@ -24,6 +24,7 @@
|
||||||
#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>
|
||||||
|
#include <spot/priv/robin_hood.hh>
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
|
|
@ -386,6 +387,21 @@ namespace spot
|
||||||
acc_cond posacc = aut->acc();
|
acc_cond posacc = aut->acc();
|
||||||
acc_cond negacc(posacc.num_sets(), posacc.get_acceptance().complement());
|
acc_cond negacc(posacc.num_sets(), posacc.get_acceptance().complement());
|
||||||
|
|
||||||
|
// Cache for nodes. The first instance of any node that
|
||||||
|
// has {colors,minstate} is given in this map. This can be used
|
||||||
|
// to speedup the computation of successors of other nodes
|
||||||
|
// with the same parameters.
|
||||||
|
typedef std::pair<acc_cond::mark_t, unsigned> nmap_key_t;
|
||||||
|
struct nmap_key_hash
|
||||||
|
{
|
||||||
|
std::size_t
|
||||||
|
operator() (const std::pair<acc_cond::mark_t, unsigned> &pair) const
|
||||||
|
{
|
||||||
|
return pair.first.hash() ^ std::hash<unsigned>()(pair.second);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
robin_hood::unordered_node_map<nmap_key_t, unsigned, nmap_key_hash> nmap;
|
||||||
|
|
||||||
if (nstates <= 1)
|
if (nstates <= 1)
|
||||||
// The ordering heuristic does not help if we have a single state.
|
// The ordering heuristic does not help if we have a single state.
|
||||||
opt_ = opt_ - acd_options::ORDER_HEURISTIC;
|
opt_ = opt_ - acd_options::ORDER_HEURISTIC;
|
||||||
|
|
@ -430,6 +446,8 @@ namespace spot
|
||||||
n.parent = root;
|
n.parent = root;
|
||||||
n.level = 0;
|
n.level = 0;
|
||||||
n.scc = scc;
|
n.scc = scc;
|
||||||
|
n.colors = si_->acc_sets_of(scc);
|
||||||
|
n.minstate = si_->one_state_of(scc);
|
||||||
for (auto& e: si_->inner_edges_of(scc))
|
for (auto& e: si_->inner_edges_of(scc))
|
||||||
{
|
{
|
||||||
n.edges.set(aut->edge_number(e));
|
n.edges.set(aut->edge_number(e));
|
||||||
|
|
@ -441,9 +459,35 @@ namespace spot
|
||||||
{
|
{
|
||||||
unsigned size;
|
unsigned size;
|
||||||
std::unique_ptr<bitvect> trans;
|
std::unique_ptr<bitvect> trans;
|
||||||
|
acc_cond::mark_t colors;
|
||||||
|
unsigned minstate;
|
||||||
};
|
};
|
||||||
std::vector<size_model> out;
|
std::vector<size_model> out;
|
||||||
|
|
||||||
|
auto handle_leaf = [&](unsigned scc, unsigned lvl)
|
||||||
|
{
|
||||||
|
// this node is a leaf.
|
||||||
|
if (trees_[scc].is_even)
|
||||||
|
max_level_of_even_tree = lvl;
|
||||||
|
else
|
||||||
|
max_level_of_odd_tree = lvl;
|
||||||
|
};
|
||||||
|
// Chain the children together, and connect them to the parent
|
||||||
|
auto chain_children = [this](unsigned node, unsigned before, unsigned after)
|
||||||
|
{
|
||||||
|
// Chain the children together, and connect them to the parent
|
||||||
|
for (unsigned child = before; child < after; ++child)
|
||||||
|
{
|
||||||
|
unsigned next = child + 1;
|
||||||
|
if (next == after)
|
||||||
|
{
|
||||||
|
next = before;
|
||||||
|
nodes_[node].first_child = before;
|
||||||
|
}
|
||||||
|
nodes_[child].next_sibling = next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<bitvect> seen_src;
|
std::unique_ptr<bitvect> seen_src;
|
||||||
std::unique_ptr<bitvect> seen_dst;
|
std::unique_ptr<bitvect> seen_dst;
|
||||||
std::unique_ptr<bitvect> seen_dup;
|
std::unique_ptr<bitvect> seen_dup;
|
||||||
|
|
@ -456,19 +500,57 @@ namespace spot
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
unsigned scc = nodes_[node].scc;
|
auto& nn = nodes_[node];
|
||||||
unsigned lvl = nodes_[node].level;
|
unsigned scc = nn.scc;
|
||||||
|
unsigned lvl = nn.level;
|
||||||
bool accepting_node = (lvl & 1) != trees_[scc].is_even;
|
bool accepting_node = (lvl & 1) != trees_[scc].is_even;
|
||||||
|
|
||||||
|
// If we have already computed an SCC restricted to nn.colors and using
|
||||||
|
// nn.minstate as smallest state, then simply copy its successors.
|
||||||
|
if (auto it = nmap.find({nn.colors, nn.minstate}); it != nmap.end())
|
||||||
|
{
|
||||||
|
auto& ref = nodes_[it->second];
|
||||||
|
unsigned fc = ref.first_child;
|
||||||
|
if (!fc)
|
||||||
|
{
|
||||||
|
handle_leaf(scc, lvl);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned child = fc;
|
||||||
|
unsigned before = nodes_.size();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto& c = nodes_[child];
|
||||||
|
nodes_.emplace_back(c.edges, c.states);
|
||||||
|
auto& n = nodes_.back();
|
||||||
|
n.parent = node;
|
||||||
|
n.level = lvl + 1;
|
||||||
|
n.scc = ref.scc;
|
||||||
|
n.colors = c.colors;
|
||||||
|
n.minstate = c.minstate;
|
||||||
|
child = c.next_sibling;
|
||||||
|
}
|
||||||
|
while (child != fc);
|
||||||
|
chain_children(node, before, nodes_.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nmap.emplace(nmap_key_t{nn.colors, nn.minstate}, node);
|
||||||
|
}
|
||||||
|
|
||||||
out.clear();
|
out.clear();
|
||||||
auto callback = [&](scc_info si, unsigned siscc)
|
auto callback = [&](scc_info si, unsigned siscc)
|
||||||
{
|
{
|
||||||
bitvect* bv = make_bitvect(nedges);
|
bitvect* bv = make_bitvect(nedges);
|
||||||
unsigned sz = 0;
|
unsigned sz = 0;
|
||||||
|
acc_cond::mark_t colors = si.acc_sets_of(siscc);
|
||||||
|
unsigned minstate = -1U;
|
||||||
for (auto& e: si.inner_edges_of(siscc))
|
for (auto& e: si.inner_edges_of(siscc))
|
||||||
{
|
{
|
||||||
bv->set(aut->edge_number(e));
|
bv->set(aut->edge_number(e));
|
||||||
++sz;
|
++sz;
|
||||||
|
minstate = std::min(minstate, e.src);
|
||||||
}
|
}
|
||||||
auto iter = out.begin();
|
auto iter = out.begin();
|
||||||
while (iter != out.end())
|
while (iter != out.end())
|
||||||
|
|
@ -476,7 +558,11 @@ namespace spot
|
||||||
if (iter->size < sz)
|
if (iter->size < sz)
|
||||||
// We have checked all larger models.
|
// We have checked all larger models.
|
||||||
break;
|
break;
|
||||||
if (bv->is_subset_of(*iter->trans))
|
// Checking inclusion of edges is enough,
|
||||||
|
// but checking inclusion of colors is way faster
|
||||||
|
if (colors.subset(iter->colors)
|
||||||
|
&& (minstate == iter->minstate
|
||||||
|
|| bv->is_subset_of(*iter->trans)))
|
||||||
// ignore smaller models
|
// ignore smaller models
|
||||||
{
|
{
|
||||||
delete bv;
|
delete bv;
|
||||||
|
|
@ -485,7 +571,8 @@ namespace spot
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
// insert the model
|
// insert the model
|
||||||
iter = out.insert(iter, {sz, std::unique_ptr<bitvect>(bv)});
|
iter = out.insert(iter, {sz, std::unique_ptr<bitvect>(bv),
|
||||||
|
colors, minstate});
|
||||||
++iter;
|
++iter;
|
||||||
// erase all models it contains
|
// erase all models it contains
|
||||||
out.erase(std::remove_if(iter, out.end(),
|
out.erase(std::remove_if(iter, out.end(),
|
||||||
|
|
@ -502,8 +589,11 @@ namespace spot
|
||||||
// Find states that appear in multiple children
|
// Find states that appear in multiple children
|
||||||
seen_dup->clear_all(); // duplicate states
|
seen_dup->clear_all(); // duplicate states
|
||||||
seen_src->clear_all(); // union of all children states
|
seen_src->clear_all(); // union of all children states
|
||||||
for (auto& [sz, bv]: out)
|
for (auto& [sz, bv, colors, minstate]: out)
|
||||||
{
|
{
|
||||||
|
(void) sz;
|
||||||
|
(void) colors;
|
||||||
|
(void) minstate;
|
||||||
seen_dst->clear_all(); // local states of the node
|
seen_dst->clear_all(); // local states of the node
|
||||||
bv->foreach_set_index([&aut, &seen_dst](unsigned e)
|
bv->foreach_set_index([&aut, &seen_dst](unsigned e)
|
||||||
{
|
{
|
||||||
|
|
@ -517,8 +607,10 @@ namespace spot
|
||||||
// Now the union in seen_src is not useful anymore. Process
|
// Now the union in seen_src is not useful anymore. Process
|
||||||
// each node again, but consider only the states that are in
|
// each node again, but consider only the states that are in
|
||||||
// seen_dup.
|
// seen_dup.
|
||||||
for (auto& [sz, bv]: out)
|
for (auto& [sz, bv, colors, minstate]: out)
|
||||||
{
|
{
|
||||||
|
(void) colors;
|
||||||
|
(void) minstate;
|
||||||
seen_src->clear_all(); // local source of the node
|
seen_src->clear_all(); // local source of the node
|
||||||
bv->foreach_set_index([&aut, &seen_src](unsigned e)
|
bv->foreach_set_index([&aut, &seen_src](unsigned e)
|
||||||
{
|
{
|
||||||
|
|
@ -545,9 +637,11 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned before_size = nodes_.size();
|
unsigned before_size = nodes_.size();
|
||||||
for (const auto& [sz, bv]: out)
|
for (const auto& [sz, bv, colors, minstate]: out)
|
||||||
{
|
{
|
||||||
(void)sz;
|
(void) sz;
|
||||||
|
(void) colors;
|
||||||
|
(void) minstate;
|
||||||
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));
|
||||||
|
|
@ -556,29 +650,16 @@ namespace spot
|
||||||
n.level = lvl + 1;
|
n.level = lvl + 1;
|
||||||
n.scc = scc;
|
n.scc = scc;
|
||||||
n.edges |= *bv;
|
n.edges |= *bv;
|
||||||
|
n.colors = colors;
|
||||||
|
n.minstate = minstate;
|
||||||
}
|
}
|
||||||
unsigned after_size = nodes_.size();
|
unsigned after_size = nodes_.size();
|
||||||
unsigned children = after_size - before_size;
|
unsigned children = after_size - before_size;
|
||||||
|
chain_children(node, before_size, after_size);
|
||||||
// Chain the children together, and connect them to the parent
|
|
||||||
for (unsigned child = before_size; child < after_size; ++child)
|
|
||||||
{
|
|
||||||
unsigned next = child + 1;
|
|
||||||
if (next == after_size)
|
|
||||||
{
|
|
||||||
next = before_size;
|
|
||||||
nodes_[node].first_child = before_size;
|
|
||||||
}
|
|
||||||
nodes_[child].next_sibling = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (children == 0)
|
if (children == 0)
|
||||||
{
|
{
|
||||||
// this node is a leaf.
|
handle_leaf(scc, lvl);
|
||||||
if (trees_[scc].is_even)
|
|
||||||
max_level_of_even_tree = lvl;
|
|
||||||
else
|
|
||||||
max_level_of_odd_tree = lvl;
|
|
||||||
}
|
}
|
||||||
else if (children > 1)
|
else if (children > 1)
|
||||||
{
|
{
|
||||||
|
|
@ -592,8 +673,10 @@ namespace spot
|
||||||
{
|
{
|
||||||
std::unique_ptr<bitvect> seen(make_bitvect(nstates));
|
std::unique_ptr<bitvect> seen(make_bitvect(nstates));
|
||||||
std::unique_ptr<bitvect> cur(make_bitvect(nstates));
|
std::unique_ptr<bitvect> cur(make_bitvect(nstates));
|
||||||
for (const auto& [sz, bv]: out)
|
for (const auto& [sz, bv, colors, minstate]: out)
|
||||||
{
|
{
|
||||||
|
(void) colors;
|
||||||
|
(void) minstate;
|
||||||
cur->clear_all();
|
cur->clear_all();
|
||||||
bv->foreach_set_index([&aut, &cur](unsigned e)
|
bv->foreach_set_index([&aut, &cur](unsigned e)
|
||||||
{
|
{
|
||||||
|
|
@ -782,7 +865,6 @@ namespace spot
|
||||||
<< "\"\n node [id=\"N\\N\", style=filled, fillcolor=white]\n";
|
<< "\"\n node [id=\"N\\N\", style=filled, fillcolor=white]\n";
|
||||||
for (unsigned n = 0; n < ns; ++n)
|
for (unsigned n = 0; n < ns; ++n)
|
||||||
{
|
{
|
||||||
acc_cond::mark_t m = {};
|
|
||||||
os << " " << n << " [label=\"";
|
os << " " << n << " [label=\"";
|
||||||
unsigned scc = nodes_[n].scc;
|
unsigned scc = nodes_[n].scc;
|
||||||
// The top of each tree has level 0 or 1, depending on whether
|
// The top of each tree has level 0 or 1, depending on whether
|
||||||
|
|
@ -820,11 +902,9 @@ namespace spot
|
||||||
lastchange = n;
|
lastchange = n;
|
||||||
sep = ",";
|
sep = ",";
|
||||||
}
|
}
|
||||||
if (val)
|
|
||||||
m |= aut_->edge_data(n).acc;
|
|
||||||
}
|
}
|
||||||
unsigned first_child = nodes_[n].first_child;
|
unsigned first_child = nodes_[n].first_child;
|
||||||
os << '\n' << m;
|
os << '\n' << nodes_[n].colors;
|
||||||
auto& states = nodes_[n].states;
|
auto& states = nodes_[n].states;
|
||||||
unsigned nstates = states.size();
|
unsigned nstates = states.size();
|
||||||
sep = "\nQ: ";
|
sep = "\nQ: ";
|
||||||
|
|
@ -893,13 +973,20 @@ namespace spot
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the level of a node.
|
/// Return the level of a node.
|
||||||
unsigned acd::node_level(unsigned n)
|
unsigned acd::node_level(unsigned n) const
|
||||||
{
|
{
|
||||||
if (SPOT_UNLIKELY(nodes_.size() <= n))
|
if (SPOT_UNLIKELY(nodes_.size() <= n))
|
||||||
throw std::runtime_error("acd::node_level(): unknown node");
|
throw std::runtime_error("acd::node_level(): unknown node");
|
||||||
return nodes_[n].level;
|
return nodes_[n].level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const acc_cond::mark_t& acd::node_colors(unsigned n) const
|
||||||
|
{
|
||||||
|
if (SPOT_UNLIKELY(nodes_.size() <= n))
|
||||||
|
throw std::runtime_error("acd::node_colors(): unknown node");
|
||||||
|
return nodes_[n].colors;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<unsigned> acd::edges_of_node(unsigned n) const
|
std::vector<unsigned> acd::edges_of_node(unsigned n) const
|
||||||
{
|
{
|
||||||
if (SPOT_UNLIKELY(nodes_.size() <= n))
|
if (SPOT_UNLIKELY(nodes_.size() <= n))
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,10 @@ namespace spot
|
||||||
bool node_acceptance(unsigned n) const;
|
bool node_acceptance(unsigned n) const;
|
||||||
|
|
||||||
/// Return the level of a node.
|
/// Return the level of a node.
|
||||||
unsigned node_level(unsigned n);
|
unsigned node_level(unsigned n) const;
|
||||||
|
|
||||||
|
/// Return the colors of a node.
|
||||||
|
const acc_cond::mark_t& node_colors(unsigned n) const;
|
||||||
|
|
||||||
/// \brief Whether the ACD corresponds to a min even or min odd
|
/// \brief Whether the ACD corresponds to a min even or min odd
|
||||||
/// parity acceptance in SCC \a scc.
|
/// parity acceptance in SCC \a scc.
|
||||||
|
|
@ -349,6 +352,8 @@ namespace spot
|
||||||
unsigned first_child = 0;
|
unsigned first_child = 0;
|
||||||
unsigned level;
|
unsigned level;
|
||||||
unsigned scc;
|
unsigned scc;
|
||||||
|
acc_cond::mark_t colors;
|
||||||
|
unsigned minstate;
|
||||||
bitvect& edges;
|
bitvect& edges;
|
||||||
bitvect& states;
|
bitvect& states;
|
||||||
acd_node(bitvect& e, bitvect& s) noexcept
|
acd_node(bitvect& e, bitvect& s) noexcept
|
||||||
|
|
@ -362,7 +367,7 @@ namespace spot
|
||||||
// Likewise for bitvectors: this is the support for all edge vectors
|
// Likewise for bitvectors: this is the support for all edge vectors
|
||||||
// and state vectors used in acd_node.
|
// and state vectors used in acd_node.
|
||||||
std::deque<std::unique_ptr<bitvect>> bitvectors;
|
std::deque<std::unique_ptr<bitvect>> bitvectors;
|
||||||
// Information about a tree of the ACD. Each tree correspond
|
// Information about a tree of the ACD. Each treinserte correspond
|
||||||
// to an SCC.
|
// to an SCC.
|
||||||
struct scc_data
|
struct scc_data
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue