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

Add new simplification rules like: "a | (Xa R b)" gives "b U a".

* src/ltlvisit/simplify.cc: Add new rules.
* doc/tl/tl.tex: Document them.
* src/ltltest/reduccmp.test: Add test cases.
parent 00598853
......@@ -559,7 +559,7 @@ is usually used to simplify proofs.
\G f &\equiv \NOT\F\NOT f \equiv \NOT((\NOT f)\M\1)\\
f\U g&\equiv ((\X g)\M f)\OR g \\
f \W g&\equiv (f\U g)\OR \G f \equiv ((\X g)\M f)\OR g\OR \NOT((\NOT f)\M\1)\\
f \R g&\equiv (f \M g)\OR\G f \equiv (f \M g)\OR \NOT((\NOT f)\M\1)
f \R g&\equiv (f \M g)\OR\G g \equiv (f \M g)\OR \NOT((\NOT g)\M\1)
\end{align*}}
\ltltodo[noline]{Why do we have two ways to rewrite $f\W g$ with $\U$,
and two ways to rewrite $f\M g$ with $\R$, but no similar rules for other operators? What are we missing?}
......@@ -571,10 +571,9 @@ successively rewrite $\U\to\M\to\R\to\U$ we get
\begin{align*}
f\U g &\equiv ((\X g)\M f)\OR g \\
&\equiv (((\X g) \R f)\AND\NOT (\0\R\NOT \X g))\OR g \\
&\equiv (((f \U (\X g\AND f))\OR\NOT(\1\U\NOT f)) \AND\NOT (\underbrace{((\X g) \U
\0)}_{\text{trivially false}}\OR\NOT(\1\U\NOT\X g)))\OR g\\
&\equiv (((f \U (\X g\AND f))\OR\NOT(\1\U\NOT f))
\AND(\1\U\NOT\X g))\OR g\\
&\equiv (((f \U (\X g\AND f))\OR\NOT(\1\U\NOT f)) \AND\NOT (\underbrace{((\NOT\X g) \U
(\0\AND \NOT\X g))}_{\text{trivially false}}\OR\NOT(\1\U\NOT\NOT\X g)))\OR g\\
&\equiv (((f \U (\X g\AND f))\OR\NOT(\1\U\NOT f)) \AND(\1\U\X g))\OR g\\
\end{align*}
......@@ -1382,7 +1381,11 @@ $\OR$):
(\F f)\AND (f \R g)&\equiv f\M g &
(\G g)\OR (f \R g)&\equiv f\R g \\
(\F f)\AND (f \M g)&\equiv f\M g &
(\G g)\OR (f \M g)&\equiv f\R g
(\G g)\OR (f \M g)&\equiv f\R g \\
f \AND ((\X f) \W g) &\equiv g \R f &
f \OR ((\X f) \R g) &\equiv g \W f \\
f \AND ((\X f) \U g) &\equiv g \M f &
f \OR ((\X f) \M g) &\equiv g \U f \\
\end{align*}
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
......
......@@ -181,6 +181,17 @@ for x in ../reduccmp ../reductaustr; do
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)'
run 0 $x 'a | (Xa R b) | c' '(b W a) | c'
run 0 $x 'a | (Xa M b) | c' '(b U a) | c'
run 0 $x 'a | (Xa M b) | (Xa R c)' '(b U a) | (c W a)'
run 0 $x 'a | (Xa M b) | XF(a)' 'Fa'
run 0 $x 'a | (Xa R b) | XF(a)' '(b W a) | Fa' # Gb | Fa ?
run 0 $x 'a & (Xa W b) & c' '(b R a) & c'
run 0 $x 'a & (Xa U b) & c' '(b M a) & c'
run 0 $x 'a & (Xa W b) & (Xa U c)' '(b R a) & (c M a)'
run 0 $x 'a & (Xa W b) & XGa' 'Ga'
run 0 $x 'a & (Xa U b) & XGa' '(b M a) & Ga' # Fb & Ga ?
# 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,40 @@ namespace spot
return is_binop(f, binop::W);
}
// X(a) R b or X(a) M b
// This returns a.
formula*
is_XRM(formula* f)
{
if (f->kind() != formula::BinOp)
return 0;
binop* bo = static_cast<binop*>(f);
binop::type t = bo->op();
if (t != binop::R && t != binop::M)
return 0;
unop* uo = is_X(bo->first());
if (!uo)
return 0;
return uo->child();
}
// X(a) W b or X(a) U b
// This returns a.
formula*
is_XWU(formula* f)
{
if (f->kind() != formula::BinOp)
return 0;
binop* bo = static_cast<binop*>(f);
binop::type t = bo->op();
if (t != binop::W && t != binop::U)
return 0;
unop* uo = is_X(bo->first());
if (!uo)
return 0;
return uo->child();
}
multop*
is_multop(formula* f, multop::type op)
{
......@@ -1870,20 +1904,35 @@ namespace spot
if (!mo->is_sere_formula())
{
// a & X(G(a&b...) & c...) = Ga & X(G(b...) & c...)
// a & (Xa W b) = b R a
// a & (Xa U b) = b M a
if (!mo->is_X_free())
{
typedef Sgi::hash_set<formula*,
ptr_hash<formula> > fset_t;
fset_t xgset;
fset_t xset;
typedef Sgi::hash_map<formula*, std::set<unsigned>,
ptr_hash<formula> > fmap_t;
fset_t xgset; // XG(...)
fset_t xset; // X(...)
fmap_t wuset; // (X...)W(...) or (X...)U(...)
unsigned s = res->size();
std::vector<bool> tokill(s);
// 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;
formula* xarg = is_XWU((*res)[n]);
if (xarg)
{
wuset[xarg].insert(n);
continue;
}
unop* uo = is_X((*res)[n]);
if (!uo)
continue;
......@@ -1930,7 +1979,38 @@ namespace spot
(*res)[n]->destroy();
(*res)[n] = 0;
}
// Make second pass to search for terms 'a'
// Make a second pass to check if the "a"
// terms can be used to simplify "Xa W b" or
// "Xa U b".
for (unsigned n = 0; n < s; ++n)
{
if (!(*res)[n])
continue;
fmap_t::const_iterator gs =
wuset.find((*res)[n]);
if (gs == wuset.end())
continue;
const std::set<unsigned>& s = gs->second;
std::set<unsigned>::const_iterator g;
for (g = s.begin(); g != s.end(); ++g)
{
// a & (Xa W b) = b R a
// a & (Xa U b) = b M a
unsigned pos = *g;
binop* wu = down_cast<binop*>((*res)[pos]);
binop::type t = (wu->op() == binop::U)
? binop::M : binop::R;
unop* xa = down_cast<unop*>(wu->first());
formula* a = xa->child()->clone();
formula* b = wu->second()->clone();
wu->destroy();
(*res)[pos] = binop::instance(t, b, a);
// Remember to kill "a".
tokill[n] = true;
}
}
// Make third 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)
......@@ -1947,6 +2027,11 @@ namespace spot
gf->destroy();
(*res)[n] = unop::instance(unop::G, x);
}
else if (tokill[n])
{
(*res)[n]->destroy();
(*res)[n] = 0;
}
}
multop::vec* xv = new multop::vec;
......@@ -2471,21 +2556,36 @@ namespace spot
}
case multop::Or:
{
// a | X(F(a|b...) | c...) = Fa | X(F(b...) | c...)
// a | X(F(a) | c...) = Fa | X(c...)
// a | (Xa R b) = b W a
// a | (Xa M b) = b U a
if (!mo->is_X_free())
{
typedef Sgi::hash_set<formula*,
ptr_hash<formula> > fset_t;
fset_t xfset;
fset_t xset;
typedef Sgi::hash_map<formula*, std::set<unsigned>,
ptr_hash<formula> > fmap_t;
fset_t xfset; // XF(...)
fset_t xset; // X(...)
fmap_t rmset; // (X...)R(...) or (X...)M(...)
unsigned s = res->size();
std::vector<bool> tokill(s);
// 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;
formula* xarg = is_XRM((*res)[n]);
if (xarg)
{
rmset[xarg].insert(n);
continue;
}
unop* uo = is_X((*res)[n]);
if (!uo)
continue;
......@@ -2544,6 +2644,29 @@ namespace spot
if (f != xfset.end())
--allofthem;
assert(allofthem != -1U);
// At the same time, check if "a" can also
// be used to simplify "Xa R b" or "Xa M b".
fmap_t::const_iterator gs = rmset.find(x);
if (gs == rmset.end())
continue;
const std::set<unsigned>& s = gs->second;
std::set<unsigned>::const_iterator g;
for (g = s.begin(); g != s.end(); ++g)
{
// a | (Xa R b) = b W a
// a | (Xa M b) = b U a
unsigned pos = *g;
binop* rm = down_cast<binop*>((*res)[pos]);
binop::type t =
(rm->op() == binop::M) ? binop::U : binop::W;
unop* xa = down_cast<unop*>(rm->first());
formula* a = xa->child()->clone();
formula* b = rm->second()->clone();
rm->destroy();
(*res)[pos] = binop::instance(t, b, a);
// Remember to kill "a".
tokill[n] = true;
}
}
// If we can remove all of them...
if (allofthem == 0)
......@@ -2563,8 +2686,18 @@ namespace spot
xfset.erase(f);
ff->destroy();
(*res)[n] = unop::instance(unop::F, x);
// We don't need to kill "a" anymore.
tokill[n] = false;
}
}
// Kill any remaining "a", used to simplify Xa R b
// or Xa M b.
for (unsigned n = 0; n < s; ++n)
if (tokill[n] && (*res)[n])
{
(*res)[n]->destroy();
(*res)[n] = 0;
}
// Now rebuild the formula that remains.
multop::vec* xv = new multop::vec;
......
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