Commit 395793d9 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

Rewrite a&XGa as Ga, and a|XFa as Fa.

The actual rules are a bit more complex:
a & X(G(a&b...)&c...) = Ga & X(G(b...)&c...)
a | X(Fa | c) = F(a) | c
with the second rule being applied only if all XF can
be removed.  See the documentation for an example.

* src/ltlvisit/simplify.cc: Implement these new rules.
* doc/tl/tl.tex: Document them.
* src/ltltest/reduccmp.test: Add test cases.
parent 58f99203
......@@ -1008,8 +1008,8 @@ instance using the following methods:
\\\texttt{is\_universal()}& Whether the formula is purely universal.
\\\texttt{is\_syntactic\_safety()}& Whether the formula is a syntactic
safety property.
\\\texttt{is\_syntactic\_guaranty()}& Whether the formula is a syntactic
guaranty property.
\\\texttt{is\_syntactic\_guarantee()}& Whether the formula is a syntactic
guarantee property.
\\\texttt{is\_syntactic\_obligation()}& Whether the formula is a syntactic
obligation property.
\\\texttt{is\_syntactic\_recurrence()}& Whether the formula is a syntactic
......@@ -1144,7 +1144,7 @@ The following grammar rules describes extend the aforementioned
work slightly by dealing with PSL operators. These are the
rules used by Spot to decide upon
construction to which class a formula belongs (see the methods
\texttt{is\_syntactic\_safety()}, \texttt{is\_syntactic\_guaranty()},
\texttt{is\_syntactic\_safety()}, \texttt{is\_syntactic\_guarantee()},
\texttt{is\_syntactic\_obligation()},
\texttt{is\_syntactic\_recurrence()}, and
\texttt{is\_syntactic\_persistence()} listed on
......@@ -1291,7 +1291,7 @@ The goals in most of these simplification are to:
a kind of disjunctive form: $\displaystyle\bigvee_i
\left(\beta_i\land\X\psi_i\right)$ where $\beta_i$s are Boolean
formul\ae{} and $\psi_i$s are LTL formul\ae{}. Moving $\X$ to the
front therefore simplify the translation.
front therefore simplifies the translation.
\item move the $\F$ operators to the front of the formula (e.g., $\F(f
\OR g)$ is better than the equivalent $(\F f)\OR (\F g)$), but not
before $\X$ ($\X\F f$ is better than $\F\X f$). Because $\F f$
......@@ -1347,6 +1347,8 @@ $\OR$):
(\X f) \OR (\X g) &\equiv \X(f\OR g) \\
(\X f) \AND(\F\G g) &\equiv \X(f\AND \F\G g) &
(\X f) \OR (\G\F g) &\equiv \X(f\OR \G\F g) \\
(\G f) \AND(\G g) &\equiv \G(f\AND g) &
(\F f) \OR (\F g) &\equiv \F(f\OR g) \\
(f_1 \U f_2)\AND (f_3 \U f_2)&\equiv (f_1\AND f_3)\U f_2&
(f_1 \U f_2)\OR (f_1 \U f_3)&\equiv f_1\U (f_2\OR f_3) \\
(f_1 \U f_2)\AND (f_3 \W f_2)&\equiv (f_1\AND f_3)\U f_2&
......@@ -1372,6 +1374,19 @@ The above rules are applied even if more terms are presents in the
operator's arguments. For instance $\F\G(a)\AND \G(b) \AND \F\G(c) \AND
\X(d)$ will be rewritten as $\X(d \AND \F\G(a\AND c))\AND \G(b)$.
The following more complicated rules are generalization of $f\AND
\X\G f\equiv \G f$ and $f\OR \X\F f\equiv \F f$:
\begin{align*}
f\AND \X(\G(f\AND g\ldots)\AND h\ldots) &\equiv \G(f) \AND \X(\G(g\ldots)\AND h\ldots) \\
f\OR \X(\F(f)\OR h\ldots) &\equiv \F(f) \OR \X(h\ldots)
\end{align*}
The latter rule for $f\OR \X(\F(f)\OR h\ldots)$ is only applied if all
$\F$-formul\ae{} can be removed from the argument of $\X$ with the
rewriting. For instance $a \OR b \OR c\OR \X(\F(a\OR b)\OR \F(c)\OR \G d)$
will be rewritten to $\F(a \OR b \OR c) \OR \X\G d$ but
$b \OR c\OR \X(\F(a\OR b)\OR \F(c)\OR \G d)$ would only become
$b \OR c\OR \X(\F(a\OR b\OR c)\OR \G d)$.
Finally the following rule is applied only when no other terms are present
in the OR arguments:
\begin{align*}
......
......@@ -171,6 +171,16 @@ for x in ../reduccmp ../reductaustr; do
run 0 $x 'G(a R b)' 'Gb'
run 0 $x 'G(a W b)' 'G(a | b)'
run 0 $x 'a & XGa' 'Ga'
run 0 $x 'a & XG(a&b)' '(XGb)&(Ga)'
run 0 $x 'a & b & XG(a&b)' 'G(a&b)'
run 0 $x 'a & b & X(Ga&Gb)' 'G(a&b)'
run 0 $x 'a & b & XGa &XG(b)' 'G(a&b)'
run 0 $x 'a & b & XGa & XGc' 'b & Ga & XGc'
run 0 $x 'a & b & X(G(a&d) & b) & X(Gc)' 'b & Ga & X(b & G(c&d))'
run 0 $x 'a|b|c|X(F(a|b)|F(c)|Gd)' 'F(a|b|c)|XGd'
run 0 $x 'b|c|X(F(a|b)|F(c)|Gd)' 'b|c|X(F(a|b|c)|Gd)'
# Syntactic implication
run 0 $x '(a & b) R (a R c)' '(a & b)R c'
run 0 $x 'a R ((a & b) R c)' '(a & b)R c'
......
......@@ -754,6 +754,29 @@ namespace spot
return is_binop(f, binop::W);
}
multop*
is_multop(formula* f, multop::type op)
{
if (f->kind() != formula::MultOp)
return 0;
multop* mo = static_cast<multop*>(f);
if (mo->op() != op)
return 0;
return mo;
}
multop*
is_And(formula* f)
{
return is_multop(f, multop::And);
}
multop*
is_Or(formula* f)
{
return is_multop(f, multop::Or);
}
formula*
unop_multop(unop::type uop, multop::type mop, multop::vec* v)
{
......@@ -1846,6 +1869,106 @@ namespace spot
case multop::And:
if (!mo->is_sere_formula())
{
// a & X(G(a&b...) & c...) = Ga & X(G(b...) & c...)
if (!mo->is_X_free())
{
typedef Sgi::hash_set<formula*,
ptr_hash<formula> > fset_t;
fset_t xgset;
fset_t xset;
unsigned s = res->size();
// Make a pass to search for subterms
// of the form XGa or X(... & G(...&a&...) & ...)
for (unsigned n = 0; n < s; ++n)
{
if (!(*res)[n])
continue;
unop* uo = is_X((*res)[n]);
if (!uo)
continue;
formula* c = uo->child();
multop* a;
unop* g;
if ((g = is_G(c)))
{
#define HANDLE_G multop* a2; \
if ((a2 = is_And(g->child()))) \
{ \
unsigned y = a2->size(); \
for (unsigned n = 0; n < y; ++n) \
xgset. \
insert(a2->nth(n)->clone()); \
} \
else \
{ \
xgset.insert(g->child()->clone()); \
}
HANDLE_G;
}
else if ((a = is_And(c)))
{
unsigned z = a->size();
for (unsigned m = 0; m < z; ++m)
{
formula* x = a->nth(m);
if ((g = is_G(x)))
{
HANDLE_G;
}
else
{
xset.insert(x->clone());
}
}
}
else
{
xset.insert(c->clone());
}
(*res)[n]->destroy();
(*res)[n] = 0;
}
// Make second pass to search for terms 'a'
// that also appears as 'XGa'. Replace them
// by 'Ga' and delete XGa.
for (unsigned n = 0; n < s; ++n)
{
formula* x = (*res)[n];
if (!x)
continue;
fset_t::const_iterator g = xgset.find(x);
if (g != xgset.end())
{
// x can appear only once.
formula* gf = *g;
xgset.erase(g);
gf->destroy();
(*res)[n] = unop::instance(unop::G, x);
}
}
multop::vec* xv = new multop::vec;
size_t xgs = xgset.size();
xv->reserve(xset.size() + 1);
if (xgs > 0)
{
multop::vec* xgv = new multop::vec;
xgv->reserve(xgs);
fset_t::iterator i;
for (i = xgset.begin(); i != xgset.end(); ++i)
xgv->push_back(*i);
formula* gv = multop::instance(multop::And, xgv);
xv->push_back(unop::instance(unop::G, gv));
}
fset_t::iterator j;
for (j = xset.begin(); j != xset.end(); ++j)
xv->push_back(*j);
formula* av = multop::instance(multop::And, xv);
res->push_back(unop::instance(unop::X, av));
}
// Gather all operands by type.
mospliter s(mospliter::Strip_X |
mospliter::Strip_FG |
......@@ -1855,9 +1978,11 @@ namespace spot
mospliter::Split_R_or_M |
mospliter::Split_EventUniv,
res, c_);
// FG(a) & FG(b) = FG(a & b)
formula* allFG = unop_unop_multop(unop::F, unop::G,
multop::And, s.res_FG);
// Xa & Xb = X(a & b)
// Xa & Xb & FG(c) = X(a & b & FG(c))
// For Universal&Eventual formulae f1...fn we also have:
......@@ -2346,6 +2471,124 @@ namespace spot
}
case multop::Or:
{
// a | X(F(a|b...) | c...) = Fa | X(F(b...) | c...)
if (!mo->is_X_free())
{
typedef Sgi::hash_set<formula*,
ptr_hash<formula> > fset_t;
fset_t xfset;
fset_t xset;
unsigned s = res->size();
// Make a pass to search for subterms
// of the form XFa or X(... | F(...|a|...) | ...)
for (unsigned n = 0; n < s; ++n)
{
if (!(*res)[n])
continue;
unop* uo = is_X((*res)[n]);
if (!uo)
continue;
formula* c = uo->child();
multop* o;
unop* f;
if ((f = is_F(c)))
{
#define HANDLE_F multop* o2; \
if ((o2 = is_Or(f->child()))) \
{ \
unsigned y = o2->size(); \
for (unsigned n = 0; n < y; ++n) \
xfset. \
insert(o2->nth(n)->clone()); \
} \
else \
{ \
xfset.insert(f->child()->clone()); \
}
HANDLE_F;
}
else if ((o = is_Or(c)))
{
unsigned z = o->size();
for (unsigned m = 0; m < z; ++m)
{
formula* x = o->nth(m);
if ((f = is_F(x)))
{
HANDLE_F;
}
else
{
xset.insert(x->clone());
}
}
}
else
{
xset.insert(c->clone());
}
(*res)[n]->destroy();
(*res)[n] = 0;
}
// Make a second pass to check if we can
// remove all instance of XF(a).
unsigned allofthem = xfset.size();
for (unsigned n = 0; n < s; ++n)
{
formula* x = (*res)[n];
if (!x)
continue;
fset_t::const_iterator f = xfset.find(x);
if (f != xfset.end())
--allofthem;
assert(allofthem != -1U);
}
// If we can remove all of them...
if (allofthem == 0)
// Make third pass to search for terms 'a'
// that also appears as 'XFa'. Replace them
// by 'Fa' and delete XFa.
for (unsigned n = 0; n < s; ++n)
{
formula* x = (*res)[n];
if (!x)
continue;
fset_t::const_iterator f = xfset.find(x);
if (f != xfset.end())
{
// x can appear only once.
formula* ff = *f;
xfset.erase(f);
ff->destroy();
(*res)[n] = unop::instance(unop::F, x);
}
}
// Now rebuild the formula that remains.
multop::vec* xv = new multop::vec;
size_t xfs = xfset.size();
xv->reserve(xset.size() + 1);
if (xfs > 0)
{
// Group all XF(a)|XF(b|c|...)|... as XF(a|b|c|...)
multop::vec* xfv = new multop::vec;
xfv->reserve(xfs);
fset_t::iterator i;
for (i = xfset.begin(); i != xfset.end(); ++i)
xfv->push_back(*i);
formula* fv = multop::instance(multop::Or, xfv);
xv->push_back(unop::instance(unop::F, fv));
}
// Also gather the remaining Xa | X(b|c) as X(b|c).
fset_t::iterator j;
for (j = xset.begin(); j != xset.end(); ++j)
xv->push_back(*j);
formula* ov = multop::instance(multop::Or, xv);
res->push_back(unop::instance(unop::X, ov));
}
// Gather all operand by type.
mospliter s(mospliter::Strip_X |
mospliter::Strip_GF |
......@@ -2395,7 +2638,7 @@ namespace spot
// F(a|GFb)|Gc 5st.11tr. without initial self-loop
// Fa|GFb|Gc 5st.10tr. without initial self-loop
// (counting the number of "subtransitions"
// or, degeneralizing the automaton amplify
// or, degeneralizing the automaton amplifies
// these differences)
s.res_F->push_back(allGF);
allGF = 0;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment