acd: remove redundant nodes
Reported by Florian Renkin. * spot/twaalgos/zlktree.cc (acd::_build): Use a sorted list to remove redundant children, has done in zielonka_tree. * tests/python/zlktree.ipynb: Add Florian's test case. * tests/python/toparity.py: Adjust, and revert some tests uncommented by mistake in a previous patch.
This commit is contained in:
parent
d5b641a7dc
commit
170d839c4b
3 changed files with 1679 additions and 892 deletions
|
|
@ -20,9 +20,11 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <memory>
|
||||||
#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/misc/bitvect.hh>
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
@ -405,6 +407,13 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct size_model
|
||||||
|
{
|
||||||
|
unsigned size;
|
||||||
|
std::unique_ptr<bitvect> trans;
|
||||||
|
};
|
||||||
|
std::vector<size_model> out;
|
||||||
|
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
|
|
@ -412,7 +421,45 @@ namespace spot
|
||||||
unsigned lvl = nodes_[node].level;
|
unsigned lvl = nodes_[node].level;
|
||||||
bool accepting_node = (lvl & 1) != trees_[scc].is_even;
|
bool accepting_node = (lvl & 1) != trees_[scc].is_even;
|
||||||
|
|
||||||
|
out.clear();
|
||||||
auto callback = [&](scc_info si, unsigned siscc)
|
auto callback = [&](scc_info si, unsigned siscc)
|
||||||
|
{
|
||||||
|
bitvect* bv = make_bitvect(nedges);
|
||||||
|
unsigned sz = 0;
|
||||||
|
for (auto& e: si.inner_edges_of(siscc))
|
||||||
|
{
|
||||||
|
bv->set(aut->edge_number(e));
|
||||||
|
++sz;
|
||||||
|
}
|
||||||
|
auto iter = out.begin();
|
||||||
|
while (iter != out.end())
|
||||||
|
{
|
||||||
|
if (iter->size < sz)
|
||||||
|
// We have checked all larger models.
|
||||||
|
break;
|
||||||
|
if (bv->is_subset_of(*iter->trans))
|
||||||
|
// ignore smaller models
|
||||||
|
{
|
||||||
|
delete bv;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
// insert the model
|
||||||
|
iter = out.insert(iter, {sz, std::unique_ptr<bitvect>(bv)});
|
||||||
|
++iter;
|
||||||
|
// erase all models it contains
|
||||||
|
out.erase(std::remove_if(iter, out.end(),
|
||||||
|
[&](auto& mod) {
|
||||||
|
return mod.trans->is_subset_of(*bv);
|
||||||
|
}), out.end());
|
||||||
|
};
|
||||||
|
maximal_accepting_loops_for_scc(*si_, scc,
|
||||||
|
accepting_node ? negacc : posacc,
|
||||||
|
nodes_[node].edges, callback);
|
||||||
|
|
||||||
|
unsigned before_size = nodes_.size();
|
||||||
|
for (const auto& [sz, bv]: out)
|
||||||
{
|
{
|
||||||
unsigned vnum = trees_[scc].num_nodes++;
|
unsigned vnum = trees_[scc].num_nodes++;
|
||||||
allocate_vectors_maybe(vnum);
|
allocate_vectors_maybe(vnum);
|
||||||
|
|
@ -421,17 +468,13 @@ namespace spot
|
||||||
n.parent = node;
|
n.parent = node;
|
||||||
n.level = lvl + 1;
|
n.level = lvl + 1;
|
||||||
n.scc = scc;
|
n.scc = scc;
|
||||||
for (auto& e: si.inner_edges_of(siscc))
|
for (unsigned e = 1; e < nedges; ++e)
|
||||||
|
if (bv->get(e))
|
||||||
{
|
{
|
||||||
n.edges[aut->edge_number(e)] = true;
|
n.edges[e] = true;
|
||||||
n.states[e.src] = true;
|
n.states[aut->edge_storage(e).src] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
unsigned before_size = nodes_.size();
|
|
||||||
maximal_accepting_loops_for_scc(*si_, scc,
|
|
||||||
accepting_node ? negacc : posacc,
|
|
||||||
nodes_[node].edges, callback);
|
|
||||||
unsigned after_size = nodes_.size();
|
unsigned after_size = nodes_.size();
|
||||||
unsigned children = after_size - before_size;
|
unsigned children = after_size - before_size;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ def test(aut, expected_num_states=[], full=True):
|
||||||
spot.reduce_parity_here(p1)
|
spot.reduce_parity_here(p1)
|
||||||
assert spot.are_equivalent(aut, p1)
|
assert spot.are_equivalent(aut, p1)
|
||||||
if expected_num is not None:
|
if expected_num is not None:
|
||||||
print(p1.num_states(), expected_num)
|
# print(p1.num_states(), expected_num)
|
||||||
assert p1.num_states() == expected_num
|
assert p1.num_states() == expected_num
|
||||||
if full and opt is not None:
|
if full and opt is not None:
|
||||||
# Make sure passing opt is the same as setting
|
# Make sure passing opt is the same as setting
|
||||||
|
|
@ -240,11 +240,11 @@ State: 1
|
||||||
[!0&!1] 0 {1 2}
|
[!0&!1] 0 {1 2}
|
||||||
--END--"""), [9, 3, 2, 3, 3, 3, 2, 2])
|
--END--"""), [9, 3, 2, 3, 3, 3, 2, 2])
|
||||||
|
|
||||||
#for i,f in enumerate(spot.randltl(10, 400)):
|
for i,f in enumerate(spot.randltl(10, 200)):
|
||||||
# test(spot.translate(f, "det", "G"), full=(i<100))
|
test(spot.translate(f, "det", "G"), full=(i<50))
|
||||||
#
|
|
||||||
#for f in spot.randltl(5, 2500):
|
for f in spot.randltl(5, 500):
|
||||||
# test(spot.translate(f), full=False)
|
test(spot.translate(f), full=False)
|
||||||
|
|
||||||
|
|
||||||
test(spot.automaton("""
|
test(spot.automaton("""
|
||||||
|
|
@ -278,7 +278,7 @@ State: 3
|
||||||
[!0&1] 2 {1 4}
|
[!0&1] 2 {1 4}
|
||||||
[0&1] 3 {0}
|
[0&1] 3 {0}
|
||||||
--END--
|
--END--
|
||||||
"""), [80, 47, 104, 104, 102, 29, 6, 6])
|
"""), [80, 47, 104, 104, 102, 29, 6, 5])
|
||||||
|
|
||||||
test(spot.automaton("""
|
test(spot.automaton("""
|
||||||
HOA: v1
|
HOA: v1
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue