Translate Boolean formulae as BDD using the ltl_simplifier cache.

* src/ltlvisit/simplify.hh, src/ltlvisit/simplify.cc
(ltl_simplifier::ltl_simplifier, ltl_simplifier::get_dict): Make
it possible to supply and retrieve the dictionary used.
(ltl_simplifier::as_bdd): New function, exported from the cache.
* src/tgbaalgos/ltl2tgba_fm.cc (translate_dict): Store the
ltl_simplifier object.
(translate_dict::boolean_to_bdd): Call ltl_simplifier::as_bdd.
(translate_ratexp): New wrapper around the ratexp_trad_visitor,
calling boolean_to_bdd whenever possible.
(ratexp_trad_visitor): Do not deal with negated formulae, there
are necessarily Boolean and handled by translate_ratexp().
(ltl_visitor): Adjust to call translate_ratexp.
(ltl_to_tgba_fm): Adjust passing of the ltl_simplifier to the
translate_dict, and make sure everybody is using the same
dictionary.
* src/tgbatest/ltl2tgba.cc: Pass the dictionary to the
ltl_simplifier.
This commit is contained in:
Alexandre Duret-Lutz 2011-10-30 18:59:17 +01:00
parent 369ad87e50
commit 07e40e706a
4 changed files with 149 additions and 77 deletions

View file

@ -26,10 +26,8 @@
#define trace while (0) std::cerr #define trace while (0) std::cerr
#endif #endif
#include "simplify.hh" #include "simplify.hh"
#include "misc/hash.hh" #include "misc/hash.hh"
#include "tgba/bdddict.hh"
#include "ltlast/allnodes.hh" #include "ltlast/allnodes.hh"
#include "ltlast/visitor.hh" #include "ltlast/visitor.hh"
#include "ltlvisit/contain.hh" #include "ltlvisit/contain.hh"
@ -52,7 +50,7 @@ namespace spot
typedef std::pair<const formula*, const formula*> pairf; typedef std::pair<const formula*, const formula*> pairf;
typedef std::map<pairf, bool> syntimpl_cache_t; typedef std::map<pairf, bool> syntimpl_cache_t;
public: public:
bdd_dict dict; bdd_dict* dict;
ltl_simplifier_options options; ltl_simplifier_options options;
language_containment_checker lcc; language_containment_checker lcc;
@ -98,16 +96,16 @@ namespace spot
} }
} }
dict_.unregister_all_my_variables(this); dict->unregister_all_my_variables(this);
} }
ltl_simplifier_cache() ltl_simplifier_cache(bdd_dict* d)
: lcc(&dict, true, true, false, false) : dict(d), lcc(d, true, true, false, false)
{ {
} }
ltl_simplifier_cache(ltl_simplifier_options opt) ltl_simplifier_cache(bdd_dict* d, ltl_simplifier_options opt)
: options(opt), lcc(&dict, true, true, false, false) : dict(d), options(opt), lcc(d, true, true, false, false)
{ {
opt.containment_checks |= opt.containment_checks_stronger; opt.containment_checks |= opt.containment_checks_stronger;
} }
@ -134,7 +132,7 @@ namespace spot
assert(!"Unsupported operator"); assert(!"Unsupported operator");
break; break;
case formula::AtomicProp: case formula::AtomicProp:
result = bdd_ithvar(dict_.register_proposition(f, this)); result = bdd_ithvar(dict->register_proposition(f, this));
break; break;
case formula::UnOp: case formula::UnOp:
{ {
@ -185,9 +183,9 @@ namespace spot
result |= as_bdd(mo->nth(n)); result |= as_bdd(mo->nth(n));
break; break;
} }
case multop::AndNLM:
case multop::Concat: case multop::Concat:
case multop::Fusion: case multop::Fusion:
case multop::AndNLM:
assert(!"Unsupported operator"); assert(!"Unsupported operator");
break; break;
} }
@ -328,7 +326,6 @@ namespace spot
} }
private: private:
bdd_dict dict_;
f2b_map as_bdd_; f2b_map as_bdd_;
f2f_map simplified_; f2f_map simplified_;
f2f_map nenoform_; f2f_map nenoform_;
@ -2592,19 +2589,42 @@ namespace spot
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// ltl_simplifier // ltl_simplifier
ltl_simplifier::ltl_simplifier() ltl_simplifier::ltl_simplifier(bdd_dict* d)
: cache_(new ltl_simplifier_cache)
{ {
if (!d)
{
d = new bdd_dict;
owndict = true;
}
else
{
owndict = false;
}
cache_ = new ltl_simplifier_cache(d);
} }
ltl_simplifier::ltl_simplifier(ltl_simplifier_options& opt) ltl_simplifier::ltl_simplifier(ltl_simplifier_options& opt, bdd_dict* d)
: cache_(new ltl_simplifier_cache(opt))
{ {
if (!d)
{
d = new bdd_dict;
owndict = true;
}
else
{
owndict = false;
}
cache_ = new ltl_simplifier_cache(d, opt);
} }
ltl_simplifier::~ltl_simplifier() ltl_simplifier::~ltl_simplifier()
{ {
bdd_dict* todelete = 0;
if (owndict)
todelete = cache_->dict;
delete cache_; delete cache_;
// It has to be deleted after the cache.
delete todelete;
} }
formula* formula*
@ -2644,5 +2664,17 @@ namespace spot
return cache_->lcc.equal(f, g); return cache_->lcc.equal(f, g);
} }
bdd
ltl_simplifier::as_bdd(const formula* f)
{
return cache_->as_bdd(f);
}
bdd_dict*
ltl_simplifier::get_dict() const
{
return cache_->dict;
}
} }
} }

