improve translation of ms-phi-h=2..3
* spot/twaalgos/gfguarantee.cc: Rework the history computation to keep an overapproximation of the history, and a longer one. Also replay the history even if there is no initial trivial SCC. This helps with translating FG(!a|XXXb) where we need to keep the history of a, but we were previously unable to do so because some state had both "a" and "ab" as input. * spot/twaalgos/translate.cc: Optimize the product of suspendable automata by removing useless trivial SCCs. * tests/core/genltl.test, tests/core/satmin.test, NEWS: Adjust expected results.
This commit is contained in:
parent
c9131aee72
commit
621fb818e3
5 changed files with 135 additions and 61 deletions
4
NEWS
4
NEWS
|
|
@ -113,8 +113,8 @@ New in spot 2.5.3.dev (not yet released)
|
|||
(GFa1 & GFa2) -> GFz 12 1 1
|
||||
(GFa1 & GFa2 & GFa3) -> GFz 41 1 1
|
||||
FG(a|b)|FG(!a|Xb) 4 2 2
|
||||
FG(a|b)|FG(!a|Xb)|FG(a|XXb) 21 5 4
|
||||
FG(a|b)|FG(!a|Xb)|FG(a|XXb)|FG(!a|XXXb) 170 15 8
|
||||
FG(a|b)|FG(!a|Xb)|FG(a|XXb) 21 4 4
|
||||
FG(a|b)|FG(!a|Xb)|FG(a|XXb)|FG(!a|XXXb) 170 8 8
|
||||
|
||||
- print_dot(), used to print automata in GraphViz's format,
|
||||
underwent several changes:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <spot/twaalgos/minimize.hh>
|
||||
#include <spot/twaalgos/dualize.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
// #include <spot/twa/bddprint.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
|
@ -54,6 +55,7 @@ namespace spot
|
|||
static twa_graph_ptr
|
||||
do_g_f_terminal_inplace(scc_info& si, bool state_based)
|
||||
{
|
||||
bool want_merge_edges = false;
|
||||
twa_graph_ptr aut = std::const_pointer_cast<twa_graph>(si.get_aut());
|
||||
|
||||
if (!is_terminal_automaton(aut, &si, true))
|
||||
|
|
@ -82,56 +84,56 @@ namespace spot
|
|||
// state. However if some successor of the initial state is
|
||||
// also trivial, we might be able to remove it as well if we
|
||||
// are able to replay two step from the initial state: this
|
||||
// can be generalized to more depth and require computing some
|
||||
// history for each state (i.e., a common suffix to all finite
|
||||
// words leading to this state).
|
||||
// can be generalized to more depth and requires computing
|
||||
// some history for each state (i.e., a common suffix to all
|
||||
// finite words leading to this state).
|
||||
//
|
||||
// We will replay such histories regardless of whether there
|
||||
// is actually some trivial leading SCCs that could be
|
||||
// removed, because it reduces the size of cycles in the
|
||||
// automaton, and this helps getting smaller products when
|
||||
// combining several automata generated this way.
|
||||
std::vector<std::vector<bdd>> histories;
|
||||
bool initial_state_trivial = si.is_trivial(si.initial());
|
||||
|
||||
if (is_det && initial_state_trivial)
|
||||
if (is_det)
|
||||
{
|
||||
// Compute the max number of steps we want to keep in
|
||||
// the history of each state. This is the largest
|
||||
// number of trivial SCCs you can chain from the initial
|
||||
// state.
|
||||
unsigned max_histories = 1;
|
||||
// the history of each state. This is the length of the
|
||||
// maximal path we can build from the initial state.
|
||||
unsigned max_histories;
|
||||
{
|
||||
std::vector<unsigned> depths(ns, 0U);
|
||||
for (unsigned d: si.succ(ns - 1))
|
||||
depths[d] = 2;
|
||||
for (int scc = ns - 2; scc >= 0; --scc)
|
||||
for (unsigned scc = 0; scc < ns; ++scc)
|
||||
{
|
||||
if (!si.is_trivial(scc))
|
||||
continue;
|
||||
unsigned sccd = depths[scc];
|
||||
max_histories = std::max(max_histories, sccd);
|
||||
++sccd;
|
||||
for (unsigned d: si.succ(scc))
|
||||
depths[d] = std::max(depths[d], sccd);
|
||||
unsigned depth = 0;
|
||||
for (unsigned succ: si.succ(scc))
|
||||
depth = std::max(depth, depths[succ]);
|
||||
depths[scc] = depth + si.states_of(scc).size();
|
||||
}
|
||||
max_histories = depths[ns - 1] - 1;
|
||||
}
|
||||
|
||||
unsigned numstates = aut->num_states();
|
||||
histories.resize(numstates);
|
||||
// Compute the one-letter history of each state. If all
|
||||
// transition entering a state have the same label, the history
|
||||
// is that label. Otherwise set it to bddfalse.
|
||||
// transition entering a state have the same label, the
|
||||
// history is that label. Otherwise, we make a
|
||||
// disjunction of all those labels, so that what we have
|
||||
// is an over-approximation of the history.
|
||||
for (auto& e: aut->edges())
|
||||
{
|
||||
std::vector<bdd>& hd = histories[e.dst];
|
||||
if (hd.empty())
|
||||
hd.push_back(e.cond);
|
||||
else if (hd[0] != e.cond)
|
||||
hd[0] = bddfalse;
|
||||
else
|
||||
hd[0] |= e.cond;
|
||||
}
|
||||
// remove all bddfalse, and check if there is a chance to build
|
||||
// a larger history.
|
||||
// check if there is a chance to build a larger history.
|
||||
bool should_continue = false;
|
||||
for (auto&h: histories)
|
||||
if (h.empty())
|
||||
continue;
|
||||
else if (h[0] == bddfalse)
|
||||
h.pop_back();
|
||||
else
|
||||
should_continue = true;
|
||||
// Augment those histories with more letters.
|
||||
|
|
@ -152,9 +154,11 @@ namespace spot
|
|||
else
|
||||
hd.push_back(bddfalse);
|
||||
}
|
||||
else if (hs.size() < historypos
|
||||
|| hd[historypos] != hs[historypos - 1])
|
||||
else
|
||||
{
|
||||
if (hs.size() >= historypos)
|
||||
hd[historypos] |= hs[historypos - 1];
|
||||
else
|
||||
hd[historypos] = bddfalse;
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +167,6 @@ namespace spot
|
|||
for (unsigned n = 0; n < numstates; ++n)
|
||||
{
|
||||
auto& h = histories[n];
|
||||
//for (auto&h: histories)
|
||||
if (h.size() <= historypos)
|
||||
continue;
|
||||
else if (h[historypos] == bddfalse)
|
||||
|
|
@ -196,10 +199,14 @@ namespace spot
|
|||
// It will loop
|
||||
if (term[si.scc_of(e.dst)])
|
||||
{
|
||||
if (!initial_state_trivial
|
||||
// If the source state is the initial state, we
|
||||
// should not try to combine it with itself...
|
||||
|| e.src == init)
|
||||
//
|
||||
// Also if the automaton is not deterministic
|
||||
// and there is no trivial initial state, then
|
||||
// we simply loop back to the initial state.
|
||||
if (e.src == init
|
||||
|| (!is_det && !initial_state_trivial))
|
||||
{
|
||||
e.dst = init;
|
||||
e.acc = {0};
|
||||
|
|
@ -224,11 +231,13 @@ namespace spot
|
|||
// history will get us back the terminal state. In both
|
||||
// cases, we should try again with a smaller history.
|
||||
unsigned moved_init = init;
|
||||
bdd econd = e.cond;
|
||||
bool first;
|
||||
if (is_det)
|
||||
{
|
||||
auto& h = histories[e.src];
|
||||
int hsize = h.size();
|
||||
for (int hlen = hsize - 1; hlen > 0; --hlen)
|
||||
for (int hlen = hsize - 1; hlen >= 0; --hlen)
|
||||
{
|
||||
for (int pos = hlen - 1; pos >= 0; --pos)
|
||||
{
|
||||
|
|
@ -248,16 +257,66 @@ namespace spot
|
|||
}
|
||||
// we have successfully played all history
|
||||
// to a non-terminal state.
|
||||
//
|
||||
// Combine this edge with any compatible edge from
|
||||
// the initial state.
|
||||
first = true;
|
||||
for (auto& ei: aut->out(moved_init))
|
||||
{
|
||||
bdd cond = ei.cond & econd;
|
||||
if (cond != bddfalse)
|
||||
{
|
||||
// We should never reach the terminal state,
|
||||
// unless the history is empty. In that
|
||||
// case (ts=true) we have to make a loop to
|
||||
// the initial state without any
|
||||
// combination.
|
||||
bool ts = term[si.scc_of(ei.dst)];
|
||||
if (ts && hlen > 0)
|
||||
goto failed;
|
||||
if (ts)
|
||||
want_merge_edges = true;
|
||||
if (first)
|
||||
{
|
||||
e.acc = {0};
|
||||
e.cond = cond;
|
||||
first = false;
|
||||
if (!ts)
|
||||
{
|
||||
e.dst = ei.dst;
|
||||
if (new_init == -1U)
|
||||
new_init = e.src;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_init = e.dst = init;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ts)
|
||||
{
|
||||
aut->new_edge(e.src, init, cond, {0});
|
||||
new_init = init;
|
||||
}
|
||||
else
|
||||
{
|
||||
aut->new_edge(e.src, ei.dst,
|
||||
cond, {0});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
failed:
|
||||
moved_init = init;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Combine this edge with any compatible edge from
|
||||
// the initial state.
|
||||
bdd econd = e.cond;
|
||||
bool first = true;
|
||||
else
|
||||
{
|
||||
first = true;
|
||||
for (auto& ei: aut->out(moved_init))
|
||||
{
|
||||
bdd cond = ei.cond & econd;
|
||||
|
|
@ -269,16 +328,15 @@ namespace spot
|
|||
e.acc = {0};
|
||||
e.cond = cond;
|
||||
first = false;
|
||||
if (new_init == -1U)
|
||||
new_init = e.src;
|
||||
}
|
||||
else
|
||||
{
|
||||
aut->new_edge(e.src, ei.dst, cond, {0});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -315,6 +373,8 @@ namespace spot
|
|||
}
|
||||
|
||||
aut->purge_unreachable_states();
|
||||
if (want_merge_edges)
|
||||
aut->merge_edges();
|
||||
return aut;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <spot/twaalgos/gfguarantee.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
|
@ -264,6 +265,19 @@ namespace spot
|
|||
else
|
||||
susp_aut = product_or(susp_aut, one);
|
||||
}
|
||||
if (susp_aut->prop_universal().is_true())
|
||||
{
|
||||
// In a deterministic and suspendable automaton, all
|
||||
// state recognize the same language, so we can move
|
||||
// the initial state into a bottom accepting SCC.
|
||||
scc_info si(susp_aut, scc_info_options::NONE);
|
||||
if (si.is_trivial(si.scc_of(susp_aut->get_init_state_number())))
|
||||
{
|
||||
assert(!si.is_trivial(0));
|
||||
susp_aut->set_init_state(si.one_state_of(0));
|
||||
susp_aut->purge_unreachable_states();
|
||||
}
|
||||
}
|
||||
if (aut == nullptr)
|
||||
aut = susp_aut;
|
||||
else if (is_and)
|
||||
|
|
|
|||
|
|
@ -191,9 +191,9 @@ cat >exp<<EOF
|
|||
"ms-phi-s=2",1
|
||||
"ms-phi-h=0",1
|
||||
"ms-phi-h=1",2
|
||||
"ms-phi-h=2",5
|
||||
"ms-phi-h=3",19
|
||||
"ms-phi-h=4",83
|
||||
"ms-phi-h=2",4
|
||||
"ms-phi-h=3",8
|
||||
"ms-phi-h=4",16
|
||||
"gf-equiv=0",1
|
||||
"gf-equiv=1",1
|
||||
"gf-equiv=2",1
|
||||
|
|
|
|||
|
|
@ -660,8 +660,8 @@ cat >expected <<'EOF'
|
|||
"!(G((p0) -> ((p1) U (p2))))","15",3
|
||||
"!(G((p0) -> ((p1) U (p2))))","16",3
|
||||
"!(G((p0) -> ((p1) U (p2))))","17",3
|
||||
"G(F((p0) <-> (X(X(p1)))))","1",6
|
||||
"G(F((p0) <-> (X(X(p1)))))","2",6
|
||||
"G(F((p0) <-> (X(X(p1)))))","1",4
|
||||
"G(F((p0) <-> (X(X(p1)))))","2",4
|
||||
"G(F((p0) <-> (X(X(p1)))))","3",4
|
||||
"G(F((p0) <-> (X(X(p1)))))","4",4
|
||||
"G(F((p0) <-> (X(X(p1)))))","6",4
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue