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

Introduce rational operators and trivial simplification rules.

Trivial simplifications rules (such as "FFa=Fa" or "x&1=x")
are performed any time a formule is instanciated.

* src/ltlast/constant.hh, src/ltlast/constant.cc
(true_instance, true_instance_): Declare the true_instance_ as a
static member, and move true_instance() into the .hh so it gets
inlined.  Have true_instance_ as a class variable will ensure that
it is the first formula instantiated.  Binop simplifications rely
on this to order arguments.
(false_instance, false_instance_): Likewise.
(empty_word_instance, empty_word_instance_): New method and static
member.
* src/ltlast/formula.hh (formula::formula): If max_count_ ever
loops, skip the first three values so that constants always have
smaller hash codes.
* src/ltlast/binop.hh, src/ltlast/binop.cc (instance): Add
simplifications and document them.
* src/ltlast/multop.hh (multop::Concat): New operator.
* src/ltlast/multop.cc (op_name): Handle Concat.
(instance): Inline Concat arguments without reordering.  Handle
absorbent and neutral elements for all operators.
* src/ltlast/unop.hh (unop::Star): New operator.
* src/ltlast/unop.cc (op_name): Handle Star.
(instance): Handle Star, and add trivial simplifications for
other unary operators.
* src/ltlparse/ltlparse.yy (OP_CONCAT, OP_STAR, CONST_EMPTYWORD):
Declare these new operators and add rules for them.
* src/ltlparse/ltlscan.ll (OP_CONCAT, OP_STAR, CONST_EMPTYWORD):
Output these new operators.
* src/ltltest/equals.test: New tests.
* src/ltltest/parse.test: Remove redundant test.
* src/ltltest/tunabbrev.test, src/tgbatest/emptchk.test: Adjust tests.
* src/ltlvisit/basicreduce.cc, src/ltlvisit/contain.cc,
src/ltlvisit/nenoform.cc, src/ltlvisit/reduce.cc,
src/ltlvisit/syntimpl.cc, src/ltlvisit/tostring.cc,
src/ltlvisit/tunabbrev.cc: Complete visitors to handle new
operators.
* src/ltltest/nenoform.test: More tests.
* src/ltlvisit/lunabbrev.cc (unabbreviate_logic_visitor::visit):
Clone formulae before instance() function actually have a chance
to destroy them.
* src/tgba/formula2bdd.cc, src/tgbaalgos/eltl2tgba_lacim.cc,
src/tgbaalgos/ltl2taa.cc, src/tgbaalgos/ltl2tgba_fm.cc,
src/tgbaalgos/ltl2tgba_lacim.cc: Adjust switches to assert on new
operators.
parent 4fcc4f82
......@@ -24,6 +24,8 @@
#include <cassert>
#include <utility>
#include "binop.hh"
#include "unop.hh"
#include "constant.hh"
#include "visitor.hh"
#include <iostream>
......@@ -127,25 +129,127 @@ namespace spot
binop::map binop::instances;
binop*
formula*
binop::instance(type op, formula* first, formula* second)
{
// Sort the operands of commutative operators, so that for
// example the formula instance for 'a xor b' is the same as
// that for 'b xor a'.
/// Trivial identities:
switch (op)
{
case Xor:
{
// Xor is commutative: sort operands.
formula_ptr_less_than cmp;
if (cmp(second, first) > 0)
std::swap(first, second);
}
// - (1 ^ Exp) = !Exp
// - (0 ^ Exp) = Exp
if (first == constant::true_instance())
return unop::instance(unop::Not, second);
if (first == constant::false_instance())
return second;
// We expect constants to appear first, because they are
// instantiated first.
assert(second != constant::false_instance());
assert(second != constant::true_instance());
break;
case Equiv:
if (second < first)
std::swap(first, second);
{
// Equiv is commutative: sort operands.
formula_ptr_less_than cmp;
if (cmp(second, first) > 0)
std::swap(first, second);
}
// - (0 <=> Exp) = !Exp
// - (1 <=> Exp) = Exp
if (first == constant::false_instance())
return unop::instance(unop::Not, second);
if (first == constant::true_instance())
return second;
// We expect constants to appear first, because they are
// instantiated first.
assert(second != constant::false_instance());
assert(second != constant::true_instance());
break;
case Implies:
/// - (1 => Exp) = Exp
/// - (0 => Exp) = 0
/// - (Exp => 1) = 1
/// - (Exp => 0) = !Exp
if (first == constant::true_instance())
return second;
if (first == constant::false_instance())
{
second->destroy();
return first;
}
if (second == constant::true_instance())
{
first->destroy();
return second;
}
if (second == constant::false_instance())
return unop::instance(unop::Not, first);
break;
case U:
case R:
/// - (Exp U 1) = 1
/// - (Exp U 0) = 0
/// - (0 U Exp) = Exp
if (second == constant::true_instance()
|| second == constant::false_instance()
|| first == constant::false_instance())
{
first->destroy();
return second;
}
break;
case W:
/// - (Exp W 1) = 1
/// - (0 W Exp) = Exp
/// - (1 W Exp) = 1
if (second == constant::true_instance()
|| first == constant::false_instance())
{
first->destroy();
return second;
}
if (first == constant::true_instance())
{
second->destroy();
return first;
}
break;
case R:
/// - (Exp R 1) = 1
/// - (Exp R 0) = 0
/// - (1 R Exp) = Exp
if (second == constant::true_instance()
|| second == constant::false_instance()
|| first == constant::true_instance())
{
first->destroy();
return second;
}
break;
case M:
// Non commutative operators.
/// - (Exp M 0) = 0
/// - (1 M Exp) = Exp
/// - (0 M Exp) = 0
if (second == constant::false_instance()
|| first == constant::true_instance())
{
first->destroy();
return second;
}
if (first == constant::false_instance())
{
second->destroy();
return first;
}
break;
}
......
......@@ -56,9 +56,37 @@ namespace spot
M //< strong release (dual of weak until)
};
/// Build an unary operator with operation \a op and
/// \brief Build a unary operator with operation \a op and
/// children \a first and \a second.
static binop* instance(type op, formula* first, formula* second);
///
/// Some reordering will be performed on arguments of commutative
/// operators (Xor and Equiv) to ensure that for instance (a <=> b)
/// is the same formula as (b <=> a).
///
/// Furthermore, the following trivial simplifications are
/// performed (the left formula is rewritten as the right
/// formula):
/// - (1 => Exp) = Exp
/// - (0 => Exp) = 0
/// - (Exp => 1) = 1
/// - (Exp => 0) = !Exp
/// - (1 ^ Exp) = !Exp
/// - (0 ^ Exp) = Exp
/// - (0 <=> Exp) = !Exp
/// - (1 <=> Exp) = Exp
/// - (Exp U 1) = 1
/// - (Exp U 0) = 0
/// - (0 U Exp) = Exp
/// - (Exp W 1) = 1
/// - (0 W Exp) = Exp
/// - (1 W Exp) = 1
/// - (Exp R 1) = 1
/// - (Exp R 0) = 0
/// - (1 R Exp) = Exp
/// - (Exp M 0) = 0
/// - (1 M Exp) = Exp
/// - (0 M Exp) = 0
static formula* instance(type op, formula* first, formula* second);
virtual void accept(visitor& v);
virtual void accept(const_visitor& v) const;
......
// Copyright (C) 2009 Laboratoire de Recherche et Développement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et D�veloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
// 6 (LIP6), d�partement Syst�mes R�partis Coop�ratifs (SRC),
// Universit� Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -29,6 +29,10 @@ namespace spot
{
namespace ltl
{
constant constant::true_instance_(constant::True);
constant constant::false_instance_(constant::False);
constant constant::empty_word_instance_(constant::EmptyWord);
constant::constant(type val)
: val_(val)
{
......@@ -47,6 +51,8 @@ namespace spot
return "constant(1)";
case False:
return "constant(0)";
case EmptyWord:
return "constant(e)";
}
// Unreachable code.
assert(0);
......@@ -80,24 +86,12 @@ namespace spot
return "1";
case False:
return "0";
case EmptyWord:
return "#e";
}
// Unreachable code.
assert(0);
return 0;
}
constant*
constant::false_instance()
{
static constant f(constant::False);
return &f;
}
constant*
constant::true_instance()
{
static constant t(constant::True);
return &t;
}
}
}
// Copyright (C) 2009 Laboratoire de Recherche et Développement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et D�veloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -38,7 +36,7 @@ namespace spot
class constant : public formula
{
public:
enum type { False, True };
enum type { False, True, EmptyWord };
virtual void accept(visitor& v);
virtual void accept(const_visitor& v) const;
......@@ -50,9 +48,11 @@ namespace spot
virtual std::string dump() const;
/// Get the sole instance of spot::ltl::constant::constant(True).
static constant* true_instance();
static constant* true_instance() { return &true_instance_; }
/// Get the sole instance of spot::ltl::constant::constant(False).
static constant* false_instance();
static constant* false_instance() { return &false_instance_; }
/// Get the sole instance of spot::ltl::constant::constant(EmptyWord).
static constant* empty_word_instance() { return &empty_word_instance_; }
protected:
constant(type val);
......@@ -60,6 +60,12 @@ namespace spot
private:
type val_;
static constant true_instance_;
static constant false_instance_;
static constant empty_word_instance_;
// If you add new constants here, be sure to update the
// formula::formula() constructor.
};
}
......
// Copyright (C) 2008, 2009 Laboratoire de Recherche et Développement
// Copyright (C) 2008, 2009, 2010 Laboratoire de Recherche et D�veloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -73,7 +71,15 @@ namespace spot
class formula
{
public:
formula() : count_(++max_count) {}
formula() : count_(max_count++)
{
// If the counter of formulae ever loops, we want to skip the
// first three values, because they are permanently associated
// to constants, and its convenient to have constants smaller
// than all other formulae.
if (max_count == 0)
max_count = 3;
}
/// Entry point for vspot::ltl::visitor instances.
virtual void accept(visitor& v) = 0;
......
// Copyright (C) 2009 Laboratoire de Recherche et Dveloppement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et D�veloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
// Paris 6 (LIP6), dpartement Systmes Rpartis Coopratifs (SRC),
// Universit Pierre et Marie Curie.
// Paris 6 (LIP6), d�partement Syst�mes R�partis Coop�ratifs (SRC),
// Universit� Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -110,6 +110,8 @@ namespace spot
return "And";
case Or:
return "Or";
case Concat:
return "Concat";
}
// Unreachable code.
assert(0);
......@@ -146,13 +148,24 @@ namespace spot
}
else
{
// All operator except "Concat" not commutative, so
// we just keep a list of the inlined arguments that
// should later be added to the vector.
// For concat we have to keep track of the order of
// all the arguments.
if (op == Concat)
inlined.push_back(*i);
++i;
}
}
v->insert(v->end(), inlined.begin(), inlined.end());
if (op == Concat)
*v = inlined;
else
v->insert(v->end(), inlined.begin(), inlined.end());
}
std::sort(v->begin(), v->end(), formula_ptr_less_than());
if (op != Concat)
std::sort(v->begin(), v->end(), formula_ptr_less_than());
formula* neutral;
formula* abs;
......@@ -166,15 +179,19 @@ namespace spot
neutral = constant::false_instance();
abs = constant::true_instance();
break;
case Concat:
neutral = constant::empty_word_instance();
abs = constant::false_instance();
break;
default:
neutral = 0;
abs = 0;
break;
}
// Remove duplicates. We can't use std::unique(), because we
// must destroy() any formula we drop. Also ignore neutral
// elements and handle absorbent elements.
// Remove duplicates (except for Concat). We can't use
// std::unique(), because we must destroy() any formula we drop.
// Also ignore neutral elements and handle absorbent elements.
{
formula* last = 0;
vec::iterator i = v->begin();
......@@ -194,7 +211,9 @@ namespace spot
}
else
{
last = *i++;
if (op != Concat) // Don't remove duplicates for Concat.
last = *i;
++i;
}
}
}
......
// Copyright (C) 2009 Laboratoire de Recherche et Développement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et D�veloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
// 6 (LIP6), d�partement Syst�mes R�partis Coop�ratifs (SRC),
// Universit� Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -38,12 +38,10 @@ namespace spot
/// \brief Multi-operand operators.
/// \ingroup ltl_ast
///
/// These operators are considered commutative and associative.
class multop : public ref_formula
{
public:
enum type { Or, And };
enum type { Or, And, Concat };
/// List of formulae.
typedef std::vector<formula*> vec;
......@@ -51,29 +49,46 @@ namespace spot
/// \brief Build a spot::ltl::multop with two children.
///
/// If one of the children itself is a spot::ltl::multop
/// with the same type, it will be merged. I.e., children
/// if that child will be added, and that child itself will
/// with the same type, it will be inlined. I.e., children
/// of that child will be added, and that child itself will
/// be destroyed. This allows incremental building of
/// n-ary ltl::multop.
///
/// This functions can perform slight optimizations and
/// may not return an ltl::multop objects. For instance
/// if \c first and \c second are equal, that formula is
/// returned as-is.
/// may not return an ltl::multop object. See the other
/// instance function for the list of rewritings.
static formula* instance(type op, formula* first, formula* second);
/// \brief Build a spot::ltl::multop with many children.
///
/// Same as the other instance() function, but take a vector of
/// formula in argument. This vector is acquired by the
/// formulae as argument. This vector is acquired by the
/// spot::ltl::multop class, the caller should allocate it with
/// \c new, but not use it (especially not destroy it) after it
/// has been passed to spot::ltl::multop.
///
/// This functions can perform slight optimizations and
/// may not return an ltl::multop objects. For instance
/// if the vector contain only one unique element, this
/// this formula will be returned as-is.
/// All operators (Or, And, Concat) are associative, and are
/// automatically inlined. Or and And are commutative, so their
/// argument are also sorted, to ensure that "a & b" is equal to
/// "b & a". For Or and And, duplicate arguments are also
/// removed.
///
/// Furthermore this function can perform slight optimizations
/// and may not return an ltl::multop object. For instance if
/// the vector contains only one unique element, this this
/// formula will be returned as-is. Neutral and absorbent element
/// are also taken care of. The following rewriting are performed
/// (the left patterns are rewritten as shown on the right):
///
/// - Concat(Exps1...,#e,Exps2...) = Concat(Exps1...,Exps2...)
/// - Concat(Exps1...,0,Exps2...) = 0
/// - Concat(Exp) = Exp
/// - And(Exps1...,1,Exps2...) = And(Exps1...,Exps2...)
/// - And(Exps1...,0,Exps2...) = 0
/// - And(Exp) = Exp
/// - Or(Exps1...,1,Exps2...) = 1
/// - Or(Exps1...,0,Exps2...) = And(Exps1...,Exps2...)
/// - Or(Exp) = Exp
static formula* instance(type op, vec* v);
virtual void accept(visitor& v);
......
// Copyright (C) 2009 Laboratoire de Recherche et Dveloppement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et Dveloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris
// 6 (LIP6), dpartement Systmes Rpartis Coopratifs (SRC),
// Universit Pierre et Marie Curie.
// 6 (LIP6), dpartement Systmes Rpartis Coopratifs (SRC),
// Universit Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -25,6 +25,7 @@
#include "visitor.hh"
#include <cassert>
#include <iostream>
#include "constant.hh"
namespace spot
{
......@@ -98,6 +99,8 @@ namespace spot
return "G";
case Finish:
return "Finish";
case Star:
return "Star";
}
// Unreachable code.
assert(0);
......@@ -106,9 +109,78 @@ namespace spot
unop::map unop::instances;
unop*
formula*
unop::instance(type op, formula* child)
{
// Some trivial simplifications.
switch (op)
{
// We have (0*) == (#e)
// (#e*) == (#e)
case Star:
if (child == constant::false_instance()
|| child == constant::empty_word_instance())
return constant::empty_word_instance();
// -- fall thru --
case F:
case G:
{
// F, G, * are idempotent.
unop* u = dynamic_cast<unop*>(child);
if (u && u->op() == op)
return u;
if (op == Star)
break;
// F(0) = G(0) = 0
// F(1) = G(1) = 1
if (child == constant::false_instance()
|| child == constant::true_instance())
return child;
// F(#e) = G(#e) = 1
if (child == constant::empty_word_instance())
return constant::true_instance();
}
case Not:
{
// !1 = 0
if (child == constant::true_instance())
return constant::false_instance();
// !0 = 1
if (child == constant::false_instance())
return constant::true_instance();
// Not is an involution.
unop* u = dynamic_cast<unop*>(child);
if (u && u->op() == op)
{
formula* c = u->child()->clone();
u->destroy();
return c;
}
break;
}
case X:
// X(1) = 1, X(0) = 0
if (child == constant::true_instance()
|| child == constant::false_instance())
return child;
// X(#e) = 1
if (child == constant::empty_word_instance())
return constant::true_instance();
break;
case Finish:
// No simplifications for Finish.
break;
}
pair p(op, child);
map::iterator i = instances.find(p);
if (i != instances.end())
......
// Copyright (C) 2009 Laboratoire de Recherche et Développement
// Copyright (C) 2009, 2010 Laboratoire de Recherche et Dveloppement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
// 6 (LIP6), dpartement Systmes Rpartis Coopratifs (SRC),
// Universit Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
......@@ -40,11 +40,40 @@ namespace spot
class unop : public ref_formula
{
public:
enum type { Not, X, F, G, Finish }; // Finish is used in ELTL formulae.
/// Build an unary operator with operation \a op and
enum type {
// LTL
Not, X, F, G,
// ELTL
Finish,
// Kleene Star
Star,
};
/// \brief Build an unary operator with operation \a op and
/// child \a child.
static unop* instance(type op, formula* child);
///
/// The following trivial simplifications are performed
/// automatically (the left expression is rewritten as the right
/// expression):
/// - 0* = #e
/// - #e* = #e
/// - Exp** = Exp*
/// - FF(Exp) = F(Exp)
/// - GG(Exp) = G(Exp)
/// - F(0) = 0
/// - G(0) = 0
/// - F(1) = 1
/// - G(1) = 1
/// - F(#e) = 1
/// - G(#e) = 1
/// - !1 = 0
/// - !0 = 1
/// - !!Exp = Exp
///
/// This rewriting implies that it is not possible to build an