View file

@ -23,6 +23,7 @@
#include "ltlast/formula.hh" #include "ltlast/formula.hh"
#include "bdd.h" #include "bdd.h"
#include "tgba/bdddict.hh"
namespace spot namespace spot
{ {
@ -66,8 +67,8 @@ namespace spot
class ltl_simplifier class ltl_simplifier
{ {
public: public:
ltl_simplifier(); ltl_simplifier(bdd_dict* dict = 0);
ltl_simplifier(ltl_simplifier_options& opt); ltl_simplifier(ltl_simplifier_options& opt, bdd_dict* dict = 0);
~ltl_simplifier(); ~ltl_simplifier();
/// Simplify the formula \a f (using options supplied to the /// Simplify the formula \a f (using options supplied to the
@ -118,11 +119,20 @@ namespace spot
/// two products, and two emptiness checks. /// two products, and two emptiness checks.
bool are_equivalent(const formula* f, const formula* g); bool are_equivalent(const formula* f, const formula* g);
/// \brief Convert a Boolean formula as a BDD.
///
/// If you plan to use this method, be sure to pass a bdd_dict
/// to the constructor.
bdd as_bdd(const formula* f);
/// Return the bdd_dict used.
bdd_dict* get_dict() const;
private: private:
ltl_simplifier_cache* cache_; ltl_simplifier_cache* cache_;
// Copy disallowed. // Copy disallowed.
ltl_simplifier(const ltl_simplifier&); ltl_simplifier(const ltl_simplifier&);
bool owndict;
}; };
} }

View file

