twa_graph: add a merge_univ_dests() method

and call it after parsing

* spot/twa/twagraph.cc, spot/twa/twagraph.hh
(twa_graph::merge_univ_dests): New method.
* spot/parseaut/parseaut.yy: Call it.
* spot/twaalgos/dot.cc: Improve output, now that
several edges can use the same universal destination.
* tests/core/alternating.test, tests/core/complete.test,
tests/core/parseaut.test, tests/python/_altscc.ipynb,
tests/python/alternating.py, tests/python/alternation.ipynb: Adjust
test case.
* doc/org/tut24.org: Adjust example.
This commit is contained in:
Alexandre Duret-Lutz 2016-12-27 12:35:16 +01:00
parent 3d0a971aa8
commit 12f6c8cf10
11 changed files with 567 additions and 517 deletions

View file

@ -2423,6 +2423,8 @@ namespace spot
fix_acceptance(r);
fix_initial_state(r);
fix_properties(r);
if (r.h->aut && r.h->aut->is_alternating())
r.h->aut->merge_univ_dests();
return r.h;
};

View file

@ -43,10 +43,66 @@ namespace spot
delete namer;
}
/// \brief Merge universal destinations
///
/// If several states have the same universal destination, merge
/// them all. Also remove unused destination, and any redundant
/// state in each destination.
void twa_graph::merge_univ_dests()
{
auto& g = get_graph();
auto& dests = g.dests_vector();
auto& edges = g.edge_vector();
std::vector<unsigned> old_dests;
std::swap(dests, old_dests);
std::vector<unsigned> seen(old_dests.size(), -1U);
std::map<std::vector<unsigned>, unsigned> uniq;
auto fixup = [&](unsigned& in_dst)
{
unsigned dst = in_dst;
if ((int) dst >= 0) // not a universal edge
return;
dst = ~dst;
unsigned& nd = seen[dst];
if (nd == -1U)
{
std::vector<unsigned>
tmp(old_dests.data() + dst + 1,
old_dests.data() + dst + 1 + old_dests[dst]);
std::sort(tmp.begin(), tmp.end());
tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
auto p = uniq.emplace(tmp, 0);
if (!p.second)
{
nd = p.first->second;
}
else
{
nd = g.new_univ_dests(tmp.begin(), tmp.end());
p.first->second = nd;
}
}
in_dst = nd;
};
unsigned tend = edges.size();
for (unsigned t = 1; t < tend; t++)
{
if (g.is_dead_edge(t))
continue;
fixup(edges[t].dst);
}
fixup(init_number_);
}
void twa_graph::merge_edges()
{
set_named_prop("highlight-edges", nullptr);
g_.remove_dead_edges_();
if (is_alternating())
merge_univ_dests();
typedef graph_t::edge_storage_t tr_t;
g_.sort_edges_([](const tr_t& lhs, const tr_t& rhs)

View file

@ -521,6 +521,11 @@ namespace spot
/// extremities and acceptance.
void merge_edges();
/// \brief marge common universal destination
///
/// This is already called by merge_edges().
void merge_univ_dests();
/// \brief Remove all dead states
///
/// Dead states are all the states that cannot be part of

View file

@ -360,10 +360,14 @@ namespace spot
return bdd_format_formula(aut_->get_dict(), label);
}
std::set<int> done;
void
print_dst(int dst, const char* style = nullptr)
{
os_ << " " << dst << " [label=<>,width=0,height=0,shape=none]\n";
if (!done.emplace(dst).second)
return;
os_ << " " << dst << " [label=<>,shape=point]\n";
for (unsigned d: aut_->univ_dests(dst))
{
os_ << " " << dst << " -> " << d;
@ -465,7 +469,7 @@ namespace spot
}
else
{
os_ << " [dir=none]\n";
os_ << " [arrowhead=onormal]\n";
print_dst(init);
}
}
@ -626,11 +630,10 @@ namespace spot
os_ << ", " << highlight;
}
}
// No arrow tip of the common part of a universal transition
if ((int)t.dst < 0)
os_ << ", dir=none";
if (aut_->is_univ_dest(t.dst))
os_ << ", arrowhead=onormal";
os_ << "]\n";
if ((int)t.dst < 0) // Universal destination
if (aut_->is_univ_dest(t.dst))
print_dst(t.dst, highlight.c_str());
}