@ -56,8 +56,9 @@ namespace spot
{ {
public: public:
translate_dict(bdd_dict* dict) translate_dict(bdd_dict* dict, ltl_simplifier* ls)
: dict(dict), : dict(dict),
ls(ls),
a_set(bddtrue), a_set(bddtrue),
var_set(bddtrue), var_set(bddtrue),
next_set(bddtrue) next_set(bddtrue)
@ -73,6 +74,7 @@ namespace spot
} }
bdd_dict* dict; bdd_dict* dict;
ltl_simplifier* ls;
typedef bdd_dict::fv_map fv_map; typedef bdd_dict::fv_map fv_map;
typedef bdd_dict::vf_map vf_map; typedef bdd_dict::vf_map vf_map;
@ -154,6 +156,14 @@ namespace spot
return 0; return 0;
} }
bdd
boolean_to_bdd(const formula* f)
{
bdd res = ls->as_bdd(f);
var_set &= bdd_support(res);
return res;
}
formula* formula*
conj_bdd_to_formula(bdd b, multop::type op = multop::And) const conj_bdd_to_formula(bdd b, multop::type op = multop::And) const
{ {
@ -299,15 +309,17 @@ namespace spot
bdd res_; bdd res_;
}; };
bdd translate_ratexp(const formula* f, translate_dict& dict,
formula* to_concat = 0);
// Rewrite rule for rational operators. // Rewrite rule for rational operators.
class ratexp_trad_visitor: public const_visitor class ratexp_trad_visitor: public const_visitor
{ {
public: public:
// negated should only be set for constants or atomic properties // negated should only be set for constants or atomic properties
ratexp_trad_visitor(translate_dict& dict, ratexp_trad_visitor(translate_dict& dict,
formula* to_concat = 0, formula* to_concat = 0)
bool negated = false) : dict_(dict), to_concat_(to_concat)
: dict_(dict), to_concat_(to_concat), negated_(negated)
{ {
} }
@ -351,33 +363,13 @@ namespace spot
void void
visit(const atomic_prop* node) visit(const atomic_prop* node)
{ {
if (negated_) res_ = bdd_ithvar(dict_.register_proposition(node));
res_ = bdd_nithvar(dict_.register_proposition(node));
else
res_ = bdd_ithvar(dict_.register_proposition(node));
res_ &= next_to_concat(); res_ &= next_to_concat();
} }
void void
visit(const constant* node) visit(const constant* node)
{ {
if (negated_)
{
switch (node->val())
{
case constant::True:
res_ = bddfalse;
return;
case constant::False:
res_ = next_to_concat();
return;
case constant::EmptyWord:
assert(!"EmptyWord should not be negated");
return;
}
}
switch (node->val()) switch (node->val())
{ {
case constant::True: case constant::True:
@ -411,10 +403,10 @@ namespace spot
{ {
// Not can only appear in front of Boolean // Not can only appear in front of Boolean
// expressions. // expressions.
// propositions.
const formula* f = node->child(); const formula* f = node->child();
assert(f->is_boolean()); assert(f->is_boolean());
res_ = recurse_and_concat(f, true); res_ = !recurse(f);
res_ &= next_to_concat();
return; return;
} }
} }
@ -785,26 +777,62 @@ namespace spot
} }
bdd bdd
recurse(const formula* f, formula* to_concat = 0, bool negated = false) recurse(const formula* f, formula* to_concat = 0)
{ {
ratexp_trad_visitor v(dict_, to_concat, negated); return translate_ratexp(f, dict_, to_concat);
f->accept(v);
return v.result();
} }
bdd bdd
recurse_and_concat(const formula* f, bool negated = false) recurse_and_concat(const formula* f)
{ {
return recurse(f, to_concat_ ? to_concat_->clone() : 0, negated); return translate_ratexp(f, dict_,
to_concat_ ? to_concat_->clone() : 0);
} }
private: private:
translate_dict& dict_; translate_dict& dict_;
bdd res_; bdd res_;
formula* to_concat_; formula* to_concat_;
bool negated_;
}; };
bdd
translate_ratexp(const formula* f, translate_dict& dict,
formula* to_concat)
{
// static unsigned indent = 0;
// for (unsigned i = indent; i > 0; --i)
// std::cerr << "| ";
// std::cerr << "translate_ratexp[" << to_string(f);
// if (to_concat)
// std::cerr << ", " << to_string(to_concat);
// std::cerr << "]" << std::endl;
// ++indent;
bdd res;
if (!f->is_boolean())
{
ratexp_trad_visitor v(dict, to_concat);
f->accept(v);
res = v.result();
}
else
{
res = dict.boolean_to_bdd(f);
// See comment for similar code in next_to_concat.
if (!to_concat)
to_concat = constant::empty_word_instance();
int x = dict.register_next_variable(to_concat);
res &= bdd_ithvar(x);
to_concat->destroy();
}
// --indent;
// for (unsigned i = indent; i > 0; --i)
// std::cerr << "| ";
// std::cerr << "\\ ";
// bdd_print_set(std::cerr, dict.dict, res) << std::endl;
return res;
}
// The rewrite rules used here are adapted from Jean-Michel // The rewrite rules used here are adapted from Jean-Michel
// Couvreur's FM paper, augmented to support rational operators. // Couvreur's FM paper, augmented to support rational operators.
@ -939,12 +967,9 @@ namespace spot
return; return;
} }
ratexp_trad_visitor v(dict_); bdd f1 = translate_ratexp(node->child(), dict_);
node->child()->accept(v);
bdd f1 = v.result();
res_ = bddfalse; res_ = bddfalse;
if (exprop_) if (exprop_)
{ {
bdd var_set = bdd_existcomp(bdd_support(f1), dict_.var_set); bdd var_set = bdd_existcomp(bdd_support(f1), dict_.var_set);
@ -985,6 +1010,10 @@ namespace spot
bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); bdd dest_bdd = bdd_existcomp(cube, dict_.next_set);
formula* dest = dict_.conj_bdd_to_formula(dest_bdd); formula* dest = dict_.conj_bdd_to_formula(dest_bdd);
// std::cerr << "dest_bdd=";
// bdd_print_set(std::cerr, dict_.dict, dest_bdd)
// << ", dest=" << to_string(dest) << std::endl;
const formula* dest2; const formula* dest2;
if (dest->accepts_eword()) if (dest->accepts_eword())
{ {
@ -1016,9 +1045,7 @@ namespace spot
return; return;
} }
ratexp_trad_visitor v(dict_); bdd f1 = translate_ratexp(node->child(), dict_);
node->child()->accept(v);
bdd f1 = v.result();
// trace_ltl_bdd(dict_, f1); // trace_ltl_bdd(dict_, f1);
@ -1146,9 +1173,7 @@ namespace spot
// Recognize f2 on transitions going to destinations // Recognize f2 on transitions going to destinations
// that accept the empty word. // that accept the empty word.
bdd f2 = recurse(node->second()); bdd f2 = recurse(node->second());
ratexp_trad_visitor v(dict_); bdd f1 = translate_ratexp(node->first(), dict_);
node->first()->accept(v);
bdd f1 = v.result();
res_ = bddfalse; res_ = bddfalse;
if (mark_all_) if (mark_all_)
@ -1221,9 +1246,7 @@ namespace spot
// word should recognize f2, and the automaton for f1 // word should recognize f2, and the automaton for f1
// should be understood as universal. // should be understood as universal.
bdd f2 = recurse(node->second()); bdd f2 = recurse(node->second());
ratexp_trad_visitor v(dict_); bdd f1 = translate_ratexp(node->first(), dict_);
node->first()->accept(v);
bdd f1 = v.result();
res_ = bddtrue; res_ = bddtrue;
bdd var_set = bdd_existcomp(bdd_support(f1), dict_.var_set); bdd var_set = bdd_existcomp(bdd_support(f1), dict_.var_set);
@ -1497,13 +1520,14 @@ namespace spot
t.has_rational = v_.has_rational(); t.has_rational = v_.has_rational();
t.has_marked = v_.has_marked(); t.has_marked = v_.has_marked();
// std::cerr << "-----" << std::endl; // std::cerr << "-----" << std::endl;
// std::cerr << "Formula: " << to_string(f) << std::endl; // std::cerr << "Formula: " << to_string(f) << std::endl;
// std::cerr << "Rational: " << t.has_rational << std::endl; // std::cerr << "Rational: " << t.has_rational << std::endl;
// std::cerr << "Marked: " << t.has_marked << std::endl; // std::cerr << "Marked: " << t.has_marked << std::endl;
// std::cerr << "Mark all: " << !has_mark(f) << std::endl; // std::cerr << "Mark all: " << !f->is_marked() << std::endl;
// std::cerr << "Transitions:" << std::endl; // std::cerr << "Transitions:" << std::endl;
// trace_ltl_bdd(v_.get_dict(), t.symbolic); // trace_ltl_bdd(v_.get_dict(), t.symbolic);
// std::cerr << "-----" << std::endl;
if (t.has_rational) if (t.has_rational)
{ {
@ -1636,13 +1660,14 @@ namespace spot
ltl_simplifier* simplifier) ltl_simplifier* simplifier)
{ {
formula* f2; formula* f2;
ltl_simplifier* s = simplifier;
// Simplify the formula, if requested. // Simplify the formula, if requested.
if (simplifier) if (s)
{ {
// This will normalize the formula regardless of the // This will normalize the formula regardless of the
// configuration of the simplifier. // configuration of the simplifier.
f2 = simplifier->simplify(f); f2 = s->simplify(f);
} }
else else
{ {
@ -1650,14 +1675,16 @@ namespace spot
// negations on the atomic propositions. We also suppress // negations on the atomic propositions. We also suppress
// logic abbreviations such as <=>, =>, or XOR, since they // logic abbreviations such as <=>, =>, or XOR, since they
// would involve negations at the BDD level. // would involve negations at the BDD level.
ltl_simplifier s; s = new ltl_simplifier(dict);
f2 = s.negative_normal_form(f, false); f2 = s->negative_normal_form(f, false);
} }
typedef std::set<const formula*, formula_ptr_less_than> set_type; typedef std::set<const formula*, formula_ptr_less_than> set_type;
set_type formulae_to_translate; set_type formulae_to_translate;
translate_dict d(dict); assert(dict == s->get_dict());
translate_dict d(dict, s);
// Compute the set of all promises that can possibly occur // Compute the set of all promises that can possibly occur
// inside the formula. // inside the formula.
@ -1933,6 +1960,9 @@ namespace spot
} }
} }
if (!simplifier)
delete s;
// Turn all promises into real acceptance conditions. // Turn all promises into real acceptance conditions.
a->complement_all_acceptance_conditions(); a->complement_all_acceptance_conditions();
return a; return a;

View file

@ -813,7 +813,7 @@ main(int argc, char** argv)
{ {
spot::ltl::ltl_simplifier* simp = 0; spot::ltl::ltl_simplifier* simp = 0;
if (simpltl) if (simpltl)
simp = new spot::ltl::ltl_simplifier(redopt); simp = new spot::ltl::ltl_simplifier(redopt, dict);
if (simp) if (simp)
{ {