3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-31 00:13:16 +00:00

checkpoint

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-21 13:32:12 -07:00
parent 4722fdfca5
commit add684d8e9
377 changed files with 204 additions and 62 deletions

View file

@ -1,161 +0,0 @@
#include"arith_bounds_tactic.h"
#include"assertion_set_util.h"
#include"arith_decl_plugin.h"
struct arith_bounds_tactic : public tactic {
ast_manager& m;
arith_util a;
volatile bool m_cancel;
arith_bounds_tactic(ast_manager& m):
m(m),
a(m),
m_cancel(false)
{
}
ast_manager& get_manager() { return m; }
void set_cancel(bool f) {
m_cancel = f;
}
virtual void cleanup() {
m_cancel = false;
}
virtual void operator()(/* in */ goal_ref const & in,
/* out */ goal_ref_buffer & result,
/* out */ model_converter_ref & mc,
/* out */ proof_converter_ref & pc,
/* out */ expr_dependency_ref & core) {
bounds_arith_subsumption(in, result);
}
virtual tactic* translate(ast_manager& m) {
return alloc(arith_bounds_tactic, m);
}
void checkpoint() {
if (m_cancel) {
throw tactic_exception(TACTIC_CANCELED_MSG);
}
}
struct info { rational r; unsigned idx; bool is_strict;};
/**
\brief Basic arithmetic subsumption simplification based on bounds.
*/
void mk_proof(proof_ref& pr, goal_ref const& s, unsigned i, unsigned j) {
if (s->proofs_enabled()) {
proof* th_lemma = m.mk_th_lemma(a.get_family_id(), m.mk_implies(s->form(i), s->form(j)), 0, 0);
pr = m.mk_modus_ponens(s->pr(i), th_lemma);
}
}
bool is_le_or_lt(expr* e, expr*& e1, expr*& e2, bool& is_strict) {
bool is_negated = m.is_not(e, e);
if ((!is_negated && (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1))) ||
(is_negated && (a.is_lt(e, e2, e1) || a.is_gt(e, e1, e2)))) {
is_strict = false;
return true;
}
if ((!is_negated && (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1))) ||
(is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) {
is_strict = true;
return true;
}
return false;
}
void bounds_arith_subsumption(goal_ref const& g, goal_ref_buffer& result) {
info inf;
rational r;
goal_ref s(g); // initialize result.
obj_map<expr, info> lower, upper;
expr* e1, *e2;
TRACE("arith_subsumption", s->display(tout); );
for (unsigned i = 0; i < s->size(); ++i) {
checkpoint();
expr* lemma = s->form(i);
bool is_strict = false;
bool is_lower = false;
if (!is_le_or_lt(lemma, e1, e2, is_strict)) {
continue;
}
// e1 <= e2 or e1 < e2
if (a.is_numeral(e2, r)) {
is_lower = true;
}
else if (a.is_numeral(e1, r)) {
is_lower = false;
}
else {
continue;
}
proof_ref new_pr(m);
if (is_lower && upper.find(e1, inf)) {
if (inf.r > r || (inf.r == r && is_strict && !inf.is_strict)) {
mk_proof(new_pr, s, i, inf.idx);
s->update(inf.idx, m.mk_true(), new_pr);
inf.r = r;
inf.is_strict = is_strict;
inf.idx = i;
upper.insert(e1, inf);
}
else {
mk_proof(new_pr, s, inf.idx, i);
s->update(i, m.mk_true(), new_pr);
}
}
else if (is_lower) {
inf.r = r;
inf.is_strict = is_strict;
inf.idx = i;
upper.insert(e1, inf);
}
else if (!is_lower && lower.find(e2, inf)) {
if (inf.r < r || (inf.r == r && is_strict && !inf.is_strict)) {
mk_proof(new_pr, s, i, inf.idx);
s->update(inf.idx, m.mk_true(), new_pr);
inf.r = r;
inf.is_strict = is_strict;
inf.idx = i;
lower.insert(e2, inf);
}
else {
mk_proof(new_pr, s, inf.idx, i);
s->update(i, m.mk_true());
}
}
else if (!is_lower) {
inf.r = r;
inf.is_strict = is_strict;
inf.idx = i;
lower.insert(e2, inf);
}
}
s->elim_true();
result.push_back(s.get());
TRACE("arith_subsumption", s->display(tout); );
}
};
tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p) {
return alloc(arith_bounds_tactic, m);
}

View file

@ -1,37 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
arith_bounds_tactic.h
Abstract:
Fast/rudimentary arithmetic subsumption tactic.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-6
Notes:
Background: The Farkas learner in PDR generates tons
of inequalities that contain redundancies.
It therefore needs a fast way to reduce these redundancies before
passing the results to routines that are more expensive.
The arith subsumption_strategy encapsulates a rudimentary
routine for simplifying inequalities. Additional simplification
routines can be added here or composed with this strategy.
Note: The bound_manager subsumes some of the collection methods used
for assembling bounds, but it does not have a way to check for
subsumption of atoms.
--*/
#ifndef _ARITH_BOUNDS_TACTIC_H_
#define _ARITH_BOUNDS_TACTIC_H_
#include "tactic.h"
tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -1,316 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_eq_adapter.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-25.
Revision History:
--*/
#include"smt_context.h"
#include"arith_eq_adapter.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"stats.h"
#include"simplifier.h"
#include"ast_smt2_pp.h"
namespace smt {
class already_processed_trail : public trail<context> {
// Remark: it is safer to use a trail object, because it guarantees that the enodes
// are still alive when the undo operation is performed.
//
// If a local backtracking stack is used in the class arith_eq_adapter is used,
// then we cannot guarantee that.
arith_eq_adapter::already_processed & m_already_processed;
enode * m_n1;
enode * m_n2;
public:
already_processed_trail(arith_eq_adapter::already_processed & m, enode * n1, enode * n2):
m_already_processed(m),
m_n1(n1),
m_n2(n2) {
}
virtual void undo(context & ctx) {
m_already_processed.erase(m_n1, m_n2);
TRACE("arith_eq_adapter_profile", tout << "del #" << m_n1->get_owner_id() << " #" << m_n2->get_owner_id() << "\n";);
}
};
/**
\brief The atoms m_eq, m_le, and m_ge should be marked as relevant only after
m_n1 and m_n2 are marked as relevant.
*/
class arith_eq_relevancy_eh : public relevancy_eh {
expr * m_n1;
expr * m_n2;
expr * m_eq;
expr * m_le;
expr * m_ge;
public:
arith_eq_relevancy_eh(expr * n1, expr * n2, expr * eq, expr * le, expr * ge):
m_n1(n1),
m_n2(n2),
m_eq(eq),
m_le(le),
m_ge(ge) {
}
virtual ~arith_eq_relevancy_eh() {}
virtual void operator()(relevancy_propagator & rp) {
if (!rp.is_relevant(m_n1))
return;
if (!rp.is_relevant(m_n2))
return;
rp.mark_as_relevant(m_eq);
rp.mark_as_relevant(m_le);
rp.mark_as_relevant(m_ge);
}
};
arith_simplifier_plugin * arith_eq_adapter::get_simplifier() {
if (!m_as) {
simplifier & s = get_context().get_simplifier();
m_as = static_cast<arith_simplifier_plugin*>(s.get_plugin(m_owner.get_family_id()));
}
return m_as;
}
void arith_eq_adapter::mk_axioms(enode * n1, enode * n2) {
SASSERT(n1 != n2);
ast_manager & m = get_manager();
TRACE("arith_eq_adapter_mk_axioms", tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";
tout << mk_ismt2_pp(n1->get_owner(), m) << "\n" << mk_ismt2_pp(n2->get_owner(), m) << "\n";);
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
app * t1 = n1->get_owner();
app * t2 = n2->get_owner();
if (m.is_value(t1) && m.is_value(t2)) {
// Nothing to be done
// We don't need to create axioms for 2 = 3
return;
}
context & ctx = get_context();
CTRACE("arith_eq_adapter_relevancy", !(ctx.is_relevant(n1) && ctx.is_relevant(n2)),
tout << "is_relevant(n1): #" << n1->get_owner_id() << " " << ctx.is_relevant(n1) << "\n";
tout << "is_relevant(n2): #" << n2->get_owner_id() << " " << ctx.is_relevant(n2) << "\n";
tout << mk_pp(n1->get_owner(), get_manager()) << "\n";
tout << mk_pp(n2->get_owner(), get_manager()) << "\n";
ctx.display(tout););
//
// The atoms d.m_t1_eq_t2, d.m_le, and d.m_ge should only be marked as relevant
// after n1 and n2 are marked as relevant.
//
data d;
if (m_already_processed.find(n1, n2, d))
return;
TRACE("arith_eq_adapter_profile", tout << "mk #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " " <<
m_already_processed.size() << " " << ctx.get_scope_level() << "\n";);
m_stats.m_num_eq_axioms++;
TRACE("arith_eq_adapter_profile_detail",
tout << "mk_detail " << mk_bounded_pp(n1->get_owner(), m, 5) << " " <<
mk_bounded_pp(n2->get_owner(), m, 5) << "\n";);
app_ref t1_eq_t2(m);
t1_eq_t2 = ctx.mk_eq_atom(t1, t2);
SASSERT(!m.is_false(t1_eq_t2));
TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(t1_eq_t2, m) << "\n"
<< mk_bounded_pp(t1, m) << "\n"
<< mk_bounded_pp(t2, m) << "\n";);
// UNRESOLVED ISSUE:
//
// arith_eq_adapter is still creating problems.
// The following disabled code fixes the issues, but create performance problems.
// The alternative does not works 100%. It generates problems when the literal
// created by the adapter during the search is included in a learned clause.
// Here is a sequence of events that triggers a crash:
// 1) The terms t1 >= t2 and t1 <= t2 are not in simplified form.
// For example, let us assume t1 := (* -1 x) and t2 := x.
// Since, t1 and t2 were internalized at this point, the following code works.
// That is the arith internalizer accepts the formula (+ (* -1 x) (* -1 x))
// that is not in simplified form. Let s be the term (+ (* -1 x) (* -1 x))
// 2) Assume now that a conflict is detected a lemma containing s is created.
// 3) The enodes associated with t1, t2 and s are destroyed during backtracking.
// 4) The term s is reinternalized at smt::context::reinit_clauses. Term t2 is
// also reinitialized, but t1 is not. We only create a "name" for a term (* -1 x)
// if it is embedded in a function application.
// 5) theory_arith fails to internalize (+ (* -1 x) (* -1 x)), and Z3 crashes.
//
#if 0
// This block of code uses the simplifier for creating the literals t1 >= t2 and t1 <= t2.
// It has serious performance problems in VCC benchmarks.
// The problem seems to be the following:
// t1 and t2 are slacks (i.e., names for linear polynomials).
// The simplifier will create inequalities that will indirectly imply that t1 >= t2 and t1 <= t2.
// Example if: t1 := 1 + a
// t2 := 2 + b
// the simplifier will create
// a - b >= -1
// a - b <= -1
// These inequalities imply that 1+a >= 2+b and 1+a <= 2+b,
// but the tableau is complete different.
// BTW, note that we don't really need to handle the is_numeral case when using
// the simplifier. However, doing that, it seems we minimize the performance problem.
expr_ref le(m);
expr_ref ge(m);
if (m_util.is_numeral(t1))
std::swap(t1, t2);
if (m_util.is_numeral(t2)) {
le = m_util.mk_le(t1, t2);
ge = m_util.mk_ge(t1, t2);
}
else {
arith_simplifier_plugin & s = *(get_simplifier());
s.mk_le(t1, t2, le);
s.mk_ge(t1, t2, ge);
}
TRACE("arith_eq_adapter_perf", tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";);
#else
// Old version that used to be buggy.
// I fixed the theory arithmetic internalizer to accept non simplified terms of the form t1 - t2
// if t1 and t2 already have slacks (theory variables) associated with them.
app * le = 0;
app * ge = 0;
if (m_util.is_numeral(t1))
std::swap(t1, t2);
if (m_util.is_numeral(t2)) {
le = m_util.mk_le(t1, t2);
ge = m_util.mk_ge(t1, t2);
}
else {
sort * st = m.get_sort(t1);
app * minus_one = m_util.mk_numeral(rational::minus_one(), st);
app * zero = m_util.mk_numeral(rational::zero(), st);
app_ref s(m_util.mk_add(t1, m_util.mk_mul(minus_one, t2)), m);
le = m_util.mk_le(s, zero);
ge = m_util.mk_ge(s, zero);
}
TRACE("arith_eq_adapter_perf",
tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";);
#endif
ctx.push_trail(already_processed_trail(m_already_processed, n1, n2));
m_already_processed.insert(n1, n2, data(t1_eq_t2, le, ge));
TRACE("arith_eq_adapter_profile", tout << "insert #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";);
ctx.internalize(t1_eq_t2, true);
literal t1_eq_t2_lit(ctx.get_bool_var(t1_eq_t2));
TRACE("interface_eq",
tout << "core should try true phase first for the equality: " << t1_eq_t2_lit << "\n";
tout << "#" << n1->get_owner_id() << " == #" << n2->get_owner_id() << "\n";
tout << "try_true_first: " << ctx.try_true_first(t1_eq_t2_lit.var()) << "\n";);
TRACE("arith_eq_adapter_bug",
tout << "le: " << mk_ismt2_pp(le, m) << "\nge: " << mk_ismt2_pp(ge, m) << "\n";);
ctx.internalize(le, true);
ctx.internalize(ge, true);
SASSERT(ctx.lit_internalized(le));
SASSERT(ctx.lit_internalized(ge));
literal le_lit = ctx.get_literal(le);
literal ge_lit = ctx.get_literal(ge);
if (ctx.try_true_first(t1_eq_t2_lit.var())) {
// Remark: I need to propagate the try_true_first flag to the auxiliary atom le_lit and ge_lit.
// Otherwise model based theory combination will be ineffective, because if the core
// case splits in le_lit and ge_lit before t1_eq_t2_lit it will essentially assign an arbitrary phase to t1_eq_t2_lit.
ctx.set_true_first_flag(le_lit.var());
ctx.set_true_first_flag(ge_lit.var());
}
theory_id tid = m_owner.get_id();
if (m.proofs_enabled() && m_proof_hint.empty()) {
m_proof_hint.push_back(parameter(symbol("triangle-eq")));
}
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
ctx.mk_th_axiom(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr());
TRACE("arith_eq_adapter", tout << "internalizing: "
<< " " << mk_pp(le, m) << ": " << le_lit
<< " " << mk_pp(ge, m) << ": " << ge_lit
<< " " << mk_pp(t1_eq_t2, m) << ": " << t1_eq_t2_lit << "\n";);
if (m_params.m_arith_add_binary_bounds) {
TRACE("arith_eq_adapter", tout << "adding binary bounds...\n";);
ctx.mk_th_axiom(tid, le_lit, ge_lit, 3, m_proof_hint.c_ptr());
}
if (ctx.relevancy()) {
relevancy_eh * eh = ctx.mk_relevancy_eh(arith_eq_relevancy_eh(n1->get_owner(), n2->get_owner(), t1_eq_t2, le, ge));
ctx.add_relevancy_eh(n1->get_owner(), eh);
ctx.add_relevancy_eh(n2->get_owner(), eh);
}
if (!m_params.m_arith_lazy_adapter && !ctx.at_base_level() &&
n1->get_iscope_lvl() <= ctx.get_base_level() && n2->get_iscope_lvl() <= ctx.get_base_level()) {
m_restart_pairs.push_back(enode_pair(n1, n2));
}
TRACE("arith_eq_adapter_detail", ctx.display(tout););
}
void arith_eq_adapter::new_eq_eh(theory_var v1, theory_var v2) {
TRACE("arith_eq_adapter", tout << "v" << v1 << " = v" << v2 << " #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";);
TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";);
mk_axioms(get_enode(v1), get_enode(v2));
}
void arith_eq_adapter::new_diseq_eh(theory_var v1, theory_var v2) {
TRACE("arith_eq_adapter", tout << "v" << v1 << " != v" << v2 << " #" << get_enode(v1)->get_owner_id() << " != #" << get_enode(v2)->get_owner_id() << "\n";);
mk_axioms(get_enode(v1), get_enode(v2));
}
void arith_eq_adapter::init_search_eh() {
m_restart_pairs.reset();
}
void arith_eq_adapter::reset_eh() {
TRACE("arith_eq_adapter", tout << "reset\n";);
m_already_processed .reset();
m_restart_pairs .reset();
m_stats .reset();
}
void arith_eq_adapter::restart_eh() {
TRACE("arith_eq_adapter", tout << "restart\n";);
svector<enode_pair> tmp(m_restart_pairs);
svector<enode_pair>::iterator it = tmp.begin();
svector<enode_pair>::iterator end = tmp.end();
m_restart_pairs.reset();
for (; it != end; ++it) {
TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << it->first->get_owner_id() << " #" <<
it->second->get_owner_id() << "\n";);
mk_axioms(it->first, it->second);
}
}
void arith_eq_adapter::collect_statistics(::statistics & st) const {
st.update("eq adapter", m_stats.m_num_eq_axioms);
}
void arith_eq_adapter::display_already_processed(std::ostream & out) const {
obj_pair_map<enode, enode, data>::iterator it = m_already_processed.begin();
obj_pair_map<enode, enode, data>::iterator end = m_already_processed.end();
for (; it != end; ++it) {
enode * n1 = it->get_key1();
enode * n2 = it->get_key2();
out << "eq_adapter: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";
}
}
};

View file

@ -1,97 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_eq_adapter.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-25.
Revision History:
--*/
#ifndef _ARITH_EQ_ADAPTER_H_
#define _ARITH_EQ_ADAPTER_H_
#include"smt_theory.h"
#include"obj_pair_hashtable.h"
#include"arith_decl_plugin.h"
#include"statistics.h"
#include"arith_simplifier_plugin.h"
namespace smt {
struct arith_eq_adapter_stats {
unsigned m_num_eq_axioms;
void reset() { m_num_eq_axioms = 0; }
arith_eq_adapter_stats() { reset(); }
};
/**
\brief Auxiliary class used to convert (dis) equalities
propagated from the core into arith equalities/inequalities
atoms. This class is used by the arithmetic theories to
handle the (dis) equalities propagated from the logical context.
- config 1:
recreate axioms at restart
- config 2:
lazy diseq split
*/
class arith_eq_adapter {
public:
arith_eq_adapter_stats m_stats;
private:
struct data {
expr * m_t1_eq_t2;
expr * m_le;
expr * m_ge;
data():m_t1_eq_t2(0), m_le(0), m_ge(0) {}
data(expr * t1_eq_t2, expr * le, expr * ge):m_t1_eq_t2(t1_eq_t2), m_le(le), m_ge(ge) {}
};
public:
typedef obj_pair_map<enode, enode, data> already_processed;
private:
theory & m_owner;
theory_arith_params & m_params;
arith_util & m_util;
arith_simplifier_plugin * m_as;
already_processed m_already_processed;
svector<enode_pair> m_restart_pairs;
svector<parameter> m_proof_hint;
context & get_context() const { return m_owner.get_context(); }
ast_manager & get_manager() const { return m_owner.get_manager(); }
enode * get_enode(theory_var v) const { return m_owner.get_enode(v); }
arith_simplifier_plugin * get_simplifier();
public:
arith_eq_adapter(theory & owner, theory_arith_params & params, arith_util & u):m_owner(owner), m_params(params), m_util(u), m_as(0) {}
void new_eq_eh(theory_var v1, theory_var v2);
void new_diseq_eh(theory_var v1, theory_var v2);
void reset_eh();
void init_search_eh();
void restart_eh();
/**
\brief Add the eq axioms for n1 and n2.
*/
void mk_axioms(enode * n1, enode * n2);
void collect_statistics(::statistics & st) const;
void display_already_processed(std::ostream & out) const;
};
};
#endif /* _ARITH_EQ_ADAPTER_H_ */

View file

@ -1,632 +0,0 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_eq_solver.cpp
Abstract:
Solver for linear arithmetic equalities.
Author:
Nikolaj Bjorner (nbjorner) 2012-02-25
--*/
#include"arith_eq_solver.h"
arith_eq_solver::~arith_eq_solver() {
}
arith_eq_solver::arith_eq_solver(ast_manager & m, params_ref const& p):
m(m),
m_params(p),
m_util(m),
m_arith_rewriter(m)
{
m_params.set_bool(":gcd-rounding", true);
// m_params.set_bool(":sum", true);
m_arith_rewriter.updt_params(m_params);
}
/**
\brief Return true if the first monomial of t is negative.
*/
bool arith_eq_solver::is_neg_poly(expr * t) const {
if (m_util.is_add(t)) {
t = to_app(t)->get_arg(0);
}
if (m_util.is_mul(t)) {
t = to_app(t)->get_arg(0);
rational r;
bool is_int;
if (m_util.is_numeral(t, r, is_int))
return r.is_neg();
}
return false;
}
void arith_eq_solver::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) {
SASSERT(m_util.is_int(e));
SASSERT(k.is_int() && k.is_pos());
numeral n;
bool is_int;
if (depth == 0) {
result = e;
}
else if (m_util.is_add(e) || m_util.is_mul(e)) {
expr_ref_vector args(m);
expr_ref tmp(m);
app* a = to_app(e);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
prop_mod_const(a->get_arg(i), depth - 1, k, tmp);
args.push_back(tmp);
}
m_arith_rewriter.mk_app(a->get_decl(), args.size(), args.c_ptr(), result);
}
else if (m_util.is_numeral(e, n, is_int) && is_int) {
result = m_util.mk_numeral(mod(n, k), true);
}
else {
result = e;
}
}
void arith_eq_solver::gcd_normalize(vector<numeral>& values) {
numeral g(0);
for (unsigned i = 0; !g.is_one() && i < values.size(); ++i) {
SASSERT(values[i].is_int());
if (!values[i].is_zero()) {
if (g.is_zero()) {
g = abs(values[i]);
}
else {
g = gcd(abs(values[i]), g);
}
}
}
if (g.is_zero() || g.is_one()) {
return;
}
for (unsigned i = 0; i < values.size(); ++i) {
values[i] = values[i] / g;
SASSERT(values[i].is_int());
}
}
unsigned arith_eq_solver::find_abs_min(vector<numeral>& values) {
SASSERT(values.size() >= 2);
unsigned index = 0;
numeral v(0);
for (unsigned i = 1; i < values.size(); ++i) {
numeral w = abs(values[i]);
if (v.is_zero() || (!w.is_zero() && w < v)) {
index = i;
v = w;
}
}
return index;
}
static void print_row(std::ostream& out, vector<rational> const& row) {
for(unsigned i = 0; i < row.size(); ++i) {
out << row[i] << " ";
}
out << "\n";
}
static void print_rows(std::ostream& out, vector<vector<rational> > const& rows) {
for (unsigned i = 0; i < rows.size(); ++i) {
print_row(out, rows[i]);
}
}
//
// The gcd of the coefficients to variables have to divide the
// coefficient to the constant.
// The constant is the last value in the array.
//
bool arith_eq_solver::gcd_test(vector<numeral>& values) {
SASSERT(values.size() > 0);
numeral g(0);
numeral first_value = values[0];
for (unsigned i = 1; !g.is_one() && i < values.size(); ++i) {
if (!values[i].is_zero()) {
if (g.is_zero()) {
g = abs(values[i]);
}
else {
g = gcd(abs(values[i]), g);
}
}
}
if (g.is_one()) {
return true;
}
if (g.is_zero()) {
return first_value.is_zero();
}
numeral r = first_value/g;
return r.is_int();
}
bool arith_eq_solver::solve_integer_equation(
vector<numeral>& values,
unsigned& index,
bool& is_fresh
)
{
TRACE("arith_eq_solver",
tout << "solving: ";
print_row(tout, values);
);
//
// perform one step of the omega test equality elimination.
//
// Given:
// a1*x1 + a2*x2 + .. + a_n*x_n + a_{n+1} = 0
//
// Assume gcd(a1,..,a_n,a_{n+1}) = 1
// Assume gcd(a1,...,a_n) divides a_{n+1} (eg. gcd(a1,..,an) = 1)
//
// post-condition: values[index] = -1.
//
// Let a_index be index of least absolute value.
//
// If |a_index| = 1, then return row and index.
// Otherwise:
// Let m = |a_index| + 1
// Set
//
// m*x_index'
// =
// ((a1 mod_hat m)*x1 + (a2 mod_hat m)*x2 + .. + (a_n mod_hat m)*x_n + (k mod_hat m))
// =
// (a1'*x1 + a2'*x2 + .. (-)1*x_index + ...)
//
// <=> Normalize signs so that sign to x_index is -1.
// (-)a1'*x1 + (-)a2'*x2 + .. -1*x_index + ... + m*x_index' = 0
//
// Return row, where the coefficient to x_index is implicit.
// Instead used the coefficient 'm' at position 'index'.
//
gcd_normalize(values);
if (!gcd_test(values)) {
TRACE("arith_eq_solver", tout << "not sat\n";
print_row(tout, values););
return false;
}
index = find_abs_min(values);
SASSERT(1 <= index && index < values.size());
numeral a = values[index];
numeral abs_a = abs(a);
if (abs_a.is_zero()) {
// The equation is trivial.
return true;
}
if (a.is_one()) {
for (unsigned i = 0; i < values.size(); ++i) {
values[i].neg();
}
}
is_fresh = !abs_a.is_one();
if (is_fresh) {
numeral m = abs_a + numeral(1);
for (unsigned i = 0; i < values.size(); ++i) {
values[i] = mod_hat(values[i], m);
}
if (values[index].is_one()) {
for (unsigned i = 0; i < values.size(); ++i) {
values[i].neg();
}
}
SASSERT(values[index].is_minus_one());
values[index] = m;
}
TRACE("arith_eq_solver",
tout << "solved at index " << index << ": ";
print_row(tout, values);
);
return true;
}
void arith_eq_solver::substitute(
row& r,
row const& s,
unsigned index
)
{
SASSERT(1 <= index && index < s.size());
TRACE("arith_eq_solver",
tout << "substitute " << index << ":\n";
print_row(tout, r);
print_row(tout, s);
);
if (index >= r.size()) {
return;
}
numeral c = r[index];
if (c.is_zero()) {
// no-op
}
else if (abs(s[index]).is_one()) {
//
// s encodes an equation that contains a variable
// with a unit coefficient.
//
// Let
// c = r[index]
// s = s[index]*x + s'*y = 0
// r = c*x + r'*y = 0
//
// =>
//
// 0
// =
// -sign(s[index])*c*s + r
// =
// -s[index]*sign(s[index])*c*x - sign(s[index])*c*s'*y + c*x + r'*y
// =
// -c*x - sign(s[index])*c*s'*y + c*x + r'*y
// =
// -sign(s[index])*c*s'*y + r'*y
//
numeral sign_s = s[index].is_pos()?numeral(1):numeral(-1);
for (unsigned i = 0; i < r.size(); ++i) {
r[i] -= c*sign_s*s[i];
}
for (unsigned i = r.size(); i < s.size(); ++i) {
r.push_back(-c*sign_s*s[i]);
}
}
else {
//
// s encodes a substitution using an auxiliary variable.
// the auxiliary variable is at position 'index'.
//
// Let
// c = r[index]
// s = s[index]*x + s'*y = 0
// r = c*x + r'*y = 0
//
// s encodes : x |-> s[index]*x' + s'*y
//
// Set:
//
// r := c*s + r'*y
//
r[index] = numeral(0);
for (unsigned i = 0; i < r.size(); ++i) {
r[i] += c*s[i];
}
for (unsigned i = r.size(); i < s.size(); ++i) {
r.push_back(c*s[i]);
}
}
TRACE("arith_eq_solver",
tout << "result: ";
print_row(tout, r);
);
}
bool arith_eq_solver::solve_integer_equations(
vector<row>& rows,
row& unsat_row
)
{
// return solve_integer_equations_units(rows, unsat_row);
return solve_integer_equations_gcd(rows, unsat_row);
}
//
// Naive integer equation solver where only units are eliminated.
//
bool arith_eq_solver::solve_integer_equations_units(
vector<row>& rows,
row& unsat_row
)
{
TRACE("arith_eq_solver", print_rows(tout << "solving:\n", rows););
unsigned_vector todo, done;
for (unsigned i = 0; i < rows.size(); ++i) {
todo.push_back(i);
row& r = rows[i];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); );
return false;
}
}
for (unsigned i = 0; i < todo.size(); ++i) {
row& r = rows[todo[i]];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); );
return false;
}
unsigned index = find_abs_min(r);
SASSERT(1 <= index && index < r.size());
numeral a = r[index];
numeral abs_a = abs(a);
if (abs_a.is_zero()) {
continue;
}
else if (abs_a.is_one()) {
for (unsigned j = i+1; j < todo.size(); ++j) {
substitute(rows[todo[j]], r, index);
}
for (unsigned j = 0; j < done.size(); ++j) {
row& r2 = rows[done[j]];
if (!r2[index].is_zero()) {
substitute(r2, r, index);
todo.push_back(done[j]);
done.erase(done.begin()+j);
--j;
}
}
}
else {
done.push_back(todo[i]);
}
}
TRACE("arith_eq_solver",
tout << ((done.size()<=1)?"solved ":"incomplete check ") << done.size() << "\n";
for (unsigned i = 0; i < done.size(); ++i) {
print_row(tout, rows[done[i]]);
}
);
return true;
}
//
// Partial solver based on the omega test equalities.
// unsatisfiability is not preserved when eliminating
// auxiliary variables.
//
bool arith_eq_solver::solve_integer_equations_omega(
vector<row> & rows,
row& unsat_row
)
{
unsigned index;
bool is_fresh;
vector<row> rows_solved;
unsigned_vector indices;
unsigned_vector aux_indices;
for (unsigned i = 0; i < rows.size(); ++i) {
rows_solved.push_back(rows[i]);
row& r = rows_solved.back();
for (unsigned j = 0; j + 1 < rows_solved.size(); ++j) {
substitute(r, rows_solved[j], indices[j]);
}
if (!solve_integer_equation(r, index, is_fresh)) {
unsat_row = r;
gcd_normalize(unsat_row);
// invert the substitution for every index that is fresh.
TRACE("arith_eq_solver",
tout << "unsat:\n";
print_row(tout, unsat_row);
for (unsigned l = 0; l + 1< rows_solved.size(); ++l) {
print_row(tout, rows_solved[l]);
});
for (unsigned j = rows_solved.size()-1; j > 0; ) {
--j;
row& solved_row = rows_solved[j];
unsigned index_j = indices[j];
unsigned aux_index_j = aux_indices[j];
SASSERT(index_j <= aux_index_j);
if (unsat_row.size() <= aux_index_j) {
unsat_row.resize(aux_index_j+1);
}
numeral m = solved_row[aux_index_j];
numeral k = unsat_row[aux_index_j];
if (aux_index_j != index_j && !k.is_zero()) {
//
// solved_row: -x_index + m*sigma + r1 = 0
// unsat_row: k*sigma + r2 = 0
//
// <=>
//
// solved_row: -k*x_index + k*m*sigma + k*r1 = 0
// unsat_row: m*k*sigma + m*r2 = 0
//
// =>
//
// m*k*sigma + m*r2 + k*x_index - k*m*sigma - k*r1 = 0
//
for (unsigned l = 0; l < unsat_row.size(); ++l) {
unsat_row[l] *= m;
unsat_row[l] -= k*solved_row[l];
}
for (unsigned l = unsat_row.size(); l < solved_row.size(); ++l) {
unsat_row.push_back(solved_row[l]);
}
gcd_normalize(unsat_row);
TRACE("arith_eq_solver",
tout << "gcd: ";
print_row(tout, solved_row);
print_row(tout, unsat_row);
);
}
if (gcd_test(unsat_row)) {
TRACE("arith_eq_solver", tout << "missed pure explanation\n";);
return true;
}
SASSERT(!gcd_test(unsat_row));
}
return false;
}
else if (r[index].is_zero()) {
// Row is trival
rows_solved.pop_back();
continue;
}
else if (!abs(r[index]).is_one()) {
//
// The solution introduces a fresh auxiliary variable.
// Make space for this variable as a fresh numeral.
//
indices.push_back(index);
aux_indices.push_back(r.size());
r.push_back(r[index]);
r[index] = numeral(-1);
// re-solve the same row.
--i;
}
else {
indices.push_back(index);
aux_indices.push_back(index);
}
}
return true;
}
//
// Eliminate variables by searching for combination of rows where
// the coefficients have gcd = 1.
//
bool arith_eq_solver::solve_integer_equations_gcd(
vector<row> & rows,
row& unsat_row
)
{
unsigned_vector live, useful, gcd_pos;
vector<rational> gcds;
rational u, v;
if (rows.empty()) {
return true;
}
for (unsigned i = 0; i < rows.size(); ++i) {
live.push_back(i);
row& r = rows[i];
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); );
return false;
}
}
unsigned max_column = rows[0].size();
bool change = true;
while (change && !live.empty()) {
change = false;
for (unsigned i = 1; i < max_column; ++i) {
rational g(0);
gcds.reset();
gcd_pos.reset();
unsigned j = 0;
for (; j < live.size(); ++j) {
rational const& k = rows[live[j]][i];
if (k.is_zero()) {
continue;
}
if (g.is_zero()) {
g = abs(k);
}
else {
g = gcd(g, abs(k));
}
if (abs(g).is_one()) {
break;
}
gcds.push_back(g);
gcd_pos.push_back(live[j]);
}
if (j == live.size()) {
continue;
}
change = true;
// found gcd, now identify reduced set of rows with GCD = 1.
g = abs(rows[live[j]][i]);
useful.push_back(live[j]);
unsigned live_pos = j;
for (j = gcds.size(); !g.is_one() && j > 0; ) {
SASSERT(g.is_pos());
--j;
if (j == 0 || !gcd(g, gcds[j-1]).is_one()) {
useful.push_back(gcd_pos[j]);
g = gcd(g, gcds[j]);
SASSERT(j == 0 || gcd(g,gcds[j-1]).is_one());
}
}
//
// we now have a set "useful" of rows whose combined GCD = 1.
// pivot the remaining with the first row.
//
row& r0 = rows[useful[0]];
for (j = 1; j < useful.size(); ++j) {
row& r1 = rows[useful[j]];
g = gcd(r0[i], r1[i], u, v);
for (unsigned k = 0; k < max_column; ++k) {
r0[k] = u*r0[k] + v*r1[k];
}
SASSERT(g == r0[i]);
}
SASSERT(abs(r0[i]).is_one());
live.erase(live.begin()+live_pos);
for (j = 0; j < live.size(); ++j) {
row& r = rows[live[j]];
if (!r[i].is_zero()) {
substitute(r, r0, i);
gcd_normalize(r);
if (!gcd_test(r)) {
unsat_row = r;
TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); );
return false;
}
}
}
}
}
TRACE("arith_eq_solver",
tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n";
for (unsigned i = 0; i < live.size(); ++i) {
print_row(tout, rows[live[i]]);
}
);
return true;
}

View file

@ -1,108 +0,0 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
arith_eq_solver.h
Abstract:
Solver for linear arithmetic equalities.
Author:
Nikolaj Bjorner (nbjorner) 2012-02-25
--*/
#ifndef _ARITH_EQ_SOLVER_H_
#define _ARITH_EQ_SOLVER_H_
#include"arith_decl_plugin.h"
#include"arith_rewriter.h"
/**
\brief Simplifier for the arith family.
*/
class arith_eq_solver {
typedef rational numeral;
ast_manager& m;
params_ref m_params;
arith_util m_util;
arith_rewriter m_arith_rewriter;
bool is_neg_poly(expr * t) const;
void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result);
bool gcd_test(vector<numeral>& value);
unsigned find_abs_min(vector<numeral>& values);
void gcd_normalize(vector<numeral>& values);
void substitute(vector<numeral>& r, vector<numeral> const& s, unsigned index);
bool solve_integer_equations_units(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
bool solve_integer_equations_omega(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
void compute_hnf(vector<vector<numeral> >& A);
bool solve_integer_equations_hermite(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
bool solve_integer_equations_gcd(
vector<vector<numeral> > & rows,
vector<numeral>& unsat_row
);
public:
arith_eq_solver(ast_manager & m, params_ref const& p = params_ref());
~arith_eq_solver();
// Integer linear solver for a single equation.
// The array values contains integer coefficients
//
// Determine integer solutions to:
//
// a+k = 0
//
// where a = sum_i a_i*k_i
//
typedef vector<numeral> row;
typedef vector<row> matrix;
bool solve_integer_equation(
row& values,
unsigned& index,
bool& is_fresh
);
// Integer linear solver.
// Determine integer solutions to:
//
// a+k = 0
//
// where a = sum_i a_i*k_i
//
// Solution, if there is any, is returned as a substitution.
// The return value is "true".
// If there is no solution, then return "false".
// together with equality "eq_unsat", such that
//
// eq_unsat = 0
//
// is implied and is unsatisfiable over the integers.
//
bool solve_integer_equations(vector<row>& rows, row& unsat_row);
};
#endif /* _ARITH_EQ_SOLVER_H_ */

View file

@ -1,104 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_solver_plugin.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-30.
Revision History:
--*/
#include"arith_solver_plugin.h"
#include"ast_pp.h"
arith_solver_plugin::arith_solver_plugin(arith_simplifier_plugin & simp):
solver_plugin(symbol("arith"), simp.get_manager()),
m_simplifier(simp) {
}
bool arith_solver_plugin::solve(expr * lhs, expr * rhs, expr_mark const & forbidden, app_ref & var, expr_ref & subst) {
rational k;
if (!m_simplifier.is_numeral(rhs, k))
return false;
bool _is_int = m_simplifier.is_int(lhs);
ptr_buffer<expr> monomials;
ptr_buffer<expr> todo;
bool already_found = false;
rational c;
todo.push_back(lhs);
while (!todo.empty()) {
expr * curr = todo.back();
todo.pop_back();
rational coeff;
if (m_simplifier.is_add(curr)) {
SASSERT(to_app(curr)->get_num_args() == 2);
todo.push_back(to_app(curr)->get_arg(1));
todo.push_back(to_app(curr)->get_arg(0));
}
else {
if (!already_found) {
if (m_simplifier.is_mul(curr) &&
m_simplifier.is_numeral(to_app(curr)->get_arg(0), coeff) && !coeff.is_zero() && (!_is_int || coeff.is_minus_one()) &&
is_uninterp_const(to_app(curr)->get_arg(1)) &&
!forbidden.is_marked(to_app(curr)->get_arg(1))) {
c = coeff;
var = to_app(to_app(curr)->get_arg(1));
already_found = true;
}
else if (is_uninterp_const(curr) && !forbidden.is_marked(curr)) {
c = rational::one();
var = to_app(curr);
already_found = true;
}
else {
monomials.push_back(curr);
}
}
else {
monomials.push_back(curr);
}
}
}
if (!already_found)
return false;
SASSERT(!c.is_zero());
k /= c;
expr_ref_vector new_monomials(m_manager);
if (!k.is_zero())
new_monomials.push_back(m_simplifier.mk_numeral(k, _is_int));
c.neg();
expr_ref inv_c(m_manager);
if (!c.is_one()) {
rational inv(1);
inv /= c;
inv_c = m_simplifier.mk_numeral(inv, _is_int);
}
// divide monomials by c
unsigned sz = monomials.size();
for (unsigned i = 0; i < sz; i++) {
expr * m = monomials[i];
expr_ref new_m(m_manager);
if (!c.is_one())
m_simplifier.mk_mul(inv_c, m, new_m);
else
new_m = m;
new_monomials.push_back(new_m);
}
if (new_monomials.empty())
subst = m_simplifier.mk_numeral(rational(0), _is_int);
else
m_simplifier.mk_add(new_monomials.size(), new_monomials.c_ptr(), subst);
TRACE("arith_solver", tout << "solving:\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager)
<< "\nresult:\n" << mk_pp(var, m_manager) << "\n" << mk_pp(subst, m_manager) << "\n";);
return true;
}

View file

@ -1,34 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
arith_solver_plugin.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-30.
Revision History:
--*/
#ifndef _ARITH_SOLVER_PLUGIN_H_
#define _ARITH_SOLVER_PLUGIN_H_
#include"solver_plugin.h"
#include"arith_simplifier_plugin.h"
class arith_solver_plugin : public solver_plugin {
arith_simplifier_plugin & m_simplifier;
public:
arith_solver_plugin(arith_simplifier_plugin & simp);
virtual ~arith_solver_plugin() {}
virtual bool solve(expr * lhs, expr * rhs, expr_mark const & forbidden, app_ref & var, expr_ref & subst);
};
#endif /* _ARITH_SOLVER_PLUGIN_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,200 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
asserted_formulas.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-11.
Revision History:
--*/
#ifndef _ASSERTED_FORMULAS_H_
#define _ASSERTED_FORMULAS_H_
#include"front_end_params.h"
#include"simplifier.h"
#include"basic_simplifier_plugin.h"
#include"static_features.h"
#include"macro_manager.h"
#include"macro_finder.h"
#include"defined_names.h"
#include"solver_plugin.h"
#include"maximise_ac_sharing.h"
#include"bit2int.h"
#include"qe.h"
#include"statistics.h"
#include"user_rewriter.h"
class arith_simplifier_plugin;
class bv_simplifier_plugin;
class asserted_formulas {
ast_manager & m_manager;
front_end_params & m_params;
simplifier m_pre_simplifier;
subst_simplifier m_simplifier;
basic_simplifier_plugin * m_bsimp;
bv_simplifier_plugin * m_bvsimp;
defined_names m_defined_names;
static_features m_static_features;
expr_ref_vector m_asserted_formulas; // formulas asserted by user
proof_ref_vector m_asserted_formula_prs; // proofs for the asserted formulas.
unsigned m_asserted_qhead;
expr_map m_subst;
ptr_vector<app> m_vars; // domain of m_subst
unsigned m_vars_qhead;
expr_mark m_forbidden;
ptr_vector<app> m_forbidden_vars;
macro_manager m_macro_manager;
scoped_ptr<macro_finder> m_macro_finder;
typedef plugin_manager<solver_plugin> solver_plugins;
solver_plugins m_solver_plugins;
bit2int m_bit2int;
maximise_bv_sharing m_bv_sharing;
user_rewriter m_user_rewriter;
bool m_inconsistent;
qe::expr_quant_elim_star1 m_quant_elim;
struct scope {
unsigned m_asserted_formulas_lim;
unsigned m_vars_lim;
unsigned m_forbidden_vars_lim;
bool m_inconsistent_old;
};
svector<scope> m_scopes;
volatile bool m_cancel_flag;
void setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp);
bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & subst, proof_ref& pr);
bool is_pos_literal(expr * n);
bool is_neg_literal(expr * n);
bool solve_ite_definition_core(expr * lhs1, expr * rhs1, expr * lhs2, expr * rhs2, expr * cond, app_ref & var, expr_ref & subst);
bool solve_ite_definition(expr * arg1, expr * arg2, expr * arg3, app_ref & var, expr_ref & subst);
bool solve_core(expr * n, app_ref & var, expr_ref & subst, proof_ref& pr);
bool solve_core();
void solve();
void reduce_asserted_formulas();
void swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
void find_macros_core();
void find_macros();
void expand_macros();
void apply_demodulators();
void apply_quasi_macros();
void nnf_cnf();
bool apply_eager_bit_blaster();
bool apply_user_rewriter();
void infer_patterns();
void eliminate_term_ite();
void reduce_and_solve();
void flush_cache() { m_pre_simplifier.reset(); m_simplifier.reset(); }
void restore_subst(unsigned old_size);
void restore_forbidden_vars(unsigned old_size);
void set_eliminate_and(bool flag);
void propagate_values();
void propagate_booleans();
bool pull_cheap_ite_trees();
bool pull_nested_quantifiers();
void push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs);
void context_simplifier();
void strong_context_simplifier();
void eliminate_and();
void refine_inj_axiom();
bool cheap_quant_fourier_motzkin();
bool quant_elim();
bool apply_der_core();
void apply_der();
void apply_distribute_forall();
bool apply_bit2int();
void lift_ite();
bool elim_bvs_from_quantifiers();
void ng_lift_ite();
#ifdef Z3DEBUG
bool check_well_sorted() const;
#endif
unsigned get_total_size() const;
bool has_bv() const;
void max_bv_sharing();
bool canceled() { return m_cancel_flag; }
public:
asserted_formulas(ast_manager & m, front_end_params & p);
~asserted_formulas();
void setup();
void assert_expr(expr * e, proof * in_pr);
void assert_expr(expr * e);
void reset();
void set_cancel_flag(bool f);
void push_scope();
void pop_scope(unsigned num_scopes);
bool inconsistent() const { return m_inconsistent; }
proof * get_inconsistency_proof() const;
void reduce();
unsigned get_num_formulas() const { return m_asserted_formulas.size(); }
unsigned get_formulas_last_level() const;
unsigned get_qhead() const { return m_asserted_qhead; }
void commit();
expr * get_formula(unsigned idx) const { return m_asserted_formulas.get(idx); }
proof * get_formula_proof(unsigned idx) const { return m_manager.proofs_enabled() ? m_asserted_formula_prs.get(idx) : 0; }
expr * const * get_formulas() const { return m_asserted_formulas.c_ptr(); }
proof * const * get_formula_proofs() const { return m_asserted_formula_prs.c_ptr(); }
void init(unsigned num_formulas, expr * const * formulas, proof * const * prs);
void register_simplifier_plugin(simplifier_plugin * p) { m_simplifier.register_plugin(p); }
simplifier & get_simplifier() { return m_simplifier; }
void set_user_rewriter(void* ctx, user_rewriter::fn* rw) { m_user_rewriter.set_rewriter(ctx, rw); }
void get_assertions(ptr_vector<expr> & result);
bool empty() const { return m_asserted_formulas.empty(); }
void collect_static_features();
void display(std::ostream & out) const;
void display_ll(std::ostream & out, ast_mark & pp_visited) const;
void collect_statistics(statistics & st) const;
// TODO: improve precision of the following method.
bool has_quantifiers() const { return m_simplifier.visited_quantifier(); /* approximation */ }
// -----------------------------------
//
// Macros
//
// -----------------------------------
unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); }
unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); }
func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); }
func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); }
quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); }
// auxiliary function used to create a logic context based on a model.
void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_macro_manager.insert(f, m, pr); }
// -----------------------------------
//
// Eliminated vars
//
// -----------------------------------
ptr_vector<app>::const_iterator begin_subst_vars() const { return m_vars.begin(); }
ptr_vector<app>::const_iterator end_subst_vars() const { return m_vars.end(); }
ptr_vector<app>::const_iterator begin_subst_vars_last_level() const {
unsigned sidx = m_scopes.empty() ? 0 : m_scopes.back().m_vars_lim;
return m_vars.begin() + sidx;
}
expr * get_subst(app * var) { expr * def = 0; proof * pr; m_subst.get(var, def, pr); return def; }
bool is_subst(app * var) const { return m_subst.contains(var); }
void get_ordered_subst_vars(ptr_vector<app> & ordered_vars);
};
#endif /* _ASSERTED_FORMULAS_H_ */

View file

@ -1,717 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
assertion_set2sat.cpp
Abstract:
"Compile" an assertion set into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into an assertion set.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into an assertion set.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into an assertion set.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-05-22
Notes:
--*/
#include"assertion_set2sat.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
#include"cooperate.h"
#include"filter_model_converter.h"
#include"model_evaluator.h"
#include"for_each_expr.h"
#include"model_v2_pp.h"
#include"assertion_set_util.h"
struct assertion_set2sat::imp {
struct frame {
app * m_t;
unsigned m_root:1;
unsigned m_sign:1;
unsigned m_idx;
frame(app * t, bool r, bool s, unsigned idx):
m_t(t), m_root(r), m_sign(s), m_idx(idx) {}
};
ast_manager & m;
svector<frame> m_frame_stack;
svector<sat::literal> m_result_stack;
obj_map<app, sat::literal> m_cache;
obj_hashtable<expr> m_interface_vars;
sat::solver & m_solver;
atom2bool_var & m_map;
sat::bool_var m_true;
bool m_ite_extra;
unsigned long long m_max_memory;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map):
m(_m),
m_solver(s),
m_map(map) {
updt_params(p);
m_cancel = false;
m_true = sat::null_bool_var;
}
void updt_params(params_ref const & p) {
m_ite_extra = p.get_bool(":ite-extra", true);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void throw_op_not_handled() {
throw assertion_set2sat_exception("operator not supported, apply simplifier before invoking translator");
}
void mk_clause(sat::literal l) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l << "\n";);
m_solver.mk_clause(1, &l);
}
void mk_clause(sat::literal l1, sat::literal l2) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";);
m_solver.mk_clause(l1, l2);
}
void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";);
m_solver.mk_clause(l1, l2, l3);
}
void mk_clause(unsigned num, sat::literal * lits) {
TRACE("assertion_set2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";);
m_solver.mk_clause(num, lits);
}
sat::bool_var mk_true() {
// create fake variable to represent true;
if (m_true == sat::null_bool_var) {
m_true = m_solver.mk_var();
mk_clause(sat::literal(m_true, false)); // v is true
}
return m_true;
}
void convert_atom(expr * t, bool root, bool sign) {
SASSERT(m.is_bool(t));
sat::literal l;
sat::bool_var v = m_map.to_bool_var(t);
if (v == sat::null_bool_var) {
if (m.is_true(t)) {
l = sat::literal(mk_true(), sign);
}
else if (m.is_false(t)) {
l = sat::literal(mk_true(), !sign);
}
else {
bool ext = !is_uninterp_const(t) || m_interface_vars.contains(t);
sat::bool_var v = m_solver.mk_var(ext);
m_map.insert(t, v);
l = sat::literal(v, sign);
TRACE("assertion_set2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";);
}
}
else {
SASSERT(v != sat::null_bool_var);
l = sat::literal(v, sign);
}
SASSERT(l != sat::null_literal);
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
}
bool process_cached(app * t, bool root, bool sign) {
sat::literal l;
if (m_cache.find(t, l)) {
if (sign)
l.neg();
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
return true;
}
return false;
}
bool visit(expr * t, bool root, bool sign) {
if (!is_app(t)) {
convert_atom(t, root, sign);
return true;
}
if (process_cached(to_app(t), root, sign))
return true;
if (to_app(t)->get_family_id() != m.get_basic_family_id()) {
convert_atom(t, root, sign);
return true;
}
switch (to_app(t)->get_decl_kind()) {
case OP_NOT:
case OP_OR:
case OP_IFF:
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
case OP_ITE:
case OP_EQ:
if (m.is_bool(to_app(t)->get_arg(1))) {
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
}
convert_atom(t, root, sign);
return true;
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
TRACE("assertion_set2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";);
throw_op_not_handled();
default:
convert_atom(t, root, sign);
return true;
}
}
void convert_or(app * t, bool root, bool sign) {
TRACE("assertion_set2sat", tout << "convert_or:\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned num = t->get_num_args();
if (root) {
SASSERT(num == m_result_stack.size());
if (sign) {
// this case should not really happen.
for (unsigned i = 0; i < num; i++) {
sat::literal l = m_result_stack[i];
l.neg();
mk_clause(l);
}
}
else {
mk_clause(m_result_stack.size(), m_result_stack.c_ptr());
m_result_stack.reset();
}
}
else {
SASSERT(num <= m_result_stack.size());
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num;
for (unsigned i = 0; i < num; i++) {
mk_clause(~lits[i], l);
}
m_result_stack.push_back(~l);
lits = m_result_stack.end() - num - 1;
// remark: mk_clause may perform destructive updated to lits.
// I have to execute it after the binary mk_clause above.
mk_clause(num+1, lits);
unsigned old_sz = m_result_stack.size() - num - 1;
m_result_stack.shrink(old_sz);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_ite(app * n, bool root, bool sign) {
unsigned sz = m_result_stack.size();
SASSERT(sz >= 3);
sat::literal c = m_result_stack[sz-3];
sat::literal t = m_result_stack[sz-2];
sat::literal e = m_result_stack[sz-1];
if (root) {
SASSERT(sz == 3);
if (sign) {
mk_clause(~c, ~t);
mk_clause(c, ~e);
}
else {
mk_clause(~c, t);
mk_clause(c, e);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(n, l);
mk_clause(~l, ~c, t);
mk_clause(~l, c, e);
mk_clause(l, ~c, ~t);
mk_clause(l, c, ~e);
if (m_ite_extra) {
mk_clause(~t, ~e, l);
mk_clause(t, e, ~l);
}
m_result_stack.shrink(sz-3);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_iff(app * t, bool root, bool sign) {
TRACE("assertion_set2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned sz = m_result_stack.size();
SASSERT(sz >= 2);
sat::literal l1 = m_result_stack[sz-1];
sat::literal l2 = m_result_stack[sz-2];
if (root) {
SASSERT(sz == 2);
if (sign) {
mk_clause(l1, l2);
mk_clause(~l1, ~l2);
}
else {
mk_clause(l1, ~l2);
mk_clause(~l1, l2);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
mk_clause(~l, l1, ~l2);
mk_clause(~l, ~l1, l2);
mk_clause(l, l1, l2);
mk_clause(l, ~l1, ~l2);
m_result_stack.shrink(sz-2);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert(app * t, bool root, bool sign) {
SASSERT(t->get_family_id() == m.get_basic_family_id());
switch (to_app(t)->get_decl_kind()) {
case OP_OR:
convert_or(t, root, sign);
break;
case OP_ITE:
convert_ite(t, root, sign);
break;
case OP_IFF:
case OP_EQ:
convert_iff(t, root, sign);
break;
default:
UNREACHABLE();
}
}
void process(expr * n) {
TRACE("assertion_set2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";);
if (visit(n, true, false)) {
SASSERT(m_result_stack.empty());
return;
}
while (!m_frame_stack.empty()) {
loop:
cooperate("assertion_set2sat");
if (m_cancel)
throw assertion_set2sat_exception(STE_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw assertion_set2sat_exception(STE_MAX_MEMORY_MSG);
frame & fr = m_frame_stack.back();
app * t = fr.m_t;
bool root = fr.m_root;
bool sign = fr.m_sign;
TRACE("assertion_set2sat_bug", tout << "result stack\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
if (fr.m_idx == 0 && process_cached(t, root, sign)) {
m_frame_stack.pop_back();
continue;
}
if (m.is_not(t)) {
m_frame_stack.pop_back();
visit(t->get_arg(0), root, !sign);
continue;
}
unsigned num = t->get_num_args();
while (fr.m_idx < num) {
expr * arg = t->get_arg(fr.m_idx);
fr.m_idx++;
if (!visit(arg, false, false))
goto loop;
}
TRACE("assertion_set2sat_bug", tout << "converting\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
convert(t, root, sign);
m_frame_stack.pop_back();
}
SASSERT(m_result_stack.empty());
}
void operator()(assertion_set const & s) {
m_interface_vars.reset();
collect_boolean_interface(s, m_interface_vars);
unsigned size = s.size();
for (unsigned idx = 0; idx < size; idx++) {
expr * f = s.form(idx);
process(f);
}
}
void operator()(unsigned sz, expr * const * fs) {
m_interface_vars.reset();
collect_boolean_interface(m, sz, fs, m_interface_vars);
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
void set_cancel(bool f) { m_cancel = f; }
};
struct unsupported_bool_proc {
struct found {};
ast_manager & m;
unsupported_bool_proc(ast_manager & _m):m(_m) {}
void operator()(var *) {}
void operator()(quantifier *) {}
void operator()(app * n) {
if (n->get_family_id() == m.get_basic_family_id()) {
switch (n->get_decl_kind()) {
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
throw found();
default:
break;
}
}
}
};
/**
\brief Return true if s contains an unsupported Boolean operator.
assertion_set_rewriter (with the following configuration) can be used to
eliminate unsupported operators.
:elim-and true
:blast-distinct true
*/
bool assertion_set2sat::has_unsupported_bool(assertion_set const & s) {
return test<unsupported_bool_proc>(s);
}
assertion_set2sat::assertion_set2sat():m_imp(0) {
}
void assertion_set2sat::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":ite-extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas");
}
struct assertion_set2sat::scoped_set_imp {
assertion_set2sat * m_owner;
scoped_set_imp(assertion_set2sat * o, assertion_set2sat::imp * i):m_owner(o) {
#pragma omp critical (assertion_set2sat)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (assertion_set2sat)
{
m_owner->m_imp = 0;
}
}
};
void assertion_set2sat::operator()(assertion_set const & s, params_ref const & p, sat::solver & t, atom2bool_var & m) {
imp proc(s.m(), p, t, m);
scoped_set_imp set(this, &proc);
proc(s);
}
void assertion_set2sat::operator()(ast_manager & mng, unsigned num, expr * const * fs, params_ref const & p, sat::solver & t, atom2bool_var & m) {
imp proc(mng, p, t, m);
scoped_set_imp set(this, &proc);
proc(num, fs);
}
void assertion_set2sat::set_cancel(bool f) {
#pragma omp critical (assertion_set2sat)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
class sat_model_converter : public model_converter {
sat::model_converter m_mc;
// TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck.
// We need to save only the expressions associated with variables that occur in m_mc.
// This information may be stored as a vector of pairs.
// The mapping is only created during the model conversion.
expr_ref_vector m_var2expr;
ref<filter_model_converter> m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion
sat_model_converter(ast_manager & m):
m_var2expr(m) {
}
public:
sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) {
m_mc.copy(s.get_model_converter());
m_fmc = alloc(filter_model_converter, m);
}
ast_manager & m() { return m_var2expr.get_manager(); }
void insert(expr * atom, bool aux) {
m_var2expr.push_back(atom);
if (aux) {
SASSERT(is_uninterp_const(atom));
SASSERT(m().is_bool(atom));
m_fmc->insert(to_app(atom)->get_decl());
}
}
virtual void operator()(model_ref & md) {
TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout););
// REMARK: potential problem
// model_evaluator can't evaluate quantifiers. Then,
// an eliminated variable that depends on a quantified expression can't be recovered.
// A similar problem also affects any model_converter that uses elim_var_model_converter.
//
// Possible solution:
// model_converters reject any variable elimination that depends on a quantified expression.
model_evaluator ev(*md);
ev.set_model_completion(false);
// create a SAT model using md
sat::model sat_md;
unsigned sz = m_var2expr.size();
expr_ref val(m());
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
ev(atom, val);
if (m().is_true(val))
sat_md.push_back(l_true);
else if (m().is_false(val))
sat_md.push_back(l_false);
else
sat_md.push_back(l_undef);
}
// apply SAT model converter
m_mc(sat_md);
// register value of non-auxiliary boolean variables back into md
sz = m_var2expr.size();
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
if (is_uninterp_const(atom)) {
func_decl * d = to_app(atom)->get_decl();
lbool new_val = sat_md[v];
if (new_val == l_true)
md->register_decl(d, m().mk_true());
else if (new_val == l_false)
md->register_decl(d, m().mk_false());
}
}
// apply filter model converter
(*m_fmc)(md);
TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md););
}
virtual model_converter * translate(ast_translation & translator) {
sat_model_converter * res = alloc(sat_model_converter, translator.to());
res->m_fmc = static_cast<filter_model_converter*>(m_fmc->translate(translator));
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++)
res->m_var2expr.push_back(translator(m_var2expr.get(i)));
return res;
}
void display(std::ostream & out) {
out << "(sat-model-converter\n";
m_mc.display(out);
sat::bool_var_set vars;
m_mc.collect_vars(vars);
out << "(atoms";
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++) {
if (vars.contains(i)) {
out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")";
}
}
out << ")\n";
m_fmc->display(out);
out << ")\n";
}
};
struct sat2assertion_set::imp {
ast_manager & m;
expr_ref_vector m_lit2expr;
unsigned long long m_max_memory;
bool m_learned;
bool m_produce_models;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p):m(_m), m_lit2expr(m), m_cancel(false) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_produce_models = p.get_bool(":produce-models", false);
m_learned = p.get_bool(":learned", false);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void checkpoint() {
if (m_cancel)
throw sat2assertion_set_exception(STE_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw sat2assertion_set_exception(STE_MAX_MEMORY_MSG);
}
void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc) {
ref<sat_model_converter> _mc;
if (m_produce_models) {
_mc = alloc(sat_model_converter, m, s);
}
unsigned num_vars = s.num_vars();
m_lit2expr.resize(num_vars * 2);
map.mk_inv(m_lit2expr);
sort * b = m.mk_bool_sort();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
sat::literal l(v, false);
if (m_lit2expr.get(l.index()) == 0) {
SASSERT(m_lit2expr.get((~l).index()) == 0);
app * aux = m.mk_fresh_const(0, b);
if (_mc)
_mc->insert(aux, true);
m_lit2expr.set(l.index(), aux);
m_lit2expr.set((~l).index(), m.mk_not(aux));
}
else {
if (_mc)
_mc->insert(m_lit2expr.get(l.index()), false);
SASSERT(m_lit2expr.get((~l).index()) != 0);
}
}
mc = _mc.get();
}
expr * lit2expr(sat::literal l) {
return m_lit2expr.get(l.index());
}
void assert_clauses(sat::clause * const * begin, sat::clause * const * end, assertion_set & r) {
ptr_buffer<expr> lits;
for (sat::clause * const * it = begin; it != end; it++) {
checkpoint();
lits.reset();
sat::clause const & c = *(*it);
unsigned sz = c.size();
for (unsigned i = 0; i < sz; i++) {
lits.push_back(lit2expr(c[i]));
}
r.assert_expr(m.mk_or(lits.size(), lits.c_ptr()));
}
}
void operator()(sat::solver const & s, atom2bool_var const & map, assertion_set & r, model_converter_ref & mc) {
if (s.inconsistent()) {
r.assert_expr(m.mk_false());
return;
}
init_lit2expr(s, map, mc);
// collect units
unsigned num_vars = s.num_vars();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
switch (s.value(v)) {
case l_true:
r.assert_expr(lit2expr(sat::literal(v, false)));
break;
case l_false:
r.assert_expr(lit2expr(sat::literal(v, true)));
break;
case l_undef:
break;
}
}
// collect binary clauses
svector<sat::solver::bin_clause> bin_clauses;
s.collect_bin_clauses(bin_clauses, m_learned);
svector<sat::solver::bin_clause>::iterator it = bin_clauses.begin();
svector<sat::solver::bin_clause>::iterator end = bin_clauses.end();
for (; it != end; ++it) {
checkpoint();
r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second)));
}
// collect clauses
assert_clauses(s.begin_clauses(), s.end_clauses(), r);
if (m_learned)
assert_clauses(s.begin_learned(), s.end_learned(), r);
}
void set_cancel(bool f) { m_cancel = f; }
};
sat2assertion_set::sat2assertion_set():m_imp(0) {
}
void sat2assertion_set::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":learned", CPK_BOOL, "(default: false) collect also learned clauses.");
}
struct sat2assertion_set::scoped_set_imp {
sat2assertion_set * m_owner;
scoped_set_imp(sat2assertion_set * o, sat2assertion_set::imp * i):m_owner(o) {
#pragma omp critical (sat2assertion_set)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (sat2assertion_set)
{
m_owner->m_imp = 0;
}
}
};
void sat2assertion_set::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p,
assertion_set & s, model_converter_ref & mc) {
imp proc(s.m(), p);
scoped_set_imp set(this, &proc);
proc(t, m, s, mc);
}
void sat2assertion_set::set_cancel(bool f) {
#pragma omp critical (sat2assertion_set)
{
if (m_imp)
m_imp->set_cancel(f);
}
}

View file

@ -1,95 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
assertion_set2sat.h
Abstract:
"Compile" an assertion set into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into an assertion set.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into an assertion set.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into an assertion set.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-05-22
Notes:
--*/
#ifndef _ASSERTION_SET2SAT_H_
#define _ASSERTION_SET2SAT_H_
#include"assertion_set.h"
#include"sat_solver.h"
#include"strategy_exception.h"
#include"model_converter.h"
#include"atom2bool_var.h"
MK_ST_EXCEPTION(assertion_set2sat_exception);
class assertion_set2sat {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
assertion_set2sat();
static void collect_param_descrs(param_descrs & r);
static bool has_unsupported_bool(assertion_set const & s);
/**
\brief "Compile" the assertion set into the given sat solver.
Store a mapping from atoms to boolean variables into m.
\remark m doesn't need to be empty. the definitions there are
reused.
*/
void operator()(assertion_set const & s, params_ref const & p, sat::solver & t, atom2bool_var & m);
/**
\brief "Compile" the formulas fs into the given sat solver.
Store a mapping from atoms to boolean variables into m.
\remark m doesn't need to be empty. the definitions there are
reused.
*/
void operator()(ast_manager & mng, unsigned num, expr * const * fs, params_ref const & p, sat::solver & t, atom2bool_var & m);
void operator()(ast_manager & mng, expr * f, params_ref const & p, sat::solver & t, atom2bool_var & m) { operator()(mng, 1, &f, p, t, m); }
void set_cancel(bool f);
};
MK_ST_EXCEPTION(sat2assertion_set_exception);
class sat2assertion_set {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
sat2assertion_set();
static void collect_param_descrs(param_descrs & r);
/**
\brief Translate the state of the SAT engine back into an assertion set.
The SAT solver may use variables that are not in \c m. The translator
creates fresh boolean AST variables for them. They are stored in fvars.
*/
void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, assertion_set & s, model_converter_ref & mc);
void set_cancel(bool f);
};
#endif

View file

@ -1,121 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
atom2bool_var.cpp
Abstract:
The mapping between SAT boolean variables and atoms
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"atom2bool_var.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
#include"assertion_set.h" // TODO delete
#include"goal.h"
void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const {
obj_map<expr, var>::iterator it = m_mapping.begin();
obj_map<expr, var>::iterator end = m_mapping.end();
for (; it != end; ++it) {
sat::literal l(static_cast<sat::bool_var>(it->m_value), false);
lit2expr.set(l.index(), it->m_key);
l.neg();
lit2expr.set(l.index(), m().mk_not(it->m_key));
}
}
sat::bool_var atom2bool_var::to_bool_var(expr * n) const {
sat::bool_var v = sat::null_bool_var;
m_mapping.find(n, v);
return v;
}
struct collect_boolean_interface_proc {
struct visitor {
obj_hashtable<expr> & m_r;
visitor(obj_hashtable<expr> & r):m_r(r) {}
void operator()(var * n) {}
void operator()(app * n) { if (is_uninterp_const(n)) m_r.insert(n); }
void operator()(quantifier * n) {}
};
ast_manager & m;
expr_fast_mark2 fvisited;
expr_fast_mark1 tvisited;
ptr_vector<expr> todo;
visitor proc;
collect_boolean_interface_proc(ast_manager & _m, obj_hashtable<expr> & r):
m(_m),
proc(r) {
}
void process(expr * f) {
if (fvisited.is_marked(f))
return;
fvisited.mark(f);
todo.push_back(f);
while (!todo.empty()) {
expr * t = todo.back();
todo.pop_back();
if (is_uninterp_const(t))
continue;
if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) {
decl_kind k = to_app(t)->get_decl_kind();
if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) {
unsigned num = to_app(t)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * arg = to_app(t)->get_arg(i);
if (fvisited.is_marked(arg))
continue;
fvisited.mark(arg);
todo.push_back(arg);
}
}
}
else {
quick_for_each_expr(proc, tvisited, t);
}
}
}
template<typename T>
void operator()(T const & g) {
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++)
process(g.form(i));
}
void operator()(unsigned sz, expr * const * fs) {
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
};
template<typename T>
void collect_boolean_interface_core(T const & s, obj_hashtable<expr> & r) {
collect_boolean_interface_proc proc(s.m(), r);
proc(s);
}
void collect_boolean_interface(assertion_set const & s, obj_hashtable<expr> & r) {
collect_boolean_interface_core(s, r);
}
void collect_boolean_interface(goal const & g, obj_hashtable<expr> & r) {
collect_boolean_interface_core(g, r);
}
void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable<expr> & r) {
collect_boolean_interface_proc proc(m, r);
proc(num, fs);
}

View file

@ -1,45 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
atom2bool_var.h
Abstract:
The mapping between SAT boolean variables and atoms
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _ATOM2BOOL_VAR_H_
#define _ATOM2BOOL_VAR_H_
#include"expr2var.h"
#include"sat_types.h"
/**
\brief Mapping from atoms into SAT boolean variables.
*/
class atom2bool_var : public expr2var {
public:
atom2bool_var(ast_manager & m):expr2var(m) {}
void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); }
sat::bool_var to_bool_var(expr * n) const;
void mk_inv(expr_ref_vector & lit2expr) const;
// return true if the mapping contains uninterpreted atoms.
bool interpreted_atoms() const { return expr2var::interpreted_vars(); }
};
class assertion_set; // TODO: delete
class goal;
void collect_boolean_interface(assertion_set const & s, obj_hashtable<expr> & r); // TODO delete
void collect_boolean_interface(goal const & g, obj_hashtable<expr> & r);
void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable<expr> & r);
#endif

View file

@ -1,421 +0,0 @@
/*++
Copyright (c) 2009 Microsoft Corporation
Module Name:
bit2cpp.cpp
Abstract:
Routines for simplifying bit2int expressions.
This propagates bv2int over arithmetical symbols as much as possible,
converting arithmetic operations into bit-vector operations.
Author:
Nikolaj Bjorner (nbjorner) 2009-08-28
Revision History:
--*/
#include "bit2int.h"
#include "ast_pp.h"
#include "ast_ll_pp.h"
#include "for_each_ast.h"
#define CHECK(_x_) if (!(_x_)) { UNREACHABLE(); }
bit2int::bit2int(ast_manager & m) :
m_manager(m), m_bv_util(m), m_arith_util(m), m_cache(m), m_bit0(m) {
m_bit0 = m_bv_util.mk_numeral(0,1);
}
void bit2int::operator()(expr * m, expr_ref & result, proof_ref& p) {
flush_cache();
expr_reduce emap(*this);
for_each_ast(emap, m);
result = get_cached(m);
if (m_manager.proofs_enabled() && m != result.get()) {
// TBD: rough
p = m_manager.mk_rewrite(m, result);
}
TRACE("bit2int",
tout << mk_pp(m, m_manager) << "======>\n"
<< mk_pp(result, m_manager) << "\n";);
}
unsigned bit2int::get_b2i_size(expr* n) {
SASSERT(m_bv_util.is_bv2int(n));
return m_bv_util.get_bv_size(to_app(n)->get_arg(0));
}
unsigned bit2int::get_numeral_bits(numeral const& k) {
numeral two(2);
numeral n(abs(k));
unsigned num_bits = 1;
n = div(n, two);
while (n.is_pos()) {
++num_bits;
n = div(n, two);
}
return num_bits;
}
void bit2int::align_size(expr* e, unsigned sz, expr_ref& result) {
unsigned sz1 = m_bv_util.get_bv_size(e);
SASSERT(sz1 <= sz);
m_bv_simplifier->mk_zeroext(sz-sz1, e, result);
}
void bit2int::align_sizes(expr_ref& a, expr_ref& b) {
unsigned sz1 = m_bv_util.get_bv_size(a);
unsigned sz2 = m_bv_util.get_bv_size(b);
expr_ref tmp(m_manager);
if (sz1 > sz2) {
m_bv_simplifier->mk_zeroext(sz1-sz2, b, tmp);
b = tmp;
}
else if (sz2 > sz1) {
m_bv_simplifier->mk_zeroext(sz2-sz1, a, tmp);
a = tmp;
}
}
bool bit2int::extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv) {
numeral k;
bool is_int;
if (m_bv_util.is_bv2int(n)) {
bv = to_app(n)->get_arg(0);
sz = m_bv_util.get_bv_size(bv);
sign = false;
return true;
}
else if (m_arith_util.is_numeral(n, k, is_int) && is_int) {
sz = get_numeral_bits(k);
bv = m_bv_util.mk_numeral(k, m_bv_util.mk_sort(sz));
sign = k.is_neg();
return true;
}
else {
return false;
}
}
bool bit2int::mk_add(expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 &&
extract_bv(e2, sz2, sign2, tmp2) && !sign2) {
unsigned sz;
numeral k;
if (m_bv_util.is_numeral(tmp1, k, sz) && k.is_zero()) {
result = e2;
return true;
}
if (m_bv_util.is_numeral(tmp2, k, sz) && k.is_zero()) {
result = e1;
return true;
}
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(1, tmp1, tmp1);
m_bv_simplifier->mk_zeroext(1, tmp2, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
m_bv_simplifier->mk_add(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
return true;
}
return false;
}
bool bit2int::mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 &&
extract_bv(e2, sz2, sign2, tmp2) && !sign2) {
align_sizes(tmp1, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
switch(ty) {
case lt:
m_bv_simplifier->mk_leq_core(false, tmp2, tmp1, tmp3);
result = m_manager.mk_not(tmp3);
break;
case le:
m_bv_simplifier->mk_leq_core(false,tmp1, tmp2, result);
break;
case eq:
result = m_manager.mk_eq(tmp1,tmp2);
break;
}
return true;
}
return false;
}
bool bit2int::mk_mul(expr* e1, expr* e2, expr_ref& result) {
unsigned sz1, sz2;
bool sign1, sign2;
expr_ref tmp1(m_manager), tmp2(m_manager);
expr_ref tmp3(m_manager);
if (extract_bv(e1, sz1, sign1, tmp1) &&
extract_bv(e2, sz2, sign2, tmp2)) {
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp1), tmp1, tmp1);
m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp2), tmp2, tmp2);
SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
m_bv_simplifier->mk_mul(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
if (sign1 != sign2) {
result = m_arith_util.mk_uminus(result);
}
return true;
}
return false;
}
bool bit2int::is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg) {
ptr_vector<expr> todo;
expr_ref tmp(m_manager);
numeral k;
bool is_int;
todo.push_back(n);
m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), pos);
m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), neg);
while (!todo.empty()) {
n = todo.back();
todo.pop_back();
if (m_bv_util.is_bv2int(n)) {
CHECK(mk_add(n, pos, pos));
}
else if (m_arith_util.is_numeral(n, k, is_int) && is_int) {
if (k.is_nonneg()) {
CHECK(mk_add(n, pos, pos));
}
else {
tmp = m_arith_util.mk_numeral(-k, true);
CHECK(mk_add(tmp, neg, neg));
}
}
else if (m_arith_util.is_add(n)) {
for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) {
todo.push_back(to_app(n)->get_arg(i));
}
}
else if (m_arith_util.is_mul(n) &&
to_app(n)->get_num_args() == 2 &&
m_arith_util.is_numeral(to_app(n)->get_arg(0), k, is_int) && is_int && k.is_minus_one() &&
m_bv_util.is_bv2int(to_app(n)->get_arg(1))) {
CHECK(mk_add(to_app(n)->get_arg(1), neg, neg));
}
else if (m_arith_util.is_mul(n) &&
to_app(n)->get_num_args() == 2 &&
m_arith_util.is_numeral(to_app(n)->get_arg(1), k, is_int) && is_int && k.is_minus_one() &&
m_bv_util.is_bv2int(to_app(n)->get_arg(0))) {
CHECK(mk_add(to_app(n)->get_arg(0), neg, neg));
}
else if (m_arith_util.is_uminus(n) &&
m_bv_util.is_bv2int(to_app(n)->get_arg(0))) {
CHECK(mk_add(to_app(n)->get_arg(0), neg, neg));
}
else {
TRACE("bit2int", tout << "Not a poly: " << mk_pp(n, m_manager) << "\n";);
return false;
}
}
return true;
}
void bit2int::visit(quantifier* q) {
expr_ref result(m_manager);
result = get_cached(q->get_expr());
result = m_manager.update_quantifier(q, result);
cache_result(q, result);
}
void bit2int::visit(app* n) {
func_decl* f = n->get_decl();
unsigned num_args = n->get_num_args();
m_args.reset();
for (unsigned i = 0; i < num_args; ++i) {
m_args.push_back(get_cached(n->get_arg(i)));
}
expr* const* args = m_args.c_ptr();
bool has_b2i =
m_arith_util.is_le(n) || m_arith_util.is_ge(n) || m_arith_util.is_gt(n) ||
m_arith_util.is_lt(n) || m_manager.is_eq(n);
expr_ref result(m_manager);
for (unsigned i = 0; !has_b2i && i < num_args; ++i) {
has_b2i = m_bv_util.is_bv2int(args[i]);
}
if (!has_b2i) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
//
// bv2int(x) + bv2int(y) -> bv2int(pad(x) + pad(y))
// bv2int(x) + k -> bv2int(pad(x) + pad(k))
// bv2int(x) * bv2int(y) -> bv2int(pad(x) * pad(y))
// bv2int(x) * k -> sign(k)*bv2int(pad(x) * pad(k))
// bv2int(x) - bv2int(y) <= z -> bv2int(x) <= bv2int(y) + z
// bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z
//
expr* e1, *e2;
expr_ref tmp1(m_manager), tmp2(m_manager);
expr_ref tmp3(m_manager);
expr_ref pos1(m_manager), neg1(m_manager);
expr_ref pos2(m_manager), neg2(m_manager);
expr_ref e2bv(m_manager);
bool sign2;
numeral k;
unsigned sz2;
if (num_args >= 2) {
e1 = args[0];
e2 = args[1];
}
if (m_arith_util.is_add(n) && num_args >= 1) {
result = e1;
for (unsigned i = 1; i < num_args; ++i) {
e1 = result;
e2 = args[i];
if (!mk_add(e1, e2, result)) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
}
cache_result(n, result);
}
else if (m_arith_util.is_mul(n) && num_args >= 1) {
result = e1;
for (unsigned i = 1; i < num_args; ++i) {
e1 = result;
e2 = args[i];
if (!mk_mul(e1, e2, result)) {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
return;
}
}
cache_result(n, result);
}
else if (m_manager.is_eq(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(eq, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_le(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(le, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_lt(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(lt, tmp1, tmp2, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_ge(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(le, tmp2, tmp1, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_gt(n) &&
is_bv_poly(e1, pos1, neg1) &&
is_bv_poly(e2, pos2, neg2) &&
mk_add(pos1, neg2, tmp1) &&
mk_add(neg1, pos2, tmp2) &&
mk_comp(lt, tmp2, tmp1, result)) {
cache_result(n, result);
}
else if (m_arith_util.is_mod(n) &&
is_bv_poly(e1, pos1, neg1) &&
extract_bv(e2, sz2, sign2, e2bv) && !sign2) {
//
// (pos1 - neg1) mod e2 = (pos1 + (e2 - (neg1 mod e2))) mod e2
//
unsigned sz_p, sz_n, sz;
bool sign_p, sign_n;
expr_ref tmp_p(m_manager), tmp_n(m_manager);
CHECK(extract_bv(pos1, sz_p, sign_p, tmp_p));
CHECK(extract_bv(neg1, sz_n, sign_n, tmp_n));
SASSERT(!sign_p && !sign_n);
// pos1 mod e2
if (m_bv_util.is_numeral(tmp_n, k, sz) && k.is_zero()) {
tmp1 = tmp_p;
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
cache_result(n, result);
return;
}
// neg1 mod e2;
tmp1 = tmp_n;
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
// e2 - (neg1 mod e2)
tmp1 = e2bv;
tmp2 = tmp3;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_sub(tmp1, tmp2, tmp3);
// pos1 + (e2 - (neg1 mod e2))
tmp1 = tmp_p;
tmp2 = tmp3;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_zeroext(1, tmp1, tmp_p);
m_bv_simplifier->mk_zeroext(1, tmp2, tmp_n);
m_bv_simplifier->mk_add(tmp_p, tmp_n, tmp1);
// (pos1 + (e2 - (neg1 mod e2))) mod e2
tmp2 = e2bv;
align_sizes(tmp1, tmp2);
m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
cache_result(n, result);
}
else {
result = m_manager.mk_app(f, num_args, args);
cache_result(n, result);
}
}
expr * bit2int::get_cached(expr * n) const {
return const_cast<bit2int*>(this)->m_cache.find(n);
}
void bit2int::cache_result(expr * n, expr * r) {
TRACE("bit2int_verbose", tout << "caching:\n" << mk_ll_pp(n, m_manager) <<
"======>\n" << mk_ll_pp(r, m_manager) << "\n";);
m_cache.insert(n, r);
}

View file

@ -1,96 +0,0 @@
/*++
Copyright (c) 2009 Microsoft Corporation
Module Name:
bit2int.h
Abstract:
Routines for simplifying bit2int expressions.
Author:
Nikolaj Bjorner (nbjorner) 2009-08-28
Revision History:
--*/
#ifndef _BIT2INT_H_
#define _BIT2INT_H_
#include"bv_decl_plugin.h"
#include"arith_decl_plugin.h"
#include"act_cache.h"
#include"basic_simplifier_plugin.h"
#include"bv_simplifier_plugin.h"
class bit2int {
protected:
typedef rational numeral;
enum eq_type {
lt,
le,
eq
};
class expr_reduce {
bit2int& m_super;
public:
expr_reduce(bit2int& s) : m_super(s) {}
void operator()(var* v) {
m_super.cache_result(v, v);
}
void operator()(quantifier* q) {
m_super.visit(q);
}
void operator()(app* a) {
m_super.visit(a);
}
void operator()(ast* a) {}
};
typedef act_cache expr_map;
ast_manager & m_manager;
bv_util m_bv_util;
arith_util m_arith_util;
bv_simplifier_plugin * m_bv_simplifier;
expr_map m_cache; // map: ast -> ast ref. counters are incremented when inserted here.
expr_ref m_bit0;
ptr_vector<expr> m_args;
void visit(app* n);
void visit(quantifier* q);
unsigned get_b2i_size(expr * n);
bool extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv);
unsigned get_numeral_bits(numeral const& k);
bool is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg);
bool mk_mul(expr* a, expr* b, expr_ref& result);
bool mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result);
bool mk_add(expr* e1, expr* e2, expr_ref& result);
expr * get_cached(expr * n) const;
bool is_cached(expr * n) const { return get_cached(n) != 0; }
void cache_result(expr * n, expr * r);
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.cleanup(); }
void align_size(expr* e, unsigned sz, expr_ref& result);
void align_sizes(expr_ref& a, expr_ref& b);
public:
bit2int(ast_manager & m);
void set_bv_simplifier(bv_simplifier_plugin * p) { m_bv_simplifier = p; }
void operator()(expr * m, expr_ref & result, proof_ref& p);
};
#endif /* _BIT2INT_H_ */

View file

@ -1,128 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-05.
Revision History:
--*/
#include"bit_blaster.h"
#include"bit_blaster_tpl_def.h"
#include"ast_pp.h"
#include"bv_decl_plugin.h"
bit_blaster_cfg::bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s):
m_util(u),
m_params(p),
s(_s) {
}
static void sort_args(expr * & l1, expr * & l2, expr * & l3) {
expr * args[3] = {l1, l2, l3};
// ast_lt_proc is based on the AST ids. So, it is a total order on AST nodes.
// No need for stable_sort
std::sort(args, args+3, ast_lt_proc());
l1 = args[0]; l2 = args[1]; l3 = args[2];
}
void bit_blaster_cfg::mk_xor3(expr * l1, expr * l2, expr * l3, expr_ref & r) {
TRACE("xor3", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
sort_args(l1, l2, l3);
TRACE("xor3_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
if (m_params.m_bb_ext_gates) {
if (l1 == l2)
r = l3;
else if (l1 == l3)
r = l2;
else if (l2 == l3)
r = l1;
else if (m().is_complement(l1, l2))
s.mk_not(l3, r);
else if (m().is_complement(l1, l3))
s.mk_not(l2, r);
else if (m().is_complement(l2, l3))
s.mk_not(l1, r);
else if (m().is_true(l1))
s.mk_iff(l2, l3, r);
else if (m().is_false(l1))
s.mk_xor(l2, l3, r);
else if (m().is_true(l2))
s.mk_iff(l1, l3, r);
else if (m().is_false(l2))
s.mk_xor(l1, l3, r);
else if (m().is_true(l3))
s.mk_iff(l1, l2, r);
else if (m().is_false(l3))
s.mk_xor(l1, l2, r);
else
r = m().mk_app(m_util.get_family_id(), OP_XOR3, l1, l2, l3);
}
else {
expr_ref t(m());
s.mk_xor(l1, l2, t);
s.mk_xor(t, l3, r);
}
}
void bit_blaster_cfg::mk_carry(expr * l1, expr * l2, expr * l3, expr_ref & r) {
TRACE("carry", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
sort_args(l1, l2, l3);
TRACE("carry_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id(););
if (m_params.m_bb_ext_gates) {
if ((m().is_false(l1) && m().is_false(l2)) ||
(m().is_false(l1) && m().is_false(l3)) ||
(m().is_false(l2) && m().is_false(l3)))
r = m().mk_false();
else if ((m().is_true(l1) && m().is_true(l2)) ||
(m().is_true(l1) && m().is_true(l3)) ||
(m().is_true(l2) && m().is_true(l3)))
r = m().mk_true();
else if (l1 == l2 && l1 == l3)
r = l1;
else if (m().is_false(l1))
s.mk_and(l2, l3, r);
else if (m().is_false(l2))
s.mk_and(l1, l3, r);
else if (m().is_false(l3))
s.mk_and(l1, l2, r);
else if (m().is_true(l1))
s.mk_or(l2, l3, r);
else if (m().is_true(l2))
s.mk_or(l1, l3, r);
else if (m().is_true(l3))
s.mk_or(l1, l2, r);
else if (m().is_complement(l1, l2))
r = l3;
else if (m().is_complement(l1, l3))
r = l2;
else if (m().is_complement(l2, l3))
r = l1;
else
r = m().mk_app(m_util.get_family_id(), OP_CARRY, l1, l2, l3);
}
else {
expr_ref t1(m()), t2(m()), t3(m());
s.mk_and(l1, l2, t1);
s.mk_and(l1, l3, t2);
s.mk_and(l2, l3, t3);
s.mk_or(t1, t2, t3, r);
}
}
template class bit_blaster_tpl<bit_blaster_cfg>;
bit_blaster::bit_blaster(ast_manager & m, bit_blaster_params const & params):
bit_blaster_tpl<bit_blaster_cfg>(bit_blaster_cfg(m_util, params, m_simp)),
m_util(m),
m_simp(m) {
}

View file

@ -1,65 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-05.
Revision History:
--*/
#ifndef _BIT_BLASTER_H_
#define _BIT_BLASTER_H_
#include"basic_simplifier_plugin.h"
#include"bit_blaster_params.h"
#include"bit_blaster_tpl.h"
#include"bv_decl_plugin.h"
#include"rational.h"
class bit_blaster_cfg {
public:
typedef rational numeral;
protected:
bv_util & m_util;
bit_blaster_params const & m_params;
basic_simplifier_plugin & s;
public:
bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s);
ast_manager & m() const { return m_util.get_manager(); }
numeral power(unsigned n) const { return m_util.power_of_two(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { s.mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r);
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r);
void mk_iff(expr * a, expr * b, expr_ref & r) { s.mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { s.mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { s.mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { s.mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { s.mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { s.mk_not(a, r); }
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { s.mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { s.mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { s.mk_nor(a, b, r); }
};
class bit_blaster : public bit_blaster_tpl<bit_blaster_cfg> {
bv_util m_util;
basic_simplifier_plugin m_simp;
public:
bit_blaster(ast_manager & m, bit_blaster_params const & params);
bit_blaster_params const & get_params() const { return this->m_params; }
};
#endif /* _BIT_BLASTER_H_ */

View file

@ -1,187 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_model_convert.cpp
Abstract:
Model converter for bit-blasting tactics.
Author:
Leonardo (leonardo) 2011-05-09
Notes:
--*/
#include"model.h"
#include"model_pp.h"
#include"model_converter.h"
#include"bv_decl_plugin.h"
#include"ast_smt2_pp.h"
/**
If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans.
If TO_BOOL == false, then bit-vectors of size n were blasted into n-tuples of bit-vectors of size 1.
*/
template<bool TO_BOOL>
struct bit_blaster_model_converter : public model_converter {
func_decl_ref_vector m_vars;
expr_ref_vector m_bits;
ast_manager & m() const { return m_vars.get_manager(); }
bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits):m_vars(m), m_bits(m) {
obj_map<func_decl, expr*>::iterator it = const2bits.begin();
obj_map<func_decl, expr*>::iterator end = const2bits.end();
for (; it != end; ++it) {
func_decl * v = it->m_key;
expr * bits = it->m_value;
SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV));
SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT));
m_vars.push_back(v);
m_bits.push_back(bits);
}
}
virtual ~bit_blaster_model_converter() {
}
void collect_bits(obj_hashtable<func_decl> & bits) {
unsigned sz = m_bits.size();
for (unsigned i = 0; i < sz; i++) {
expr * bs = m_bits.get(i);
SASSERT(!TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_MKBV));
SASSERT(TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_CONCAT));
unsigned num_args = to_app(bs)->get_num_args();
for (unsigned j = 0; j < num_args; j++) {
expr * bit = to_app(bs)->get_arg(j);
SASSERT(!TO_BOOL || m().is_bool(bit));
SASSERT(TO_BOOL || is_sort_of(m().get_sort(bit), m().get_family_id("bv"), BV_SORT));
SASSERT(is_uninterp_const(bit));
bits.insert(to_app(bit)->get_decl());
}
}
TRACE("blaster_mc",
tout << "bits that should not be included in the model:\n";
obj_hashtable<func_decl>::iterator it = bits.begin();
obj_hashtable<func_decl>::iterator end = bits.end();
for (; it != end; ++it) {
tout << (*it)->get_name() << " ";
}
tout << "\n";);
}
void copy_non_bits(obj_hashtable<func_decl> & bits, model * old_model, model * new_model) {
unsigned num = old_model->get_num_constants();
for (unsigned i = 0; i < num; i++) {
func_decl * f = old_model->get_constant(i);
if (bits.contains(f))
continue;
TRACE("blaster_mc", tout << "non-bit: " << f->get_name() << "\n";);
expr * fi = old_model->get_const_interp(f);
new_model->register_decl(f, fi);
}
TRACE("blaster_mc", tout << "after copy non bits:\n"; model_pp(tout, *new_model););
new_model->copy_func_interps(*old_model);
new_model->copy_usort_interps(*old_model);
TRACE("blaster_mc", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model););
}
void mk_bvs(model * old_model, model * new_model) {
bv_util util(m());
rational val;
rational two(2);
SASSERT(m_vars.size() == m_bits.size());
unsigned sz = m_vars.size();
for (unsigned i = 0; i < sz; i++) {
expr * bs = m_bits.get(i);
val.reset();
unsigned bv_sz = to_app(bs)->get_num_args();
if (TO_BOOL) {
SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV));
unsigned j = bv_sz;
while (j > 0) {
--j;
val *= two;
expr * bit = to_app(bs)->get_arg(j);
SASSERT(m().is_bool(bit));
SASSERT(is_uninterp_const(bit));
func_decl * bit_decl = to_app(bit)->get_decl();
expr * bit_val = old_model->get_const_interp(bit_decl);
// remark: if old_model does not assign bit_val, then assume it is false.
if (bit_val != 0 && m().is_true(bit_val))
val++;
}
}
else {
SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT));
for (unsigned j = 0; j < bv_sz; j++) {
val *= two;
expr * bit = to_app(bs)->get_arg(j);
SASSERT(util.is_bv(bit));
SASSERT(util.get_bv_size(bit) == 1);
SASSERT(is_uninterp_const(bit));
func_decl * bit_decl = to_app(bit)->get_decl();
expr * bit_val = old_model->get_const_interp(bit_decl);
// remark: if old_model does not assign bit_val, then assume it is false.
if (bit_val != 0 && !util.is_zero(bit_val))
val++;
}
}
expr * new_val = util.mk_numeral(val, bv_sz);
new_model->register_decl(m_vars.get(i), new_val);
}
}
virtual void operator()(model_ref & md, unsigned goal_idx) {
SASSERT(goal_idx == 0);
model * new_model = alloc(model, m());
obj_hashtable<func_decl> bits;
collect_bits(bits);
copy_non_bits(bits, md.get(), new_model);
mk_bvs(md.get(), new_model);
md = new_model;
}
virtual void operator()(model_ref & md) {
operator()(md, 0);
}
virtual void display(std::ostream & out) {
out << "(bit-blaster-model-converter";
unsigned sz = m_vars.size();
for (unsigned i = 0; i < sz; i++) {
out << "\n (" << m_vars.get(i)->get_name() << " ";
unsigned indent = m_vars.get(i)->get_name().size() + 4;
out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")";
}
out << ")" << std::endl;
}
protected:
bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m) { }
public:
virtual model_converter * translate(ast_translation & translator) {
bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to());
for (unsigned i = 0; i < m_vars.size(); i++)
res->m_vars.push_back(translator(m_vars[i].get()));
for (unsigned i = 0; i < m_bits.size(); i++)
res->m_bits.push_back(translator(m_bits[i].get()));
return res;
}
};
model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits) {
return alloc(bit_blaster_model_converter<true>, m, const2bits);
}
model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits) {
return alloc(bit_blaster_model_converter<false>, m, const2bits);
}

View file

@ -1,27 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_model_convert.h
Abstract:
Model converter for bit-blasting tactics.
Author:
Leonardo (leonardo) 2011-05-09
Notes:
--*/
#ifndef _BIT_BLASTER_MODEL_CONVERTER_H_
#define _BIT_BLASTER_MODEL_CONVERTER_H_
#include"model_converter.h"
model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits);
model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits);
#endif

View file

@ -1,649 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_rewriter.cpp
Abstract:
Bit-blasting rewriter
Author:
Leonardo (leonardo) 2012-10-04
Notes:
--*/
#include"bit_blaster_rewriter.h"
#include"bv_decl_plugin.h"
#include"bit_blaster_tpl_def.h"
#include"rewriter_def.h"
#include"bool_rewriter.h"
#include"ref_util.h"
#include"ast_smt2_pp.h"
struct blaster_cfg {
typedef rational numeral;
bool_rewriter & m_rewriter;
bv_util & m_util;
blaster_cfg(bool_rewriter & r, bv_util & u):m_rewriter(r), m_util(u) {}
ast_manager & m() const { return m_util.get_manager(); }
numeral power(unsigned n) const { return m_util.power_of_two(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) {
expr_ref tmp(m());
mk_xor(b, c, tmp);
mk_xor(a, tmp, r);
}
void mk_iff(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { m_rewriter.mk_not(a, r); }
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) {
expr_ref t1(m()), t2(m()), t3(m());
#if 1
mk_and(a, b, t1);
mk_and(a, c, t2);
mk_and(b, c, t3);
mk_or(t1, t2, t3, r);
#else
mk_or(a, b, t1);
mk_or(a, c, t2);
mk_or(b, c, t3);
mk_and(t1, t2, t3, r);
#endif
}
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { m_rewriter.mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); }
};
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
// template class bit_blaster_tpl<blaster_cfg>;
class blaster : public bit_blaster_tpl<blaster_cfg> {
bool_rewriter m_rewriter;
bv_util m_util;
public:
blaster(ast_manager & m):
bit_blaster_tpl<blaster_cfg>(blaster_cfg(m_rewriter, m_util)),
m_rewriter(m),
m_util(m) {
m_rewriter.set_flat(false);
m_rewriter.set_elim_and(true);
}
bv_util & butil() { return m_util; }
};
struct blaster_rewriter_cfg : public default_rewriter_cfg {
ast_manager & m_manager;
blaster & m_blaster;
expr_ref_vector m_in1;
expr_ref_vector m_in2;
expr_ref_vector m_out;
obj_map<func_decl, expr*> m_const2bits;
expr_ref_vector m_bindings;
bool m_blast_mul;
bool m_blast_add;
bool m_blast_quant;
bool m_blast_full;
unsigned long long m_max_memory;
unsigned m_max_steps;
ast_manager & m() const { return m_manager; }
bv_util & butil() { return m_blaster.butil(); }
void cleanup_buffers() {
m_in1.finalize();
m_in2.finalize();
m_out.finalize();
m_bindings.finalize();
}
blaster_rewriter_cfg(ast_manager & m, blaster & b, params_ref const & p):
m_manager(m),
m_blaster(b),
m_in1(m),
m_in2(m),
m_out(m),
m_bindings(m) {
updt_params(p);
}
~blaster_rewriter_cfg() {
dec_ref_map_key_values(m_manager, m_const2bits);
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_blast_add = p.get_bool(":blast-add", true);
m_blast_mul = p.get_bool(":blast-mul", true);
m_blast_full = p.get_bool(":blast-full", false);
m_blast_quant = p.get_bool(":blast-quant", false);
m_blaster.set_max_memory(m_max_memory);
}
bool rewrite_patterns() const { return true; }
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("bit blaster");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
void get_bits(expr * t, expr_ref_vector & out_bits) {
if (butil().is_mkbv(t)) {
out_bits.append(to_app(t)->get_num_args(), to_app(t)->get_args());
}
else {
unsigned bv_size = butil().get_bv_size(t);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
out_bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t));
}
SASSERT(bv_size == out_bits.size());
}
}
template<typename V>
app * mk_mkbv(V const & bits) {
return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr());
}
void mk_const(func_decl * f, expr_ref & result) {
SASSERT(f->get_family_id() == null_family_id);
SASSERT(f->get_arity() == 0);
expr * r;
if (m_const2bits.find(f, r)) {
result = r;
return;
}
sort * s = f->get_range();
SASSERT(butil().is_bv_sort(s));
unsigned bv_size = butil().get_bv_size(s);
sort * b = m().mk_bool_sort();
m_out.reset();
for (unsigned i = 0; i < bv_size; i++) {
m_out.push_back(m().mk_fresh_const(0, b));
}
r = mk_mkbv(m_out);
m_const2bits.insert(f, r);
m_manager.inc_ref(f);
m_manager.inc_ref(r);
result = r;
}
#define MK_UNARY_REDUCE(OP, BB_OP) \
void OP(expr * arg, expr_ref & result) { \
m_in1.reset(); \
get_bits(arg, m_in1); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_out); \
result = mk_mkbv(m_out); \
}
MK_UNARY_REDUCE(reduce_not, mk_not);
MK_UNARY_REDUCE(reduce_redor, mk_redor);
MK_UNARY_REDUCE(reduce_redand, mk_redand);
#define MK_BIN_REDUCE(OP, BB_OP) \
void OP(expr * arg1, expr * arg2, expr_ref & result) { \
m_in1.reset(); m_in2.reset(); \
get_bits(arg1, m_in1); \
get_bits(arg2, m_in2); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); \
result = mk_mkbv(m_out); \
}
MK_BIN_REDUCE(reduce_shl, mk_shl);
MK_BIN_REDUCE(reduce_ashr, mk_ashr);
MK_BIN_REDUCE(reduce_lshr, mk_lshr);
MK_BIN_REDUCE(reduce_udiv, mk_udiv);
MK_BIN_REDUCE(reduce_urem, mk_urem);
MK_BIN_REDUCE(reduce_sdiv, mk_sdiv);
MK_BIN_REDUCE(reduce_srem, mk_srem);
MK_BIN_REDUCE(reduce_smod, mk_smod);
MK_BIN_REDUCE(reduce_ext_rotate_left, mk_ext_rotate_left);
MK_BIN_REDUCE(reduce_ext_rotate_right, mk_ext_rotate_right);
#define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
result = args[0]; \
expr_ref new_result(m_manager); \
for (unsigned i = 1; i < num_args; i++) { \
BIN_OP(result.get(), args[i], new_result); \
result = new_result; \
} \
}
MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder);
MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier);
MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or);
MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor);
#define MK_BIN_PRED_REDUCE(OP, BB_OP) \
void OP(expr * arg1, expr * arg2, expr_ref & result) { \
m_in1.reset(); m_in2.reset(); \
get_bits(arg1, m_in1); \
get_bits(arg2, m_in2); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), result); \
}
MK_BIN_PRED_REDUCE(reduce_eq, mk_eq);
MK_BIN_PRED_REDUCE(reduce_sle, mk_sle);
MK_BIN_PRED_REDUCE(reduce_ule, mk_ule);
MK_BIN_PRED_REDUCE(reduce_umul_no_overflow, mk_umul_no_overflow);
MK_BIN_PRED_REDUCE(reduce_smul_no_overflow, mk_smul_no_overflow);
MK_BIN_PRED_REDUCE(reduce_smul_no_underflow, mk_smul_no_underflow);
#define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \
void OP(expr * arg, unsigned n, expr_ref & result) { \
m_in1.reset(); \
get_bits(arg, m_in1); \
m_out.reset(); \
m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), n, m_out); \
result = mk_mkbv(m_out); \
}
MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
void reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
m_in1.reset();
m_in2.reset();
get_bits(arg2, m_in1);
get_bits(arg3, m_in2);
m_out.reset();
m_blaster.mk_multiplexer(arg1, m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out);
result = mk_mkbv(m_out);
}
void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) {
m_out.reset();
unsigned i = num_args;
while (i > 0) {
i--;
m_in1.reset();
get_bits(args[i], m_in1);
m_out.append(m_in1.size(), m_in1.c_ptr());
}
result = mk_mkbv(m_out);
}
void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) {
m_in1.reset();
get_bits(arg, m_in1);
m_out.reset();
for (unsigned i = start; i <= end; ++i)
m_out.push_back(m_in1.get(i));
result = mk_mkbv(m_out);
}
void reduce_num(func_decl * f, expr_ref & result) {
SASSERT(f->get_num_parameters() == 2);
SASSERT(f->get_parameter(0).is_rational());
SASSERT(f->get_parameter(1).is_int());
rational v = f->get_parameter(0).get_rational();
unsigned bv_sz = f->get_parameter(1).get_int();
m_out.reset();
m_blaster.num2bits(v, bv_sz, m_out);
result = mk_mkbv(m_out);
}
void throw_unsupported() {
throw tactic_exception("operator is not supported, you must simplify the goal before applying bit-blasting");
}
void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) {
ptr_buffer<expr> bits;
unsigned bv_size = butil().get_bv_size(t);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t));
}
result = mk_mkbv(bits);
result_pr = 0;
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) {
mk_const(f, result);
return BR_DONE;
}
if (m().is_eq(f)) {
SASSERT(num == 2);
if (butil().is_bv(args[0])) {
reduce_eq(args[0], args[1], result);
return BR_DONE;
}
return BR_FAILED;
}
if (m().is_ite(f)) {
SASSERT(num == 3);
if (butil().is_bv(args[1])) {
reduce_ite(args[0], args[1], args[2], result);
return BR_DONE;
}
return BR_FAILED;
}
if (f->get_family_id() == butil().get_family_id()) {
switch (f->get_decl_kind()) {
case OP_BV_NUM:
SASSERT(num == 0);
reduce_num(f, result);
return BR_DONE;
case OP_BADD:
if (!m_blast_add)
return BR_FAILED;
reduce_add(num, args, result);
return BR_DONE;
case OP_BMUL:
if (!m_blast_mul)
return BR_FAILED;
reduce_mul(num, args, result);
return BR_DONE;
case OP_BSDIV:
case OP_BUDIV:
case OP_BSREM:
case OP_BUREM:
case OP_BSMOD:
if (m_blast_mul)
throw_unsupported(); // must simplify to DIV_I AND DIV0
return BR_FAILED; // keep them
case OP_BSDIV0:
case OP_BUDIV0:
case OP_BSREM0:
case OP_BUREM0:
case OP_BSMOD0:
return BR_FAILED;
case OP_BSDIV_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_sdiv(args[0], args[1], result);
return BR_DONE;
case OP_BUDIV_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_udiv(args[0], args[1], result);
return BR_DONE;
case OP_BSREM_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_srem(args[0], args[1], result);
return BR_DONE;
case OP_BUREM_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_urem(args[0], args[1], result);
return BR_DONE;
case OP_BSMOD_I:
if (!m_blast_mul)
return BR_FAILED;
SASSERT(num == 2);
reduce_smod(args[0], args[1], result);
return BR_DONE;
case OP_ULEQ:
SASSERT(num == 2);
reduce_ule(args[0], args[1], result);
return BR_DONE;
case OP_SLEQ:
SASSERT(num == 2);
reduce_sle(args[0], args[1], result);
return BR_DONE;
case OP_BOR:
reduce_or(num, args, result);
return BR_DONE;
case OP_BNOT:
SASSERT(num == 1);
reduce_not(args[0], result);
return BR_DONE;
case OP_BXOR:
reduce_xor(num, args, result);
return BR_DONE;
case OP_CONCAT:
reduce_concat(num, args, result);
return BR_DONE;
case OP_SIGN_EXT:
SASSERT(num == 1);
reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result);
return BR_DONE;
case OP_EXTRACT:
SASSERT(num == 1);
reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result);
return BR_DONE;
case OP_BREDOR:
SASSERT(num == 1);
reduce_redor(args[0], result);
return BR_DONE;
case OP_BREDAND:
SASSERT(num == 1);
reduce_redand(args[0], result);
return BR_DONE;
case OP_BSHL:
SASSERT(num == 2);
reduce_shl(args[0], args[1], result);
return BR_DONE;
case OP_BLSHR:
SASSERT(num == 2);
reduce_lshr(args[0], args[1], result);
return BR_DONE;
case OP_BASHR:
SASSERT(num == 2);
reduce_ashr(args[0], args[1], result);
return BR_DONE;
case OP_EXT_ROTATE_LEFT:
SASSERT(num == 2);
reduce_ext_rotate_left(args[0], args[1], result);
return BR_DONE;
case OP_EXT_ROTATE_RIGHT:
SASSERT(num == 2);
reduce_ext_rotate_right(args[0], args[1], result);
return BR_DONE;
case OP_BUMUL_NO_OVFL:
SASSERT(num == 2);
reduce_umul_no_overflow(args[0], args[1], result);
return BR_DONE;
case OP_BSMUL_NO_OVFL:
SASSERT(num == 2);
reduce_smul_no_overflow(args[0], args[1], result);
return BR_DONE;
case OP_BSMUL_NO_UDFL:
SASSERT(num == 2);
reduce_smul_no_underflow(args[0], args[1], result);
return BR_DONE;
case OP_BIT2BOOL:
case OP_MKBV:
case OP_INT2BV:
case OP_BV2INT:
return BR_FAILED;
default:
TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;);
throw_unsupported();
}
}
if (m_blast_full && butil().is_bv_sort(f->get_range())) {
blast_bv_term(m().mk_app(f, num, args), result, result_pr);
return BR_DONE;
}
return BR_FAILED;
}
bool pre_visit(expr * t) {
if (m_blast_quant && is_quantifier(t)) {
quantifier * q = to_quantifier(t);
ptr_buffer<expr> new_bindings;
ptr_buffer<expr> new_args;
unsigned i = q->get_num_decls();
unsigned j = 0;
while (i > 0) {
--i;
sort * s = q->get_decl_sort(i);
if (butil().is_bv_sort(s)) {
unsigned bv_size = butil().get_bv_size(s);
new_args.reset();
for (unsigned k = 0; k < bv_size; k++) {
new_args.push_back(m().mk_var(j, m().mk_bool_sort()));
j++;
}
new_bindings.push_back(mk_mkbv(new_args));
}
else {
new_bindings.push_back(m().mk_var(j, s));
j++;
}
}
SASSERT(new_bindings.size() == q->get_num_decls());
i = q->get_num_decls();
while (i > 0) {
i--;
m_bindings.push_back(new_bindings[i]);
}
}
return true;
}
bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) {
if (m_blast_quant) {
if (t->get_idx() >= m_bindings.size())
return false;
result = m_bindings.get(m_bindings.size() - t->get_idx() - 1);
result_pr = 0;
return true;
}
if (m_blast_full && butil().is_bv(t)) {
blast_bv_term(t, result, result_pr);
return true;
}
return false;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
if (!m_blast_quant)
return false;
unsigned curr_sz = m_bindings.size();
SASSERT(old_q->get_num_decls() <= curr_sz);
unsigned num_decls = old_q->get_num_decls();
unsigned old_sz = curr_sz - num_decls;
string_buffer<> name_buffer;
ptr_buffer<sort> new_decl_sorts;
sbuffer<symbol> new_decl_names;
for (unsigned i = 0; i < num_decls; i++) {
symbol const & n = old_q->get_decl_name(i);
sort * s = old_q->get_decl_sort(i);
if (butil().is_bv_sort(s)) {
unsigned bv_size = butil().get_bv_size(s);
for (unsigned j = 0; j < bv_size; j++) {
name_buffer.reset();
name_buffer << n << "." << j;
new_decl_names.push_back(symbol(name_buffer.c_str()));
new_decl_sorts.push_back(m().mk_bool_sort());
}
}
else {
new_decl_sorts.push_back(s);
new_decl_names.push_back(n);
}
}
result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(),
new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(),
old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns);
result_pr = 0;
m_bindings.shrink(old_sz);
return true;
}
};
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
// template class rewriter_tpl<blaster_rewriter_cfg>;
struct bit_blaster_rewriter::imp : public rewriter_tpl<blaster_rewriter_cfg> {
blaster m_blaster;
blaster_rewriter_cfg m_cfg;
imp(ast_manager & m, params_ref const & p):
rewriter_tpl<blaster_rewriter_cfg>(m,
m.proofs_enabled(),
m_cfg),
m_blaster(m),
m_cfg(m, m_blaster, p) {
SASSERT(m_blaster.butil().get_family_id() == m.get_family_id("bv"));
}
};
bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p):
m_imp(alloc(imp, m, p)) {
}
bit_blaster_rewriter::~bit_blaster_rewriter() {
dealloc(m_imp);
}
void bit_blaster_rewriter::updt_params(params_ref const& p) {
m_imp->m_cfg.updt_params(p);
}
void bit_blaster_rewriter::set_cancel(bool f) {
m_imp->set_cancel(f);
m_imp->m_blaster.set_cancel(f);
}
ast_manager & bit_blaster_rewriter::m() const {
return m_imp->m();
}
unsigned bit_blaster_rewriter::get_num_steps() const {
return m_imp->get_num_steps();
}
void bit_blaster_rewriter::cleanup() {
m_imp->cleanup();
}
obj_map<func_decl, expr*> const & bit_blaster_rewriter::const2bits() const {
return m_imp->m_cfg.m_const2bits;
}
void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) {
m_imp->operator()(e, result, result_proof);
}

View file

@ -1,42 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_rewriter.h
Abstract:
Bit-blasting rewriter
Author:
Leonardo (leonardo) 2012-10-04
Notes:
--*/
#ifndef _BIT_BLASTER_REWRITER_H_
#define _BIT_BLASTER_REWRITER_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"params.h"
class bit_blaster_rewriter {
struct imp;
imp * m_imp;
public:
bit_blaster_rewriter(ast_manager & m, params_ref const & p);
~bit_blaster_rewriter();
void updt_params(params_ref const & p);
void set_cancel(bool f);
ast_manager & m() const;
unsigned get_num_steps() const;
void cleanup();
obj_map<func_decl, expr*> const& const2bits() const;
void operator()(expr * e, expr_ref & result, proof_ref & result_proof);
};
#endif

View file

@ -1,165 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_tactic.cpp
Abstract:
Apply bit-blasting to a given goal
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"tactical.h"
#include"bit_blaster_model_converter.h"
#include"bit_blaster_rewriter.h"
#include"ast_smt2_pp.h"
#include"model_pp.h"
class bit_blaster_tactic : public tactic {
struct imp {
bit_blaster_rewriter m_rewriter;
unsigned m_num_steps;
bool m_blast_quant;
imp(ast_manager & m, params_ref const & p):
m_rewriter(m, p) {
updt_params(p);
}
void updt_params_core(params_ref const & p) {
m_blast_quant = p.get_bool(":blast-quant", false);
}
void updt_params(params_ref const & p) {
m_rewriter.updt_params(p);
updt_params_core(p);
}
ast_manager & m() const { return m_rewriter.m(); }
void set_cancel(bool f) {
m_rewriter.set_cancel(f);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
mc = 0; pc = 0; core = 0;
bool proofs_enabled = g->proofs_enabled();
if (proofs_enabled && m_blast_quant)
throw tactic_exception("quantified variable blasting does not support proof generation");
tactic_report report("bit-blaster", *g);
TRACE("before_bit_blaster", g->display(tout););
m_num_steps = 0;
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
m_rewriter(curr, new_curr, new_pr);
m_num_steps += m_rewriter.get_num_steps();
if (proofs_enabled) {
proof * pr = g->pr(idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
}
g->update(idx, new_curr, new_pr, g->dep(idx));
}
if (g->models_enabled())
mc = mk_bit_blaster_model_converter(m(), m_rewriter.const2bits());
else
mc = 0;
g->inc_depth();
result.push_back(g.get());
TRACE("after_bit_blaster", g->display(tout); if (mc) mc->display(tout); tout << "\n";);
m_rewriter.cleanup();
}
unsigned get_num_steps() const { return m_num_steps; }
};
imp * m_imp;
params_ref m_params;
public:
bit_blaster_tactic(ast_manager & m, params_ref const & p):
m_params(p){
m_imp = alloc(imp, m, p);
}
virtual tactic * translate(ast_manager & m) {
return alloc(bit_blaster_tactic, m, m_params);
}
virtual ~bit_blaster_tactic() {
dealloc(m_imp);
}
virtual void updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
virtual void collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
insert_max_steps(r);
r.insert(":blast-mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders).");
r.insert(":blast-add", CPK_BOOL, "(default: true) bit-blast adders.");
r.insert(":blast-quant", CPK_BOOL, "(default: false) bit-blast quantified variables.");
r.insert(":blast-full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.");
}
virtual void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
(*m_imp)(g, result, mc, pc, core);
}
virtual void cleanup() {
ast_manager & m = m_imp->m();
imp * d = m_imp;
#pragma omp critical (tactic_cancel)
{
m_imp = 0;
}
dealloc(d);
d = alloc(imp, m, m_params);
#pragma omp critical (tactic_cancel)
{
m_imp = d;
}
}
unsigned get_num_steps() const {
return m_imp->get_num_steps();
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(bit_blaster_tactic, m, p));
}

View file

@ -1,28 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bit_blaster_tactic.h
Abstract:
Apply bit-blasting to a given goal.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _BIT_BLASTER_TACTIC_H_
#define _BIT_BLASTER_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif

View file

@ -1,135 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_blaster_tpl.h
Abstract:
Template for bit-blaster operations
Author:
Leonardo de Moura (leonardo) 2011-05-02.
Revision History:
--*/
#ifndef _BIT_BLASTER_TPL_H_
#define _BIT_BLASTER_TPL_H_
#include"rational.h"
#include"strategy_exception.h"
template<typename Cfg>
class bit_blaster_tpl : public Cfg {
public:
typedef rational numeral;
protected:
template<bool Signed>
void mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
template<unsigned k>
void mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
template<bool Left>
void mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
unsigned long long m_max_memory;
volatile bool m_cancel;
bool m_use_wtm; /* Wallace Tree Multiplier */
bool m_use_bcm; /* Booth Multiplier for constants */
void checkpoint();
public:
bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX, bool use_wtm = false, bool use_bcm=false):
Cfg(cfg),
m_max_memory(max_memory),
m_cancel(false),
m_use_wtm(use_wtm),
m_use_bcm(use_bcm) {
}
void set_max_memory(unsigned long long max_memory) {
m_max_memory = max_memory;
}
void set_cancel(bool f) { m_cancel = f; }
void cancel() { set_cancel(true); }
void reset_cancel() { set_cancel(false); }
// Cfg required API
ast_manager & m() const { return Cfg::m(); }
numeral power(unsigned n) const { return Cfg::power(n); }
void mk_xor(expr * a, expr * b, expr_ref & r) { Cfg::mk_xor(a, b, r); }
void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_xor3(a, b, c, r); }
void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_carry(a, b, c, r); }
void mk_iff(expr * a, expr * b, expr_ref & r) { Cfg::mk_iff(a, b, r); }
void mk_and(expr * a, expr * b, expr_ref & r) { Cfg::mk_and(a, b, r); }
void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_and(a, b, c, r); }
void mk_and(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_and(sz, args, r); }
void mk_or(expr * a, expr * b, expr_ref & r) { Cfg::mk_or(a, b, r); }
void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_or(a, b, c, r); }
void mk_or(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_or(sz, args, r); }
void mk_not(expr * a, expr_ref & r) { Cfg::mk_not(a, r); }
void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { Cfg::mk_ite(c, t, e, r); }
void mk_nand(expr * a, expr * b, expr_ref & r) { Cfg::mk_nand(a, b, r); }
void mk_nor(expr * a, expr * b, expr_ref & r) { Cfg::mk_nor(a, b, r); }
//
bool is_numeral(unsigned sz, expr * const * bits) const;
bool is_numeral(unsigned sz, expr * const * bits, numeral & r) const;
bool is_minus_one(unsigned sz, expr * const * bits) const;
void num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const;
void mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout);
void mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout);
void mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout);
void mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits);
void mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits);
void mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits);
void mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits);
void mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits);
void mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out);
void mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs);
void mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_and(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_or(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_xor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_xnor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_nand(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_nor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
void mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result);
void mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out);
void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits);
void mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,508 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bv1_blaster_tactic.cpp
Abstract:
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
This rewriter only supports concat and extract operators.
This transformation is useful for handling benchmarks that contain
many BV equalities.
Remark: other operators can be mapped into concat/extract by using
the simplifiers.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"tactical.h"
#include"bit_blaster_model_converter.h"
#include"bv_decl_plugin.h"
#include"rewriter_def.h"
#include"for_each_expr.h"
#include"cooperate.h"
class bv1_blaster_tactic : public tactic {
struct rw_cfg : public default_rewriter_cfg {
ast_manager & m_manager;
bv_util m_util;
obj_map<func_decl, expr*> m_const2bits;
expr_ref_vector m_saved;
expr_ref m_bit1;
expr_ref m_bit0;
unsigned long long m_max_memory; // in bytes
unsigned m_max_steps;
bool m_produce_models;
ast_manager & m() const { return m_manager; }
bv_util & butil() { return m_util; }
bv_util const & butil() const { return m_util; }
void cleanup_buffers() {
m_saved.finalize();
}
rw_cfg(ast_manager & m, params_ref const & p):
m_manager(m),
m_util(m),
m_saved(m),
m_bit1(m),
m_bit0(m) {
m_bit1 = butil().mk_numeral(rational(1), 1);
m_bit0 = butil().mk_numeral(rational(0), 1);
updt_params(p);
}
void updt_params(params_ref const & p) {
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
m_max_steps = p.get_uint(":max-steps", UINT_MAX);
m_produce_models = p.get_bool(":produce-models", false);
}
bool rewrite_patterns() const { UNREACHABLE(); return false; }
bool max_steps_exceeded(unsigned num_steps) const {
cooperate("bv1 blaster");
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
return num_steps > m_max_steps;
}
typedef ptr_buffer<expr, 128> bit_buffer;
void get_bits(expr * arg, bit_buffer & bits) {
SASSERT(butil().is_concat(arg) || butil().get_bv_size(arg) == 1);
if (butil().is_concat(arg))
bits.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
else
bits.push_back(arg);
}
void mk_const(func_decl * f, expr_ref & result) {
SASSERT(f->get_family_id() == null_family_id);
SASSERT(f->get_arity() == 0);
expr * r;
if (m_const2bits.find(f, r)) {
result = r;
return;
}
sort * s = f->get_range();
SASSERT(butil().is_bv_sort(s));
unsigned bv_size = butil().get_bv_size(s);
if (bv_size == 1) {
result = m().mk_const(f);
return;
}
sort * b = butil().mk_sort(1);
ptr_buffer<expr> bits;
for (unsigned i = 0; i < bv_size; i++) {
bits.push_back(m().mk_fresh_const(0, b));
}
r = butil().mk_concat(bits.size(), bits.c_ptr());
m_saved.push_back(r);
m_const2bits.insert(f, r);
result = r;
}
void blast_bv_term(expr * t, expr_ref & result) {
bit_buffer bits;
unsigned bv_size = butil().get_bv_size(t);
if (bv_size == 1) {
result = t;
return;
}
unsigned i = bv_size;
while (i > 0) {
--i;
bits.push_back(butil().mk_extract(i, i, t));
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_eq(expr * arg1, expr * arg2, expr_ref & result) {
bit_buffer bits1;
bit_buffer bits2;
get_bits(arg1, bits1);
get_bits(arg2, bits2);
SASSERT(bits1.size() == bits2.size());
bit_buffer new_eqs;
unsigned i = bits1.size();
while (i > 0) {
--i;
new_eqs.push_back(m().mk_eq(bits1[i], bits2[i]));
}
result = m().mk_and(new_eqs.size(), new_eqs.c_ptr());
}
void reduce_ite(expr * c, expr * t, expr * e, expr_ref & result) {
bit_buffer t_bits;
bit_buffer e_bits;
get_bits(t, t_bits);
get_bits(e, e_bits);
SASSERT(t_bits.size() == e_bits.size());
bit_buffer new_ites;
unsigned num = t_bits.size();
for (unsigned i = 0; i < num; i++)
new_ites.push_back(m().mk_ite(c, t_bits[i], e_bits[i]));
result = butil().mk_concat(new_ites.size(), new_ites.c_ptr());
}
void reduce_num(func_decl * f, expr_ref & result) {
SASSERT(f->get_num_parameters() == 2);
SASSERT(f->get_parameter(0).is_rational());
SASSERT(f->get_parameter(1).is_int());
bit_buffer bits;
rational v = f->get_parameter(0).get_rational();
rational two(2);
unsigned sz = f->get_parameter(1).get_int();
for (unsigned i = 0; i < sz; i++) {
if ((v % two).is_zero())
bits.push_back(m_bit0);
else
bits.push_back(m_bit1);
v = div(v, two);
}
std::reverse(bits.begin(), bits.end());
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_extract(func_decl * f, expr * arg, expr_ref & result) {
bit_buffer arg_bits;
get_bits(arg, arg_bits);
SASSERT(arg_bits.size() == butil().get_bv_size(arg));
unsigned high = butil().get_extract_high(f);
unsigned low = butil().get_extract_low(f);
unsigned sz = arg_bits.size();
unsigned start = sz - 1 - high;
unsigned end = sz - 1 - low;
bit_buffer bits;
for (unsigned i = start; i <= end; i++) {
bits.push_back(arg_bits[i]);
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_concat(unsigned num, expr * const * args, expr_ref & result) {
bit_buffer bits;
bit_buffer arg_bits;
for (unsigned i = 0; i < num; i++) {
expr * arg = args[i];
arg_bits.reset();
get_bits(arg, arg_bits);
bits.append(arg_bits.size(), arg_bits.c_ptr());
}
result = butil().mk_concat(bits.size(), bits.c_ptr());
}
void reduce_bin_xor(expr * arg1, expr * arg2, expr_ref & result) {
bit_buffer bits1;
bit_buffer bits2;
get_bits(arg1, bits1);
get_bits(arg2, bits2);
SASSERT(bits1.size() == bits2.size());
bit_buffer new_bits;
unsigned num = bits1.size();
for (unsigned i = 0; i < num; i++) {
new_bits.push_back(m().mk_ite(m().mk_eq(bits1[i], bits2[i]), m_bit0, m_bit1));
}
result = butil().mk_concat(new_bits.size(), new_bits.c_ptr());
}
void reduce_xor(unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(num_args > 0);
#if 1
if (num_args == 1) {
result = args[0];
return;
}
reduce_bin_xor(args[0], args[1], result);
for (unsigned i = 2; i < num_args; i++) {
reduce_bin_xor(result, args[i], result);
}
#else
ptr_buffer<bit_buffer> args_bits;
for (unsigned i = 0; i < num_args; i++) {
bit_buffer * buff_i = alloc(bit_buffer);
get_bits(args[i], *buff_i);
args_bits.push_back(buff_i);
}
bit_buffer new_bits;
unsigned sz = butil().get_bv_size(args[0]);
for (unsigned i = 0; i < sz; i++) {
ptr_buffer<expr> eqs;
for (unsigned j = 0; j < num_args; j++) {
bit_buffer * buff_j = args_bits[j];
eqs.push_back(m().mk_eq(buff_j->get(i), m_bit1));
}
expr * cond = m().mk_xor(eqs.size(), eqs.c_ptr());
new_bits.push_back(m().mk_ite(cond, m_bit1, m_bit0));
}
result = butil().mk_concat(new_bits.size(), new_bits.c_ptr());
std::for_each(args_bits.begin(), args_bits.end(), delete_proc<bit_buffer>());
#endif
}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = 0;
if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) {
mk_const(f, result);
return BR_DONE;
}
if (m().is_eq(f)) {
SASSERT(num == 2);
if (butil().is_bv(args[0])) {
reduce_eq(args[0], args[1], result);
return BR_DONE;
}
return BR_FAILED;
}
if (m().is_ite(f)) {
SASSERT(num == 3);
if (butil().is_bv(args[1])) {
reduce_ite(args[0], args[1], args[2], result);
return BR_DONE;
}
return BR_FAILED;
}
if (f->get_family_id() == butil().get_family_id()) {
switch (f->get_decl_kind()) {
case OP_BV_NUM:
reduce_num(f, result);
return BR_DONE;
case OP_EXTRACT:
SASSERT(num == 1);
reduce_extract(f, args[0], result);
return BR_DONE;
case OP_CONCAT:
reduce_concat(num, args, result);
return BR_DONE;
case OP_BXOR:
reduce_xor(num, args, result);
return BR_DONE;
default:
UNREACHABLE();
return BR_FAILED;
}
}
if (butil().is_bv_sort(f->get_range())) {
blast_bv_term(m().mk_app(f, num, args), result);
return BR_DONE;
}
return BR_FAILED;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
expr * const * new_no_patterns,
expr_ref & result,
proof_ref & result_pr) {
UNREACHABLE();
return false;
}
};
struct rw : public rewriter_tpl<rw_cfg> {
rw_cfg m_cfg;
rw(ast_manager & m, params_ref const & p):
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m, p) {
}
};
struct imp {
rw m_rw;
unsigned m_num_steps;
imp(ast_manager & m, params_ref const & p):
m_rw(m, p) {
}
struct not_target {};
struct visitor {
family_id m_bv_fid;
visitor(family_id bv_fid):m_bv_fid(bv_fid) {}
void operator()(var const * n) { throw not_target(); }
void operator()(app const * n) {
if (n->get_family_id() == m_bv_fid) {
switch (n->get_decl_kind()) {
case OP_BV_NUM:
case OP_EXTRACT:
case OP_CONCAT:
return;
case OP_BXOR:
// it doesn't payoff to do the reduction in this case.
throw not_target();
default:
throw not_target();
}
}
}
void operator()(quantifier const * n) { throw not_target(); }
};
bool is_target(goal const & g) const {
expr_fast_mark1 visited;
unsigned sz = g.size();
visitor proc(m_rw.cfg().butil().get_family_id());
try {
for (unsigned i = 0; i < sz; i++) {
expr * f = g.form(i);
for_each_expr_core<visitor, expr_fast_mark1, false, true>(proc, visited, f);
}
}
catch (not_target) {
return false;
}
return true;
}
ast_manager & m() const { return m_rw.m(); }
void set_cancel(bool f) {
m_rw.set_cancel(f);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
mc = 0; pc = 0; core = 0;
if (!is_target(*g))
throw tactic_exception("bv1 blaster cannot be applied to goal");
tactic_report report("bv1-blaster", *g);
m_num_steps = 0;
bool proofs_enabled = g->proofs_enabled();
expr_ref new_curr(m());
proof_ref new_pr(m());
unsigned size = g->size();
for (unsigned idx = 0; idx < size; idx++) {
if (g->inconsistent())
break;
expr * curr = g->form(idx);
m_rw(curr, new_curr, new_pr);
m_num_steps += m_rw.get_num_steps();
if (proofs_enabled) {
proof * pr = g->pr(idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
}
g->update(idx, new_curr, new_pr, g->dep(idx));
}
if (g->models_enabled())
mc = mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits);
g->inc_depth();
result.push_back(g.get());
m_rw.cfg().cleanup();
}
unsigned get_num_steps() const { return m_num_steps; }
};
imp * m_imp;
params_ref m_params;
public:
bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()):
m_params(p) {
m_imp = alloc(imp, m, p);
}
virtual tactic * translate(ast_manager & m) {
return alloc(bv1_blaster_tactic, m, m_params);
}
virtual ~bv1_blaster_tactic() {
dealloc(m_imp);
}
virtual void updt_params(params_ref const & p) {
m_params = p;
m_imp->m_rw.cfg().updt_params(p);
}
virtual void collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
insert_max_steps(r);
}
bool is_target(goal const & g) const {
return m_imp->is_target(g);
}
/**
\brief "Blast" bit-vectors of size n in s into bit-vectors of size 1.
If s contains other bit-vectors operators different from concat/extract, then this is method is a NO-OP.
It also does not support quantifiers.
Return a model_converter that converts any model for the updated set into a model for the old set.
*/
virtual void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
(*m_imp)(g, result, mc, pc, core);
}
virtual void cleanup() {
ast_manager & m = m_imp->m();
imp * d = m_imp;
#pragma omp critical (tactic_cancel)
{
d = m_imp;
}
dealloc(d);
d = alloc(imp, m, m_params);
#pragma omp critical (tactic_cancel)
{
m_imp = d;
}
}
unsigned get_num_steps() const {
return m_imp->get_num_steps();
}
protected:
virtual void set_cancel(bool f) {
if (m_imp)
m_imp->set_cancel(f);
}
};
tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(bv1_blaster_tactic, m, p));
}
class is_qfbv_eq_probe : public probe {
public:
virtual result operator()(goal const & g) {
bv1_blaster_tactic t(g.m());
return t.is_target(g);
}
};
probe * mk_is_qfbv_eq_probe() {
return alloc(is_qfbv_eq_probe);
}

View file

@ -1,35 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bv1_blaster_tactic.h
Abstract:
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
This rewriter only supports concat and extract operators.
This transformation is useful for handling benchmarks that contain
many BV equalities.
Remark: other operators can be mapped into concat/extract by using
the simplifiers.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _BV1_BLASTER_TACTIC_H_
#define _BV1_BLASTER_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref());
probe * mk_is_qfbv_eq_probe();
#endif

View file

@ -1,113 +0,0 @@
#include "bv_elim.h"
#include "bv_decl_plugin.h"
#include "var_subst.h"
#include <sstream>
void bv_elim::elim(quantifier* q, quantifier_ref& r) {
svector<symbol> names, _names;
sort_ref_buffer sorts(m_manager), _sorts(m_manager);
expr_ref_buffer pats(m_manager);
expr_ref_buffer no_pats(m_manager);
expr_ref_buffer subst_map(m_manager), _subst_map(m_manager);
var_subst subst(m_manager);
bv_util bv(m_manager);
expr_ref new_body(m_manager);
expr* old_body = q->get_expr();
unsigned num_decls = q->get_num_decls();
family_id bfid = m_manager.get_family_id("bv");
//
// Traverse sequence of bound variables to eliminate
// bit-vecctor variables and replace them by
// Booleans.
//
unsigned var_idx = 0;
for (unsigned i = num_decls; i > 0; ) {
--i;
sort* s = q->get_decl_sort(i);
symbol nm = q->get_decl_name(i);
if (bv.is_bv_sort(s)) {
// convert n-bit bit-vector variable into sequence of n-Booleans.
unsigned num_bits = bv.get_bv_size(s);
expr_ref_buffer args(m_manager);
expr_ref bv(m_manager);
for (unsigned j = 0; j < num_bits; ++j) {
std::ostringstream new_name;
new_name << nm.str();
new_name << "_";
new_name << j;
var* v = m_manager.mk_var(var_idx++, m_manager.mk_bool_sort());
args.push_back(v);
_sorts.push_back(m_manager.mk_bool_sort());
_names.push_back(symbol(new_name.str().c_str()));
}
bv = m_manager.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
_subst_map.push_back(bv.get());
}
else {
_subst_map.push_back(m_manager.mk_var(var_idx++, s));
_sorts.push_back(s);
_names.push_back(nm);
}
}
//
// reverse the vectors.
//
SASSERT(_names.size() == _sorts.size());
for (unsigned i = _names.size(); i > 0; ) {
--i;
names.push_back(_names[i]);
sorts.push_back(_sorts[i]);
}
for (unsigned i = _subst_map.size(); i > 0; ) {
--i;
subst_map.push_back(_subst_map[i]);
}
expr* const* sub = subst_map.c_ptr();
unsigned sub_size = subst_map.size();
subst(old_body, sub_size, sub, new_body);
for (unsigned j = 0; j < q->get_num_patterns(); j++) {
expr_ref pat(m_manager);
subst(q->get_pattern(j), sub_size, sub, pat);
pats.push_back(pat);
}
for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
expr_ref nopat(m_manager);
subst(q->get_no_pattern(j), sub_size, sub, nopat);
no_pats.push_back(nopat);
}
r = m_manager.mk_quantifier(true,
names.size(),
sorts.c_ptr(),
names.c_ptr(),
new_body.get(),
q->get_weight(),
q->get_qid(),
q->get_skid(),
pats.size(), pats.c_ptr(),
no_pats.size(), no_pats.c_ptr());
}
bool bv_elim_star::visit_quantifier(quantifier* q) {
// behave like destructive resolution, do not recurse.
return true;
}
void bv_elim_star::reduce1_quantifier(quantifier* q) {
quantifier_ref r(m_manager);
proof_ref pr(m_manager);
m_bv_elim.elim(q, r);
if (m_manager.fine_grain_proofs()) {
pr = m_manager.mk_rewrite(q, r.get());
}
else {
pr = 0;
}
cache_result(q, r, pr);
}

View file

@ -1,45 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bv_elim.h
Abstract:
Eliminate bit-vectors variables from clauses, by
replacing them by bound Boolean variables.
Author:
Nikolaj Bjorner (nbjorner) 2008-12-16.
Revision History:
--*/
#ifndef _BV_ELIM_H_
#define _BV_ELIM_H_
#include "ast.h"
#include "simplifier.h"
class bv_elim {
ast_manager& m_manager;
public:
bv_elim(ast_manager& m) : m_manager(m) {};
void elim(quantifier* q, quantifier_ref& r);
};
class bv_elim_star : public simplifier {
protected:
bv_elim m_bv_elim;
virtual bool visit_quantifier(quantifier* q);
virtual void reduce1_quantifier(quantifier* q);
public:
bv_elim_star(ast_manager& m) : simplifier(m), m_bv_elim(m) { enable_ac_support(false); }
virtual ~bv_elim_star() {}
};
#endif /* _BV_ELIM_H_ */

View file

@ -1,79 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cached_var_subst.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-23.
Revision History:
--*/
#include"cached_var_subst.h"
bool cached_var_subst::key_eq_proc::operator()(cached_var_subst::key * k1, cached_var_subst::key * k2) const {
if (k1->m_qa != k2->m_qa)
return false;
if (k1->m_num_bindings != k2->m_num_bindings)
return false;
for (unsigned i = 0; i < k1->m_num_bindings; i++)
if (k1->m_bindings[i] != k2->m_bindings[i])
return false;
return true;
}
cached_var_subst::cached_var_subst(ast_manager & m):
m_proc(m),
m_refs(m) {
}
void cached_var_subst::reset() {
m_refs.reset();
m_instances.reset();
m_region.reset();
m_new_keys.reset();
}
void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result) {
m_new_keys.reserve(num_bindings+1, 0);
key * new_key = m_new_keys[num_bindings];
if (new_key == 0)
new_key = static_cast<key*>(m_region.allocate(sizeof(key) + sizeof(expr*)*num_bindings));
new_key->m_qa = qa;
new_key->m_num_bindings = num_bindings;
for (unsigned i = 0; i < num_bindings; i++)
new_key->m_bindings[i] = bindings[i]->get_owner();
instances::entry * entry = m_instances.insert_if_not_there2(new_key, 0);
if (entry->get_data().m_key != new_key) {
SASSERT(entry->get_data().m_value != 0);
// entry was already there
m_new_keys[num_bindings] = new_key; // recycle key
result = entry->get_data().m_value;
return;
}
m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings, result);
// cache result
entry->get_data().m_value = result;
// remove key from cache
m_new_keys[num_bindings] = 0;
// increment reference counters
m_refs.push_back(qa);
for (unsigned i = 0; i < new_key->m_num_bindings; i++)
m_refs.push_back(new_key->m_bindings[i]);
m_refs.push_back(result);
}

View file

@ -1,53 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cached_var_subst.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2009-01-23.
Revision History:
--*/
#ifndef _CACHED_VAR_SUBST_H_
#define _CACHED_VAR_SUBST_H_
#include"var_subst.h"
#include"map.h"
#include"smt_enode.h"
class cached_var_subst {
struct key {
quantifier * m_qa;
unsigned m_num_bindings;
expr * m_bindings[0];
};
struct key_hash_proc {
unsigned operator()(key * k) const {
return string_hash(reinterpret_cast<char const *>(k->m_bindings), sizeof(expr *) * k->m_num_bindings, k->m_qa->get_id());
}
};
struct key_eq_proc {
bool operator()(key * k1, key * k2) const;
};
typedef map<key *, expr *, key_hash_proc, key_eq_proc> instances;
var_subst m_proc;
expr_ref_vector m_refs;
instances m_instances;
region m_region;
ptr_vector<key> m_new_keys; // mapping from num_bindings -> next key
public:
cached_var_subst(ast_manager & m);
void operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result);
void reset();
};
#endif /* _CACHED_VAR_SUBST_H_ */

View file

@ -1,104 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cost_evaluator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-14.
Revision History:
--*/
#include"cost_evaluator.h"
#include"warning.h"
cost_evaluator::cost_evaluator(ast_manager & m):
m_manager(m),
m_util(m) {
}
float cost_evaluator::eval(expr * f) const {
#define E(IDX) eval(to_app(f)->get_arg(IDX))
if (is_app(f)) {
unsigned num_args;
family_id fid = to_app(f)->get_family_id();
if (fid == m_manager.get_basic_family_id()) {
switch (to_app(f)->get_decl_kind()) {
case OP_TRUE: return 1.0f;
case OP_FALSE: return 0.0f;
case OP_NOT: return E(0) == 0.0f ? 1.0f : 0.0f;
case OP_AND:
num_args = to_app(f)->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (E(i) == 0.0f)
return 0.0f;
return 1.0f;
case OP_OR:
num_args = to_app(f)->get_num_args();
for (unsigned i = 0; i < num_args; i++)
if (E(i) != 0.0f)
return 1.0f;
return 0.0f;
case OP_ITE: return E(0) != 0.0f ? E(1) : E(2);
case OP_EQ:
case OP_IFF: return E(0) == E(1) ? 1.0f : 0.0f;
case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f;
case OP_IMPLIES:
if (E(0) == 0.0f)
return 1.0f;
return E(1) != 0.0f ? 1.0f : 0.0f;
default:
;
}
}
else if (fid == m_util.get_family_id()) {
switch (to_app(f)->get_decl_kind()) {
case OP_NUM: {
rational r = to_app(f)->get_decl()->get_parameter(0).get_rational();
return static_cast<float>(numerator(r).get_int64())/static_cast<float>(denominator(r).get_int64());
}
case OP_LE: return E(0) <= E(1) ? 1.0f : 0.0f;
case OP_GE: return E(0) >= E(1) ? 1.0f : 0.0f;
case OP_LT: return E(0) < E(1) ? 1.0f : 0.0f;
case OP_GT: return E(0) > E(1) ? 1.0f : 0.0f;
case OP_ADD: return E(0) + E(1);
case OP_SUB: return E(0) - E(1);
case OP_UMINUS: return - E(0);
case OP_MUL: return E(0) * E(1);
case OP_DIV: {
float q = E(1);
if (q == 0.0f) {
warning_msg("cost function division by zero");
return 1.0f;
}
return E(0) / q;
}
default:
;
}
}
}
else if (is_var(f)) {
unsigned idx = to_var(f)->get_idx();
if (idx < m_num_args)
return m_args[m_num_args - idx - 1];
}
warning_msg("cost function evaluation error");
return 1.0f;
}
float cost_evaluator::operator()(expr * f, unsigned num_args, float const * args) {
m_num_args = num_args;
m_args = args;
return eval(f);
}

View file

@ -1,43 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
cost_evaluator.h
Abstract:
Simple evaluator for cost function
Author:
Leonardo de Moura (leonardo) 2008-06-14.
Revision History:
--*/
#ifndef _COST_EVALUATOR_H_
#define _COST_EVALUATOR_H_
#include"ast.h"
#include"arith_decl_plugin.h"
class cost_evaluator {
ast_manager & m_manager;
arith_util m_util;
unsigned m_num_args;
float const * m_args;
float eval(expr * f) const;
public:
cost_evaluator(ast_manager & m);
/**
I'm using the same standard used in quantifier instantiation.
(VAR 0) is stored in the last position of the array.
...
(VAR (num_args - 1)) is stored in the first position of the array.
*/
float operator()(expr * f, unsigned num_args, float const * args);
};
#endif /* _COST_EVALUATOR_H_ */

View file

@ -1,906 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
demodulator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-12.
Revision History:
Christoph Wintersteiger 2010-04-21: Implementation
--*/
#include"ast_pp.h"
#include"demodulator.h"
#include"for_each_expr.h"
#include"var_subst.h"
#include"uint_set.h"
demodulator::demodulator(ast_manager & m, basic_simplifier_plugin & p):
m_manager(m),
m_match_subst(m),
m_bsimp(p),
m_todo(m),
m_rewrite_todo(m),
m_rewrite_cache(m),
m_new_exprs(m) {
}
demodulator::~demodulator() {
reset_dealloc_values(m_fwd_idx);
reset_dealloc_values(m_back_idx);
for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end(); it++) {
m_manager.dec_ref(it->m_key);
m_manager.dec_ref(it->m_value.first);
m_manager.dec_ref(it->m_value.second);
}
}
bool demodulator::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const {
if (e->get_kind() == AST_QUANTIFIER) {
quantifier * q = to_quantifier(e);
if (q->is_forall()) {
expr * qe = q->get_expr();
if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) {
app * eq = to_app(q->get_expr());
expr * lhs = eq->get_arg(0);
expr * rhs = eq->get_arg(1);
int subset = is_subset(lhs, rhs);
int smaller = is_smaller(lhs, rhs);
TRACE("demodulator", tout << "testing is_demodulator:\n"
<< mk_pp(lhs, m_manager) << "\n"
<< mk_pp(rhs, m_manager) << "\n"
<< "subset: " << subset << ", smaller: " << smaller << "\n";);
// We only track uninterpreted functions, everything else is likely too expensive.
if ((subset == +1 || subset == +2) && smaller == +1) {
if (is_uninterp(rhs)) {
large = rhs;
small = lhs;
return true;
}
#if 1
// lhs = (not rhs) --> (not lhs) = rhs
expr * not_rhs;
if (m_manager.is_not(rhs, not_rhs) && is_uninterp(not_rhs)) {
large = not_rhs;
small = m_manager.mk_not(lhs);
return true;
}
#endif
}
if ((subset == -1 || subset == +2) && smaller == -1) {
if (is_uninterp(lhs)) {
large = lhs;
small = rhs;
return true;
}
#if 1
// (not lhs) = rhs --> lhs = (not rhs)
expr * not_lhs;
if (m_manager.is_not(lhs, not_lhs) && is_uninterp(not_lhs)) {
large = not_lhs;
small = m_manager.mk_not(rhs);
return true;
}
#endif
}
} else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0))) {
// this is like (not (f ... )) --> (= (f ...) false)
large = to_app(qe)->get_arg(0);
small = m_manager.mk_false();
return true;
} else if (is_uninterp(qe)) {
// this is like (f ... ) --> (= (f ...) true)
large = to_app(qe);
small = m_manager.mk_true();
return true;
}
}
}
return false;
}
class var_set_proc {
uint_set & m_set;
public:
var_set_proc(uint_set &s):m_set(s) {}
void operator()(var * n) { m_set.insert(n->get_idx()); }
void operator()(quantifier * n) {}
void operator()(app * n) {}
};
int demodulator::is_subset(expr * e1, expr * e2) const {
uint_set ev1, ev2;
if (m_manager.is_value(e1))
return 1; // values are always a subset!
var_set_proc proc1(ev1);
for_each_expr(proc1, e1);
var_set_proc proc2(ev2);
for_each_expr(proc2, e2);
return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal.
(ev1.subset_of(ev2)) ? +1 :
(ev2.subset_of(ev1)) ? -1 :
0 ;
}
int demodulator::is_smaller(expr * e1, expr * e2) const {
unsigned sz1 = 0, sz2 = 0;
// values are always smaller!
if (m_manager.is_value(e1))
return +1;
else if (m_manager.is_value(e2))
return -1;
// interpreted stuff is always better than uninterpreted.
if (!is_uninterp(e1) && is_uninterp(e2))
return +1;
else if (is_uninterp(e1) && !is_uninterp(e2))
return -1;
// two uninterpreted functions are ordered first by the number of
// arguments, then by their id.
if (is_uninterp(e1) && is_uninterp(e2)) {
if (to_app(e1)->get_num_args() < to_app(e2)->get_num_args())
return +1;
else if (to_app(e1)->get_num_args() > to_app(e2)->get_num_args())
return -1;
else {
unsigned a = to_app(e1)->get_decl()->get_id();
unsigned b = to_app(e2)->get_decl()->get_id();
if (a < b)
return +1;
else if (a > b)
return -1;
}
}
switch (e1->get_kind()) {
case AST_VAR: sz1 = 1; break;
case AST_QUANTIFIER: sz1 = to_quantifier(e1)->get_depth(); break;
case AST_APP: sz1 = to_app(e1)->get_depth(); break;
default: UNREACHABLE();
}
switch (e2->get_kind()) {
case AST_VAR: sz2 = 1; break;
case AST_QUANTIFIER: sz2 = to_quantifier(e2)->get_depth(); break;
case AST_APP: sz2 = to_app(e2)->get_depth(); break;
default: UNREACHABLE();
}
return (sz1 == sz2) ? 0 :
(sz1 < sz2) ? +1 :
-1 ;
}
class max_var_id_proc {
unsigned m_max_var_id;
public:
max_var_id_proc(void):m_max_var_id(0) {}
void operator()(var * n) {
if(n->get_idx() > m_max_var_id)
m_max_var_id = n->get_idx();
}
void operator()(quantifier * n) {}
void operator()(app * n) {}
unsigned get_max(void) { return m_max_var_id; }
};
unsigned demodulator::max_var_id(expr * e)
{
max_var_id_proc proc;
for_each_expr(proc, e);
return proc.get_max();
}
void demodulator::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) {
SASSERT(large->get_kind() == AST_APP);
SASSERT(demodulator);
SASSERT(large && small);
TRACE("demodulator_fwd", tout << "INSERT: " << mk_pp(demodulator, m_manager) << std::endl; );
func_decl * fd = to_app(large)->get_decl();
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd);
if (it == m_fwd_idx.end()) {
quantifier_set * qs = alloc(quantifier_set, 1);
m_fwd_idx.insert(fd, qs);
it = m_fwd_idx.find_iterator(fd);
}
SASSERT(it->m_value);
it->m_value->insert(demodulator);
m_manager.inc_ref(demodulator);
m_manager.inc_ref(large);
m_manager.inc_ref(small);
m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small));
}
void demodulator::remove_fwd_idx(func_decl * f, quantifier * demodulator) {
TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; );
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f);
if (it != m_fwd_idx.end()) {
demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator);
m_manager.dec_ref(fit->m_value.first);
m_manager.dec_ref(fit->m_value.second);
m_manager.dec_ref(demodulator);
m_demodulator2lhs_rhs.erase(demodulator);
it->m_value->erase(demodulator);
} else {
SASSERT(m_demodulator2lhs_rhs.contains(demodulator));
}
}
bool demodulator::check_fwd_idx_consistency(void) {
for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) {
quantifier_set * set = it->m_value;
SASSERT(set);
for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) {
if (!m_demodulator2lhs_rhs.contains(*sit)) return false;
}
}
return true;
}
void demodulator::show_fwd_idx(std::ostream & out) {
for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) {
quantifier_set * set = it->m_value;
SASSERT(!set);
out << it->m_key->get_name() << ": " << std::endl;
for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) {
out << std::hex << (size_t)*sit << std::endl;
}
}
out << "D2LR: " << std::endl;
for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end() ; it++) {
out << (size_t) it->m_key << std::endl;
}
}
bool demodulator::rewrite1(func_decl * f, ptr_vector<expr> & m_new_args, expr_ref & np) {
fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f);
if (it != m_fwd_idx.end()) {
TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n";
for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; });
quantifier_set::iterator dit = it->m_value->begin();
quantifier_set::iterator dend = it->m_value->end();
for ( ; dit != dend ; dit++ ) {
quantifier * d = *dit;
SASSERT(m_demodulator2lhs_rhs.contains(d));
expr_pair l_s;
m_demodulator2lhs_rhs.find(d, l_s);
app * large = to_app(l_s.first);
if (large->get_num_args() != m_new_args.size())
continue;
TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m_manager) << std::endl; );
SASSERT(large->get_decl() == f);
if (m_match_subst(large, l_s.second, m_new_args.c_ptr(), np)) {
TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m_manager) << "\n===>\n" << mk_pp(np, m_manager) << "\n";);
return true;
}
}
}
return false;
}
bool demodulator::rewrite_visit_children(app * a) {
bool res=true;
unsigned j = a->get_num_args();
while (j > 0) {
expr * e = a->get_arg(--j);
if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) {
m_rewrite_todo.push_back(e);
res = false;
}
}
return res;
}
void demodulator::rewrite_cache(expr * e, expr * new_e, bool done) {
m_rewrite_cache.insert(e, expr_bool_pair(new_e, done));
}
expr * demodulator::rewrite(expr * n) {
if (m_fwd_idx.empty())
return n;
TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m_manager) << std::endl; );
app * a;
SASSERT(m_rewrite_todo.empty());
m_rewrite_cache.reset();
m_rewrite_todo.push_back(n);
while (!m_rewrite_todo.empty()) {
TRACE("demodulator_stack", tout << "STACK: " << std::endl;
for ( unsigned i = 0; i<m_rewrite_todo.size(); i++)
tout << std::dec << i << ": " << std::hex << (size_t)m_rewrite_todo[i] << std::endl;
);
expr * e = m_rewrite_todo.back();
expr * actual = e;
if (m_rewrite_cache.contains(e)) {
const expr_bool_pair &ebp = m_rewrite_cache.get(e);
if (ebp.second) {
m_rewrite_todo.pop_back();
continue;
}
else {
actual = ebp.first;
}
}
switch (actual->get_kind()) {
case AST_VAR:
rewrite_cache(e, actual, true);
m_rewrite_todo.pop_back();
break;
case AST_APP:
a = to_app(actual);
if (rewrite_visit_children(a)) {
func_decl * f = a->get_decl();
m_new_args.reset();
unsigned num_args = a->get_num_args();
bool all_untouched=true;
for (unsigned i = 0 ; i < num_args ; i++ ) {
expr * o_child = a->get_arg(i);
expr * n_child;
SASSERT(m_rewrite_cache.contains(o_child) && m_rewrite_cache.get(o_child).second);
expr_bool_pair const & ebp = m_rewrite_cache.get(o_child);
n_child = ebp.first;
if (n_child != o_child)
all_untouched = false;
m_new_args.push_back(n_child);
}
expr_ref np(m_manager);
if (rewrite1(f, m_new_args, np)) {
rewrite_cache(e, np, false);
// No pop.
} else {
if(all_untouched) {
rewrite_cache(e, actual, true);
}
else {
expr_ref na(m_manager);
if (f->get_family_id() != m_manager.get_basic_family_id())
na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr());
else
m_bsimp.reduce(f, m_new_args.size(), m_new_args.c_ptr(), na);
TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m_manager) << "\nnew_args: \n";
for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }
tout << "=====>\n";
tout << "na:\n " << mk_pp(na, m_manager) << "\n";);
rewrite_cache(e, na, true);
}
m_rewrite_todo.pop_back();
}
}
break;
case AST_QUANTIFIER: {
expr * body = to_quantifier(actual)->get_expr();
if (m_rewrite_cache.contains(body)) {
const expr_bool_pair ebp = m_rewrite_cache.get(body);
SASSERT(ebp.second);
expr * new_body = ebp.first;
quantifier_ref q(m_manager);
q = m_manager.update_quantifier(to_quantifier(actual), new_body);
m_new_exprs.push_back(q);
expr_ref new_q(m_manager);
elim_unused_vars(m_manager, q, new_q);
m_new_exprs.push_back(new_q);
rewrite_cache(e, new_q, true);
m_rewrite_todo.pop_back();
} else {
m_rewrite_todo.push_back(body);
}
break;
}
default:
UNREACHABLE();
}
}
SASSERT(m_rewrite_cache.contains(n));
const expr_bool_pair & ebp = m_rewrite_cache.get(n);
SASSERT(ebp.second);
expr * r = ebp.first;
TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m_manager) << std::endl; );
return r;
}
class demodulator::add_back_idx_proc {
ast_manager & m_manager;
back_idx_map & m_back_idx;
expr * m_expr;
public:
add_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
// We track only uninterpreted and constant functions.
if (n->get_num_args()==0) return;
SASSERT(m_expr && m_expr != (expr*) 0x00000003);
func_decl * d=n->get_decl();
if (d->get_family_id() == null_family_id) {
back_idx_map::iterator it = m_back_idx.find_iterator(d);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
it->m_value->insert(m_expr);
} else {
expr_set * e = alloc(expr_set);
e->insert(m_expr);
m_back_idx.insert(d, e);
}
}
}
};
class demodulator::remove_back_idx_proc {
ast_manager & m_manager;
back_idx_map & m_back_idx;
expr * m_expr;
public:
remove_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
// We track only uninterpreted and constant functions.
if (n->get_num_args()==0) return;
func_decl * d=n->get_decl();
if (d->get_family_id() == null_family_id) {
back_idx_map::iterator it = m_back_idx.find_iterator(d);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
it->m_value->remove(m_expr);
}
}
}
};
void demodulator::reschedule_processed(func_decl * f) {
//use m_back_idx to find all formulas p in m_processed that contains f {
back_idx_map::iterator it = m_back_idx.find_iterator(f);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
expr_set temp;
expr_set::iterator sit = it->m_value->begin();
expr_set::iterator send = it->m_value->end();
for ( ; sit != send ; sit++ ) {
expr * p = *sit;
if (m_processed.contains(p))
temp.insert(p);
}
sit = temp.begin();
send = temp.end();
for ( ; sit != send; sit++) {
expr * p = *sit;
// remove p from m_processed and m_back_idx
m_processed.remove(p);
remove_back_idx_proc proc(m_manager, m_back_idx, p); // this could change it->m_value, thus we need the `temp' set.
for_each_expr(proc, p);
// insert p into m_todo
m_todo.push_back(p);
}
}
}
bool demodulator::can_rewrite(expr * n, expr * lhs) {
// this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'.
// we cannot use the trick used for m_processed, since the main loop would not terminate.
ptr_vector<expr> stack;
expr * curr;
expr_mark visited;
stack.push_back(n);
while (!stack.empty()) {
curr = stack.back();
if (visited.is_marked(curr)) {
stack.pop_back();
continue;
}
switch(curr->get_kind()) {
case AST_VAR:
visited.mark(curr, true);
stack.pop_back();
break;
case AST_APP:
if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) {
if (m_match_subst(lhs, curr))
return true;
visited.mark(curr, true);
stack.pop_back();
}
break;
case AST_QUANTIFIER:
if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(),
to_quantifier(curr)->get_patterns())) {
break;
}
if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(),
to_quantifier(curr)->get_no_patterns())) {
break;
}
if (!visited.is_marked(to_quantifier(curr)->get_expr())) {
stack.push_back(to_quantifier(curr)->get_expr());
break;
}
stack.pop_back();
break;
default:
UNREACHABLE();
}
}
return false;
}
void demodulator::reschedule_demodulators(func_decl * f, expr * lhs) {
// use m_back_idx to find all demodulators d in m_fwd_idx that contains f {
//ptr_vector<expr> to_remove;
back_idx_map::iterator it = m_back_idx.find_iterator(f);
if (it != m_back_idx.end()) {
SASSERT(it->m_value);
expr_set all_occurrences;
expr_ref l(m_manager);
expr_set::iterator esit = it->m_value->begin();
expr_set::iterator esend = it->m_value->end();
for ( ; esit != esend ; esit++)
all_occurrences.insert(*esit);
// Run over all f-demodulators
esit = all_occurrences.begin();
esend = all_occurrences.end();
for ( ; esit != esend ; esit++ ) {
expr * occ = *esit;
if (!is_quantifier(occ))
continue;
// Use the fwd idx to find out whether this is a demodulator.
demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ));
if (d2lr_it != m_demodulator2lhs_rhs.end()) {
l = d2lr_it->m_value.first;
quantifier_ref d(m_manager);
func_decl_ref df(m_manager);
d = to_quantifier(occ);
df = to_app(l)->get_decl();
// Now we know there is an occurrence of f in d
// if n' can rewrite d {
if (can_rewrite(d, lhs)) {
TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m_manager) << std::endl; );
// remove d from m_fwd_idx
remove_fwd_idx(df, d);
// remove d from m_back_idx
// just remember it here, because otherwise it and/or esit might become invalid?
// to_remove.insert(d);
remove_back_idx_proc proc(m_manager, m_back_idx, d);
for_each_expr(proc, d);
// insert d into m_todo
m_todo.push_back(d);
}
}
}
}
//for (ptr_vector<expr>::iterator it = to_remove.begin(); it != to_remove.end(); it++) {
// expr * d = *it;
// remove_back_idx_proc proc(m_manager, m_back_idx, d);
// for_each_expr(proc, d);
//}
}
void demodulator::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (m_manager.proofs_enabled()) {
// Let us not waste time with proof production
warning_msg("PRE_DEMODULATOR=true is not supported when proofs are enabled.");
new_exprs.append(n, exprs);
new_prs.append(n, prs);
return;
}
TRACE("demodulator", tout << "before demodulator:\n";
for ( unsigned i = 0 ; i < n ; i++ )
tout << mk_pp(exprs[i], m_manager) << std::endl; );
// Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty.
unsigned max_vid = 0;
for ( unsigned i = 0 ; i < n ; i++ ) {
m_todo.push_back(exprs[i]);
max_vid = std::max(max_vid, max_var_id(exprs[i]));
}
m_match_subst.reserve(max_vid);
while (!m_todo.empty()) {
// let n be the next formula in m_todo.
expr_ref cur(m_manager);
cur = m_todo.back();
m_todo.pop_back();
// rewrite cur using m_fwd_idx, and let n' be the result.
expr * np = rewrite(cur);
// at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'.
SASSERT(rewrite(np)==np);
// if (n' is not a demodulator) {
expr_ref large(m_manager), small(m_manager);
if (!is_demodulator(np, large, small)) {
// insert n' into m_processed
m_processed.insert(np);
// update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx)
add_back_idx_proc proc(m_manager, m_back_idx, np);
for_each_expr(proc, np);
} else {
// np is a demodulator that allows us to replace 'large' with 'small'.
TRACE("demodulator", tout << "Found demodulator: " << std::endl;
tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " <<
std::endl << mk_pp(small.get(), m_manager) << std::endl; );
TRACE("demodulator_s", tout << "Found demodulator: " << std::endl;
tout << to_app(large)->get_decl()->get_name() <<
"[" << to_app(large)->get_depth() << "]" << " ---> ";
if (is_app(small))
tout << to_app(small)->get_decl()->get_name() <<
"[" << to_app(small)->get_depth() << "]" << std::endl;
else
tout << mk_pp(small.get(), m_manager) << std::endl; );
// let f be the top symbol of n'
SASSERT(is_app(large));
func_decl * f = to_app(large)->get_decl();
reschedule_processed(f);
reschedule_demodulators(f, large);
// insert n' into m_fwd_idx
insert_fwd_idx(large, small, to_quantifier(np));
// update m_back_idx
add_back_idx_proc proc(m_manager, m_back_idx, np);
for_each_expr(proc, np);
}
}
// the result is the contents of m_processed + all demodulators in m_fwd_idx.
obj_hashtable<expr>::iterator pit = m_processed.begin();
obj_hashtable<expr>::iterator pend = m_processed.end();
for ( ; pit != pend ; pit++ ) {
new_exprs.push_back(*pit);
TRACE("demodulator", tout << mk_pp(*pit, m_manager) << std::endl; );
}
fwd_idx_map::iterator fit = m_fwd_idx.begin();
fwd_idx_map::iterator fend = m_fwd_idx.end();
for ( ; fit != fend ; fit++ ) {
if (fit->m_value) {
quantifier_set::iterator dit = fit->m_value->begin();
quantifier_set::iterator dend = fit->m_value->end();
for ( ; dit != dend ; dit++ ) {
expr * e = *dit;
new_exprs.push_back(e);
TRACE("demodulator", tout << mk_pp(*dit, m_manager) << std::endl; );
}
}
}
TRACE("demodulator", tout << "after demodulator:\n";
for ( unsigned i = 0 ; i < new_exprs.size() ; i++ )
tout << mk_pp(new_exprs[i].get(), m_manager) << std::endl; );
}
demodulator::match_subst::match_subst(ast_manager & m):
m_manager(m),
m_subst(m) {
}
/**
\brief Auxiliary functor used to implement optimization in match_args. See comment there.
*/
struct match_args_aux_proc {
substitution & m_subst;
struct no_match {};
match_args_aux_proc(substitution & s):m_subst(s) {}
void operator()(var * n) {
expr_offset r;
if (m_subst.find(n, 0, r)) {
if (r.get_expr() != n) {
SASSERT(r.get_offset() == 1);
throw no_match();
}
else {
m_subst.insert(n, 0, expr_offset(n, 1));
}
}
}
void operator()(quantifier * n) { throw no_match(); }
void operator()(app * n) {}
};
bool demodulator::match_subst::match_args(app * lhs, expr * const * args) {
m_cache.reset();
m_todo.reset();
// fill todo-list, and perform quick success/failure tests
m_all_args_eq = true;
unsigned num_args = lhs->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * t_arg = lhs->get_arg(i);
expr * i_arg = args[i];
if (t_arg != i_arg)
m_all_args_eq = false;
if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) {
// quick failure...
return false;
}
m_todo.push_back(expr_pair(t_arg, i_arg));
}
if (m_all_args_eq) {
// quick success worked...
return true;
}
m_subst.reset();
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
if (is_var(p.first)) {
expr_offset r;
if (m_subst.find(to_var(p.first), 0, r)) {
if (r.get_expr() != p.second)
return false;
}
else {
m_subst.insert(to_var(p.first), 0, expr_offset(p.second, 1));
}
m_todo.pop_back();
continue;
}
if (is_var(p.second))
return false;
// we may have nested quantifiers.
if (is_quantifier(p.first) || is_quantifier(p.second))
return false;
SASSERT(is_app(p.first) && is_app(p.second));
if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground())
return false;
if (p.first == p.second && to_app(p.first)->is_ground()) {
SASSERT(to_app(p.second)->is_ground());
m_todo.pop_back();
continue;
}
if (m_cache.contains(p)) {
m_todo.pop_back();
continue;
}
if (p.first == p.second) {
// p.first and p.second is not ground...
// Traverse p.first and check whether every variable X:0 in p.first
// 1) is unbounded (then we bind X:0 -> X:1)
// 2) or, is already bounded to X:1
// If that is, the case, we execute:
// m_todo.pop_back();
// m_cache.insert(p);
// continue;
// Otherwise
// return false;
match_args_aux_proc proc(m_subst);
try {
for_each_expr(proc, p.first);
// succeeded
m_todo.pop_back();
m_cache.insert(p);
continue;
}
catch (match_args_aux_proc::no_match) {
return false;
}
}
app * n1 = to_app(p.first);
app * n2 = to_app(p.second);
if (n1->get_decl() != n2->get_decl())
return false;
unsigned num_args1 = n1->get_num_args();
if (num_args1 != n2->get_num_args())
return false;
m_todo.pop_back();
if (num_args1 == 0)
continue;
m_cache.insert(p);
unsigned j = num_args1;
while (j > 0) {
--j;
m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j)));
}
}
return true;
}
bool demodulator::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) {
if (match_args(lhs, args)) {
if (m_all_args_eq) {
// quick success...
new_rhs = rhs;
return true;
}
unsigned deltas[2] = { 0, 0 };
m_subst.apply(2, deltas, expr_offset(rhs, 0), new_rhs);
return true;
}
return false;
}
bool demodulator::match_subst::operator()(expr * t, expr * i) {
m_cache.reset();
m_todo.reset();
if (is_var(t))
return true;
if (is_app(t) && is_app(i) && to_app(t)->get_decl() == to_app(i)->get_decl() && to_app(t)->get_num_args() == to_app(i)->get_num_args()) {
return match_args(to_app(t), to_app(i)->get_args());
}
return false;
}

View file

@ -1,265 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
demodulator.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-12.
Revision History:
--*/
#ifndef _DEMODULATOR_H_
#define _DEMODULATOR_H_
#include"ast.h"
#include"substitution.h"
#include"obj_hashtable.h"
#include"obj_pair_hashtable.h"
#include"array_map.h"
#include"basic_simplifier_plugin.h"
/**
\brief Apply demodulators as a preprocessing technique.
In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form:
Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn]
Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and
L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn].
The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn].
In FOTP, they use term orderings to decide what does it mean to be smaller.
We are using demodulators in a different context (pre-processing).
So, I suggest we have a virtual method is_smaller for comparing expressions.
The default implementation just compares the size of the expressions.
Similarly, in our context, formulas using iff are also demodulators.
Forall X1, ..., Xn. L[X1, ..., Xn] iff R[X1, ..., Xn]
After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn].
Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn].
For example, suppose we have
Forall x, y. f(x+y, y) = y
and
f(g(b) + h(c), h(c)) <= 0
The term f(g(b) + h(c), h(c)) is an instance of f(x+y, y) if we replace x <- g(b) and y <- h(c).
So, we can replace it with "y" which is bound to h(c) in this example. So, the result of the transformation is:
Forall x, y. f(x+y, y) = y
and
h(c) <= 0
In the first implementation, let us ignore theory matching. That is,
for us the term f(a+1) is not an instance of f(1+x), because the
matcher doesn't know + is commutative. Observe the demodulator is
*not* copied to the macro manager in this case.
Another complication is when we are looking for instances inside other universally quantified formulas.
The problem is that both formulas (demodular) and target are reusing variables names (ids).
To avoid renaming, we use offsets. The idea is to represent renames implicitly. In this case,
each offset is a different "variable bank". A pair (expr, offset) is essentially an expression
where every variable in expr is assumed to be from the "bank" offset.
The class substitution (in substitution.h) manages offsets for us.
The class matcher (in matcher.h) can be use to test whether an expression is an instance of another one.
Finally, there is the problem when we have N demodulators (where N is big), and a big formula, and we want
to traverse the formula only once looking for opportunities for applying these N demodulators.
We want to efficiently find the applicable demodulars.
We can start with a simple optimization that given a func_decl it returns the set of demodulators that start with this declaration.
For example, suppose we have the demodulators.
forall x, f(x, g(0)) = 10
forall x, f(g(h(x)), g(1)) = 20
Then, in our "index" f would map to these two demodulators.
As a final optimization, we should adapt the code to use substitution-trees.
The current implementation in Z3 is not efficient, and I think it is buggy.
So, it would be great to replace it with a new one.
The code in spc_rewriter.* does something like that. We cannot reuse this code directly since it is meant
for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor.
*/
class demodulator {
class rewrite_proc;
class add_back_idx_proc;
class remove_back_idx_proc;
class can_rewrite_proc;
typedef std::pair<expr *, bool> expr_bool_pair;
class plugin {
ast_manager& m_manager;
public:
plugin(ast_manager& m): m_manager(m) { }
void ins_eh(expr* k, expr_bool_pair v) { m_manager.inc_ref(k); m_manager.inc_ref(v.first); }
void del_eh(expr* k, expr_bool_pair v) { m_manager.dec_ref(k); m_manager.dec_ref(v.first); }
static unsigned to_int(expr const * k) { return k->get_id(); }
};
typedef array_map<expr*, expr_bool_pair, plugin> expr_map;
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_hashtable<expr> expr_set;
typedef obj_map<func_decl, expr_set *> back_idx_map;
typedef obj_hashtable<quantifier> quantifier_set;
typedef obj_map<func_decl, quantifier_set *> fwd_idx_map;
typedef obj_map<quantifier, expr_pair> demodulator2lhs_rhs;
typedef expr_map rewrite_cache_map;
/**
\brief Custom matcher & substitution application
*/
class match_subst {
typedef std::pair<expr *, expr *> expr_pair;
typedef obj_pair_hashtable<expr, expr> cache;
void reset();
ast_manager & m_manager;
substitution m_subst;
cache m_cache;
svector<expr_pair> m_todo;
bool m_all_args_eq;
bool match_args(app * t, expr * const * args);
public:
match_subst(ast_manager & m);
void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); }
/**
\brief Let f be the top symbol of lhs. If (f args) is an
instance of lhs, that is, there is a substitution s
s.t. s[lhs] = (f args), then return true and store s[rhs]
into new_rhs. Where s[t] represents the application of the
substitution s into t.
Assumptions, the variables in lhs and (f args) are assumed to be distinct.
So, (f x y) matches (f y x).
Moreover, the result should be in terms of the variables in (f args).
*/
bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs);
/**
\brief Return true if \c i is an instance of \c t.
*/
bool operator()(expr * t, expr * i);
};
ast_manager & m_manager;
match_subst m_match_subst;
basic_simplifier_plugin & m_bsimp;
fwd_idx_map m_fwd_idx;
back_idx_map m_back_idx;
demodulator2lhs_rhs m_demodulator2lhs_rhs;
expr_ref_buffer m_todo;
obj_hashtable<expr> m_processed;
ptr_vector<expr> m_new_args;
expr_ref_buffer m_rewrite_todo;
rewrite_cache_map m_rewrite_cache;
expr_ref_buffer m_new_exprs;
void insert_fwd_idx(expr * large, expr * small, quantifier * demodulator);
void remove_fwd_idx(func_decl * f, quantifier * demodulator);
bool check_fwd_idx_consistency(void);
void show_fwd_idx(std::ostream & out);
bool is_demodulator(expr * e, expr_ref & large, expr_ref & small) const;
bool can_rewrite(expr * n, expr * lhs);
expr * rewrite(expr * n);
bool rewrite1(func_decl * f, ptr_vector<expr> & m_new_args, expr_ref & np);
bool rewrite_visit_children(app * a);
void rewrite_cache(expr * e, expr * new_e, bool done);
void reschedule_processed(func_decl * f);
void reschedule_demodulators(func_decl * f, expr * np);
unsigned max_var_id(expr * e);
protected:
// is_smaller returns -1 for e1<e2, 0 for e1==e2 and +1 for e1>e2.
virtual int is_smaller(expr * e1, expr * e2) const;
// is_subset returns -1 for e1 subset e2, +1 for e2 subset e1, 0 else.
virtual int is_subset(expr * e1, expr * e2) const;
public:
demodulator(ast_manager & m, basic_simplifier_plugin & p);
virtual ~demodulator();
void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
/**
Given a demodulator (aka rewrite rule) of the form
Forall X. L[X] = R[X]
We say the top symbol is the first symbol in L.
For example: f is the top symbol in
Forall x, f(h(x)) = x + 1
The rewrite engine main loop is based on the DISCOUNT loop used in first-order theorem provers.
Main structures:
- m_todo: The todo-stack of formulas to be processed.
- m_fwd_idx: "Forward index" for finding efficiently which demodulators can be used to rewrite an expression.
We organize this set as a mapping from func_decl to a set of demodulators which start with the same top symbol.
- m_processed: The set of already processed formulas. We can represent it using a hashtable.
- m_back_idx: "Backward index" we use it to find efficiently which already processed expressions and demodulators may be rewritten
by a new demodulator. Again, we use a very simple index, for each uninterpreted function symbol (ignore constants)
f, store the expressions in m_processed and the demodulators that contain f.
If you prefer, you may use two m_back_idxs (one for formulas in m_processed and another for demodulators in m_fwd_idx).
Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty.
while (m_todo is not empty) {
let n be the next formula in m_todo.
rewrite n using m_fwd_idx, and let n' be the result.
// at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'.
if (n' is not a demodulator) {
insert n' into m_processed
update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx)
}
else {
let f be the top symbol of n'
use m_back_idx to find all formulas p in m_processed that contains f {
remove p from m_processed
remove p from m_back_idx
insert p into m_todo
}
use m_back_idx to find all demodulators d in m_fwd_idx that contains f {
if n' can rewrite d {
// this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'.
// we cannot use the trick used for m_processed, since the main loop would not terminate.
remove d from m_fwd_idx
remode d from m_back_idx
insert p into m_todo
}
}
insert n' into m_fwd_idx
update m_back_idx
}
}
the result is the contents of m_processed + all demodulators in m_fwd_idx.
Note: to remove p from m_back_idx, we need to traverse p, and for every function declartion f in p, we should remove the entry f->p from m_back_idx.
Note: we can implement m_back_idx for formulas as:
typedef obj_hashtable<expr> expr_set;
obj_map<func_decl, expr_set *> m_back_idx;
we should represent the sets as hashtables because we want to be able to efficiently remove elements from these sets.
ptr_vector<expr_set> m_expr_set_to_delete; // same trick we used in macro_manager.
we can use a similar structure for m_back_idx and m_fwd_idx for demodulators.
Note: m_processed should be obj_hashtable<expr> since we want to remove elements from there efficiently.
*/
};
#endif /* _DEMODULATOR_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,170 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
distribute_forall.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-02.
Revision History:
Christoph Wintersteiger 2010-04-06: Added implementation.
--*/
#include"var_subst.h"
#include"ast_ll_pp.h"
#include"distribute_forall.h"
distribute_forall::distribute_forall(ast_manager & m, basic_simplifier_plugin & p) :
m_manager(m),
m_bsimp(p),
m_cache(m) {
}
void distribute_forall::visit(expr * n, bool & visited) {
if (!is_cached(n)) {
m_todo.push_back(n);
visited = false;
}
}
bool distribute_forall::visit_children(expr * n) {
bool visited = true;
unsigned j;
switch(n->get_kind()) {
case AST_VAR:
break;
case AST_APP:
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(to_app(n)->get_arg(j), visited);
}
break;
case AST_QUANTIFIER:
visit(to_quantifier(n)->get_expr(), visited);
break;
default:
UNREACHABLE();
}
return visited;
}
void distribute_forall::reduce1(expr * n) {
switch (n->get_kind()) {
case AST_VAR:
cache_result(n, n);
break;
case AST_APP:
reduce1_app(to_app(n));
break;
case AST_QUANTIFIER:
reduce1_quantifier(to_quantifier(n));
break;
default: UNREACHABLE();
}
}
void distribute_forall::reduce1_app(app * a) {
SASSERT(a);
unsigned num_args = a->get_num_args();
unsigned j = num_args;
bool reduced = false;
m_new_args.reserve(num_args);
app * na = a;
while(j > 0) {
--j;
SASSERT(is_cached(a->get_arg(j)));
expr * c = get_cached(a->get_arg(j));
SASSERT(c!=0);
if (c != a->get_arg(j))
reduced = true;
m_new_args[j] = c;
}
if (reduced) {
na = m_manager.mk_app(a->get_decl(), num_args, m_new_args.c_ptr());
}
cache_result(a, na);
}
void distribute_forall::reduce1_quantifier(quantifier * q) {
// This transformation is applied after skolemization/quantifier elimination. So, all quantifiers are universal.
SASSERT(q->is_forall());
// This transformation is applied after basic pre-processing steps.
// So, we can assume that
// 1) All (and f1 ... fn) are already encoded as (not (or (not f1 ... fn)))
// 2) All or-formulas are flat (or f1 (or f2 f3)) is encoded as (or f1 f2 f3)
expr * e = get_cached(q->get_expr());
if (m_manager.is_not(e) && m_manager.is_or(to_app(e)->get_arg(0))) {
// found target for simplification
// (forall X (not (or F1 ... Fn)))
// -->
// (and (forall X (not F1))
// ...
// (forall X (not Fn)))
app * or_e = to_app(to_app(e)->get_arg(0));
unsigned num_args = or_e->get_num_args();
expr_ref_buffer new_args(m_manager);
for (unsigned i = 0; i < num_args; i++) {
expr * arg = or_e->get_arg(i);
expr_ref not_arg(m_manager);
// m_bsimp.mk_not applies basic simplifications. For example, if arg is of the form (not a), then it will return a.
m_bsimp.mk_not(arg, not_arg);
quantifier_ref tmp_q(m_manager);
tmp_q = m_manager.update_quantifier(q, not_arg);
expr_ref new_q(m_manager);
elim_unused_vars(m_manager, tmp_q, new_q);
new_args.push_back(new_q);
}
expr_ref result(m_manager);
// m_bsimp.mk_and actually constructs a (not (or ...)) formula,
// it will also apply basic simplifications.
m_bsimp.mk_and(new_args.size(), new_args.c_ptr(), result);
cache_result(q, result);
}
else {
cache_result(q, m_manager.update_quantifier(q, e));
}
}
void distribute_forall::operator()(expr * f, expr_ref & result) {
m_todo.reset();
flush_cache();
m_todo.push_back(f);
while (!m_todo.empty()) {
expr * e = m_todo.back();
if (visit_children(e)) {
m_todo.pop_back();
reduce1(e);
}
}
result = get_cached(f);
SASSERT(result!=0);
TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n"
<< mk_ll_pp(result, m_manager););
}
expr * distribute_forall::get_cached(expr * n) const {
return const_cast<distribute_forall*>(this)->m_cache.find(n);
}
void distribute_forall::cache_result(expr * n, expr * r) {
SASSERT(r != 0);
m_cache.insert(n, r);
}

View file

@ -1,82 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
distribute_forall.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-02.
Revision History:
Christoph Wintersteiger 2010-04-06: Added implementation
--*/
#ifndef _DISTRIBUTE_FORALL_H_
#define _DISTRIBUTE_FORALL_H_
#include"ast.h"
#include"basic_simplifier_plugin.h"
#include"act_cache.h"
/**
\brief Apply the following transformation
(forall X (and F1 ... Fn))
-->
(and (forall X F1) ... (forall X Fn))
The actual transformation is slightly different since the "and" connective is eliminated and
replaced with a "not or".
So, the actual transformation is:
(forall X (not (or F1 ... Fn)))
-->
(not (or (not (forall X (not F1)))
...
(not (forall X (not Fn)))))
The implementation uses the visit_children/reduce1 idiom. A cache is used as usual.
*/
class distribute_forall {
typedef act_cache expr_map;
ast_manager & m_manager;
basic_simplifier_plugin & m_bsimp; // useful for constructing formulas and/or/not in simplified form.
ptr_vector<expr> m_todo;
expr_map m_cache;
ptr_vector<expr> m_new_args;
// The new expressions are stored in a mapping that increments their reference counter. So, we do not need to store them in
// m_new_exprs
// expr_ref_vector m_new_exprs;
public:
distribute_forall(ast_manager & m, basic_simplifier_plugin & p);
/**
\brief Apply the distribute_forall transformation (when possible) to all universal quantifiers in \c f.
Store the result in \c result.
*/
void operator()(expr * f, expr_ref & result);
protected:
inline void visit(expr * n, bool & visited);
bool visit_children(expr * n);
void reduce1(expr * n);
void reduce1_quantifier(quantifier * q);
void reduce1_app(app * a);
expr * get_cached(expr * n) const;
bool is_cached(expr * n) const { return get_cached(n) != 0; }
void cache_result(expr * n, expr * r);
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.cleanup(); }
};
#endif /* _DISTRIBUTE_FORALL_H_ */

View file

@ -1,490 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_base.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#include"ast_pp.h"
#include"union_find.h"
#include"vector.h"
#include"dl_context.h"
#include"dl_base.h"
#include"bool_rewriter.h"
#include<sstream>
namespace datalog {
context & get_context_from_rel_manager(const relation_manager & rm) {
return rm.get_context();
}
ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm) {
return rm.get_context().get_manager();
}
#if DL_LEAK_HUNTING
void leak_guard_check(const symbol & s) {
}
#endif
void relation_signature::output(ast_manager & m, std::ostream & out) const {
unsigned sz=size();
out<<"(";
for(unsigned i=0; i<sz; i++) {
if(i) { out<<","; }
out<<ast_pp((*this)[i], m);
}
out<<")";
}
relation_fact::relation_fact(context & ctx) : app_ref_vector(ctx.get_manager()) {}
void relation_base::reset() {
ast_manager & m = get_plugin().get_ast_manager();
app_ref bottom_ref(m.mk_false(), m);
scoped_ptr<relation_mutator_fn> reset_fn =
get_manager().mk_filter_interpreted_fn(static_cast<relation_base &>(*this), bottom_ref);
if(!reset_fn) {
NOT_IMPLEMENTED_YET();
}
(*reset_fn)(*this);
}
void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2, table_signature & result) {
result.reset();
unsigned s1sz=s1.size();
unsigned s2sz=s2.size();
unsigned s1first_func=s1sz-s1.functional_columns();
unsigned s2first_func=s2sz-s2.functional_columns();
for(unsigned i=0; i<s1first_func; i++) {
result.push_back(s1[i]);
}
for(unsigned i=0; i<s2first_func; i++) {
result.push_back(s2[i]);
}
for(unsigned i=s1first_func; i<s1sz; i++) {
result.push_back(s1[i]);
}
for(unsigned i=s2first_func; i<s2sz; i++) {
result.push_back(s2[i]);
}
result.set_functional_columns(s1.functional_columns()+s2.functional_columns());
}
void table_signature::from_project(const table_signature & src, unsigned col_cnt,
const unsigned * removed_cols, table_signature & result) {
signature_base::from_project(src, col_cnt, removed_cols, result);
unsigned func_cnt = src.functional_columns();
if(removed_cols==0) {
result.set_functional_columns(func_cnt);
return;
}
unsigned first_src_fun = src.first_functional();
if(removed_cols[0]<first_src_fun) {
//if we remove at least one non-functional column, all the columns in the result are non-functional
result.set_functional_columns(0);
}
else {
//all columns we are removing are functional
SASSERT(func_cnt>=col_cnt);
result.set_functional_columns(func_cnt-col_cnt);
}
}
void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt,
const unsigned * removed_cols, table_signature & result) {
signature_base::from_project(src, col_cnt, removed_cols, result);
unsigned remaining_fun = src.functional_columns();
unsigned first_src_fun = src.first_functional();
for(int i=col_cnt-1; i>=0; i--) {
if(removed_cols[i]<first_src_fun) {
break;
}
remaining_fun--;
}
result.set_functional_columns(remaining_fun);
}
void table_signature::from_join_project(const table_signature & s1, const table_signature & s2,
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
const unsigned * removed_cols, table_signature & result) {
table_signature aux;
from_join(s1, s2, joined_col_cnt, cols1, cols2, aux);
//after the join the column order is
//(non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2)
if(s1.functional_columns()==0 && s2.functional_columns()==0) {
from_project(aux, removed_col_cnt, removed_cols, result);
SASSERT(result.functional_columns()==0);
return;
}
unsigned join_sig_sz = s1.size()+s2.size();
unsigned s1_first_func = s1.first_functional();
unsigned s2_first_func = s2.first_functional();
unsigned second_ofs = s1_first_func;
unsigned first_func_ofs = second_ofs + s2_first_func;
unsigned second_func_ofs = second_ofs + s1.functional_columns();
svector<unsigned> remaining_in_equivalence_class;
remaining_in_equivalence_class.resize(join_sig_sz, 0);
bool merging_rows_can_happen = false;
union_find_default_ctx uf_ctx;
union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join
for(unsigned i=0; i<join_sig_sz; i++) {
unsigned v = uf.mk_var();
SASSERT(v==i);
}
for(unsigned i=0; i<joined_col_cnt; i++) {
unsigned idx1 = (s1_first_func>cols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func);
unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func);
uf.merge(idx1, idx2);
}
for(unsigned i=0; i<first_func_ofs; i++) { //we only count the non-functional columns
remaining_in_equivalence_class[uf.find(i)]++;
}
for(unsigned i=0; i<removed_col_cnt; i++) {
unsigned rc = removed_cols[i];
if(rc>=first_func_ofs) {
//removing functional columns won't make us merge rows
continue;
}
unsigned eq_class_idx = uf.find(rc);
if(remaining_in_equivalence_class[eq_class_idx]>1) {
remaining_in_equivalence_class[eq_class_idx]--;
}
else {
merging_rows_can_happen = true;
break;
}
}
if(merging_rows_can_happen) {
//this one marks all columns as non-functional
from_project(aux, removed_col_cnt, removed_cols, result);
SASSERT(result.functional_columns()==0);
}
else {
//this one preserves columns to be functional
from_project_with_reduce(aux, removed_col_cnt, removed_cols, result);
}
}
// -----------------------------------
//
// table_base
//
// -----------------------------------
//here we give generic implementation of table operations using iterators
bool table_base::empty() const {
return begin()==end();
}
void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) {
for(unsigned i=0; i<fact_cnt; i++) {
remove_fact(facts[i]);
}
}
void table_base::remove_facts(unsigned fact_cnt, const table_element * facts) {
for(unsigned i=0; i<fact_cnt; i++) {
remove_fact(facts + i*get_signature().size());
}
}
void table_base::reset() {
vector<table_fact> to_remove;
table_base::iterator it = begin();
table_base::iterator iend = end();
table_fact row;
for(; it!=iend; ++it) {
it->get_fact(row);
to_remove.append(row);
}
remove_facts(to_remove.size(), to_remove.c_ptr());
}
bool table_base::contains_fact(const table_fact & f) const {
iterator it = begin();
iterator iend = end();
table_fact row;
for(; it!=iend; ++it) {
it->get_fact(row);
if(vectors_equal(row, f)) {
return true;
}
}
return false;
}
bool table_base::fetch_fact(table_fact & f) const {
if(get_signature().functional_columns()==0) {
return contains_fact(f);
}
else {
unsigned sig_sz = get_signature().size();
unsigned non_func_cnt = sig_sz-get_signature().functional_columns();
table_base::iterator it = begin();
table_base::iterator iend = end();
table_fact row;
for(; it!=iend; ++it) {
it->get_fact(row);
bool differs = false;
for(unsigned i=0; i<non_func_cnt; i++) {
if(row[i]!=f[i]) {
differs = true;
}
}
if(differs) {
continue;
}
for(unsigned i=non_func_cnt; i<sig_sz; i++) {
f[i]=row[i];
}
return true;
}
return false;
}
}
bool table_base::suggest_fact(table_fact & f) {
if(get_signature().functional_columns()==0) {
if(contains_fact(f)) {
return false;
}
add_new_fact(f);
return true;
}
else {
if(fetch_fact(f)) {
return false;
}
add_new_fact(f);
return true;
}
}
void table_base::ensure_fact(const table_fact & f) {
if(get_signature().functional_columns()==0) {
add_fact(f);
}
else {
remove_fact(f);
add_fact(f);
}
}
table_base * table_base::clone() const {
table_base * res = get_plugin().mk_empty(get_signature());
iterator it = begin();
iterator iend = end();
table_fact row;
for(; it!=iend; ++it) {
it->get_fact(row);
res->add_new_fact(row);
}
return res;
}
table_base * table_base::complement(func_decl* p, const table_element * func_columns) const {
const table_signature & sig = get_signature();
SASSERT(sig.functional_columns()==0 || func_columns!=0);
table_base * res = get_plugin().mk_empty(sig);
table_fact fact;
fact.resize(sig.first_functional());
fact.append(sig.functional_columns(), func_columns);
if(sig.first_functional()==0) {
if(empty()) {
res->add_fact(fact);
}
return res;
}
if(sig.first_functional()!=1) { //now we support only tables with one non-functional column
NOT_IMPLEMENTED_YET();
}
uint64 upper_bound = get_signature()[0];
bool empty_table = empty();
if (upper_bound > (1 << 18)) {
std::ostringstream buffer;
buffer << "creating large table of size " << upper_bound;
if (p) buffer << " for relation " << p->get_name();
warning_msg(buffer.str().c_str());
}
for(table_element i=0; i<upper_bound; i++) {
fact[0]=i;
if(empty_table || !contains_fact(fact)) {
res->add_fact(fact);
}
}
return res;
#if 0
svector<unsigned> var_arg_indexes(arity);
var_arg_indexes.fill(0);
svector<unsigned> var_arg_domain_sizes = s;
unsigned var_cnt=var_arg_indexes.size();
table_fact fact;
fact.resize(arity);
fact.fill(0);
unsigned depth=arity;
while(true) {
if(depth==arity) {
SASSERT(!res->contains_fact(fact));
if(empty_table || !contains_fact(fact)) {
res->add_fact(fact);
}
depth--;
}
else if(fact[depth]==s[depth]-1) {
val_indexes[depth]=0;
if(depth==0) {
break;
}
depth--;
}
else {
SASSERT(val_indexes[depth]<var_arg_domain_sizes[depth]);
unsigned arg_idx = var_arg_indexes[depth];
unsigned val_idx = val_indexes[depth]++;
head_args[arg_idx]=ctx.get_arith().mk_numeral(rational(val_idx), true);
depth++;
}
}
return res;
#endif
}
void table_base::display(std::ostream & out) const {
out << "table with signature ";
print_container(get_signature(), out);
out << ":\n";
iterator it = begin();
iterator iend = end();
for(; it!=iend; ++it) {
const row_interface & r = *it;
r.display(out);
}
out << "\n";
}
class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core {
const row_interface & m_parent;
unsigned m_index;
protected:
virtual bool is_finished() const { return m_index==m_parent.size(); }
public:
fact_row_iterator(const row_interface & row, bool finished)
: m_parent(row), m_index(finished ? row.size() : 0) {}
virtual table_element operator*() {
SASSERT(!is_finished());
return m_parent[m_index];
}
virtual void operator++() {
m_index++;
SASSERT(m_index<=m_parent.size());
}
};
table_base::row_iterator table_base::row_interface::begin() const {
return row_iterator(alloc(fact_row_iterator, *this, false));
}
table_base::row_iterator table_base::row_interface::end() const {
return row_iterator(alloc(fact_row_iterator, *this, true));
}
void table_base::row_interface::get_fact(table_fact & result) const {
result.reset();
unsigned n=size();
for(unsigned i=0; i<n; i++) {
result.push_back((*this)[i]);
}
}
void table_base::row_interface::display(std::ostream & out) const {
table_fact fact;
get_fact(fact);
print_container(fact, out);
out << "\n";
}
void table_base::to_formula(relation_signature const& sig, expr_ref& fml) const {
// iterate over rows and build disjunction
ast_manager & m = fml.get_manager();
expr_ref_vector disjs(m);
expr_ref_vector conjs(m);
dl_decl_util util(m);
table_fact fact;
iterator it = begin();
iterator iend = end();
for(; it!=iend; ++it) {
const row_interface & r = *it;
r.get_fact(fact);
conjs.reset();
for (unsigned i = 0; i < fact.size(); ++i) {
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i])));
}
switch(conjs.size()) {
case 0: disjs.push_back(m.mk_true()); break;
case 1: disjs.push_back(conjs[0].get()); break;
default: disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); break;
}
}
bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,778 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_bmc_engine.cpp
Abstract:
BMC engine for fixedpoint solver.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-20
Revision History:
--*/
#include "dl_context.h"
#include "dl_rule_transformer.h"
#include "dl_bmc_engine.h"
#include "dl_mk_slice.h"
#include "smt_solver.h"
#include "datatype_decl_plugin.h"
#include "dl_mk_rule_inliner.h"
#include "dl_decl_plugin.h"
#include "bool_rewriter.h"
#include "model_smt2_pp.h"
#include "ast_smt_pp.h"
namespace datalog {
bmc::bmc(context& ctx):
m_ctx(ctx),
m(ctx.get_manager()),
m_cancel(false),
m_solver(m, m_fparams),
m_pinned(m),
m_rules(ctx),
m_query_pred(m),
m_answer(m),
m_path_sort(m) {
m_fparams.m_model = true;
m_fparams.m_model_compact = true;
m_fparams.m_mbqi = false;
// m_fparams.m_auto_config = false;
}
bmc::~bmc() {}
lbool bmc::query(expr* query) {
m_solver.reset();
m_pinned.reset();
m_pred2sort.reset();
m_sort2pred.reset();
m_pred2newpred.reset();
m_pred2args.reset();
m_answer = 0;
m_ctx.ensure_opened();
m_rules.reset();
m_ctx.get_rmanager().reset_relations();
datalog::rule_manager& rule_manager = m_ctx.get_rule_manager();
datalog::rule_set old_rules(m_ctx.get_rules());
datalog::rule_ref_vector query_rules(rule_manager);
datalog::rule_ref query_rule(rule_manager);
rule_manager.mk_query(query, m_query_pred, query_rules, query_rule);
m_ctx.add_rules(query_rules);
expr_ref bg_assertion = m_ctx.get_background_assertion();
model_converter_ref mc = datalog::mk_skip_model_converter();
m_pc = datalog::mk_skip_proof_converter();
m_ctx.set_output_predicate(m_query_pred);
m_ctx.apply_default_transformation(mc, m_pc);
if (m_ctx.get_params().get_bool(":slice", true)) {
datalog::rule_transformer transformer(m_ctx);
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
transformer.register_plugin(slice);
m_ctx.transform_rules(transformer, mc, m_pc);
m_query_pred = slice->get_predicate(m_query_pred.get());
m_ctx.set_output_predicate(m_query_pred);
}
m_rules.add_rules(m_ctx.get_rules());
m_rules.close();
m_ctx.reopen();
m_ctx.replace_rules(old_rules);
checkpoint();
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
if (m_rules.get_num_rules() == 0) {
return l_false;
}
if (is_linear()) {
return check_linear();
}
else {
IF_VERBOSE(0, verbose_stream() << "WARNING: non-linear BMC is highly inefficient\n";);
return check_nonlinear();
}
}
bool bmc::is_linear() const {
unsigned sz = m_rules.get_num_rules();
for (unsigned i = 0; i < sz; ++i) {
if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) {
return false;
}
}
return true;
}
void bmc::get_model_linear(unsigned level) {
rule_manager& rm = m_ctx.get_rule_manager();
expr_ref level_query = mk_level_predicate(m_query_pred, level);
model_ref md;
proof_ref pr(m);
rule_unifier unifier(m_ctx);
m_solver.get_model(md);
func_decl* pred = m_query_pred;
SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl())));
dl_decl_util util(m);
TRACE("dl", model_smt2_pp(tout, m, *md, 0););
rule_ref r0(rm), r1(rm), r2(rm);
while (true) {
TRACE("dl", tout << "Predicate: " << pred->get_name() << "\n";);
expr_ref_vector sub(m);
rule_vector const& rls = m_rules.get_predicate_rules(pred);
rule* r = 0;
unsigned i = 0;
for (; i < rls.size(); ++i) {
expr_ref rule_i = mk_level_rule(pred, i, level);
TRACE("dl", rls[i]->display(m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " "););
if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) {
r = rls[i];
break;
}
}
SASSERT(r);
mk_rule_vars(*r, level, i, sub);
// we have rule, we have variable names of rule.
// extract values for the variables in the rule.
for (unsigned j = 0; j < sub.size(); ++j) {
expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl());
if (vl) {
// vl can be 0 if the interpretation does not assign a value to it.
sub[j] = vl;
}
else {
sub[j] = m.mk_var(j, m.get_sort(sub[j].get()));
}
}
svector<std::pair<unsigned, unsigned> > positions;
vector<expr_ref_vector> substs;
expr_ref fml(m), concl(m);
r->to_formula(fml);
r2 = r;
rm.substitute(r2, sub.size(), sub.c_ptr());
if (r0) {
VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get()));
expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true);
expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false);
apply_subst(sub, sub2);
unifier.apply(*r0.get(), 0, *r2.get(), r1);
r1->to_formula(concl);
scoped_coarse_proof _sp(m);
proof* p = m.mk_asserted(fml);
proof* premises[2] = { pr, p };
positions.push_back(std::make_pair(0, 1));
substs.push_back(sub1);
substs.push_back(sub);
pr = m.mk_hyper_resolve(2, premises, concl, positions, substs);
r0 = r1;
}
else {
r2->to_formula(concl);
scoped_coarse_proof _sp(m);
proof* p = m.mk_asserted(fml);
if (sub.empty()) {
pr = p;
}
else {
substs.push_back(sub);
pr = m.mk_hyper_resolve(1, &p, concl, positions, substs);
}
r0 = r2;
}
if (level == 0) {
SASSERT(r->get_uninterpreted_tail_size() == 0);
break;
}
--level;
SASSERT(r->get_uninterpreted_tail_size() == 1);
pred = r->get_decl(0);
}
scoped_coarse_proof _sp(m);
apply(m, m_pc.get(), pr);
m_answer = pr;
}
lbool bmc::check_linear() {
m_fparams.m_relevancy_lvl = 0;
for (unsigned i = 0; ; ++i) {
IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";);
checkpoint();
compile_linear(i);
lbool res = check_linear(i);
if (res == l_undef) {
return res;
}
if (res == l_true) {
get_model_linear(i);
return res;
}
}
}
lbool bmc::check_linear(unsigned level) {
expr_ref level_query = mk_level_predicate(m_query_pred, level);
expr* q = level_query.get();
return m_solver.check(1, &q);
}
void bmc::assert_expr(expr* e) {
TRACE("dl", tout << mk_pp(e, m) << "\n";);
m_solver.assert_expr(e);
}
expr_ref bmc::mk_level_predicate(func_decl* p, unsigned level) {
return mk_level_predicate(p->get_name(), level);
}
expr_ref bmc::mk_level_predicate(symbol const& name, unsigned level) {
std::stringstream _name;
_name << name << "#" << level;
symbol nm(_name.str().c_str());
return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m);
}
expr_ref bmc::mk_level_arg(func_decl* pred, unsigned idx, unsigned level) {
SASSERT(idx < pred->get_arity());
std::stringstream _name;
_name << pred->get_name() << "#" << level << "_" << idx;
symbol nm(_name.str().c_str());
return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m);
}
expr_ref bmc::mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) {
SASSERT(idx < pred->get_arity());
std::stringstream _name;
_name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx;
symbol nm(_name.str().c_str());
return expr_ref(m.mk_const(nm, s), m);
}
expr_ref bmc::mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) {
std::stringstream _name;
_name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx;
symbol nm(_name.str().c_str());
return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m);
}
void bmc::mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) {
sort_ref_vector sorts(m);
r.get_vars(sorts);
// populate substitution of bound variables.
sub.reset();
sub.resize(sorts.size());
for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) {
expr* arg = r.get_head()->get_arg(k);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (!sub[idx].get()) {
sub[idx] = mk_level_arg(r.get_decl(), k, level);
}
}
}
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
SASSERT(level > 0);
func_decl* q = r.get_decl(j);
for (unsigned k = 0; k < q->get_arity(); ++k) {
expr* arg = r.get_tail(j)->get_arg(k);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (!sub[idx].get()) {
sub[idx] = mk_level_arg(q, k, level-1);
}
}
}
}
for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) {
if (sorts[j].get() && !sub[j].get()) {
sub[j] = mk_level_var(r.get_decl(), sorts[j].get(), rule_id, idx++, level);
}
}
}
void bmc::compile_linear(unsigned level) {
rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules();
rule_set::decl2rules::iterator end = m_rules.end_grouped_rules();
for (; it != end; ++it) {
func_decl* p = it->m_key;
rule_vector const& rls = *it->m_value;
// Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ...
// Assert: r_i_level => body of rule i for level + equalities for head of rule i
expr_ref level_pred = mk_level_predicate(p, level);
expr_ref_vector rules(m), sub(m), conjs(m);
expr_ref rule_body(m), tmp(m);
for (unsigned i = 0; i < rls.size(); ++i) {
sub.reset();
conjs.reset();
rule& r = *rls[i];
expr_ref rule_i = mk_level_rule(p, i, level);
rules.push_back(rule_i);
if (level == 0 && r.get_uninterpreted_tail_size() > 0) {
assert_expr(m.mk_not(rule_i));
continue;
}
mk_rule_vars(r, level, i, sub);
// apply substitution to body.
var_subst vs(m, false);
for (unsigned k = 0; k < p->get_arity(); ++k) {
vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp);
conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level)));
}
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
SASSERT(level > 0);
func_decl* q = r.get_decl(j);
for (unsigned k = 0; k < q->get_arity(); ++k) {
vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp);
conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1)));
}
conjs.push_back(mk_level_predicate(q, level-1));
}
for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) {
vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp);
conjs.push_back(tmp);
}
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body);
assert_expr(m.mk_implies(rule_i, rule_body));
}
bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp);
assert_expr(m.mk_implies(level_pred, tmp));
}
}
lbool bmc::check_nonlinear() {
m_fparams.m_relevancy_lvl = 2;
declare_datatypes();
compile_nonlinear();
return check_query();
}
func_decl_ref bmc::mk_predicate(func_decl* pred) {
std::stringstream _name;
_name << pred->get_name() << "#";
symbol nm(_name.str().c_str());
sort* pred_trace_sort = m_pred2sort.find(pred);
return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m);
}
func_decl_ref bmc::mk_rule(func_decl* p, unsigned rule_idx) {
std::stringstream _name;
_name << "rule:" << p->get_name() << "#" << rule_idx;
symbol nm(_name.str().c_str());
sort* pred_trace_sort = m_pred2sort.find(p);
return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m);
}
expr_ref bmc::mk_var_nonlinear(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) {
std::stringstream _name;
_name << pred->get_name() << "#V_" << idx;
symbol nm(_name.str().c_str());
func_decl_ref fn(m);
fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s);
return expr_ref(m.mk_app(fn, trace_arg, path_arg), m);
}
expr_ref bmc::mk_arg_nonlinear(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) {
SASSERT(idx < pred->get_arity());
std::stringstream _name;
_name << pred->get_name() << "#X_" << idx;
symbol nm(_name.str().c_str());
func_decl_ref fn(m);
fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx));
return expr_ref(m.mk_app(fn, trace_arg, path_arg), m);
}
void bmc::mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) {
datatype_util dtu(m);
sort_ref_vector sorts(m);
func_decl* p = r.get_decl();
ptr_vector<func_decl> const& succs = *dtu.get_datatype_constructors(m.get_sort(path));
// populate substitution of bound variables.
r.get_vars(sorts);
sub.reset();
sub.resize(sorts.size());
for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) {
expr* arg = r.get_head()->get_arg(k);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (!sub[idx].get()) {
sub[idx] = mk_arg_nonlinear(p, k, path, trace);
}
}
}
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
func_decl* q = r.get_decl(j);
expr_ref path_arg(m);
if (j == 0) {
path_arg = path;
}
else {
path_arg = m.mk_app(succs[j], path);
}
for (unsigned k = 0; k < q->get_arity(); ++k) {
expr* arg = r.get_tail(j)->get_arg(k);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (!sub[idx].get()) {
sub[idx] = mk_arg_nonlinear(q, k, path_arg, trace->get_arg(j));
}
}
}
}
for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) {
if (sorts[j].get() && !sub[j].get()) {
sub[j] = mk_var_nonlinear(r.get_decl(), sorts[j].get(), idx++, path, trace);
}
}
}
/**
\brief compile Horn rule into co-Horn implication.
forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)]
*/
void bmc::compile_nonlinear() {
datatype_util dtu(m);
rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules();
rule_set::decl2rules::iterator end = m_rules.end_grouped_rules();
for (; it != end; ++it) {
func_decl* p = it->m_key;
rule_vector const& rls = *it->m_value;
// Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ...
// where: r_i_level = body of rule i for level + equalities for head of rule i
expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m);
var_ref path_var(m), trace_var(m);
expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m);
sort* pred_sort = m_pred2sort.find(p);
path_var = m.mk_var(0, m_path_sort);
trace_var = m.mk_var(1, pred_sort);
sort* sorts[2] = { pred_sort, m_path_sort };
ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(pred_sort);
ptr_vector<func_decl> const& succs = *dtu.get_datatype_constructors(m_path_sort);
SASSERT(cnstrs.size() == rls.size());
pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get());
for (unsigned i = 0; i < rls.size(); ++i) {
sub.reset();
conjs.reset();
vars.reset();
rule& r = *rls[i];
func_decl_ref rule_pred_i = mk_rule(p, i);
// Create cnstr_rule_i(Vars)
func_decl* cnstr = cnstrs[i];
rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get()));
unsigned arity = cnstr->get_arity();
for (unsigned j = 0; j < arity; ++j) {
vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j)));
}
trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr());
mk_subst(r, path_var, to_app(trace_arg), sub);
// apply substitution to body.
var_subst vs(m, false);
for (unsigned k = 0; k < p->get_arity(); ++k) {
vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp);
expr_ref arg = mk_arg_nonlinear(p, k, path_var, trace_arg);
conjs.push_back(m.mk_eq(tmp, arg));
}
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
expr_ref path_arg(m);
if (j == 0) {
path_arg = path_var.get();
}
else {
path_arg = m.mk_app(succs[j], path_var.get());
}
func_decl* q = r.get_decl(j);
for (unsigned k = 0; k < q->get_arity(); ++k) {
vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp);
expr_ref arg = mk_arg_nonlinear(q, k, path_arg, vars[j].get());
conjs.push_back(m.mk_eq(tmp, arg));
}
func_decl_ref q_pred = mk_predicate(q);
conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg));
}
for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) {
vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp);
conjs.push_back(tmp);
}
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body);
ptr_vector<sort> q_sorts;
vector<symbol> names;
for (unsigned i = 0; i < vars.size(); ++i) {
q_sorts.push_back(m.get_sort(vars[i].get()));
names.push_back(symbol(i+1));
}
vars.push_back(path_var);
q_sorts.push_back(m.get_sort(path_var));
names.push_back(symbol("path"));
SASSERT(names.size() == q_sorts.size());
SASSERT(vars.size() == names.size());
symbol qid = r.name(), skid;
tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get());
patterns.reset();
patterns.push_back(m.mk_pattern(to_app(tmp)));
fml = m.mk_implies(tmp, rule_body);
fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr());
assert_expr(fml);
}
}
}
void bmc::declare_datatypes() {
rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules();
rule_set::decl2rules::iterator end = m_rules.end_grouped_rules();
datatype_util dtu(m);
ptr_vector<datatype_decl> dts;
obj_map<func_decl, unsigned> pred_idx;
for (unsigned i = 0; it != end; ++it, ++i) {
pred_idx.insert(it->m_key, i);
}
it = m_rules.begin_grouped_rules();
for (; it != end; ++it) {
rule_vector const& rls = *it->m_value;
func_decl* pred = it->m_key;
ptr_vector<constructor_decl> cnstrs;
for (unsigned i = 0; i < rls.size(); ++i) {
rule* r = rls[i];
ptr_vector<accessor_decl> accs;
for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) {
func_decl* q = r->get_decl(j);
unsigned idx = pred_idx.find(q);
std::stringstream _name;
_name << pred->get_name() << "_" << q->get_name() << j;
symbol name(_name.str().c_str());
type_ref tr(idx);
accs.push_back(mk_accessor_decl(name, tr));
}
std::stringstream _name;
_name << pred->get_name() << "_" << i;
symbol name(_name.str().c_str());
_name << "?";
symbol is_name(_name.str().c_str());
cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
}
dts.push_back(mk_datatype_decl(pred->get_name(), cnstrs.size(), cnstrs.c_ptr()));
}
sort_ref_vector new_sorts(m);
family_id dfid = m.get_family_id("datatype");
datatype_decl_plugin* dtp = static_cast<datatype_decl_plugin*>(m.get_plugin(dfid));
VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts));
it = m_rules.begin_grouped_rules();
for (unsigned i = 0; it != end; ++it, ++i) {
m_pred2sort.insert(it->m_key, new_sorts[i].get());
m_sort2pred.insert(new_sorts[i].get(), it->m_key);
m_pinned.push_back(new_sorts[i].get());
}
if (new_sorts.size() > 0) {
TRACE("dl", dtu.display_datatype(new_sorts[0].get(), tout););
}
del_datatype_decls(dts.size(), dts.c_ptr());
// declare path data-type.
{
new_sorts.reset();
dts.reset();
ptr_vector<constructor_decl> cnstrs;
unsigned max_arity = 0;
rule_set::iterator it = m_rules.begin();
rule_set::iterator end = m_rules.end();
for (; it != end; ++it) {
rule* r = *it;
unsigned sz = r->get_uninterpreted_tail_size();
max_arity = std::max(sz, max_arity);
}
cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, 0));
for (unsigned i = 0; i + 1 < max_arity; ++i) {
std::stringstream _name;
_name << "succ#" << i;
symbol name(_name.str().c_str());
_name << "?";
symbol is_name(_name.str().c_str());
std::stringstream _name2;
_name2 << "get_succ#" << i;
symbol acc_name(_name2.str().c_str());
ptr_vector<accessor_decl> accs;
type_ref tr(0);
accs.push_back(mk_accessor_decl(name, tr));
cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
}
dts.push_back(mk_datatype_decl(symbol("Path"), cnstrs.size(), cnstrs.c_ptr()));
VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts));
m_path_sort = new_sorts[0].get();
}
}
proof_ref bmc::get_proof(model_ref& md, app* trace, app* path) {
datatype_util dtu(m);
sort* trace_sort = m.get_sort(trace);
func_decl* p = m_sort2pred.find(trace_sort);
datalog::rule_vector const& rules = m_rules.get_predicate_rules(p);
ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(trace_sort);
ptr_vector<func_decl> const& succs = *dtu.get_datatype_constructors(m_path_sort);
bool found = false;
for (unsigned i = 0; i < cnstrs.size(); ++i) {
if (trace->get_decl() == cnstrs[i]) {
found = true;
svector<std::pair<unsigned, unsigned> > positions;
scoped_coarse_proof _sc(m);
proof_ref_vector prs(m);
expr_ref_vector sub(m);
vector<expr_ref_vector> substs;
proof_ref pr(m);
expr_ref fml(m), head(m), tmp(m);
app_ref path1(m);
var_subst vs(m, false);
mk_subst(*rules[i], path, trace, sub);
rules[i]->to_formula(fml);
prs.push_back(m.mk_asserted(fml));
unsigned sz = trace->get_num_args();
if (sub.empty() && sz == 0) {
pr = prs[0].get();
return pr;
}
for (unsigned j = 0; j < sub.size(); ++j) {
md->eval(sub[j].get(), tmp);
sub[j] = tmp;
}
rule_ref rl(m_ctx.get_rule_manager());
rl = rules[i];
m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr());
substs.push_back(sub);
for (unsigned j = 0; j < sz; ++j) {
if (j == 0) {
path1 = path;
}
else {
path1 = m.mk_app(succs[j], path);
}
prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1));
positions.push_back(std::make_pair(j+1,0));
substs.push_back(expr_ref_vector(m));
}
head = rl->get_head();
pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs);
return pr;
}
}
UNREACHABLE();
return proof_ref(0, m);
}
// instantiation of algebraic data-types takes care of the rest.
lbool bmc::check_query() {
sort* trace_sort = m_pred2sort.find(m_query_pred);
func_decl_ref q = mk_predicate(m_query_pred);
expr_ref trace(m), path(m);
trace = m.mk_const(symbol("trace"), trace_sort);
path = m.mk_const(symbol("path"), m_path_sort);
assert_expr(m.mk_app(q, trace.get(), path.get()));
while (true) {
lbool is_sat = m_solver.check();
model_ref md;
if (is_sat == l_false) {
return is_sat;
}
m_solver.get_model(md);
mk_answer_nonlinear(md, trace, path);
return l_true;
}
}
bool bmc::check_model_nonlinear(model_ref& md, expr* trace) {
expr_ref trace_val(m), eq(m);
md->eval(trace, trace_val);
eq = m.mk_eq(trace, trace_val);
m_solver.push();
m_solver.assert_expr(eq);
lbool is_sat = m_solver.check();
if (is_sat != l_false) {
m_solver.get_model(md);
}
m_solver.pop(1);
if (is_sat == l_false) {
IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";);
m_solver.assert_expr(m.mk_not(eq));
}
return is_sat != l_false;
}
void bmc::mk_answer_nonlinear(model_ref& md, expr_ref& trace, expr_ref& path) {
proof_ref pr(m);
IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0););
md->eval(trace, trace);
md->eval(path, path);
IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n";
for (unsigned i = 0; i < m_solver.size(); ++i) {
verbose_stream() << mk_pp(m_solver.get_formulas()[i], m) << "\n";
});
m_answer = get_proof(md, to_app(trace), to_app(path));
}
void bmc::checkpoint() {
if (m_cancel) {
throw default_exception("bmc canceled");
}
}
void bmc::cancel() {
m_cancel = true;
m_solver.cancel();
}
void bmc::cleanup() {
m_cancel = false;
m_solver.reset();
}
void bmc::display_certificate(std::ostream& out) const {
out << mk_pp(m_answer, m) << "\n";
}
void bmc::collect_statistics(statistics& st) const {
m_solver.collect_statistics(st);
}
expr_ref bmc::get_answer() {
return m_answer;
}
};

View file

@ -1,128 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_bmc_engine.h
Abstract:
BMC engine for fixedpoint solver.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-20
Revision History:
--*/
#ifndef _DL_BMC_ENGINE_H_
#define _DL_BMC_ENGINE_H_
#include "params.h"
#include "statistics.h"
#include "smt_solver.h"
namespace datalog {
class context;
class bmc {
context& m_ctx;
ast_manager& m;
front_end_params m_fparams;
smt::solver m_solver;
obj_map<func_decl, sort*> m_pred2sort;
obj_map<sort, func_decl*> m_sort2pred;
obj_map<func_decl, func_decl*> m_pred2newpred;
obj_map<func_decl, ptr_vector<func_decl> > m_pred2args;
ast_ref_vector m_pinned;
rule_set m_rules;
func_decl_ref m_query_pred;
expr_ref m_answer;
volatile bool m_cancel;
proof_converter_ref m_pc;
sort_ref m_path_sort;
lbool check_query();
proof_ref get_proof(model_ref& md, app* trace, app* path);
void checkpoint();
void declare_datatypes();
void compile_nonlinear();
void mk_rule_vars_nonlinear(rule& r, unsigned rule_id, expr* trace_arg, expr* path_arg, expr_ref_vector& sub);
expr_ref mk_var_nonlinear(func_decl* pred, sort* s, unsigned idx, expr* path_arg, expr* trace_arg);
expr_ref mk_arg_nonlinear(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg);
void mk_subst(rule& r, expr* path, app* trace, expr_ref_vector& sub);
bool is_linear() const;
lbool check_nonlinear();
bool check_model_nonlinear(model_ref& md, expr* trace);
void mk_answer_nonlinear(model_ref& md, expr_ref& trace, expr_ref& path);
func_decl_ref mk_predicate(func_decl* p);
func_decl_ref mk_rule(func_decl* p, unsigned rule_idx);
// linear check
lbool check_linear();
lbool check_linear(unsigned level);
void compile_linear();
void compile_linear(unsigned level);
void compile_linear(rule& r, unsigned level);
expr_ref mk_level_predicate(symbol const& name, unsigned level);
expr_ref mk_level_predicate(func_decl* p, unsigned level);
expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level);
expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level);
expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level);
void get_model_linear(unsigned level);
void assert_expr(expr* e);
void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub);
public:
bmc(context& ctx);
~bmc();
lbool query(expr* query);
void cancel();
void cleanup();
void display_certificate(std::ostream& out) const;
void collect_statistics(statistics& st) const;
expr_ref get_answer();
};
};
#endif

View file

@ -1,706 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_bound_relation.cpp
Abstract:
Basic (strict upper) bound relation.
Author:
Nikolaj Bjorner (nbjorner) 2010-2-11
Revision History:
--*/
#include "dl_bound_relation.h"
#include "debug.h"
#include "ast_pp.h"
namespace datalog {
bound_relation_plugin::bound_relation_plugin(relation_manager& m):
relation_plugin(bound_relation_plugin::get_name(), m),
m_arith(get_ast_manager()),
m_bsimp(get_ast_manager()) {
}
bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) {
for (unsigned i = 0; i < sig.size(); ++i) {
if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) {
return false;
}
}
return true;
}
bound_relation& bound_relation_plugin::get(relation_base& r) {
return dynamic_cast<bound_relation&>(r);
}
bound_relation const & bound_relation_plugin::get(relation_base const& r) {
return dynamic_cast<bound_relation const&>(r);
}
bound_relation* bound_relation_plugin::get(relation_base* r) {
return dynamic_cast<bound_relation*>(r);
}
bool bound_relation_plugin::is_interval_relation(relation_base const& r) {
return symbol("interval_relation") == r.get_plugin().get_name();
}
interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) {
SASSERT(is_interval_relation(r));
return dynamic_cast<interval_relation&>(r);
}
interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) {
SASSERT(is_interval_relation(r));
return dynamic_cast<interval_relation const&>(r);
}
relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) {
return alloc(bound_relation, *this, s, true);
}
relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
return alloc(bound_relation, *this, s, false);
}
class bound_relation_plugin::join_fn : public convenient_relation_join_fn {
public:
join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2)
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) {
}
virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) {
bound_relation const& r1 = get(_r1);
bound_relation const& r2 = get(_r2);
bound_relation_plugin& p = r1.get_plugin();
bound_relation* result = dynamic_cast<bound_relation*>(p.mk_full(0, get_result_signature()));
result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
return result;
}
};
relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if (!check_kind(r1) || !check_kind(r2)) {
return 0;
}
return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2);
}
class bound_relation_plugin::project_fn : public convenient_relation_project_fn {
public:
project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) {
}
virtual relation_base * operator()(const relation_base & _r) {
bound_relation const& r = get(_r);
bound_relation_plugin& p = r.get_plugin();
bound_relation* result = get(p.mk_full(0, get_result_signature()));
result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr());
return result;
}
};
relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r,
unsigned col_cnt, const unsigned * removed_cols) {
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
}
class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn {
public:
rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {
}
virtual relation_base * operator()(const relation_base & _r) {
bound_relation const& r = get(_r);
bound_relation_plugin& p = r.get_plugin();
bound_relation* result = get(p.mk_full(0, get_result_signature()));
result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr());
return result;
}
};
relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r,
unsigned cycle_len, const unsigned * permutation_cycle) {
if(check_kind(r)) {
return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle);
}
return 0;
}
class bound_relation_plugin::union_fn : public relation_union_fn {
bool m_is_widen;
public:
union_fn(bool is_widen) :
m_is_widen(is_widen) {
}
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
get(_r).mk_union(get(_src), get(_delta), m_is_widen);
}
};
class bound_relation_plugin::union_fn_i : public relation_union_fn {
bool m_is_widen;
public:
union_fn_i(bool is_widen) :
m_is_widen(is_widen) {
}
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen);
TRACE("bound_relation", _r.display(tout << "dst':\n"););
}
};
relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) {
return alloc(union_fn_i, false);
}
if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) {
return alloc(union_fn, false);
}
return 0;
}
relation_union_fn * bound_relation_plugin::mk_widen_fn(
const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) {
return alloc(union_fn_i, true);
}
if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) {
return alloc(union_fn, true);
}
return 0;
}
class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn {
unsigned_vector m_cols;
public:
filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols)
: m_cols(col_cnt, identical_cols) {}
virtual void operator()(relation_base & r) {
for (unsigned i = 1; i < m_cols.size(); ++i) {
get(r).equate(m_cols[0], m_cols[i]);
}
}
};
relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn(
const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) {
if(check_kind(t)) {
return alloc(filter_identical_fn, col_cnt, identical_cols);
}
return 0;
}
class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn {
public:
filter_equal_fn(relation_element const& value, unsigned col) {}
virtual void operator()(relation_base & r) { }
};
relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r,
const relation_element & value, unsigned col) {
if (check_kind(r)) {
return alloc(filter_equal_fn, value, col);
}
return 0;
}
class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE };
app_ref m_cond;
app_ref m_lt;
arith_util m_arith;
interval_relation* m_interval;
unsigned_vector m_vars;
kind_t m_kind;
unsigned get_var(expr* a) {
SASSERT(is_var(a));
return to_var(a)->get_idx();
}
// x = z - y
void mk_sub_eq(expr* x, expr* z, expr* y) {
SASSERT(is_var(x));
SASSERT(is_var(z));
SASSERT(is_var(y));
m_vars.push_back(get_var(x));
m_vars.push_back(get_var(z));
m_vars.push_back(get_var(y));
m_kind = EQ_SUB;
}
void mk_lt(expr* l, expr* r) {
SASSERT(is_var(l));
SASSERT(is_var(r));
m_vars.push_back(get_var(l));
m_vars.push_back(get_var(r));
m_lt = m_arith.mk_lt(l, r);
m_kind = LT_VAR;
}
void mk_le(expr* l, expr* r) {
SASSERT(is_var(l));
SASSERT(is_var(r));
m_vars.push_back(get_var(l));
m_vars.push_back(get_var(r));
m_kind = LE_VAR;
}
void mk_eq(expr* l, expr* r) {
m_vars.push_back(get_var(l));
m_vars.push_back(get_var(r));
m_kind = EQ_VAR;
}
public:
filter_interpreted_fn(ast_manager& m, app* cond) :
m_cond(cond, m),
m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) {
expr* l, *r, *r1, *r2, *c2;
rational n1;
if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) &&
is_var(l) && is_var(r)) {
mk_lt(l, r);
}
else if (m.is_not(cond, c2) &&
(m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) &&
is_var(l) && is_var(r)) {
mk_lt(l, r);
}
else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) &&
is_var(l) && is_var(r)) {
mk_le(l, r);
}
else if (m.is_not(cond, c2) &&
(m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) &&
is_var(l) && is_var(r)) {
mk_le(l, r);
}
else if (m.is_false(cond)) {
m_kind = K_FALSE;
}
else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) {
mk_eq(l, r);
}
else if (m.is_eq(cond, l, r) &&
m_arith.is_sub(r, r1, r2) &&
is_var(l) && is_var(r1) && is_var(r2)) {
mk_sub_eq(l, r1, r2);
}
else if (m.is_eq(cond, l, r) &&
m_arith.is_sub(l, r1, r2) &&
is_var(r) && is_var(r1) && is_var(r2)) {
mk_sub_eq(r, r1, r2);
}
else if (m.is_eq(cond, l, r) &&
m_arith.is_add(r, r1, r2) &&
m_arith.is_numeral(r1, n1) &&
n1.is_pos() && is_var(l) && is_var(r2)) {
mk_lt(r2, l);
}
else if (m.is_eq(cond, l, r) &&
m_arith.is_add(r, r1, r2) &&
m_arith.is_numeral(r2, n1) &&
n1.is_pos() && is_var(l) && is_var(r1)) {
mk_lt(r1, l);
}
else {
}
}
//
// x = z - y
// x = y
// x < y
// x <= y
// x < y + z
//
void operator()(relation_base& t) {
TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout););
bound_relation& r = get(t);
switch(m_kind) {
case K_FALSE:
r.set_empty();
break;
case NOT_APPLICABLE:
break;
case EQ_VAR:
r.equate(m_vars[0], m_vars[1]);
break;
case EQ_SUB:
// TBD
break;
case LT_VAR:
r.mk_lt(m_vars[0], m_vars[1]);
break;
case LE_VAR:
r.mk_le(m_vars[0], m_vars[1]);
break;
default:
UNREACHABLE();
break;
}
TRACE("dl", t.display(tout << "result\n"););
}
bool supports_attachment(relation_base& t) {
return is_interval_relation(t);
}
void attach(relation_base& t) {
SASSERT(is_interval_relation(t));
interval_relation& r = get_interval_relation(t);
m_interval = &r;
}
};
relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) {
return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition);
}
// -----------------------------
// bound_relation
void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) {
if (t.lt.empty() && t.le.empty()) {
return;
}
uint_set::iterator it = t.lt.begin(), end = t.lt.end();
unsigned_vector ltv, lev;
for (; it != end; ++it) {
unsigned elem = *it;
ltv.push_back(renaming[*it]);
}
it = t.le.begin(), end = t.le.end();
for (; it != end; ++it) {
unsigned elem = *it;
lev.push_back(renaming[*it]);
}
TRACE("dl",
tout << "project: ";
for (unsigned i = 0; i < renaming.size(); ++i)
if (renaming[i] == UINT_MAX) tout << i << " ";
tout << ": ";
it = t.lt.begin(); end = t.lt.end();
for (; it != end; ++it) tout << *it << " ";
tout << " le ";
it = t.le.begin(); end = t.le.end();
for (; it != end; ++it) tout << *it << " ";
tout << " => ";
for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " ";
tout << " le ";
for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " ";
tout << "\n";);
t.lt.reset();
for (unsigned i = 0; i < ltv.size(); ++i) {
t.lt.insert(ltv[i]);
}
t.le.reset();
for (unsigned i = 0; i < lev.size(); ++i) {
t.le.insert(lev[i]);
}
}
bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty):
vector_relation<uint_set2, bound_relation_helper>(p, s, is_empty, uint_set2())
{
}
uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const {
is_empty = false;
uint_set2 r(t1);
r.lt |= t2.lt;
r.le |= t2.le;
return r;
}
uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const {
return mk_unite(t1, t2);
}
uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const {
uint_set2 s1(t1);
s1.lt &= t2.lt;
s1.le &= t2.le;
return s1;
}
uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const {
unsigned sz = old_eqs.get_num_vars();
SASSERT(sz == new_eqs.get_num_vars());
uint_set2 result;
for (unsigned i = 0; i < sz; ++i) {
if (t.lt.contains(i)) {
unsigned j = i;
do {
result.lt.insert(new_eqs.find(j));
j = old_eqs.next(j);
}
while (j != i);
}
if (t.le.contains(i)) {
unsigned j = i;
do {
result.le.insert(new_eqs.find(j));
j = old_eqs.next(j);
}
while (j != i);
}
}
return result;
}
bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const {
uint_set2 s1, s2;
normalize(t1, s1);
normalize(t2, s2);
return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le);
}
void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) {
// [ 0 -> 2 -> 3 -> 0]
if (col_cnt == 0) return;
unsigned col1, col2;
col1 = find(cycle[0]);
col2 = find(cycle[col_cnt-1]);
bool has_col2_lt = t.lt.contains(col2);
t.lt.remove(col2);
bool has_col2_le = t.le.contains(col2);
t.le.remove(col2);
for (unsigned i = 0; i + 1 < col_cnt; ++i) {
col1 = find(cycle[i]);
col2 = find(cycle[i+1]);
if (t.lt.contains(col1)) {
t.lt.remove(col1);
t.lt.insert(col2);
}
if (t.le.contains(col1)) {
t.le.remove(col1);
t.le.insert(col2);
}
}
if (has_col2_lt) {
col1 = find(cycle[0]);
t.lt.insert(col1);
}
if (has_col2_le) {
col1 = find(cycle[0]);
t.le.insert(col1);
}
}
bool bound_relation::is_full(uint_set2 const& t) const {
return t.lt.empty() && t.le.empty();
}
bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const {
return t.lt.contains(find(index)) || t.le.contains(find(index));
}
void bound_relation::normalize(uint_set const& src, uint_set& dst) const {
uint_set::iterator it = src.begin(), end = src.end();
for (; it != end; ++it) {
dst.insert(find(*it));
}
}
void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const {
normalize(src.lt, dst.lt);
normalize(src.le, dst.le);
}
void bound_relation::mk_lt(unsigned i) {
uint_set2& dst = (*this)[i];
while (!m_todo.empty()) {
unsigned j = m_todo.back().first;
bool strict = m_todo.back().second;
if (i == j && strict) {
m_todo.reset();
m_empty = true;
return;
}
m_todo.pop_back();
if (i == j) {
continue;
}
uint_set2& src = (*m_elems)[j];
uint_set::iterator it = src.lt.begin(), end = src.lt.end();
for(; it != end; ++it) {
m_todo.push_back(std::make_pair(*it, true));
}
it = src.le.begin(), end = src.le.end();
for(; it != end; ++it) {
m_todo.push_back(std::make_pair(*it, strict));
}
if (strict) {
dst.lt.insert(j);
}
else {
dst.le.insert(j);
}
}
}
void bound_relation::mk_lt(unsigned i, unsigned j) {
m_todo.reset();
i = find(i);
m_todo.push_back(std::make_pair(find(j), true));
mk_lt(i);
}
void bound_relation::mk_le(unsigned i, unsigned j) {
m_todo.reset();
i = find(i);
m_todo.push_back(std::make_pair(find(j), false));
mk_lt(i);
}
bool bound_relation::is_lt(unsigned i, unsigned j) const {
return (*this)[i].lt.contains(find(j));
}
void bound_relation::add_fact(const relation_fact & f) {
bound_relation r(get_plugin(), get_signature(), false);
for (unsigned i = 0; i < f.size(); ++i) {
scoped_ptr<relation_mutator_fn> fe = get_plugin().mk_filter_equal_fn(r, f[i], i);
(*fe)(r);
}
mk_union(r, 0, false);
}
bool bound_relation::contains_fact(const relation_fact & f) const {
if (empty()) {
return false;
}
// this is a very rough approximation.
return true;
}
bound_relation * bound_relation::clone() const {
bound_relation* result = 0;
if (empty()) {
result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature()));
}
else {
result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature()));
result->copy(*this);
}
return result;
}
void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) {
unsigned size = get_signature().size();
for (unsigned i = 0; i < size; ++i) {
if (find(i) != i) {
continue;
}
uint_set2& s = (*this)[i];
ext_numeral const& lo = src[i].sup();
if (lo.is_infinite()) {
s.lt.reset();
s.le.reset();
continue;
}
uint_set::iterator it = s.lt.begin(), end = s.lt.end();
for(; it != end; ++it) {
ext_numeral const& hi = src[*it].inf();
if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) {
s.lt.remove(*it);
}
}
it = s.le.begin(), end = s.le.end();
for(; it != end; ++it) {
ext_numeral const& hi = src[*it].inf();
if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) {
s.le.remove(*it);
}
}
}
}
bound_relation * bound_relation::complement(func_decl* p) const {
UNREACHABLE();
return 0;
}
void bound_relation::to_formula(expr_ref& fml) const {
ast_manager& m = get_plugin().get_ast_manager();
arith_util& arith = get_plugin().m_arith;
basic_simplifier_plugin& bsimp = get_plugin().m_bsimp;
expr_ref_vector conjs(m);
relation_signature const& sig = get_signature();
for (unsigned i = 0; i < sig.size(); ++i) {
if (i != find(i)) {
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)])));
continue;
}
uint_set2 const& upper = (*this)[i];
uint_set::iterator it = upper.lt.begin(), end = upper.lt.end();
for (; it != end; ++it) {
conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it])));
}
it = upper.le.begin(), end = upper.le.end();
for (; it != end; ++it) {
conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it])));
}
}
bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml);
}
void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const {
uint_set::iterator it = src.lt.begin(), end = src.lt.end();
out << i;
if (!src.lt.empty()) {
out << " < ";
for(; it != end; ++it) {
out << *it << " ";
}
}
if (!src.le.empty()) {
it = src.le.begin(), end = src.le.end();
out << " <= ";
for(; it != end; ++it) {
out << *it << " ";
}
}
out << "\n";
}
bound_relation_plugin& bound_relation::get_plugin() const {
return dynamic_cast<bound_relation_plugin&>(relation_base::get_plugin());
}
};

View file

@ -1,174 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_bound_relation.h
Abstract:
Basic (strict upper) bound relation.
Author:
Nikolaj Bjorner (nbjorner) 2010-2-11
Revision History:
--*/
#ifndef _DL_BOUND_RELATION_H_
#define _DL_BOUND_RELATION_H_
#include "dl_context.h"
#include "uint_set.h"
#include "dl_vector_relation.h"
#include "dl_interval_relation.h"
#include "arith_decl_plugin.h"
#include "basic_simplifier_plugin.h"
namespace datalog {
class bound_relation;
class bound_relation_plugin : public relation_plugin {
friend class bound_relation;
class join_fn;
class project_fn;
class rename_fn;
class union_fn;
class union_fn_i;
class filter_equal_fn;
class filter_identical_fn;
class filter_interpreted_fn;
class filter_intersection_fn;
arith_util m_arith;
basic_simplifier_plugin m_bsimp;
public:
bound_relation_plugin(relation_manager& m);
virtual bool can_handle_signature(const relation_signature & s);
static symbol get_name() { return symbol("bound_relation"); }
virtual relation_base * mk_empty(const relation_signature & s);
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2,
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2,
unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; }
#if 0
virtual intersection_filter_fn * mk_filter_by_intersection_fn(
const relation_base & t,
const relation_base & src, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * src_cols) {
return 0;
}
#endif
static bound_relation* get(relation_base* r);
private:
static bound_relation& get(relation_base& r);
static bound_relation const & get(relation_base const& r);
static bool is_interval_relation(relation_base const& r);
static interval_relation& get_interval_relation(relation_base& r);
static interval_relation const& get_interval_relation(relation_base const& r);
};
struct uint_set2 {
uint_set lt;
uint_set le;
uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {}
uint_set2() {}
bool operator==(const uint_set2& other) const {
return other.lt == lt && other.le == le;
}
bool operator!=(const uint_set2& other) const {
return other.lt != lt || other.le != le;
}
};
inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) {
return target << s.lt << " " << s.le;
}
class bound_relation_helper {
public:
static void mk_project_t(uint_set2& t, unsigned_vector const& renaming);
};
class bound_relation : public vector_relation<uint_set2, bound_relation_helper> {
friend class bound_relation_plugin;
svector<std::pair<unsigned, bool> > m_todo;
public:
bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty);
bound_relation& operator=(bound_relation const& other);
virtual bool empty() const { return m_empty; }
virtual void add_fact(const relation_fact & f);
virtual bool contains_fact(const relation_fact & f) const;
virtual bound_relation * clone() const;
virtual bound_relation * complement(func_decl* p) const;
virtual void to_formula(expr_ref& fml) const;
bound_relation_plugin& get_plugin() const;
void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen);
void mk_lt(unsigned i, unsigned j);
void mk_lt(unsigned i);
void mk_le(unsigned i, unsigned j);
bool is_lt(unsigned i, unsigned j) const;
private:
typedef uint_set2 T;
virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const;
virtual T mk_widen(T const& t1, T const& t2) const;
virtual T mk_unite(T const& t1, T const& t2) const;
virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const;
virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle);
virtual bool is_subset_of(T const& t1, T const& t2) const;
virtual bool is_full(T const& t) const;
virtual bool is_empty(unsigned idx, T const& t) const;
virtual void display_index(unsigned idx, T const& t, std::ostream& out) const;
void normalize(T const& src, T& dst) const;
void normalize(uint_set const& src, uint_set& dst) const;
};
};
#endif

View file

@ -1,363 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_check_table.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-11-15
Revision History:
--*/
#include "dl_check_table.h"
#include "dl_table.h"
namespace datalog {
bool check_table_plugin::can_handle_signature(table_signature const& s) {
return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s);
}
check_table & check_table_plugin::get(table_base& r) {
return static_cast<check_table&>(r);
}
check_table const & check_table_plugin::get(table_base const& r) {
return static_cast<check_table const &>(r);
}
table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; }
table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; }
table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; }
table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; }
table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; }
table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; }
table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; }
table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; }
table_base * check_table_plugin::mk_empty(const table_signature & s) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
table_base* checker = m_checker.mk_empty(s);
table_base* tocheck = m_tocheck.mk_empty(s);
return alloc(check_table, *this, s, tocheck, checker);
}
class check_table_plugin::join_fn : public table_join_fn {
scoped_ptr<table_join_fn> m_tocheck;
scoped_ptr<table_join_fn> m_checker;
public:
join_fn(check_table_plugin& p,
const table_base & t1, const table_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2);
m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2);
}
virtual table_base* operator()(const table_base & t1, const table_base & t2) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2));
table_base* tchecker = (*m_checker)(checker(t1), checker(t2));
check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
return result;
}
};
table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if (!check_kind(t1) || !check_kind(t2)) {
return 0;
}
return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2);
}
class check_table_plugin::union_fn : public table_union_fn {
scoped_ptr<table_union_fn> m_tocheck;
scoped_ptr<table_union_fn> m_checker;
public:
union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) {
m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta));
m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta));
}
virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
(*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta));
(*m_checker)(checker(tgt), checker(src), checker(delta));
get(tgt).well_formed();
if (delta) {
get(*delta).well_formed();
}
}
};
table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn, *this, tgt, src, delta);
}
class check_table_plugin::project_fn : public table_transformer_fn {
scoped_ptr<table_transformer_fn> m_checker;
scoped_ptr<table_transformer_fn> m_tocheck;
public:
project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) {
m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols);
m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols);
}
table_base* operator()(table_base const& src) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
table_base* tchecker = (*m_checker)(checker(src));
table_base* ttocheck = (*m_tocheck)(tocheck(src));
check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker);
return result;
}
};
table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) {
if (!check_kind(t)) {
return 0;
}
return alloc(project_fn, *this, t, col_cnt, removed_cols);
}
class check_table_plugin::rename_fn : public table_transformer_fn {
scoped_ptr<table_transformer_fn> m_checker;
scoped_ptr<table_transformer_fn> m_tocheck;
public:
rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) {
m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle);
m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle);
}
table_base* operator()(table_base const& src) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
table_base* tchecker = (*m_checker)(checker(src));
table_base* ttocheck = (*m_tocheck)(tocheck(src));
check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker);
return result;
}
};
table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) {
if (!check_kind(t)) {
return 0;
}
return alloc(rename_fn, *this, t, len, cycle);
}
class check_table_plugin::filter_identical_fn : public table_mutator_fn {
scoped_ptr<table_mutator_fn> m_checker;
scoped_ptr<table_mutator_fn> m_tocheck;
public:
filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols)
{
m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols);
m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols);
}
void operator()(table_base & t) {
(*m_checker)(checker(t));
(*m_tocheck)(tocheck(t));
get(t).well_formed();
}
};
table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt,
const unsigned * identical_cols) {
if (check_kind(t)) {
return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols);
}
return 0;
}
class check_table_plugin::filter_equal_fn : public table_mutator_fn {
scoped_ptr<table_mutator_fn> m_checker;
scoped_ptr<table_mutator_fn> m_tocheck;
public:
filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col)
{
m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col);
m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col);
}
virtual void operator()(table_base& src) {
(*m_checker)(checker(src));
(*m_tocheck)(tocheck(src));
get(src).well_formed();
}
};
table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) {
if (check_kind(t)) {
return alloc(filter_equal_fn, *this, t, value, col);
}
return 0;
}
class check_table_plugin::filter_interpreted_fn : public table_mutator_fn {
scoped_ptr<table_mutator_fn> m_checker;
scoped_ptr<table_mutator_fn> m_tocheck;
public:
filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition)
{
m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition);
m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition);
}
virtual void operator()(table_base& src) {
(*m_checker)(checker(src));
(*m_tocheck)(tocheck(src));
get(src).well_formed();
}
};
table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) {
if (check_kind(t)) {
return alloc(filter_interpreted_fn, *this, t, condition);
}
return 0;
}
class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn {
scoped_ptr<table_intersection_filter_fn> m_checker;
scoped_ptr<table_intersection_filter_fn> m_tocheck;
public:
filter_by_negation_fn(
check_table_plugin& p,
const table_base & t,
const table_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols) {
m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols);
m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols);
}
virtual void operator()(table_base& src, table_base const& negated_obj) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
(*m_checker)(checker(src), checker(negated_obj));
(*m_tocheck)(tocheck(src), tocheck(negated_obj));
get(src).well_formed();
}
};
table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t,
const table_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols) {
if (check_kind(t) && check_kind(negated_obj)) {
return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols);
}
return 0;
}
// ------------------
// check_table
check_table::check_table(check_table_plugin & p, const table_signature & sig):
table_base(p, sig) {
(well_formed());
}
check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker):
table_base(p, sig),
m_checker(checker),
m_tocheck(tocheck) {
well_formed();
}
check_table::~check_table() {
m_tocheck->deallocate();
m_checker->deallocate();
}
bool check_table::well_formed() const {
get_plugin().m_count++;
if (get_plugin().m_count == 497) {
std::cout << "here\n";
}
iterator it = m_tocheck->begin(), end = m_tocheck->end();
for (; it != end; ++it) {
table_fact fact;
it->get_fact(fact);
if (!m_checker->contains_fact(fact)) {
m_tocheck->display(verbose_stream());
m_checker->display(verbose_stream());
verbose_stream() << get_plugin().m_count << "\n";
UNREACHABLE();
fatal_error(0);
return false;
}
}
iterator it2 = m_checker->begin(), end2 = m_checker->end();
for (; it2 != end2; ++it2) {
table_fact fact;
it2->get_fact(fact);
if (!m_tocheck->contains_fact(fact)) {
m_tocheck->display(verbose_stream());
m_checker->display(verbose_stream());
verbose_stream() << get_plugin().m_count << "\n";
UNREACHABLE();
fatal_error(0);
return false;
}
}
return true;
}
bool check_table::empty() const {
if (m_tocheck->empty() != m_checker->empty()) {
m_tocheck->display(verbose_stream());
m_checker->display(verbose_stream());
verbose_stream() << get_plugin().m_count << "\n";
fatal_error(0);
}
return m_tocheck->empty();
}
void check_table::add_fact(const table_fact & f) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
m_tocheck->add_fact(f);
m_checker->add_fact(f);
well_formed();
}
void check_table::remove_fact(const table_element* f) {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
m_tocheck->remove_fact(f);
m_checker->remove_fact(f);
well_formed();
}
bool check_table::contains_fact(const table_fact & f) const {
return m_checker->contains_fact(f);
}
table_base * check_table::clone() const {
IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";);
check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone());
return result;
}
table_base * check_table::complement(func_decl* p) const {
check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p), m_checker->complement(p));
return result;
}
};

View file

@ -1,134 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_check_table.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-11-15
Revision History:
--*/
#ifndef _DL_CHECK_TABLE_H_
#define _DL_CHECK_TABLE_H_
#include "dl_base.h"
#include "dl_decl_plugin.h"
#include "dl_relation_manager.h"
namespace datalog {
class check_table;
class check_table_plugin : public table_plugin {
friend class check_table;
table_plugin& m_checker;
table_plugin& m_tocheck;
unsigned m_count;
protected:
class join_fn;
class union_fn;
class transformer_fn;
class rename_fn;
class project_fn;
class filter_equal_fn;
class filter_identical_fn;
class filter_interpreted_fn;
class filter_by_negation_fn;
public:
check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck)
: table_plugin(symbol("check"), manager),
m_checker(*manager.get_table_plugin(checker)),
m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {}
virtual table_base * mk_empty(const table_signature & s);
virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src,
const table_base * delta);
virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value,
unsigned col);
virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition);
virtual table_intersection_filter_fn * mk_filter_by_negation_fn(
const table_base & t,
const table_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols);
virtual bool can_handle_signature(table_signature const& s);
private:
static check_table& get(table_base& r);
static check_table const & get(table_base const& r);
static table_base& checker(table_base& r);
static table_base const& checker(table_base const& r);
static table_base* checker(table_base* r);
static table_base const* checker(table_base const* r);
static table_base& tocheck(table_base& r);
static table_base const& tocheck(table_base const& r);
static table_base* tocheck(table_base* r);
static table_base const* tocheck(table_base const* r);
};
class check_table : public table_base {
friend class check_table_plugin;
friend class check_table_plugin::join_fn;
friend class check_table_plugin::union_fn;
friend class check_table_plugin::transformer_fn;
friend class check_table_plugin::rename_fn;
friend class check_table_plugin::project_fn;
friend class check_table_plugin::filter_equal_fn;
friend class check_table_plugin::filter_identical_fn;
friend class check_table_plugin::filter_interpreted_fn;
friend class check_table_plugin::filter_by_negation_fn;
table_base* m_checker;
table_base* m_tocheck;
check_table(check_table_plugin & p, const table_signature & sig);
check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker);
virtual ~check_table();
bool well_formed() const;
public:
check_table_plugin & get_plugin() const {
return static_cast<check_table_plugin &>(table_base::get_plugin());
}
virtual bool empty() const;
virtual void add_fact(const table_fact & f);
virtual void remove_fact(const table_element* fact);
virtual bool contains_fact(const table_fact & f) const;
virtual table_base * complement(func_decl* p) const;
virtual table_base * clone() const;
virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); }
virtual iterator end() const { return m_tocheck->end(); }
virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); }
virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); }
};
};
#endif /* _DL_CHECK_TABLE_H_ */

View file

@ -1,437 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dl_cmds.cpp
Abstract:
Datalog commands for SMT2 front-end.
Author:
Leonardo (leonardo) 2011-03-28
Notes:
--*/
#include"cmd_context.h"
#include"dl_cmds.h"
#include"dl_external_relation.h"
#include"dl_context.h"
#include"dl_decl_plugin.h"
#include"dl_instruction.h"
#include"dl_compiler.h"
#include"dl_rule.h"
#include"ast_pp.h"
#include"parametric_cmd.h"
#include"cancel_eh.h"
#include"scoped_ctrl_c.h"
#include"scoped_timer.h"
#include<iomanip>
class dl_context {
cmd_context & m_cmd;
unsigned m_ref_count;
datalog::dl_decl_plugin* m_decl_plugin;
scoped_ptr<datalog::context> m_context;
public:
dl_context(cmd_context & ctx):
m_cmd(ctx),
m_ref_count(0),
m_decl_plugin(0) {}
void inc_ref() {
++m_ref_count;
}
void dec_ref() {
--m_ref_count;
if (0 == m_ref_count) {
dealloc(this);
}
}
void init() {
ast_manager& m = m_cmd.m();
if (!m_context) {
m_context = alloc(datalog::context, m, m_cmd.params());
}
if (!m_decl_plugin) {
symbol name("datalog_relation");
if (m.has_plugin(name)) {
m_decl_plugin = static_cast<datalog::dl_decl_plugin*>(m_cmd.m().get_plugin(m.get_family_id(name)));
}
else {
m_decl_plugin = alloc(datalog::dl_decl_plugin);
m.register_plugin(symbol("datalog_relation"), m_decl_plugin);
}
}
}
void reset() {
m_context = 0;
}
void add_rule(expr * rule, symbol const& name) {
init();
std::string error_msg;
m_context->add_rule(rule, name);
}
datalog::context & get_dl_context() {
init();
return *m_context;
}
};
/**
\brief rule command. It is also the owner of dl_context object.
*/
class dl_rule_cmd : public cmd {
ref<dl_context> m_dl_ctx;
mutable unsigned m_arg_idx;
expr* m_t;
symbol m_name;
public:
dl_rule_cmd(dl_context * dl_ctx):
cmd("rule"),
m_dl_ctx(dl_ctx),
m_arg_idx(0),
m_t(0) {}
virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name"; }
virtual char const * get_descr(cmd_context & ctx) const { return "add a Horn rule."; }
virtual unsigned get_arity() const { return VAR_ARITY; }
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
switch(m_arg_idx) {
case 0: return CPK_EXPR;
case 1: return CPK_SYMBOL;
default: return CPK_SYMBOL;
}
}
virtual void set_next_arg(cmd_context & ctx, expr * t) {
m_t = t;
m_arg_idx++;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
m_name = s;
}
virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); }
virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; }
virtual void finalize(cmd_context & ctx) {
}
virtual void execute(cmd_context & ctx) {
m_dl_ctx->add_rule(m_t, m_name);
}
};
class dl_query_cmd : public parametric_cmd {
ref<dl_context> m_dl_ctx;
expr* m_target;
public:
dl_query_cmd(dl_context * dl_ctx):
parametric_cmd("query"),
m_dl_ctx(dl_ctx),
m_target(0) {
}
virtual char const * get_usage() const { return "(exists (q) (and body))"; }
virtual char const * get_main_descr() const {
return "pose a query based on the Horn rules.";
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
if (m_target == 0) return CPK_EXPR;
return parametric_cmd::next_arg_kind(ctx);
}
virtual void set_next_arg(cmd_context & ctx, expr * t) {
m_target = t;
}
virtual void prepare(cmd_context & ctx) {
parametric_cmd::prepare(ctx);
m_target = 0;
}
virtual void execute(cmd_context& ctx) {
if (m_target == 0) {
throw cmd_exception("invalid query command, argument expected");
}
datalog::context& dlctx = m_dl_ctx->get_dl_context();
set_background(ctx);
dlctx.updt_params(m_params);
unsigned timeout = m_params.get_uint(":timeout", UINT_MAX);
cancel_eh<datalog::context> eh(dlctx);
lbool status = l_undef;
{
scoped_ctrl_c ctrlc(eh);
scoped_timer timer(timeout, &eh);
cmd_context::scoped_watch sw(ctx);
try {
status = dlctx.query(m_target);
}
catch (z3_error & ex) {
throw ex;
}
catch (z3_exception& ex) {
ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl;
}
dlctx.cleanup();
}
switch (status) {
case l_false:
ctx.regular_stream() << "unsat\n";
print_certificate(ctx);
break;
case l_true:
ctx.regular_stream() << "sat\n";
print_answer(ctx);
print_certificate(ctx);
break;
case l_undef:
ctx.regular_stream() << "unknown\n";
switch(dlctx.get_status()) {
case datalog::INPUT_ERROR:
break;
case datalog::MEMOUT:
ctx.regular_stream() << "memory bounds exceeded\n";
break;
case datalog::TIMEOUT:
ctx.regular_stream() << "timeout\n";
break;
case datalog::OK:
break;
default:
UNREACHABLE();
}
break;
}
print_statistics(ctx);
m_target = 0;
}
virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) {
m_dl_ctx->get_dl_context().collect_params(p);
insert_timeout(p);
p.insert(":print-answer", CPK_BOOL, "(default: false) print answer instance(s) to query.");
p.insert(":print-certificate", CPK_BOOL, "(default: false) print certificate for reachability or non-reachability.");
p.insert(":print-statistics", CPK_BOOL, "(default: false) print statistics.");
}
private:
void set_background(cmd_context& ctx) {
datalog::context& dlctx = m_dl_ctx->get_dl_context();
ast_manager& m = ctx.m();
ptr_vector<expr>::const_iterator it = ctx.begin_assertions();
ptr_vector<expr>::const_iterator end = ctx.end_assertions();
for (; it != end; ++it) {
dlctx.assert_expr(*it);
}
}
void print_answer(cmd_context& ctx) {
if (m_params.get_bool(":print-answer", false)) {
datalog::context& dlctx = m_dl_ctx->get_dl_context();
ast_manager& m = ctx.m();
expr_ref query_result(dlctx.get_answer_as_formula(), m);
sbuffer<symbol> var_names;
unsigned num_decls = 0;
if (is_quantifier(m_target)) {
num_decls = to_quantifier(m_target)->get_num_decls();
}
ctx.display(ctx.regular_stream(), query_result, 0, num_decls, "X", var_names);
ctx.regular_stream() << std::endl;
}
}
void print_statistics(cmd_context& ctx) {
if (m_params.get_bool(":print-statistics", false)) {
statistics st;
datalog::context& dlctx = m_dl_ctx->get_dl_context();
unsigned long long max_mem = memory::get_max_used_memory();
unsigned long long mem = memory::get_allocation_size();
dlctx.collect_statistics(st);
st.update("time", ctx.get_seconds());
st.update("memory", static_cast<double>(mem)/static_cast<double>(1024*1024));
st.update("max-memory", static_cast<double>(max_mem)/static_cast<double>(1024*1024));
st.display_smt2(ctx.regular_stream());
}
}
void print_certificate(cmd_context& ctx) {
if (m_params.get_bool(":print-certificate", false)) {
datalog::context& dlctx = m_dl_ctx->get_dl_context();
if (!dlctx.display_certificate(ctx.regular_stream())) {
throw cmd_exception("certificates are not supported for selected DL_ENGINE");
}
ctx.regular_stream() << "\n";
}
}
};
class dl_declare_rel_cmd : public cmd {
ref<dl_context> m_dl_ctx;
unsigned m_arg_idx;
mutable unsigned m_query_arg_idx;
symbol m_rel_name;
scoped_ptr<sort_ref_vector> m_domain;
svector<symbol> m_kinds;
void ensure_domain(cmd_context& ctx) {
if (!m_domain) m_domain = alloc(sort_ref_vector, ctx.m());
}
public:
dl_declare_rel_cmd(dl_context * dl_ctx):
cmd("declare-rel"),
m_dl_ctx(dl_ctx),
m_domain(0) {}
virtual char const * get_usage() const { return "<symbol> (<arg1 sort> ...) <representation>*"; }
virtual char const * get_descr(cmd_context & ctx) const { return "declare new relation"; }
virtual unsigned get_arity() const { return VAR_ARITY; }
virtual void prepare(cmd_context & ctx) {
m_arg_idx = 0;
m_query_arg_idx = 0;
m_domain = 0;
m_kinds.reset();
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
switch(m_query_arg_idx++) {
case 0: return CPK_SYMBOL; // relation name
case 1: return CPK_SORT_LIST; // arguments
default: return CPK_SYMBOL; // optional representation specification
}
}
virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) {
ensure_domain(ctx);
m_domain->append(num, slist);
m_arg_idx++;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
if(m_arg_idx==0) {
m_rel_name = s;
}
else {
SASSERT(m_arg_idx>1);
m_kinds.push_back(s);
}
m_arg_idx++;
}
virtual void execute(cmd_context & ctx) {
if(m_arg_idx<2) {
throw cmd_exception("at least 2 arguments expected");
}
ensure_domain(ctx);
ast_manager& m = ctx.m();
func_decl_ref pred(
m.mk_func_decl(m_rel_name, m_domain->size(), m_domain->c_ptr(), m.mk_bool_sort()), m);
ctx.insert(pred);
datalog::context& dctx = m_dl_ctx->get_dl_context();
dctx.register_predicate(pred, false);
if(!m_kinds.empty()) {
dctx.set_predicate_representation(pred, m_kinds.size(), m_kinds.c_ptr());
}
m_domain = 0;
}
};
class dl_declare_var_cmd : public cmd {
unsigned m_arg_idx;
symbol m_var_name;
sort* m_var_sort;
ref<dl_context> m_dl_ctx;
public:
dl_declare_var_cmd(dl_context* dl_ctx):
cmd("declare-var"),
m_arg_idx(0),
m_dl_ctx(dl_ctx)
{}
virtual char const * get_usage() const { return "<symbol> <sort>"; }
virtual char const * get_descr(cmd_context & ctx) const { return "declare constant as variable"; }
virtual unsigned get_arity() const { return 2; }
virtual void prepare(cmd_context & ctx) {
m_arg_idx = 0;
}
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const {
SASSERT(m_arg_idx <= 1);
if (m_arg_idx == 0) {
return CPK_SYMBOL;
}
return CPK_SORT;
}
virtual void set_next_arg(cmd_context & ctx, sort* s) {
m_var_sort = s;
++m_arg_idx;
}
virtual void set_next_arg(cmd_context & ctx, symbol const & s) {
m_var_name = s;
++m_arg_idx;
}
virtual void execute(cmd_context & ctx) {
ast_manager& m = ctx.m();
func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast<sort*const*>(0), m_var_sort), m);
ctx.insert(var);
m_dl_ctx->get_dl_context().register_variable(var);
}
};
class dl_push_cmd : public cmd {
ref<dl_context> m_ctx;
public:
dl_push_cmd(dl_context* ctx):
cmd("fixedpoint-push"),
m_ctx(ctx)
{}
virtual char const * get_usage() const { return ""; }
virtual char const * get_descr(cmd_context & ctx) const { return "push context on the fixedpoint engine"; }
virtual void execute(cmd_context& ctx) {
m_ctx->get_dl_context().push();
}
};
class dl_pop_cmd : public cmd {
ref<dl_context> m_ctx;
public:
dl_pop_cmd(dl_context* ctx):
cmd("fixedpoint-pop"),
m_ctx(ctx)
{}
virtual char const * get_usage() const { return ""; }
virtual char const * get_descr(cmd_context & ctx) const { return "pop context on the fixedpoint engine"; }
virtual void execute(cmd_context& ctx) {
m_ctx->get_dl_context().pop();
}
};
void install_dl_cmds(cmd_context & ctx) {
dl_context * dl_ctx = alloc(dl_context, ctx);
ctx.insert(alloc(dl_rule_cmd, dl_ctx));
ctx.insert(alloc(dl_query_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx));
ctx.insert(alloc(dl_declare_var_cmd, dl_ctx));
PRIVATE_PARAMS(ctx.insert(alloc(dl_push_cmd, dl_ctx));); // not exposed to keep command-extensions simple.
PRIVATE_PARAMS(ctx.insert(alloc(dl_pop_cmd, dl_ctx)););
}

View file

@ -1,47 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dl_cmds.h
Abstract:
Datalog commands for SMT2 front-end.
Author:
Leonardo (leonardo) 2011-03-28
Notes:
--*/
#ifndef _DL_CMDS_H_
#define _DL_CMDS_H_
class cmd;
class cmd_context;
void install_dl_cmds(cmd_context & ctx);
namespace datalog {
class context;
/**
Create a command for declaring relations which is connected to
a particular datalog context.
Caller must ensure the returned object is deallocated (e.g. by passing it to a cmd_context).
*/
cmd * mk_declare_rel_cmd(context& dctx);
/**
Declare a constant as a universal/existential variable.
It is implicitly existentially or universally quantified
by the rules.
*/
cmd * mk_declare_var_cmd(context& dctx);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,272 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_compiler.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#ifndef _DL_COMPILER_H_
#define _DL_COMPILER_H_
#include<iostream>
#include<list>
#include<utility>
#include "ast.h"
#include "hashtable.h"
#include "map.h"
#include "obj_pair_hashtable.h"
#include "ref_vector.h"
#include "vector.h"
#include "dl_base.h"
#include "dl_context.h"
#include "dl_instruction.h"
namespace datalog {
class compiler {
typedef instruction::reg_idx reg_idx;
typedef hashtable<unsigned, u_hash, u_eq> int_set;
typedef u_map<unsigned> int2int;
typedef u_map<unsigned_vector> int2ints;
typedef map<func_decl *, reg_idx, ptr_hash<func_decl>,ptr_eq<func_decl> > pred2idx;
typedef unsigned_vector var_vector;
typedef ptr_vector<func_decl> func_decl_vector;
enum assembling_column_kind {
ACK_BOUND_VAR,
ACK_UNBOUND_VAR,
ACK_CONSTANT
};
/**
\brief instruction for assembling head relation from a joint relation built
from rule evaluation.
ACK_BOUND_VAR(source_column) - encodes that the column contains a variable
bound in the body.
ACK_UNBOUND_VAR(var_index) - encodes that the column contains a variable that
is unbound (by the corresponding rule body),
var_index is the de-Brujin index (var->get_idx())
of the variable associated with the column.
ACK_CONSTANT(constant) - encodes that the column contains the constant.
Examples:
P(x) :- Q(x,y), Q(y,z)
The variables in the body relation are [x, y, y, z] indexed as 0, 1, 2, 3.
The variable x gets the instruction ACK_BOUND_VAR(0)
P(u,x) :- Q(x,y), Q(y,z)
The variable u gets the instruction ACK_UNBOUND_VAR(#0)
P(1, x) :- Q(x,y), Q(y,z)
The instruction for column 0 is ACK_CONSTANT(1)
*/
struct assembling_column_info {
relation_sort domain; // domain of the column
assembling_column_kind kind; // "instruction" tag
unsigned source_column; // for ACK_BOUND_VAR
unsigned var_index; // for ACK_UNBOUND_VAR
relation_element constant; // for ACK_CONSTANT
};
class instruction_observer : public instruction_block::instruction_observer {
compiler & m_parent;
rule * m_current;
public:
instruction_observer(compiler & parent) : m_parent(parent), m_current(0) {}
void start_rule(rule * r) { SASSERT(!m_current); m_current=r; }
void finish_rule() { m_current = 0; }
virtual void notify(instruction * i) {
if(m_current) {
i->set_accounting_parent_object(m_parent.m_context, m_current);
}
}
};
context & m_context;
rule_set const & m_rule_set;
/**
Invariant: the \c m_top_level_code never contains the loop that is being constructed,
so instruction that need to be executed before the loop can be pushed into it.
*/
instruction_block & m_top_level_code;
pred2idx m_pred_regs;
reg_idx m_new_reg;
vector<relation_signature> m_reg_signatures;
obj_pair_map<sort, app, reg_idx> m_constant_registers;
instruction_observer m_instruction_observer;
/**
If true, the union operation on the underlying structure only provides the information
whether the updated relation has changed or not. In this case we do not get anything
from using delta relations at position of input relations in the saturation loop, so we
would not do it.
*/
bool all_or_nothing_deltas() const { return m_context.all_or_nothing_deltas(); }
/**
If true, we compile the saturation loops in a way that allows us to use widening.
*/
bool compile_with_widening() const { return m_context.compile_with_widening(); }
reg_idx get_fresh_register(const relation_signature & sig);
reg_idx get_single_column_register(const relation_sort & s);
/**
\brief Allocate registers for predicates in \c pred and add them into the \c regs map.
\c regs must not already contain any predicate from \c preds.
*/
void get_fresh_registers(const func_decl_set & preds, pred2idx & regs);
void make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result,
instruction_block & acc);
void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars,
const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc);
void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col,
reg_idx & result, instruction_block & acc);
/**
\brief Create add an union or widen operation and put it into \c acc.
*/
void make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widening, instruction_block & acc);
void make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols,
reg_idx & result, instruction_block & acc);
void make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle,
reg_idx & result, instruction_block & acc);
void make_clone(reg_idx src, reg_idx & result, instruction_block & acc);
/**
\brief Into \c acc add code that will assemble columns of a relation according to description
in \c acis0. The source for bound variables is the table in register \c src.
If \c src is \c execution_context::void_register, it is assumed to be a full relation
with empty signature.
*/
void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector<assembling_column_info> & acis0,
reg_idx & result, instruction_block & acc);
void make_dealloc_non_void(reg_idx r, instruction_block & acc);
void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val,
reg_idx & result, instruction_block & acc);
void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result,
instruction_block & acc);
void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result,
instruction_block & acc);
void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, ptr_vector<expr>& single_res_expr,
instruction_block& acc);
void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc);
void ensure_predicate_loaded(func_decl * pred, instruction_block & acc);
/**
\brief For rule \c r with two positive uninterpreted predicates put into \c res indexes of
local variables in a table that results from join of the two positive predicates.
Used to get input for the "project" part of join-project.
*/
void get_local_indexes_for_projection(rule * r, unsigned_vector & res);
void get_local_indexes_for_projection(app * t, var_counter & globals, unsigned ofs,
unsigned_vector & res);
/**
\brief Into \c acc add instructions that will add new facts following from the rule into
\c head_reg, and add the facts that were not in \c head_reg before into \c delta_reg.
*/
void compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs,
reg_idx delta_reg, bool use_widening, instruction_block & acc);
void compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta,
bool use_widening, instruction_block & acc);
/**
\brief Generate code to evaluate rules corresponding to predicates in \c head_preds.
The rules are evaluated in the order their heads appear in the \c head_preds vector.
*/
void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds,
const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc);
void make_inloop_delta_transition(const pred2idx & global_head_deltas,
const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc);
void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds,
const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas,
const pred2idx & local_deltas, instruction_block & acc);
void compile_dependent_rules(const func_decl_set & head_preds,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
void detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds,
func_decl_set & global_deltas);
/**
Return true if there is no dependency inside the \c rules stratum.
The head predicates in stratum must be strongly connected by dependency.
*/
bool is_nonrecursive_stratum(const func_decl_set & preds) const;
/**
input_deltas==0 --> we use the actual content of relations instead of deltas
*/
void compile_nonrecursive_stratum(const func_decl_set & preds,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
void compile_strats(const rule_stratifier & stratifier,
const pred2idx * input_deltas, const pred2idx & output_deltas,
bool add_saturation_marks, instruction_block & acc);
bool all_saturated(const func_decl_set & preds) const;
void reset();
explicit compiler(context & ctx, rule_set const & rules, instruction_block & top_level_code)
: m_context(ctx),
m_rule_set(rules),
m_top_level_code(top_level_code),
m_instruction_observer(*this) {}
/**
\brief Compile \c rules in to pseudocode.
Instructions to load data and perform computations put into \c execution_code
*/
void do_compilation(instruction_block & execution_code,
instruction_block & termination_code);
public:
static void compile(context & ctx, rule_set const & rules, instruction_block & execution_code,
instruction_block & termination_code) {
compiler(ctx, rules, execution_code)
.do_compilation(execution_code, termination_code);
}
};
};
#endif /* _DL_COMPILER_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,468 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_context.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-18.
Revision History:
--*/
#ifndef _DL_CONTEXT_H_
#define _DL_CONTEXT_H_
#ifdef _CYGWIN
#undef min
#undef max
#endif
#include"arith_decl_plugin.h"
#include"front_end_params.h"
#include"map.h"
#include"th_rewriter.h"
#include"str_hashtable.h"
#include"var_subst.h"
#include"dl_base.h"
#include"dl_costs.h"
#include"dl_decl_plugin.h"
#include"dl_relation_manager.h"
#include"dl_rule_set.h"
#include"pdr_dl_interface.h"
#include"dl_bmc_engine.h"
#include"lbool.h"
#include"statistics.h"
#include"params.h"
#include"trail.h"
#include"dl_external_relation.h"
#include"model_converter.h"
#include"proof_converter.h"
#include"model2expr.h"
namespace datalog {
class rule_transformer;
enum execution_result {
OK,
TIMEOUT,
MEMOUT,
INPUT_ERROR
};
class context {
public:
typedef unsigned finite_element;
enum sort_kind {
SK_UINT64,
SK_SYMBOL
};
private:
class sort_domain;
class symbol_sort_domain;
class uint64_sort_domain;
class restore_rules;
class contains_pred;
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
typedef obj_map<const sort, sort_domain*> sort_domain_map;
typedef vector<std::pair<func_decl*,relation_fact> > fact_vector;
ast_manager & m;
front_end_params& m_fparams;
params_ref m_params;
dl_decl_util m_decl_util;
th_rewriter m_rewriter;
var_subst m_var_subst;
relation_manager m_rmanager;
rule_manager m_rule_manager;
trail_stack<context> m_trail;
ast_ref_vector m_pinned;
app_ref_vector m_vars;
sort_domain_map m_sorts;
func_decl_set m_preds;
sym2decl m_preds_by_name;
pred2syms m_argument_var_names;
decl_set m_output_preds;
rule_set m_rule_set;
expr_ref_vector m_background;
scoped_ptr<pdr::dl_interface> m_pdr;
scoped_ptr<bmc> m_bmc;
bool m_closed;
bool m_saturation_was_run;
execution_result m_last_status;
relation_base * m_last_result_relation;
expr_ref m_last_answer;
DL_ENGINE m_engine;
volatile bool m_cancel;
fact_vector m_table_facts;
bool is_fact(app * head) const;
bool has_sort_domain(relation_sort s) const;
sort_domain & get_sort_domain(relation_sort s);
const sort_domain & get_sort_domain(relation_sort s) const;
relation_plugin & get_ordinary_relation_plugin(symbol relation_name);
class engine_type_proc;
public:
context(ast_manager & m, front_end_params& params, params_ref const& p = params_ref());
~context();
void reset();
void push();
void pop();
relation_base & get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); }
relation_base * try_get_relation(func_decl * pred) const { return get_rmanager().try_get_relation(pred); }
bool saturation_was_run() const { return m_saturation_was_run; }
void notify_saturation_was_run() { m_saturation_was_run = true; }
/**
\brief Store the relation \c rel under the predicate \c pred. The \c context object
takes over the ownership of the relation object.
*/
void store_relation(func_decl * pred, relation_base * rel) {
get_rmanager().store_relation(pred, rel);
}
void configure_engine();
ast_manager & get_manager() const { return m; }
relation_manager & get_rmanager() { return m_rmanager; }
const relation_manager & get_rmanager() const { return m_rmanager; }
rule_manager & get_rule_manager() { return m_rule_manager; }
front_end_params & get_fparams() const { return m_fparams; }
params_ref const& get_params() const { return m_params; }
DL_ENGINE get_engine() { configure_engine(); return m_engine; }
th_rewriter& get_rewriter() { return m_rewriter; }
var_subst & get_var_subst() { return m_var_subst; }
dl_decl_util & get_decl_util() { return m_decl_util; }
bool output_profile() const { return m_params.get_bool(":output-profile", false); }
bool fix_unbound_vars() const { return m_params.get_bool(":fix-unbound-vars", false); }
symbol default_table() const { return m_params.get_sym(":default-table", symbol("sparse")); }
symbol default_relation() const { return m_params.get_sym(":default-relation", external_relation_plugin::get_name()); }
symbol default_table_checker() const { return m_params.get_sym(":default-table-checker", symbol("sparse")); }
bool default_table_checked() const { return m_params.get_bool(":default-table-checked", false); }
bool dbg_fpr_nonempty_relation_signature() const { return m_params.get_bool(":dbg-fpr-nonempty-relation-signatures", false); }
unsigned dl_profile_milliseconds_threshold() const { return m_params.get_uint(":profile-milliseconds-threshold", 0); }
bool all_or_nothing_deltas() const { return m_params.get_bool(":all-or-nothing-deltas", false); }
bool compile_with_widening() const { return m_params.get_bool(":compile-with-widening", false); }
bool unbound_compressor() const { return m_params.get_bool(":unbound-compressor", true); }
bool similarity_compressor() const { return m_params.get_bool(":similarity-compressor", true); }
unsigned similarity_compressor_threshold() const { return m_params.get_uint(":similarity-compressor-threshold", 11); }
unsigned soft_timeout() const { return m_fparams.m_soft_timeout; }
unsigned initial_restart_timeout() const { return m_params.get_uint(":initial-restart-timeout", 0); }
bool generate_explanations() const { return m_params.get_bool(":generate-explanations", false); }
bool explanations_on_relation_level() const { return m_params.get_bool(":explanations-on-relation-level", false); }
bool magic_sets_for_queries() const { return m_params.get_bool(":magic-sets-for-queries", false); }
bool eager_emptiness_checking() const { return m_params.get_bool(":eager-emptiness-checking", true); }
void register_finite_sort(sort * s, sort_kind k);
/**
Register uninterpreted constant to be used as an implicitly quantified variable.
The variable gets quantified in the formula passed to rule::mk_rule_from_horn.
*/
void register_variable(func_decl* var);
app_ref_vector const& get_variables() const { return m_vars; }
/**
Register datalog relation.
If names is true, we associate the predicate with its name, so that it can be
retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced
e.g. by rule transformations do not need to be named.
*/
void register_predicate(func_decl * pred, bool named = true);
bool is_predicate(func_decl * pred) const;
/**
\brief If a predicate name has a \c func_decl object assigned, return pointer to it;
otherwise return 0.
Not all \c func_decl object used as relation identifiers need to be assigned to their
names. Generally, the names coming from the parses are registered here.
*/
func_decl * try_get_predicate_decl(symbol pred_name) const;
/**
\brief Create a fresh head predicate declaration.
*/
func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
unsigned arity, sort * const * domain, func_decl* orig_pred=0);
/**
\brief Return number of a symbol in a DK_SYMBOL kind sort (\see register_sort() )
*/
finite_element get_constant_number(relation_sort sort, symbol s);
/**
\brief Return number of a symbol in a DK_UINT64 kind sort (\see register_sort() )
*/
finite_element get_constant_number(relation_sort srt, uint64 el);
/**
\brief Output name of constant with number \c num in sort \c sort.
*/
void print_constant_name(relation_sort sort, uint64 num, std::ostream & out);
bool try_get_sort_constant_count(relation_sort srt, uint64 & constant_count);
uint64 get_sort_size_estimate(relation_sort srt);
/**
\brief Assign names of variables used in the declaration of a predicate.
These names are used when printing out the relations to make the output conform
to the one of bddbddb.
*/
void set_argument_names(const func_decl * pred, svector<symbol> var_names);
symbol get_argument_name(const func_decl * pred, unsigned arg_index);
void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
symbol * const relation_names);
void set_output_predicate(func_decl * pred);
bool is_output_predicate(func_decl * pred) { return m_output_preds.contains(pred); }
const decl_set & get_output_predicates() const { return m_output_preds; }
rule_set const & get_rules() { return m_rule_set; }
void add_fact(app * head);
void add_fact(func_decl * pred, const relation_fact & fact);
void add_rule(rule_ref& r);
void add_rules(rule_ref_vector& rs);
void assert_expr(expr* e);
expr_ref get_background_assertion();
/**
Method exposed from API for adding rules.
*/
void add_rule(expr* rl, symbol const& name);
/**
Update a named rule.
*/
void update_rule(expr* rl, symbol const& name);
/**
Retrieve the maximal number of relevant unfoldings of 'pred'
with respect to the current state.
*/
unsigned get_num_levels(func_decl* pred);
/**
Retrieve the current cover of 'pred' up to 'level' unfoldings.
Return just the delta that is known at 'level'. To
obtain the full set of properties of 'pred' one should query
at 'level+1', 'level+2' etc, and include level=-1.
*/
expr_ref get_cover_delta(int level, func_decl* pred);
/**
Add a property of predicate 'pred' at 'level'.
It gets pushed forward when possible.
*/
void add_cover(int level, func_decl* pred, expr* property);
/**
\brief Check rule subsumption.
*/
bool check_subsumes(rule const& stronger_rule, rule const& weaker_rule);
/**
\brief Check if rule is well-formed according to engine.
*/
void check_rule(rule_ref& r);
/**
\brief Return true if facts to \c pred can be added using the \c add_table_fact() function.
This function should return true if \c pred is represented by a table_relation
and there is no transformation of relation values before they are put into the
table.
*/
bool can_add_table_fact(func_decl * pred);
void add_table_fact(func_decl * pred, const table_fact & fact);
void add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]);
/**
\brief To be called after all rules are added.
*/
void close();
void ensure_closed();
/**
\brief Undo the effect of the \c close operation.
*/
void reopen();
void ensure_opened();
void transform_rules(model_converter_ref& mc, proof_converter_ref& pc);
void transform_rules(rule_transformer& trans, model_converter_ref& mc, proof_converter_ref& pc);
void replace_rules(rule_set & rs);
void apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc);
void collect_params(param_descrs& r);
void updt_params(params_ref const& p);
void collect_predicates(decl_set & res);
/**
\brief Restrict the set of used predicates to \c res.
The function deallocates unsused relations, it does not deal with rules.
*/
void restrict_predicates(const decl_set & res);
void display_rules(std::ostream & out) const {
m_rule_set.display(out);
}
void display_facts(std::ostream & out) const {
m_rmanager.display(out);
}
void display(std::ostream & out) const {
display_rules(out);
display_facts(out);
}
void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out);
// -----------------------------------
//
// basic usage methods
//
// -----------------------------------
void cancel();
void cleanup();
/**
\brief check if query 'q' is satisfied under asserted rules and background.
If successful, return OK and into \c result assign a relation with all
tuples matching the query. Otherwise return reason for failure and do not modify
\c result.
The numbers of variables in the query body must form a contiguous sequence
starting from zero.
The caller becomes an owner of the relation object returned in \c result. The
relation object, however, should not outlive the datalog context since it is
linked to a relation plugin in the context.
*/
lbool query(expr* q);
/**
Query multiple output relations.
*/
lbool dl_query(unsigned num_rels, func_decl * const* rels);
/**
Reset tables that are under negation.
*/
void reset_negated_tables();
/**
Just reset all tables.
*/
void reset_tables();
/**
\brief retrieve last proof status.
*/
execution_result get_status();
/**
\brief retrieve formula corresponding to query that returns l_true.
The formula describes one or more instances of the existential variables
in the query that are derivable.
*/
expr* get_answer_as_formula();
void collect_statistics(statistics& st);
/**
\brief Display a certificate for reachability and/or unreachability.
*/
bool display_certificate(std::ostream& out);
/**
\brief query result if it contains fact.
*/
bool result_contains_fact(relation_fact const& f);
/**
\brief display facts generated for query.
*/
void display_output_facts(std::ostream & out) const {
m_rmanager.display_output_tables(out);
}
/**
\brief expose datalog saturation for test.
*/
lbool dl_saturate();
private:
void ensure_pdr();
void ensure_bmc();
void new_query();
lbool dl_query(expr* query);
lbool pdr_query(expr* query);
lbool bmc_query(expr* query);
void check_quantifier_free(rule_ref& r);
void check_uninterpreted_free(rule_ref& r);
void check_existential_tail(rule_ref& r);
void check_positive_predicates(rule_ref& r);
// auxilary functions for SMT2 pretty-printer.
void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out);
//undefined and private copy constructor and operator=
context(const context&);
context& operator=(const context&);
};
};
#endif /* _DL_CONTEXT_H_ */

View file

@ -1,160 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_costs.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#include "debug.h"
#include "stopwatch.h"
#include "dl_context.h"
#include "dl_rule.h"
#include "dl_costs.h"
namespace datalog {
// -----------------------------------
//
// costs
//
// -----------------------------------
costs::costs() : milliseconds(0), instructions(0) {}
bool costs::empty() const {
return !milliseconds && !instructions;
}
void costs::reset() {
milliseconds = 0;
instructions = 0;
}
costs costs::operator-(const costs & o) const {
costs res(*this);
SASSERT(milliseconds>o.milliseconds);
res.milliseconds-=o.milliseconds;
SASSERT(instructions>o.instructions);
res.instructions-=o.instructions;
return res;
}
void costs::operator+=(const costs & o) {
milliseconds+=o.milliseconds;
instructions+=o.instructions;
}
bool costs::passes_thresholds(context & ctx) const {
return milliseconds >= ctx.dl_profile_milliseconds_threshold();
}
void costs::output(std::ostream & out) const {
out << "instr: " << instructions << " time: " << milliseconds << "ms";
}
// -----------------------------------
//
// accounted_object
//
// -----------------------------------
accounted_object::~accounted_object() {
if(m_parent_object) {
SASSERT(m_context);
m_context->get_rule_manager().dec_ref(m_parent_object);
}
}
void accounted_object::set_accounting_parent_object(context & ctx, rule * parent) {
if(m_parent_object) {
SASSERT(m_context);
SASSERT(m_context==&ctx);
m_context->get_rule_manager().dec_ref(m_parent_object);
}
m_context = &ctx;
m_parent_object = parent;
m_context->get_rule_manager().inc_ref(m_parent_object);
}
void accounted_object::process_costs() {
costs delta = get_current_costs();
if(delta.empty()) {
return;
}
get_current_costs().reset();
accounted_object * obj = this;
do {
obj->m_processed_cost+=delta;
obj=obj->m_parent_object;
} while(obj);
}
void accounted_object::get_total_cost(costs & result) const {
result.reset();
result+=m_current_cost;
result+=m_processed_cost;
}
bool accounted_object::passes_output_thresholds(context & ctx) const {
costs c;
get_total_cost(c);
return c.passes_thresholds(ctx);
}
void accounted_object::output_profile(context & ctx, std::ostream & out) const {
costs c;
get_total_cost(c);
c.output(out);
}
// -----------------------------------
//
// cost_recorder
//
// -----------------------------------
cost_recorder::cost_recorder() : m_obj(0) {
m_stopwatch = alloc(stopwatch);
m_stopwatch->start();
}
cost_recorder::~cost_recorder() {
if(m_obj) {
finish();
}
dealloc(m_stopwatch);
}
void cost_recorder::start(accounted_object * obj) {
uint64 curr_time = static_cast<uint64>(m_stopwatch->get_current_seconds()*1000);
if(m_obj) {
costs::time_type time_delta = static_cast<costs::time_type>(curr_time-m_last_time);
costs & c = m_obj->get_current_costs();
c.instructions++;
c.milliseconds+=time_delta;
m_obj->m_being_recorded = false;
}
m_running = obj!=0;
m_obj = obj;
m_last_time = curr_time;
if(obj) {
m_obj->m_being_recorded = true;
}
}
};

View file

@ -1,115 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_costs.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-20.
Revision History:
--*/
#ifndef _DL_COSTS_H_
#define _DL_COSTS_H_
#include<iosfwd>
#include "ast.h"
class stopwatch;
namespace datalog {
class context;
class rule;
class cost_recorder;
struct costs {
typedef unsigned time_type;
time_type milliseconds;
unsigned instructions;
costs();
bool empty() const;
void reset();
costs operator-(const costs & o) const;
void operator+=(const costs & o);
bool passes_thresholds(context & ctx) const;
void output(std::ostream & out) const;
};
class accounted_object {
friend class cost_recorder;
context * m_context;
rule * m_parent_object;
costs m_current_cost;
costs m_processed_cost;
bool m_being_recorded;
protected:
accounted_object() : m_context(0), m_parent_object(0), m_being_recorded(false) {}
~accounted_object();
public:
void set_accounting_parent_object(context & ctx, rule * parent);
rule * get_parent_object() const { return m_parent_object; }
costs & get_current_costs() { return m_current_cost; }
const costs & get_current_costs() const { return m_current_cost; }
const costs & get_processed_costs() const { return m_processed_cost; }
void get_total_cost(costs & result) const;
bool being_recorded() const { return m_being_recorded; }
void process_costs();
bool passes_output_thresholds(context & ctx) const;
void output_profile(context & ctx, std::ostream & out) const;
private:
//private and undefined copy constructor and operator= to avoid the default ones
accounted_object(const accounted_object &);
accounted_object& operator=(const accounted_object &);
};
class cost_recorder {
accounted_object * m_obj;
//it's a pointer to avoid everything depending on the stopwatch.h
//(and transitively then on windows.h) header file
stopwatch * m_stopwatch;
bool m_running;
uint64 m_last_time;
public:
cost_recorder();
~cost_recorder();
/**
\brief Start recording costs for the next object.
If the recording of the previous object did not finish, it will be finished here.
Also, it will be done more efficiently than if the \c finish() function was called
before separately.
*/
void start(accounted_object *);
void finish() { start(0); }
};
};
#endif /* _DL_COSTS_H_ */

View file

@ -1,456 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_external_relation.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-05-10
Revision History:
--*/
#include "debug.h"
#include "ast_pp.h"
#include "dl_context.h"
#include "dl_external_relation.h"
#include "dl_decl_plugin.h"
namespace datalog {
external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r)
: relation_base(p, s),
m_rel(r, p.get_ast_manager()),
m_select_fn(p.get_ast_manager()),
m_store_fn(p.get_ast_manager()),
m_is_empty_fn(p.get_ast_manager())
{
}
external_relation::~external_relation() {
}
void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const {
ast_manager& m = m_rel.get_manager();
family_id fid = get_plugin().get_family_id();
ptr_vector<expr> args;
args.push_back(m_rel);
for (unsigned i = 0; i < f.size(); ++i) {
args.push_back(f[i]);
}
if (!fn.get()) {
fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr());
}
if (destructive) {
get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr());
res = m_rel;
}
else {
get_plugin().reduce(fn, args.size(), args.c_ptr(), res);
}
}
bool external_relation::empty() const {
ast_manager& m = m_rel.get_manager();
expr* r = m_rel.get();
expr_ref res(m);
if (!m_is_empty_fn.get()) {
family_id fid = get_plugin().get_family_id();
const_cast<func_decl_ref&>(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r);
}
get_plugin().reduce(m_is_empty_fn, 1, &r, res);
return m.is_true(res);
}
void external_relation::add_fact(const relation_fact & f) {
mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel);
}
bool external_relation::contains_fact(const relation_fact & f) const {
ast_manager& m = get_plugin().get_ast_manager();
expr_ref res(m);
mk_accessor(OP_RA_SELECT, const_cast<func_decl_ref&>(m_select_fn), f, false, res);
return !m.is_false(res);
}
external_relation * external_relation::clone() const {
ast_manager& m = m_rel.get_manager();
family_id fid = get_plugin().get_family_id();
expr* rel = m_rel.get();
expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m);
expr* rel_out = res.get();
func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m);
get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out);
return alloc(external_relation, get_plugin(), get_signature(), res);
}
external_relation * external_relation::complement(func_decl* p) const {
ast_manager& m = m_rel.get_manager();
family_id fid = get_plugin().get_family_id();
expr_ref res(m);
expr* rel = m_rel;
func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m);
get_plugin().reduce(fn, 1, &rel, res);
return alloc(external_relation, get_plugin(), get_signature(), res);
}
void external_relation::display(std::ostream & out) const {
out << mk_pp(m_rel, m_rel.get_manager()) << "\n";
}
void external_relation::display_tuples(func_decl & pred, std::ostream & out) const {
display(out);
}
external_relation_plugin & external_relation::get_plugin() const {
return static_cast<external_relation_plugin &>(relation_base::get_plugin());
}
// -----------------------------------
//
// external_relation_plugin
//
// -----------------------------------
external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m)
: relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {}
external_relation const & external_relation_plugin::get(relation_base const& r) {
return dynamic_cast<external_relation const&>(r);
}
external_relation & external_relation_plugin::get(relation_base & r) {
return dynamic_cast<external_relation&>(r);
}
relation_base * external_relation_plugin::mk_empty(const relation_signature & s) {
ast_manager& m = get_ast_manager();
sort* r_sort = get_relation_sort(s);
parameter param(r_sort);
family_id fid = get_family_id();
expr_ref e(m.mk_fresh_const("T", r_sort), m);
expr* args[1] = { e.get() };
func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, &param, 0, (sort*const*)0), m);
reduce_assign(empty_decl, 0, 0, 1, args);
return alloc(external_relation, *this, s, e);
}
sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) {
vector<parameter> sorts;
ast_manager& m = get_ast_manager();
family_id fid = get_family_id();
for (unsigned i = 0; i < sig.size(); ++i) {
sorts.push_back(parameter(sig[i]));
}
return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr());
}
sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) {
SASSERT(s->get_num_parameters() > col);
SASSERT(s->get_parameter(col).is_ast());
SASSERT(is_sort(s->get_parameter(col).get_ast()));
return to_sort(s->get_parameter(col).get_ast());
}
family_id external_relation_plugin::get_family_id() {
return m_ext.get_family_id();
}
void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) {
ast_manager& m = get_ast_manager();
family_id fid = get_family_id();
parameter param(condition);
f = m.mk_func_decl(fid, OP_RA_FILTER, 1, &param, 1, &s);
}
class external_relation_plugin::join_fn : public convenient_relation_join_fn {
external_relation_plugin& m_plugin;
func_decl_ref m_join_fn;
expr* m_args[2];
public:
join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2)
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2),
m_plugin(p),
m_join_fn(p.get_ast_manager()) {
ast_manager& m = p.get_ast_manager();
family_id fid = p.get_family_id();
vector<parameter> params;
for (unsigned i = 0; i < col_cnt; ++i) {
params.push_back(parameter(cols1[i]));
params.push_back(parameter(cols2[i]));
}
sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) };
m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain);
}
virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) {
expr_ref res(m_plugin.get_ast_manager());
m_args[0] = get(r1).get_relation();
m_args[1] = get(r2).get_relation();
m_plugin.reduce(m_join_fn, 2, m_args, res);
return alloc(external_relation, m_plugin, get_result_signature(), res);
}
};
relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if (!check_kind(r1) || !check_kind(r2)) {
return 0;
}
return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2);
}
class external_relation_plugin::project_fn : public convenient_relation_project_fn {
external_relation_plugin& m_plugin;
func_decl_ref m_project_fn;
public:
project_fn(external_relation_plugin& p, sort* relation_sort,
const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols),
m_plugin(p),
m_project_fn(p.get_ast_manager()) {
vector<parameter> params;
ast_manager& m = p.get_ast_manager();
family_id fid = p.get_family_id();
for (unsigned i = 0; i < removed_col_cnt; ++i) {
params.push_back(parameter(removed_cols[i]));
}
m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort);
}
virtual relation_base * operator()(const relation_base & r) {
expr_ref res(m_plugin.get_ast_manager());
expr* rel = get(r).get_relation();
m_plugin.reduce(m_project_fn, 1, &rel, res);
return alloc(external_relation, m_plugin, get_result_signature(), to_app(res));
}
};
relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r,
unsigned col_cnt, const unsigned * removed_cols) {
return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols);
}
class external_relation_plugin::rename_fn : public convenient_relation_rename_fn {
external_relation_plugin& m_plugin;
func_decl_ref m_rename_fn;
expr* m_args[2];
public:
rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle),
m_plugin(p),
m_rename_fn(p.get_ast_manager()) {
ast_manager& m = p.get_ast_manager();
family_id fid = p.get_family_id();
vector<parameter> params;
for (unsigned i = 0; i < cycle_len; ++i) {
SASSERT(cycle[i] < orig_sig.size());
params.push_back(parameter(cycle[i]));
}
m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort);
}
virtual relation_base * operator()(const relation_base & r) {
expr* rel = get(r).get_relation();
expr_ref res(m_plugin.get_ast_manager());
m_args[0] = rel;
m_plugin.reduce(m_rename_fn, 1, &rel, res);
return alloc(external_relation, m_plugin, get_result_signature(), res);
}
};
relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r,
unsigned cycle_len, const unsigned * permutation_cycle) {
if(!check_kind(r)) {
return 0;
}
return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle);
}
class external_relation_plugin::union_fn : public relation_union_fn {
external_relation_plugin& m_plugin;
func_decl_ref m_union_fn;
expr* m_args[2];
expr* m_outs[2];
public:
union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort):
m_plugin(p),
m_union_fn(p.get_ast_manager()) {
ast_manager& m = p.get_ast_manager();
sort* domain[2] = { relation_sort, relation_sort };
m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain);
}
virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) {
ast_manager& m = m_plugin.get_ast_manager();
expr_ref_vector res(m);
m_args[0] = get(r).get_relation();
m_args[1] = get(src).get_relation();
m_outs[0] = m_args[0];
unsigned num_out = 1;
if (delta) {
m_outs[1] = get(*delta).get_relation();
++num_out;
}
m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs);
}
};
relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort());
}
relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort());
}
class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
external_relation_plugin& m_plugin;
app_ref m_condition;
func_decl_ref m_filter_fn;
public:
filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition)
: m_plugin(p),
m_condition(condition, p.get_ast_manager()),
m_filter_fn(p.get_ast_manager()) {
ast_manager& m = p.get_ast_manager();
p.mk_filter_fn(relation_sort, condition, m_filter_fn);
SASSERT(m.is_bool(condition));
}
virtual void operator()(relation_base & r) {
SASSERT(m_plugin.check_kind(r));
expr* arg = get(r).get_relation();
m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg);
}
};
relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) {
if(!check_kind(r)) {
return 0;
}
return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition);
}
relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r,
const relation_element & value, unsigned col) {
if(!check_kind(r)) {
return 0;
}
ast_manager& m = get_ast_manager();
app_ref condition(m);
expr_ref var(m);
sort* relation_sort = get(r).get_sort();
sort* column_sort = get_column_sort(col, relation_sort);
var = m.mk_var(col, column_sort);
condition = m.mk_eq(var, value);
return mk_filter_interpreted_fn(r, condition);
}
class external_relation_plugin::filter_identical_fn : public relation_mutator_fn {
external_relation_plugin& m_plugin;
func_decl_ref_vector m_filter_fn;
public:
filter_identical_fn(external_relation_plugin& p, sort* relation_sort,
unsigned col_cnt, const unsigned * identical_cols)
: m_plugin(p), m_filter_fn(p.get_ast_manager()) {
ast_manager& m = p.get_ast_manager();
func_decl_ref fn(m);
app_ref eq(m);
if (col_cnt <= 1) {
return;
}
unsigned col = identical_cols[0];
sort* s = p.get_column_sort(col, relation_sort);
var* v0 = m.mk_var(col, s);
for (unsigned i = 1; i < col_cnt; ++i) {
col = identical_cols[i];
s = p.get_column_sort(col, relation_sort);
eq = m.mk_eq(v0, m.mk_var(col, s));
p.mk_filter_fn(relation_sort, eq.get(), fn);
m_filter_fn.push_back(fn);
}
}
virtual void operator()(relation_base & r) {
expr* r0 = get(r).get_relation();
for (unsigned i = 0; i < m_filter_fn.size(); ++i) {
m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0);
}
}
};
relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r,
unsigned col_cnt, const unsigned * identical_cols) {
if (!check_kind(r)) {
return 0;
}
return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols);
}
class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn {
external_relation_plugin& m_plugin;
func_decl_ref m_negated_filter_fn;
expr* m_args[2];
public:
negation_filter_fn(external_relation_plugin& p,
const relation_base & tgt, const relation_base & neg_t,
unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) :
convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols),
m_plugin(p),
m_negated_filter_fn(p.get_ast_manager())
{
ast_manager& m = p.get_ast_manager();
family_id fid = p.get_family_id();
vector<parameter> params;
for (unsigned i = 0; i < joined_col_cnt; ++i) {
params.push_back(parameter(t_cols[i]));
params.push_back(parameter(negated_cols[i]));
}
sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() };
m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain);
}
void operator()(relation_base & t, const relation_base & negated_obj) {
m_args[0] = get(t).get_relation();
m_args[1] = get(negated_obj).get_relation();
m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args);
}
};
relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols) {
if (!check_kind(t) || !check_kind(negated_obj)) {
return 0;
}
return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols);
}
};

View file

@ -1,154 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_external_relation.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2010-05-10
Revision History:
--*/
#ifndef _DL_EXTERNAL_RELATION_H_
#define _DL_EXTERNAL_RELATION_H_
#include "dl_base.h"
namespace datalog {
class external_relation;
class external_relation_context {
public:
virtual ~external_relation_context() {}
virtual family_id get_family_id() const = 0;
// reduce arguments.
virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0;
// overwrite terms passed in outs vector with values computed by function.
virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0;
};
class external_relation_plugin : public relation_plugin {
friend class external_relation;
class join_fn;
class project_fn;
class rename_fn;
class union_fn;
class filter_identical_fn;
class filter_interpreted_fn;
class negation_filter_fn;
external_relation_context& m_ext;
public:
external_relation_plugin(external_relation_context& ctx, relation_manager & m);
virtual bool can_handle_signature(const relation_signature & s) { return true; }
static symbol get_name() { return symbol("external_relation"); }
virtual relation_base * mk_empty(const relation_signature & s);
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols);
private:
static external_relation& get(relation_base& r);
static external_relation const & get(relation_base const& r);
void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) {
m_ext.reduce(f, num_args, args, result);
}
void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) {
m_ext.reduce_assign(f, num_args, args, num_out, outs);
}
sort* get_relation_sort(relation_signature const& sig);
sort* get_column_sort(unsigned col, sort* relation_sort);
void mk_filter_fn(sort* s, app* condition, func_decl_ref& f);
family_id get_family_id();
};
class external_relation : public relation_base {
friend class external_relation_plugin;
friend class external_relation_plugin::join_fn;
friend class external_relation_plugin::project_fn;
friend class external_relation_plugin::rename_fn;
friend class external_relation_plugin::union_fn;
friend class external_relation_plugin::filter_identical_fn;
friend class external_relation_plugin::filter_interpreted_fn;
friend class external_relation_plugin::negation_filter_fn;
expr_ref m_rel;
func_decl_ref m_select_fn;
func_decl_ref m_store_fn;
func_decl_ref m_is_empty_fn;
unsigned size() const { return get_signature().size(); }
sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); }
void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const;
external_relation(external_relation_plugin & p, const relation_signature & s, expr* r);
virtual ~external_relation();
public:
external_relation_plugin & get_plugin() const;
virtual bool empty() const;
virtual void add_fact(const relation_fact & f);
virtual bool contains_fact(const relation_fact & f) const;
virtual external_relation * clone() const;
virtual external_relation * complement(func_decl*) const;
virtual void display(std::ostream & out) const;
virtual void display_tuples(func_decl & pred, std::ostream & out) const;
expr* get_relation() const { return m_rel.get(); }
virtual void to_formula(expr_ref& fml) const { fml = get_relation(); }
};
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,366 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_finite_product_relation.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-24.
Revision History:
--*/
#ifndef _DL_FINITE_PRODUCT_RELATION_H_
#define _DL_FINITE_PRODUCT_RELATION_H_
#include "dl_base.h"
#include "dl_relation_manager.h"
#include "dl_table_relation.h"
namespace datalog {
class finite_product_relation;
void universal_delete(finite_product_relation* ptr);
class finite_product_relation_plugin : public relation_plugin {
friend class finite_product_relation;
public:
struct rel_spec {
family_id m_inner_kind; //null_family_id means we don't care about the kind
svector<bool> m_table_cols;
rel_spec() : m_inner_kind(null_family_id) {}
rel_spec(const svector<bool>& table_cols)
: m_inner_kind(null_family_id), m_table_cols(table_cols) {}
bool operator==(const rel_spec & o) const {
return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols);
}
struct hash {
unsigned operator()(const rel_spec & o) const {
return o.m_inner_kind^int_vector_hash(o.m_table_cols);
}
};
};
private:
class join_fn;
class converting_join_fn;
class project_fn;
class rename_fn;
class union_fn;
class inner_singleton_union_fn;
class converting_union_fn;
class filter_identical_fn;
class filter_equal_fn;
class filter_interpreted_fn;
class negation_filter_fn;
class filter_identical_pairs_fn;
relation_plugin & m_inner_plugin;
rel_spec_store<rel_spec, rel_spec::hash, default_eq<rel_spec> > m_spec_store;
static symbol get_name(relation_plugin & inner_plugin);
family_id get_relation_kind(finite_product_relation & r, const bool * table_columns);
static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s,
svector<bool> & table_columns);
void get_all_possible_table_columns(const relation_signature & s, svector<bool> & table_columns) {
get_all_possible_table_columns(get_manager(), s, table_columns);
}
void split_signatures(const relation_signature & s, table_signature & table_sig,
relation_signature & remaining_sig);
void split_signatures(const relation_signature & s, const bool * table_columns,
table_signature & table_sig, relation_signature & remaining_sig);
public:
static finite_product_relation & get(relation_base & r);
static const finite_product_relation & get(const relation_base & r);
static finite_product_relation * get(relation_base * r);
static const finite_product_relation * get(const relation_base * r);
static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner);
finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager);
virtual void initialize(family_id fid);
relation_plugin & get_inner_plugin() const { return m_inner_plugin; }
virtual bool can_handle_signature(const relation_signature & s);
virtual relation_base * mk_empty(const relation_signature & s);
/**
\c inner_kind==null_family_id means we don't care about the kind of the inner relation
*/
finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns,
family_id inner_kind=null_family_id);
finite_product_relation * mk_empty(const finite_product_relation & original);
virtual relation_base * mk_empty(const relation_base & original);
virtual relation_base * mk_empty(const relation_signature & s, family_id kind);
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
/**
\brief Return true if \c r can be converted to \c finite_product_relation_plugin either
by \c mk_from_table_relation or by \c mk_from_inner_relation.
*/
bool can_be_converted(const relation_base & r);
/**
If the conversion cannot be performed, 0 is returned.
*/
finite_product_relation * mk_from_table_relation(const table_relation & r);
finite_product_relation * mk_from_inner_relation(const relation_base & r);
bool can_convert_to_table_relation(const finite_product_relation & r);
table_relation * to_table_relation(const finite_product_relation & r);
protected:
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols);
private:
/**
\brief Create a filter that enforces equality between pairs of table and relation columns
The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation.
*/
relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt,
const unsigned * table_cols, const unsigned * rel_cols);
/**
\brief Create a join-project operation that creates a table according to \c relation_table
but with references to relations updated and removed according to the content of \c filtered_table.
\c selected_columns contains sorted indexes of data columns in \c relation_table that are also in
the \c filtered_table (so that the first column in \c filtered_table corresponds to
\c selected_columns[0] -th column in \c relation_table etc...)
Signature of \c relation_table:
(data columns)(functional column with indexes of relation objects)
Signature of \c filtered_table:
(selected data columns)(non-functional column with original relation object indexes)
(functional column with indexes of filtered relation objects)
*/
static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table,
const table_base & filtered_table, const unsigned_vector & selected_columns);
};
class finite_product_relation : public relation_base {
friend class finite_product_relation_plugin;
friend class finite_product_relation_plugin::join_fn;
friend class finite_product_relation_plugin::project_fn;
friend class finite_product_relation_plugin::union_fn;
friend class finite_product_relation_plugin::rename_fn;
friend class finite_product_relation_plugin::inner_singleton_union_fn;
friend class finite_product_relation_plugin::filter_equal_fn;
friend class finite_product_relation_plugin::filter_identical_pairs_fn;
class live_rel_collection_reducer;
public:
/**
Size of this sort determines how many different relation objects can we refer to.
*/
static const table_sort s_rel_idx_sort;
/**
\brief The last column in the signature is a functional column with index of the
associated inner relation. The other columns correspond to the relation signature
according to \c m_table2sig.
It holds that \c m_table_sig.size()-1==m_table2sig.size()
*/
table_signature m_table_sig;
unsigned_vector m_table2sig; // (ordered list)
unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX
private:
relation_signature m_other_sig;
unsigned_vector m_other2sig; // (ordered list)
public:
unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX
private:
relation_plugin & m_other_plugin;
family_id m_other_kind;
mutable table_base * m_table;
public:
mutable relation_vector m_others;
private:
mutable unsigned_vector m_available_rel_indexes;
/**
\c UINT_MAX means uninitialized.
If we can get away with it, we want to have a single full relation to refer to.
*/
mutable unsigned m_full_rel_idx;
mutable idx_set m_live_rel_collection_acc;
mutable scoped_ptr<table_transformer_fn> m_live_rel_collection_project;
mutable scoped_ptr<table_intersection_filter_fn> m_empty_rel_removal_filter;
void recycle_rel_idx(unsigned idx) const;
// creates a full relation if it does not exist.
unsigned get_full_rel_idx();
public:
relation_base & get_inner_rel(table_element idx)
{ SASSERT(idx<UINT_MAX); return get_inner_rel(static_cast<unsigned>(idx)); }
relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; }
const relation_base & get_inner_rel(unsigned idx) const
{ return const_cast<finite_product_relation &>(*this).get_inner_rel(idx); }
unsigned get_next_rel_idx() const;
/**
The relation takes ownership of the \c inner object.
*/
void set_inner_rel(table_element idx, relation_base * inner)
{ SASSERT(idx<UINT_MAX); return set_inner_rel(static_cast<unsigned>(idx), inner); }
/**
The relation takes ownership of the \c inner object.
*/
void set_inner_rel(unsigned idx, relation_base * inner) {
SASSERT(!m_others[idx]);
SASSERT(inner);
m_others[idx] = inner;
}
table_base & get_table() { return *m_table; }
table_plugin & get_table_plugin() const { return get_table().get_plugin(); }
void garbage_collect(bool remove_empty) const;
/**
\brief Initialize an empty relation with table \c table_vals and relations in \c others.
The relation object takes ownership of relations inside the \c others vector.
If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array.
*/
void init(const table_base & table_vals, const relation_vector & others, bool contiguous);
private:
/**
\brief Extract the values of table non-functional columns from the relation fact.
The value of the functional column which determines index of the inner relation is undefined.
*/
void extract_table_fact(const relation_fact rf, table_fact & tf) const;
/**
\brief Extract the values of the inner relation columns from the relation fact.
*/
void extract_other_fact(const relation_fact rf, relation_fact & of) const;
relation_base * mk_empty_inner();
relation_base * mk_full_inner(func_decl* pred);
void complement_self(func_decl* pred);
void collect_live_relation_indexes(idx_set & res) const;
/**
\brief Try to modify relations in \c rels so that they have the same columns corresponding to the table
and the inner relation (so that the union can be perofrmed on theim in a straightforward way).
Relations in \c rels must all have equal signature.
Even if the function fails and false is returned, some relations may already be modified. They are
in a valid state, but with different specification.
*/
static bool try_unify_specifications(ptr_vector<finite_product_relation> & rels);
bool try_modify_specification(const bool * table_cols);
virtual bool can_swap(const relation_base & r) const
{ return &get_plugin()==&r.get_plugin(); }
/**
\brief Swap content of the current relation with the content of \c r.
Both relations must come from the same plugin and be of the same signature.
*/
virtual void swap(relation_base & r);
/**
\brief Create a \c finite_product_relation object.
*/
finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s,
const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind);
finite_product_relation(const finite_product_relation & r);
virtual ~finite_product_relation();
public:
context & get_context() const;
finite_product_relation_plugin & get_plugin() const {
return static_cast<finite_product_relation_plugin &>(relation_base::get_plugin());
}
bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; }
const table_base & get_table() const { return *m_table; }
const relation_base & get_inner_rel(table_element idx) const
{ SASSERT(idx<UINT_MAX); return get_inner_rel(static_cast<unsigned>(idx)); }
/**
The function calls garbage_collect, so the internal state may change when it is called.
*/
virtual bool empty() const;
void reset() { m_table->reset(); garbage_collect(false); }
virtual void add_fact(const relation_fact & f);
virtual bool contains_fact(const relation_fact & f) const;
virtual finite_product_relation * clone() const;
virtual finite_product_relation * complement(func_decl* p) const;
virtual void display(std::ostream & out) const;
virtual void display_tuples(func_decl & pred, std::ostream & out) const;
virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); }
virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); }
virtual void to_formula(expr_ref& fml) const;
};
};
#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,342 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_instruction.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#ifndef _DL_INSTRUCTION_H_
#define _DL_INSTRUCTION_H_
#include<iostream>
#include<string>
#include<utility>
#include "ast.h"
#include "vector.h"
#include "dl_base.h"
#include "dl_costs.h"
namespace datalog {
class execution_context;
class instruction_block;
inline void check_overflow(unsigned i) {
if (i == UINT_MAX) {
throw out_of_memory_error();
}
}
// -----------------------------------
//
// execution_context
//
// -----------------------------------
class execution_context {
public:
typedef relation_base * reg_type;
typedef vector<reg_type> reg_vector;
typedef unsigned reg_idx;
/**
\brief A register number that should never be referenced to. Can stand e.g. for a tail
table in a rule with no tail.
*/
static const reg_idx void_register = UINT_MAX;
private:
typedef u_map<std::string> reg_annotations;
context & m_datalog_context;
reg_vector m_registers;
reg_annotations m_reg_annotation;
stopwatch * m_stopwatch;
unsigned m_timelimit_ms; //zero means no limit
/**
\brief If true, after every operation that may result in an empty relation, a check
for emptiness will be performed, and if a relation is empty, it will be deleted
and replaced by zero. This allows us to avoid performing operations that would have
no effect due to relation emptiness, but if the check for emptiness is expensive, its
cost may overcome the gains.
*/
bool m_eager_emptiness_checking;
public:
execution_context(context & datalog_context);
~execution_context();
void reset();
context & get_datalog_context() { return m_datalog_context; };
void set_timelimit(unsigned time_in_ms);
void reset_timelimit();
bool should_terminate();
bool eager_emptiness_checking() const { return m_eager_emptiness_checking; }
/**
\brief Return reference to \c i -th register that contains pointer to a relation.
If register contains zero, it should be treated as if it contains an empty relation.
*/
reg_type reg(reg_idx i) {
if (i>=m_registers.size()) {
check_overflow(i);
m_registers.resize(i+1,0);
}
return m_registers[i];
}
/**
\brief Return value of the register and assign zero into it place.
*/
reg_type release_reg(reg_idx i) {
SASSERT(i<m_registers.size());
SASSERT(m_registers[i]);
reg_type res = m_registers[i];
m_registers[i] = 0;
return res;
}
/**
\brief Assign value to a register. If it was non-empty, deallocate its content first.
*/
void set_reg(reg_idx i, reg_type val) {
if(i>=m_registers.size()) {
check_overflow(i);
m_registers.resize(i+1,0);
}
if(m_registers[i]) {
m_registers[i]->deallocate();
}
m_registers[i]=val;
}
void make_empty(reg_idx i) {
if(reg(i)) {
set_reg(i, 0);
}
}
unsigned register_count() const {
return m_registers.size();
}
bool get_register_annotation(reg_idx reg, std::string & res) const {
return m_reg_annotation.find(reg, res);
}
void set_register_annotation(reg_idx reg, std::string str) {
m_reg_annotation.insert(reg, str);
}
void report_big_relations(unsigned threshold, std::ostream & out);
};
// -----------------------------------
//
// instruction
//
// -----------------------------------
/**
\brief Base class for instructions used in datalog saturation.
A relation in a register is owned by that register and is not referenced from anywhere else.
Instructions that move context of one register to another leave the source register empty
and deallocate the previous content of the target register.
*/
class instruction : public accounted_object {
typedef u_map<base_relation_fn *> fn_cache;
fn_cache m_fn_cache;
static const int rk_encode_base = 1024;
inline static unsigned encode_kind(family_id k)
{ SASSERT(k<rk_encode_base); return k; }
inline static unsigned encode_kinds(family_id k1, family_id k2)
{ SASSERT(k1<rk_encode_base && k2<rk_encode_base); return (k1+1)*rk_encode_base + k2; }
inline static unsigned encode_kinds(family_id k1, family_id k2, family_id k3) {
SASSERT(k1<rk_encode_base && k2<rk_encode_base && k3<rk_encode_base);
return ((k1+1)*rk_encode_base + k2)*rk_encode_base + k3;
}
protected:
friend class instruction_block;
template<typename T>
bool find_fn(const relation_base & r, T* & result) const
{ return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
template<typename T>
bool find_fn(const relation_base & r1, const relation_base & r2, T*& result) const
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
template<typename T>
bool find_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, T * & result) const
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
void store_fn(const relation_base & r, base_relation_fn * fn)
{ m_fn_cache.insert(encode_kind(r.get_kind()), fn); }
void store_fn(const relation_base & r1, const relation_base & r2, base_relation_fn * fn)
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind()), fn); }
void store_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3,
base_relation_fn * fn)
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), fn); }
/**
Process not only costs associated with the current instruction, but in case of
block instructions, process also costs associated with its child instructions.
*/
virtual void process_all_costs();
/**
\brief Output one line header of the current instruction.
The newline character at the end should not be printed.
*/
virtual void display_head_impl(context & ctx, std::ostream & out) const {
out << "<instruction>";
}
/**
\brief If relevant, output the body of the current instruction.
Each line must be prepended by \c indentation and ended by a newline character.
*/
virtual void display_body_impl(context & ctx, std::ostream & out, std::string indentation) const {}
public:
typedef execution_context::reg_type reg_type;
typedef execution_context::reg_idx reg_idx;
virtual ~instruction();
virtual bool perform(execution_context & ctx) = 0;
virtual void make_annotations(execution_context & ctx) = 0;
void display(context & ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(context & ctx, std::ostream & out, std::string indentation) const;
static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt);
/**
\brief The store operation moves the relation from a register into the context. The register
is set to zero after the operation.
*/
static instruction * mk_store(ast_manager & m, func_decl * pred, reg_idx src);
static instruction * mk_dealloc(reg_idx reg); //maybe not necessary
static instruction * mk_clone(reg_idx from, reg_idx to);
static instruction * mk_move(reg_idx from, reg_idx to);
/**
\brief Return instruction that performs \c body as long as at least one register
in \c control_regs contains non-empty relation.
The instruction object takes over the ownership of the \c body object.
*/
static instruction * mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs,
instruction_block * body);
static instruction * mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2, reg_idx result);
static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col);
static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols);
static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition);
static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta);
static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta);
static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols,
reg_idx tgt);
static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt,
const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
const unsigned * removed_cols, reg_idx result);
static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle,
reg_idx tgt);
static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2);
static instruction * mk_select_equal_and_project(ast_manager & m, reg_idx src,
const relation_element & value, unsigned col, reg_idx result);
static instruction * mk_unary_singleton(ast_manager & m, func_decl* pred, const relation_sort & s, const relation_element & val, reg_idx tgt);
static instruction * mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt);
/**
\brief The mark_saturated instruction marks a relation as saturated, so that after
next restart it does not have to be part of the saturation again.
*/
static instruction * mk_mark_saturated(ast_manager & m, func_decl * pred);
static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt);
};
// -----------------------------------
//
// instruction_block
//
// -----------------------------------
class instruction_block {
public:
struct instruction_observer {
virtual ~instruction_observer() {}
virtual void notify(instruction * i) {}
};
private:
typedef ptr_vector<instruction> instr_seq_type;
instr_seq_type m_data;
instruction_observer* m_observer;
public:
instruction_block() : m_observer(0) {}
~instruction_block();
void reset();
void push_back(instruction * i) {
m_data.push_back(i);
if(m_observer) {
m_observer->notify(i);
}
}
void set_observer(instruction_observer * o) {
SASSERT(o==0 || m_observer==0);
m_observer = o;
}
/**
\brief Perform instructions in the block. If the run was interrupted before completion,
return false; otherwise return true.
The execution can terminate before completion if the function
\c execution_context::should_terminate() returns true.
*/
bool perform(execution_context & ctx) const;
void process_all_costs();
void make_annotations(execution_context & ctx);
void display(context & ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(context & ctx, std::ostream & out, std::string indentation) const;
};
};
#endif /* _DL_INSTRUCTION_H_ */

View file

@ -1,655 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_interval_relation.cpp
Abstract:
Basic interval reatlion.
Author:
Nikolaj Bjorner (nbjorner) 2010-2-11
Revision History:
--*/
#include "debug.h"
#include "optional.h"
#include "ast_pp.h"
#include "dl_interval_relation.h"
namespace datalog {
// -------------------------
// interval_relation_plugin
interval_relation_plugin::interval_relation_plugin(relation_manager& m):
relation_plugin(interval_relation_plugin::get_name(), m),
m_empty(m_dep),
m_arith(get_ast_manager()),
m_bsimp(get_ast_manager()) {
}
bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) {
for (unsigned i = 0; i < sig.size(); ++i) {
if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) {
return false;
}
}
return true;
}
relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) {
return alloc(interval_relation, *this, s, true);
}
relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
return alloc(interval_relation, *this, s, false);
}
class interval_relation_plugin::join_fn : public convenient_relation_join_fn {
public:
join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2)
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){
}
virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) {
interval_relation const& r1 = get(_r1);
interval_relation const& r2 = get(_r2);
interval_relation_plugin& p = r1.get_plugin();
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
return result;
}
};
relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if (!check_kind(r1) || !check_kind(r2)) {
return 0;
}
return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2);
}
class interval_relation_plugin::project_fn : public convenient_relation_project_fn {
public:
project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) {
}
virtual relation_base * operator()(const relation_base & _r) {
interval_relation const& r = get(_r);
interval_relation_plugin& p = r.get_plugin();
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr());
return result;
}
};
relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r,
unsigned col_cnt, const unsigned * removed_cols) {
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
}
class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn {
interval_relation_plugin& m_plugin;
public:
rename_fn(interval_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle),
m_plugin(p){
}
virtual relation_base * operator()(const relation_base & _r) {
interval_relation const& r = get(_r);
interval_relation_plugin& p = r.get_plugin();
interval_relation* result = dynamic_cast<interval_relation*>(p.mk_full(0, get_result_signature()));
result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr());
return result;
}
};
relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r,
unsigned cycle_len, const unsigned * permutation_cycle) {
if(!check_kind(r)) {
return 0;
}
return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle);
}
interval interval_relation_plugin::unite(interval const& src1, interval const& src2) {
bool l_open = src1.is_lower_open();
bool r_open = src1.is_upper_open();
ext_numeral low = src1.inf();
ext_numeral high = src1.sup();
if (src2.inf() < low || (src2.inf() == low && l_open)) {
low = src2.inf();
l_open = src2.is_lower_open();
}
if (src2.sup() > high || (src2.sup() == high && r_open)) {
high = src2.sup();
r_open = src2.is_upper_open();
}
return interval(dep(), low, l_open, 0, high, r_open, 0);
}
interval interval_relation_plugin::widen(interval const& src1, interval const& src2) {
bool l_open = src1.is_lower_open();
bool r_open = src1.is_upper_open();
ext_numeral low = src1.inf();
ext_numeral high = src1.sup();
if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) {
low = ext_numeral(false);
l_open = true;
}
if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) {
high = ext_numeral(true);
r_open = true;
}
return interval(dep(), low, l_open, 0, high, r_open, 0);
}
interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) {
isempty = false;
if (is_empty(0, src1) || is_infinite(src2)) {
return src1;
}
if (is_empty(0, src2) || is_infinite(src1)) {
return src2;
}
bool l_open = src1.is_lower_open();
bool r_open = src1.is_upper_open();
ext_numeral low = src1.inf();
ext_numeral high = src1.sup();
if (src2.inf() > low || (src2.inf() == low && !l_open)) {
low = src2.inf();
l_open = src2.is_lower_open();
}
if (src2.sup() < high || (src2.sup() == high && !r_open)) {
high = src2.sup();
r_open = src2.is_upper_open();
}
if (low > high || (low == high && (l_open || r_open))) {
isempty = true;
return interval(dep());
}
else {
return interval(dep(), low, l_open, 0, high, r_open, 0);
}
}
bool interval_relation_plugin::is_infinite(interval const& i) {
return i.plus_infinity() && i.minus_infinity();
}
bool interval_relation_plugin::is_empty(unsigned, interval const& i) {
return i.sup() < i.inf();
}
class interval_relation_plugin::union_fn : public relation_union_fn {
interval_relation_plugin& m_plugin;
bool m_is_widen;
public:
union_fn(interval_relation_plugin& p, bool is_widen) :
m_plugin(p),
m_is_widen(is_widen) {
}
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n"););
interval_relation& r = get(_r);
interval_relation const& src = get(_src);
if (_delta) {
interval_relation& d = get(*_delta);
r.mk_union(src, &d, m_is_widen);
}
else {
r.mk_union(src, 0, m_is_widen);
}
}
};
relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn, *this, false);
}
relation_union_fn * interval_relation_plugin::mk_widen_fn(
const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn, *this, true);
}
class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn {
unsigned_vector m_identical_cols;
public:
filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols)
: m_identical_cols(col_cnt, identical_cols) {}
virtual void operator()(relation_base & r) {
interval_relation & pr = get(r);
for (unsigned i = 1; i < m_identical_cols.size(); ++i) {
unsigned c1 = m_identical_cols[0];
unsigned c2 = m_identical_cols[i];
pr.equate(c1, c2);
}
}
};
relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn(
const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) {
if(!check_kind(t)) {
return 0;
}
return alloc(filter_identical_fn, col_cnt, identical_cols);
}
class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn {
unsigned m_col;
rational m_value;
public:
filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col)
: m_col(col) {
arith_util arith(m.get_context().get_manager());
VERIFY(arith.is_numeral(value, m_value));
}
virtual void operator()(relation_base & _r) {
interval_relation & r = get(_r);
interval_relation_plugin & p = r.get_plugin();
r.mk_intersect(m_col, interval(p.dep(), m_value));
TRACE("interval_relation", tout << m_value << "\n"; r.display(tout););
}
};
relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r,
const relation_element & value, unsigned col) {
if(check_kind(r)) {
return alloc(filter_equal_fn, get_manager(), value, col);
}
return 0;
}
class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
app_ref m_cond;
public:
filter_interpreted_fn(interval_relation const& t, app* cond):
m_cond(cond, t.get_plugin().get_ast_manager()) {
}
void operator()(relation_base& t) {
get(t).filter_interpreted(m_cond);
TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout););
}
};
relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) {
if (check_kind(t)) {
return alloc(filter_interpreted_fn, get(t), condition);
}
return 0;
}
interval_relation& interval_relation_plugin::get(relation_base& r) {
return dynamic_cast<interval_relation&>(r);
}
interval_relation const & interval_relation_plugin::get(relation_base const& r) {
return dynamic_cast<interval_relation const&>(r);
}
// -----------------------
// interval_relation
interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty):
vector_relation<interval>(p, s, is_empty, interval(p.dep()))
{
}
void interval_relation::add_fact(const relation_fact & f) {
interval_relation r(get_plugin(), get_signature(), false);
ast_manager& m = get_plugin().get_ast_manager();
for (unsigned i = 0; i < f.size(); ++i) {
app_ref eq(m);
expr* e = f[i];
eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e);
r.filter_interpreted(eq.get());
}
mk_union(r, 0, false);
}
bool interval_relation::contains_fact(const relation_fact & f) const {
SASSERT(f.size() == get_signature().size());
interval_relation_plugin& p = get_plugin();
for (unsigned i = 0; i < f.size(); ++i) {
if (f[i] != f[find(i)]) {
return false;
}
interval const& iv = (*this)[i];
if (p.is_infinite(iv)) {
continue;
}
rational v;
if (p.m_arith.is_numeral(f[i], v)) {
if (!iv.contains(v)) {
return false;
}
}
else {
// TBD: may or must?
}
}
return true;
}
interval_relation * interval_relation::clone() const {
interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty());
result->copy(*this);
return result;
}
interval_relation * interval_relation::complement(func_decl*) const {
UNREACHABLE();
return 0;
}
void interval_relation::to_formula(expr_ref& fml) const {
ast_manager& m = get_plugin().get_ast_manager();
arith_util& arith = get_plugin().m_arith;
basic_simplifier_plugin& bsimp = get_plugin().m_bsimp;
expr_ref_vector conjs(m);
relation_signature const& sig = get_signature();
for (unsigned i = 0; i < sig.size(); ++i) {
if (i != find(i)) {
conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]),
m.mk_var(find(i), sig[find(i)])));
continue;
}
interval const& iv = (*this)[i];
sort* ty = sig[i];
expr_ref var(m.mk_var(i, ty), m);
if (!iv.minus_infinity()) {
expr* lo = arith.mk_numeral(iv.get_lower_value(), ty);
if (iv.is_lower_open()) {
conjs.push_back(arith.mk_lt(lo, var));
}
else {
conjs.push_back(arith.mk_le(lo, var));
}
}
if (!iv.plus_infinity()) {
expr* hi = arith.mk_numeral(iv.get_upper_value(), ty);
if (iv.is_upper_open()) {
conjs.push_back(arith.mk_lt(var, hi));
}
else {
conjs.push_back(arith.mk_le(var, hi));
}
}
}
bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml);
}
void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const {
out << i << " in " << j << "\n";
}
interval_relation_plugin& interval_relation::get_plugin() const {
return static_cast<interval_relation_plugin &>(relation_base::get_plugin());
}
void interval_relation::mk_intersect(unsigned idx, interval const& i) {
bool isempty;
(*this)[idx] = mk_intersect((*this)[idx], i, isempty);
if (isempty || is_empty(idx, (*this)[idx])) {
set_empty();
}
}
void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) {
}
void interval_relation::filter_interpreted(app* cond) {
interval_relation_plugin& p = get_plugin();
rational k;
unsigned x, y;
if (p.is_lt(cond, x, k, y)) {
// 0 < x - y + k
if (x == UINT_MAX) {
// y < k
mk_intersect(y, interval(p.dep(), k, true, false, 0));
return;
}
if (y == UINT_MAX) {
// -k < x
mk_intersect(x, interval(p.dep(), -k, true, true, 0));
return;
}
// y < x + k
ext_numeral x_hi = (*this)[x].sup();
ext_numeral y_lo = (*this)[y].inf();
if (!x_hi.is_infinite()) {
mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0));
}
if (!y_lo.is_infinite()) {
mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0));
}
return;
}
bool is_int = false;
if (p.is_le(cond, x, k, y, is_int)) {
// 0 <= x - y + k
if (x == UINT_MAX) {
// y <= k
mk_intersect(y, interval(p.dep(), k, false, false, 0));
return;
}
if (y == UINT_MAX) {
// -k <= x
mk_intersect(x, interval(p.dep(), -k, false, true, 0));
return;
}
ext_numeral x_hi = (*this)[x].sup();
ext_numeral y_lo = (*this)[y].inf();
if (!x_hi.is_infinite()) {
mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0));
}
if (!y_lo.is_infinite()) {
mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0));
}
return;
}
if (p.is_eq(cond, x, k, y)) {
// y = x + k
if (x == UINT_MAX) {
SASSERT(y != UINT_MAX);
mk_intersect(y, interval(p.dep(), k));
return;
}
if (y == UINT_MAX) {
// x = - k
SASSERT(x != UINT_MAX);
mk_intersect(x, interval(p.dep(), -k));
return;
}
interval x_i = (*this)[x];
interval y_i = (*this)[y];
x_i += interval(p.dep(), k);
y_i -= interval(p.dep(), k);
mk_intersect(x, y_i);
mk_intersect(y, x_i);
}
if (get_plugin().get_ast_manager().is_false(cond)) {
set_empty();
}
}
bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const {
#define SET_VAR(_idx_) \
if (is_pos &&pos == UINT_MAX) { \
pos = _idx_; \
return true; \
} \
if (!is_pos && neg == UINT_MAX) { \
neg = _idx_; \
return true; \
} \
else { \
return false; \
}
if (is_var(e)) {
SET_VAR(to_var(e)->get_idx());
}
if (!is_app(e)) {
return false;
}
app* a = to_app(e);
if (m_arith.is_add(e)) {
for (unsigned i = 0; i < a->get_num_args(); ++i) {
if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false;
}
return true;
}
if (m_arith.is_sub(e)) {
SASSERT(a->get_num_args() == 2);
return
is_linear(a->get_arg(0), neg, pos, k, is_pos) &&
is_linear(a->get_arg(1), neg, pos, k, !is_pos);
}
rational k1;
SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2);
if (m_arith.is_mul(e) &&
m_arith.is_numeral(a->get_arg(0), k1) &&
k1.is_minus_one() &&
is_var(a->get_arg(1))) {
SET_VAR(to_var(a->get_arg(1))->get_idx());
}
if (m_arith.is_numeral(e, k1)) {
if (is_pos) {
k += k1;
}
else {
k -= k1;
}
return true;
}
return false;
}
// 0 <= x - y + k
bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const {
ast_manager& m = get_ast_manager();
k.reset();
x = UINT_MAX;
y = UINT_MAX;
if (m_arith.is_le(cond)) {
is_int = m_arith.is_int(cond->get_arg(0));
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
return (x != UINT_MAX || y != UINT_MAX);
}
if (m_arith.is_ge(cond)) {
is_int = m_arith.is_int(cond->get_arg(0));
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
return (x != UINT_MAX || y != UINT_MAX);
}
if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) {
is_int = true;
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
k -= rational::one();
return (x != UINT_MAX || y != UINT_MAX);
}
if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) {
is_int = true;
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
k += rational::one();
return (x != UINT_MAX || y != UINT_MAX);
}
if (m.is_not(cond) && is_app(cond->get_arg(0))) {
// not (0 <= x - y + k)
// <=>
// 0 > x - y + k
// <=>
// 0 <= y - x - k - 1
if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) {
k.neg();
k -= rational::one();
std::swap(x, y);
return true;
}
// not (0 < x - y + k)
// <=>
// 0 >= x - y + k
// <=>
// 0 <= y - x - k
if (is_lt(to_app(cond->get_arg(0)), x, k, y)) {
is_int = false;
k.neg();
std::swap(x, y);
return true;
}
}
return false;
}
// 0 < x - y + k
bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const {
k.reset();
x = UINT_MAX;
y = UINT_MAX;
if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) {
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
return (x != UINT_MAX || y != UINT_MAX);
}
if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) {
if (!is_linear(cond->get_arg(0), y, x, k, true)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, false)) return false;
return (x != UINT_MAX || y != UINT_MAX);
}
return false;
}
// 0 = x - y + k
bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const {
ast_manager& m = get_ast_manager();
k.reset();
x = UINT_MAX;
y = UINT_MAX;
if (m.is_eq(cond)) {
if (!is_linear(cond->get_arg(0), y, x, k, false)) return false;
if (!is_linear(cond->get_arg(1), y, x, k, true)) return false;
return (x != UINT_MAX || y != UINT_MAX);
}
return false;
}
};

View file

@ -1,140 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_interval_relation.h
Abstract:
Basic interval reatlion.
Author:
Nikolaj Bjorner (nbjorner) 2010-2-11
Revision History:
--*/
#ifndef _DL_INTERVAL_RELATION_H_
#define _DL_INTERVAL_RELATION_H_
#include "dl_context.h"
#include "old_interval.h"
#include "dl_vector_relation.h"
#include "arith_decl_plugin.h"
#include "basic_simplifier_plugin.h"
namespace datalog {
class interval_relation;
class interval_relation_plugin : public relation_plugin {
v_dependency_manager m_dep;
interval m_empty;
arith_util m_arith;
basic_simplifier_plugin m_bsimp;
class join_fn;
class project_fn;
class rename_fn;
class union_fn;
class filter_equal_fn;
class filter_identical_fn;
class filter_interpreted_fn;
friend class interval_relation;
interval unite(interval const& src1, interval const& src2);
interval widen(interval const& src1, interval const& src2);
interval meet(interval const& src1, interval const& src2, bool& is_empty);
v_dependency_manager & dep() const { return const_cast<v_dependency_manager&>(m_dep); }
public:
interval_relation_plugin(relation_manager& m);
virtual bool can_handle_signature(const relation_signature & s);
static symbol get_name() { return symbol("interval_relation"); }
virtual relation_base * mk_empty(const relation_signature & s);
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
static bool is_empty(unsigned idx, interval const& i);
static bool is_infinite(interval const& i);
private:
static interval_relation& get(relation_base& r);
static interval_relation const & get(relation_base const& r);
bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const;
// x + k <= y
bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const;
// x + k < y
bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const;
// x + k = y
bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const;
};
class interval_relation : public vector_relation<interval> {
friend class interval_relation_plugin;
friend class interval_relation_plugin::filter_equal_fn;
public:
interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty);
virtual void add_fact(const relation_fact & f);
virtual bool contains_fact(const relation_fact & f) const;
virtual interval_relation * clone() const;
virtual interval_relation * complement(func_decl*) const;
virtual void to_formula(expr_ref& fml) const;
interval_relation_plugin& get_plugin() const;
void filter_interpreted(app* cond);
private:
virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const {
return get_plugin().meet(t1, t2, is_empty);
}
virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); }
virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); }
virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; }
virtual bool is_full(interval const& t) const {
return interval_relation_plugin::is_infinite(t);
}
virtual bool is_empty(unsigned idx, interval const& t) const {
return interval_relation_plugin::is_empty(idx, t);
}
virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle);
virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const;
void mk_intersect(unsigned idx, interval const& i);
};
};
#endif

View file

@ -1,217 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_bit_blast.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2012-08-30
Revision History:
--*/
#include "dl_mk_bit_blast.h"
#include "bit_blaster_rewriter.h"
#include "rewriter_def.h"
#include "ast_pp.h"
namespace datalog {
//
// P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v).
// ->
// P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) .
//
// Introduce P_bv:
// P_bv(x,y) :- Q_bv(x,0), R_bv(1,y)
// P(bv(x,y)) :- P_bv(x,y)
// Query
class expand_mkbv_cfg : public default_rewriter_cfg {
context& m_context;
rule_ref_vector& m_rules;
ast_manager& m;
bv_util m_util;
expr_ref_vector m_args, m_f_vars, m_g_vars;
func_decl_ref_vector m_pinned;
obj_map<func_decl,func_decl*> m_pred2blast;
public:
expand_mkbv_cfg(context& ctx, rule_ref_vector& rules):
m_context(ctx),
m_rules(rules),
m(ctx.get_manager()),
m_util(m),
m_args(m),
m_f_vars(m),
m_g_vars(m),
m_pinned(m)
{}
~expand_mkbv_cfg() {}
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
rule_manager& rm = m_context.get_rule_manager();
bool found = false;
for (unsigned j = 0; !found && j < num; ++j) {
found = m_util.is_mkbv(args[j]);
}
if (!found) {
return BR_FAILED;
}
//
// f(mk_bv(args),...)
//
m_args.reset();
m_g_vars.reset();
m_f_vars.reset();
expr_ref fml(m);
unsigned idx = 0;
for (unsigned j = 0; j < num; ++j) {
expr* arg = args[j];
if (m_util.is_mkbv(arg)) {
app* a = to_app(arg);
unsigned sz = a->get_num_args();
for (unsigned i = 0; i < sz; ++i) {
m_args.push_back(a->get_arg(i));
m_g_vars.push_back(m.mk_var(idx++,m.mk_bool_sort()));
}
m_f_vars.push_back(m_util.mk_bv(sz, m_g_vars.c_ptr()+m_g_vars.size()-sz));
}
else {
m_args.push_back(arg);
m_f_vars.push_back(m.mk_var(idx++, m.get_sort(arg)));
m_g_vars.push_back(m_f_vars.back());
}
}
func_decl* g = 0;
if (!m_pred2blast.find(f, g)) {
ptr_vector<sort> domain;
for (unsigned i = 0; i < m_args.size(); ++i) {
domain.push_back(m.get_sort(m_args[i].get()));
}
g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f);
m_pinned.push_back(g);
m_pred2blast.insert(f, g);
// Create rule f(mk_mkbv(args)) :- g(args)
fml = m.mk_implies(m.mk_app(g, m_g_vars.size(), m_g_vars.c_ptr()), m.mk_app(f, m_f_vars.size(), m_f_vars.c_ptr()));
rm.mk_rule(fml, m_rules, g->get_name());
}
result = m.mk_app(g, m_args.size(), m_args.c_ptr());
result_pr = 0;
return BR_DONE;
}
};
struct expand_mkbv : public rewriter_tpl<expand_mkbv_cfg> {
expand_mkbv_cfg m_cfg;
expand_mkbv(ast_manager& m, context& ctx, rule_ref_vector& rules):
rewriter_tpl<expand_mkbv_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(ctx, rules) {
}
};
class mk_bit_blast::impl {
context & m_context;
ast_manager & m;
params_ref m_params;
rule_ref_vector m_rules;
bit_blaster_rewriter m_blaster;
expand_mkbv m_rewriter;
bool blast(expr_ref& fml) {
proof_ref pr(m);
expr_ref fml1(m), fml2(m);
m_blaster(fml, fml1, pr);
m_rewriter(fml1, fml2);
TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml1, m) << " -> " << mk_pp(fml2, m) << "\n";);
if (fml2 != fml) {
fml = fml2;
return true;
}
else {
return false;
}
}
void reset() {
m_rules.reset();
}
public:
impl(context& ctx):
m_context(ctx),
m(ctx.get_manager()),
m_rules(ctx.get_rule_manager()),
m_params(ctx.get_params()),
m_blaster(ctx.get_manager(), m_params),
m_rewriter(ctx.get_manager(), ctx, m_rules) {
m_params.set_bool(":blast-full", true);
m_params.set_bool(":blast-quant", true);
m_blaster.updt_params(m_params);
}
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
if (!m_context.get_params().get_bool(":bit-blast", false)) {
return 0;
}
if (m_context.get_engine() != PDR_ENGINE) {
return 0;
}
rule_manager& rm = m_context.get_rule_manager();
unsigned sz = source.get_num_rules();
expr_ref fml(m);
reset();
rule_set * result = alloc(rule_set, m_context);
for (unsigned i = 0; i < sz; ++i) {
rule * r = source.get_rule(i);
r->to_formula(fml);
if (blast(fml)) {
rm.mk_rule(fml, m_rules, r->name());
}
else {
m_rules.push_back(r);
}
}
for (unsigned i = 0; i < m_rules.size(); ++i) {
result->add_rule(m_rules.get(i));
}
return result;
}
};
mk_bit_blast::mk_bit_blast(context & ctx, unsigned priority) : plugin(priority) {
m_impl = alloc(impl, ctx);
}
mk_bit_blast::~mk_bit_blast() {
dealloc(m_impl);
}
rule_set * mk_bit_blast::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
return (*m_impl)(source, mc, pc);
}
};

View file

@ -1,53 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_bit_blast.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2012-08-30
Revision History:
--*/
#ifndef _DL_MK_BIT_BLAST_H_
#define _DL_MK_BIT_BLAST_H_
#include<utility>
#include"map.h"
#include"obj_pair_hashtable.h"
#include"dl_context.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Functor for bit-blasting a rule set.
*/
class mk_bit_blast : public rule_transformer::plugin {
class impl;
impl* m_impl;
void blast(expr_ref& b);
void reset();
public:
mk_bit_blast(context & ctx, unsigned priority = 35000);
~mk_bit_blast();
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_BIT_BLAST_H_ */

View file

@ -1,209 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_coalesce.cpp
Abstract:
Coalesce rules with shared bodies.
Author:
Nikolaj Bjorner (nbjorner) 2012-10-15
Revision History:
Notes:
Implements proof rule of the form:
a(x) & q(x) -> p(x), b(y) & q(y) -> p(y)
----------------------------------------------
(a(z) \/ b(z)) & q(z) -> p(z)
--*/
#include "dl_mk_coalesce.h"
#include "bool_rewriter.h"
namespace datalog {
mk_coalesce::mk_coalesce(context& ctx):
rule_transformer::plugin(50, false),
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_sub1(m),
m_sub2(m),
m_idx(0)
{}
void mk_coalesce::mk_pred(app_ref& pred, app* p1, app* p2) {
SASSERT(p1->get_decl() == p2->get_decl());
unsigned sz = p1->get_num_args();
expr_ref_vector args(m);
for (unsigned i = 0; i < sz; ++i) {
expr* a = p1->get_arg(i);
expr* b = p2->get_arg(i);
SASSERT(m.get_sort(a) == m.get_sort(b));
m_sub1.push_back(a);
m_sub2.push_back(b);
args.push_back(m.mk_var(m_idx++, m.get_sort(a)));
}
pred = m.mk_app(p1->get_decl(), args.size(), args.c_ptr());
}
void mk_coalesce::extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result) {
obj_map<expr, unsigned> indices;
bool_rewriter bwr(m);
rule_ref r(const_cast<rule*>(&rl), rm);
sort_ref_vector sorts(m);
expr_ref_vector revsub(m), conjs(m);
rl.get_vars(sorts);
revsub.resize(sorts.size());
svector<bool> valid(sorts.size(), true);
for (unsigned i = 0; i < sub.size(); ++i) {
expr* e = sub[i];
sort* s = m.get_sort(e);
expr_ref w(m.mk_var(i, s), m);
if (is_var(e)) {
unsigned v = to_var(e)->get_idx();
SASSERT(v < valid.size());
if (sorts[v].get()) {
SASSERT(s == sorts[v].get());
if (valid[v]) {
revsub[v] = w;
valid[v] = false;
}
else {
SASSERT(revsub[v].get());
SASSERT(m.get_sort(revsub[v].get()) == s);
conjs.push_back(m.mk_eq(revsub[v].get(), w));
}
}
}
else {
SASSERT(m.is_value(e));
SASSERT(m.get_sort(e) == m.get_sort(w));
conjs.push_back(m.mk_eq(e, w));
}
}
for (unsigned i = 0; i < sorts.size(); ++i) {
if (valid[i] && sorts[i].get() && !revsub[i].get()) {
revsub[i] = m.mk_var(m_idx++, sorts[i].get());
}
}
var_subst vs(m, false);
for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) {
vs(r->get_tail(i), revsub.size(), revsub.c_ptr(), result);
conjs.push_back(result);
}
bwr.mk_and(conjs.size(), conjs.c_ptr(), result);
}
void mk_coalesce::merge_rules(rule_ref& tgt, rule const& src) {
SASSERT(same_body(*tgt.get(), src));
m_sub1.reset();
m_sub2.reset();
m_idx = 0;
app_ref pred(m), head(m);
expr_ref fml1(m), fml2(m), fml(m);
app_ref_vector tail(m);
sort_ref_vector sorts1(m), sorts2(m);
expr_ref_vector conjs1(m), conjs(m);
rule_ref res(rm);
bool_rewriter bwr(m);
svector<bool> is_neg;
tgt->get_vars(sorts1);
src.get_vars(sorts2);
mk_pred(head, src.get_head(), tgt->get_head());
for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) {
mk_pred(pred, src.get_tail(i), tgt->get_tail(i));
tail.push_back(pred);
is_neg.push_back(src.is_neg_tail(i));
}
extract_conjs(m_sub1, src, fml1);
extract_conjs(m_sub2, *tgt.get(), fml2);
bwr.mk_or(fml1, fml2, fml);
SASSERT(is_app(fml));
tail.push_back(to_app(fml));
is_neg.push_back(false);
res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name());
if (m_pc) {
src.to_formula(fml1);
tgt->to_formula(fml2);
res->to_formula(fml);
#if 0
sort* ps = m.mk_proof_sort();
sort* domain[3] = { ps, ps, m.mk_bool_sort() };
func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps); // TBD: ad-hoc proof rule
expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml };
m_pc->insert(m.mk_app(merge, 3, args));
#else
svector<std::pair<unsigned, unsigned> > pos;
vector<expr_ref_vector> substs;
proof* p = m.mk_asserted(fml1);
m_pc->insert(m.mk_hyper_resolve(1, &p, fml, pos, substs));
#endif
}
tgt = res;
}
bool mk_coalesce::same_body(rule const& r1, rule const& r2) const {
SASSERT(r1.get_decl() == r2.get_decl());
unsigned sz = r1.get_uninterpreted_tail_size();
if (sz != r2.get_uninterpreted_tail_size()) {
return false;
}
for (unsigned i = 0; i < sz; ++i) {
if (r1.get_decl(i) != r2.get_decl(i)) {
return false;
}
if (r1.is_neg_tail(i) != r2.is_neg_tail(i)) {
return false;
}
}
return true;
}
rule_set * mk_coalesce::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
m_pc = 0;
ref<replace_proof_converter> rpc;
if (pc) {
rpc = alloc(replace_proof_converter, m);
m_pc = rpc.get();
}
rule_set* rules = alloc(rule_set, m_ctx);
rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules();
bool change = false;
for (; it != end; ++it) {
func_decl* p = it->m_key;
rule_ref_vector d_rules(rm);
d_rules.append(it->m_value->size(), it->m_value->c_ptr());
for (unsigned i = 0; i < d_rules.size(); ++i) {
rule_ref r1(d_rules[i].get(), rm);
for (unsigned j = i + 1; j < d_rules.size(); ++j) {
if (same_body(*r1.get(), *d_rules[j].get())) {
merge_rules(r1, *d_rules[j].get());
d_rules[j] = d_rules.back();
d_rules.pop_back();
change = true;
--j;
}
}
rules->add_rule(r1.get());
}
}
if (pc) {
pc = concat(pc.get(), rpc.get());
}
return rules;
}
};

View file

@ -1,62 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_coalesce.h
Abstract:
Coalesce rules with shared bodies.
Author:
Nikolaj Bjorner (nbjorner) 2012-10-15
Revision History:
--*/
#ifndef _DL_MK_COALESCE_H_
#define _DL_MK_COALESCE_H_
#include"dl_context.h"
#include"dl_rule_set.h"
#include"uint_set.h"
#include"dl_rule_transformer.h"
#include"dl_mk_rule_inliner.h"
namespace datalog {
/**
\brief Implements an unfolding transformation.
*/
class mk_coalesce : public rule_transformer::plugin {
context& m_ctx;
ast_manager& m;
rule_manager& rm;
expr_ref_vector m_sub1, m_sub2;
unsigned m_idx;
replace_proof_converter* m_pc;
void mk_pred(app_ref& pred, app* p1, app* p2);
void extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result);
bool same_body(rule const& r1, rule const& r2) const;
void merge_rules(rule_ref& tgt, rule const& src);
public:
/**
\brief Create coalesced rules.
*/
mk_coalesce(context & ctx);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_COALESCE_H_ */

View file

@ -1,107 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_coi_filter.cpp
Abstract:
Rule transformer which removes relations which are out of the cone of
influence of output relations
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#include <sstream>
#include"ast_pp.h"
#include"dl_mk_coi_filter.h"
#include"elim_var_model_converter.h"
namespace datalog {
// -----------------------------------
//
// mk_coi_filter
//
// -----------------------------------
rule_set * mk_coi_filter::operator()(
rule_set const & source,
model_converter_ref& mc,
proof_converter_ref& pc)
{
if (source.get_num_rules()==0) {
return 0;
}
decl_set interesting_preds;
decl_set pruned_preds;
ptr_vector<func_decl> todo;
{
const decl_set& output_preds = m_context.get_output_predicates();
decl_set::iterator oend = output_preds.end();
for (decl_set::iterator it = output_preds.begin(); it!=oend; ++it) {
todo.push_back(*it);
interesting_preds.insert(*it);
}
}
const rule_dependencies& deps = source.get_dependencies();
while (!todo.empty()) {
func_decl * curr = todo.back();
todo.pop_back();
interesting_preds.insert(curr);
const rule_dependencies::item_set& cdeps = deps.get_deps(curr);
rule_dependencies::item_set::iterator dend = cdeps.end();
for (rule_dependencies::item_set::iterator it = cdeps.begin(); it!=dend; ++it) {
func_decl * dep_pred = *it;
if (!interesting_preds.contains(dep_pred)) {
interesting_preds.insert(dep_pred);
todo.push_back(dep_pred);
}
}
}
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
rule_set::iterator rend = source.end();
for (rule_set::iterator rit = source.begin(); rit!=rend; ++rit) {
rule * r = *rit;
func_decl * pred = r->get_decl();
if (interesting_preds.contains(pred)) {
res->add_rule(r);
}
else if (mc.get()) {
pruned_preds.insert(pred);
}
}
if (res->get_num_rules() == source.get_num_rules()) {
res = 0;
}
if (res && mc) {
decl_set::iterator end = pruned_preds.end();
decl_set::iterator it = pruned_preds.begin();
elim_var_model_converter* mc0 = alloc(elim_var_model_converter, m);
for (; it != end; ++it) {
mc0->insert(*it, m.mk_true());
}
mc = concat(mc.get(), mc0);
}
return res.detach();
}
};

View file

@ -1,50 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_coi_filter.h
Abstract:
Rule transformer which removes relations which are out of the cone of
influence of output relations
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#ifndef _DL_MK_COI_FILTER_H_
#define _DL_MK_COI_FILTER_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
namespace datalog {
class mk_coi_filter : public rule_transformer::plugin {
typedef obj_map<func_decl, func_decl *> decl_map;
ast_manager & m;
context & m_context;
public:
mk_coi_filter(context & ctx, unsigned priority=45000)
: plugin(priority),
m(ctx.get_manager()),
m_context(ctx) {}
rule_set * operator()(rule_set const & source,
model_converter_ref& mc,
proof_converter_ref& pc);
};
};
#endif /* _DL_MK_COI_FILTER_H_ */

View file

@ -1,893 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_explanations.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-11-08.
Revision History:
--*/
#include <sstream>
#include"ast_pp.h"
#include "ast_smt_pp.h"
#include"dl_finite_product_relation.h"
#include"dl_product_relation.h"
#include"dl_sieve_relation.h"
#include"dl_mk_explanations.h"
namespace datalog {
// -----------------------------------
//
// explanation_relation_plugin declaration
//
// -----------------------------------
class explanation_relation;
class explanation_relation_plugin : public relation_plugin {
friend class explanation_relation;
class join_fn;
class project_fn;
class rename_fn;
class union_fn;
class foreign_union_fn;
class assignment_filter_fn;
class negation_filter_fn;
class intersection_filter_fn;
bool m_relation_level_explanations;
func_decl_ref m_union_decl;
vector<ptr_vector<explanation_relation> > m_pool;
app * mk_union(app * a1, app * a2) {
return get_ast_manager().mk_app(m_union_decl, a1, a2);
}
public:
static symbol get_name(bool relation_level) {
return symbol(relation_level ? "relation_explanation" : "fact_explanation");
}
explanation_relation_plugin(bool relation_level, relation_manager & manager)
: relation_plugin(get_name(relation_level), manager),
m_relation_level_explanations(relation_level),
m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {}
~explanation_relation_plugin() {
for (unsigned i = 0; i < m_pool.size(); ++i) {
for (unsigned j = 0; j < m_pool[i].size(); ++j) {
dealloc(m_pool[i][j]);
}
}
}
virtual bool can_handle_signature(const relation_signature & s) {
unsigned n=s.size();
for(unsigned i=0; i<n; i++) {
if(!get_context().get_decl_util().is_rule_sort(s[i])) {
return false;
}
}
return true;
}
virtual relation_base * mk_empty(const relation_signature & s);
void recycle(explanation_relation* r);
protected:
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols);
virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t,
const relation_base & src, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * src_cols);
};
// -----------------------------------
//
// explanation_relation
//
// -----------------------------------
class explanation_relation : public relation_base {
friend class explanation_relation_plugin;
friend class explanation_relation_plugin::join_fn;
friend class explanation_relation_plugin::project_fn;
friend class explanation_relation_plugin::rename_fn;
friend class explanation_relation_plugin::union_fn;
friend class explanation_relation_plugin::foreign_union_fn;
friend class explanation_relation_plugin::assignment_filter_fn;
friend class explanation_relation_plugin::intersection_filter_fn;
bool m_empty;
/**
Valid only if \c !m_empty.
Zero elements mean undefined.
*/
relation_fact m_data;
explanation_relation(explanation_relation_plugin & p, const relation_signature & s)
: relation_base(p, s), m_empty(true), m_data(p.get_ast_manager()) {
DEBUG_CODE(
unsigned sz = s.size();
for(unsigned i=0;i<sz; i++) {
SASSERT( p.get_context().get_decl_util().is_rule_sort(s[i]) );
}
);
}
void assign_data(const relation_fact & f) {
m_empty = false;
unsigned n=get_signature().size();
SASSERT(f.size()==n);
m_data.reset();
m_data.append(n, f.c_ptr());
}
void set_undefined() {
m_empty = false;
m_data.reset();
m_data.resize(get_signature().size());
}
void unite_with_data(const relation_fact & f) {
if(empty()) {
assign_data(f);
return;
}
unsigned n=get_signature().size();
SASSERT(f.size()==n);
for(unsigned i=0; i<n; i++) {
SASSERT(!is_undefined(i));
m_data[i] = get_plugin().mk_union(m_data[i], f[i]);
}
}
#if 1
virtual void deallocate() {
get_plugin().recycle(this);
}
#endif
public:
explanation_relation_plugin & get_plugin() const {
return static_cast<explanation_relation_plugin &>(relation_base::get_plugin());
}
virtual void to_formula(expr_ref& fml) const {
ast_manager& m = fml.get_manager();
fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]);
}
bool is_undefined(unsigned col_idx) const {
return m_data[col_idx]==0;
}
bool no_undefined() const {
if(empty()) {
return true;
}
unsigned n = get_signature().size();
for(unsigned i=0; i<n; i++) {
if(is_undefined(i)) {
return false;
}
}
return true;
}
virtual bool empty() const { return m_empty; }
virtual void reset() {
m_empty = true;
}
virtual void add_fact(const relation_fact & f) {
SASSERT(empty());
assign_data(f);
}
virtual bool contains_fact(const relation_fact & f) const {
UNREACHABLE();
throw 0;
}
virtual explanation_relation * clone() const {
explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
res->m_empty = m_empty;
SASSERT(res->m_data.empty());
res->m_data.append(m_data);
return res;
}
virtual relation_base * complement(func_decl* pred) const {
explanation_relation * res = static_cast<explanation_relation *>(get_plugin().mk_empty(get_signature()));
if(empty()) {
res->set_undefined();
}
return res;
}
void display_explanation(app * expl, std::ostream & out) const {
if(expl) {
//TODO: some nice explanation output
ast_smt_pp pp(get_plugin().get_ast_manager());
pp.display_expr_smt2(out, expl);
}
else {
out << "<undefined>";
}
}
virtual void display(std::ostream & out) const {
if(empty()) {
out << "<empty explanation relation>\n";
return;
}
unsigned sz = get_signature().size();
for(unsigned i=0; i<sz; i++) {
if(i!=0) {
out << ", ";
}
display_explanation(m_data[0], out);
}
out << "\n";
}
virtual unsigned get_size_estimate() const { return empty() ? 0 : 1; }
};
// -----------------------------------
//
// explanation_relation_plugin
//
// -----------------------------------
relation_base * explanation_relation_plugin::mk_empty(const relation_signature & s) {
if (m_pool.size() > s.size() && !m_pool[s.size()].empty()) {
explanation_relation* r = m_pool[s.size()].back();
m_pool[s.size()].pop_back();
r->m_empty = true;
r->m_data.reset();
return r;
}
return alloc(explanation_relation, *this, s);
}
void explanation_relation_plugin::recycle(explanation_relation* r) {
relation_signature const& sig = r->get_signature();
if (m_pool.size() <= sig.size()) {
m_pool.resize(sig.size()+1);
}
m_pool[sig.size()].push_back(r);
}
class explanation_relation_plugin::join_fn : public convenient_relation_join_fn {
public:
join_fn(const relation_signature & sig1, const relation_signature & sig2)
: convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {}
virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) {
const explanation_relation & r1 = static_cast<const explanation_relation &>(r1_0);
const explanation_relation & r2 = static_cast<const explanation_relation &>(r2_0);
explanation_relation_plugin & plugin = r1.get_plugin();
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
if(!r1.empty() && !r2.empty()) {
res->m_empty = false;
SASSERT(res->m_data.empty());
res->m_data.append(r1.m_data);
res->m_data.append(r2.m_data);
}
return res;
}
};
relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if(&r1.get_plugin()!=this || &r2.get_plugin()!=this) {
return 0;
}
if(col_cnt!=0) {
return 0;
}
return alloc(join_fn, r1.get_signature(), r2.get_signature());
}
class explanation_relation_plugin::project_fn : public convenient_relation_project_fn {
public:
project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(sig, col_cnt, removed_cols) {}
virtual relation_base * operator()(const relation_base & r0) {
const explanation_relation & r = static_cast<const explanation_relation &>(r0);
explanation_relation_plugin & plugin = r.get_plugin();
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
if(!r.empty()) {
relation_fact proj_data = r.m_data;
project_out_vector_columns(proj_data, m_removed_cols);
res->assign_data(proj_data);
}
return res;
}
};
relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt,
const unsigned * removed_cols) {
if(&r.get_plugin()!=this) {
return 0;
}
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
}
class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn {
public:
rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle)
: convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {}
virtual relation_base * operator()(const relation_base & r0) {
const explanation_relation & r = static_cast<const explanation_relation &>(r0);
explanation_relation_plugin & plugin = r.get_plugin();
explanation_relation * res = static_cast<explanation_relation *>(plugin.mk_empty(get_result_signature()));
if(!r.empty()) {
relation_fact permutated_data = r.m_data;
permutate_by_cycle(permutated_data, m_cycle);
res->assign_data(permutated_data);
}
return res;
}
};
relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r,
unsigned permutation_cycle_len, const unsigned * permutation_cycle) {
return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle);
}
class explanation_relation_plugin::union_fn : public relation_union_fn {
scoped_ptr<relation_union_fn> m_delta_union_fun;
public:
virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) {
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : 0;
explanation_relation_plugin & plugin = tgt.get_plugin();
if(!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) {
UNREACHABLE();
}
if(src.empty()) {
return;
}
if(plugin.m_relation_level_explanations) {
tgt.unite_with_data(src.m_data);
if(delta) {
if(!m_delta_union_fun) {
m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src);
SASSERT(m_delta_union_fun);
}
(*m_delta_union_fun)(*delta, src);
}
}
else {
if(tgt.empty()) {
tgt.assign_data(src.m_data);
if(delta && delta->empty()) {
delta->assign_data(src.m_data);
}
}
}
}
};
class explanation_relation_plugin::foreign_union_fn : public relation_union_fn {
scoped_ptr<relation_union_fn> m_delta_union_fun;
public:
virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) {
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
explanation_relation * delta = delta0 ? static_cast<explanation_relation *>(delta0) : 0;
if(src.empty()) {
return;
}
tgt.set_undefined();
if(delta) {
delta->set_undefined();
}
}
};
relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if(!check_kind(tgt) || (delta && !check_kind(*delta))) {
return 0;
}
if(!check_kind(src)) {
//this is to handle the product relation
return alloc(foreign_union_fn);
}
return alloc(union_fn);
}
class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn {
ast_manager & m_manager;
var_subst & m_subst;
unsigned m_col_idx;
app_ref m_new_rule;
public:
assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule)
: m_manager(ctx.get_manager()),
m_subst(ctx.get_var_subst()),
m_col_idx(col_idx),
m_new_rule(new_rule) {}
virtual void operator()(relation_base & r0) {
explanation_relation & r = static_cast<explanation_relation &>(r0);
if(!r.is_undefined(m_col_idx)) {
UNREACHABLE();
}
unsigned sz = r.get_signature().size();
ptr_vector<expr> subst_arg;
subst_arg.resize(sz, 0);
unsigned ofs = sz-1;
for(unsigned i=0; i<sz; i++) {
SASSERT(!r.is_undefined(i) || !contains_var(m_new_rule, i));
subst_arg[ofs-i] = r.m_data.get(i);
}
expr_ref res(m_manager);
m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr(), res);
r.m_data[m_col_idx] = to_app(res);
}
};
relation_mutator_fn * explanation_relation_plugin::mk_filter_interpreted_fn(const relation_base & r,
app * cond) {
if(&r.get_plugin()!=this) {
return 0;
}
ast_manager & m = get_ast_manager();
if(!m.is_eq(cond)) {
return 0;
}
expr * arg1 = cond->get_arg(0);
expr * arg2 = cond->get_arg(1);
if(is_var(arg2)) {
std::swap(arg1, arg2);
}
if(!is_var(arg1) || !is_app(arg2)) {
return 0;
}
var * col_var = to_var(arg1);
app * new_rule = to_app(arg2);
if(!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) {
return 0;
}
unsigned col_idx = col_var->get_idx();
return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager()));
}
class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn {
public:
virtual void operator()(relation_base & r, const relation_base & neg) {
if(!neg.empty()) {
r.reset();
}
}
};
relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r,
const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols,
const unsigned * negated_cols) {
if(&r.get_plugin()!=this || &neg.get_plugin()!=this) {
return 0;
}
return alloc(negation_filter_fn);
}
class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn {
explanation_relation_plugin & m_plugin;
func_decl_ref m_union_decl;
public:
intersection_filter_fn(explanation_relation_plugin & plugin)
: m_plugin(plugin), m_union_decl(plugin.m_union_decl) {}
virtual void operator()(relation_base & tgt0, const relation_base & src0) {
explanation_relation & tgt = static_cast<explanation_relation &>(tgt0);
const explanation_relation & src = static_cast<const explanation_relation &>(src0);
if(src.empty()) {
tgt.reset();
return;
}
if(tgt.empty()) {
return;
}
unsigned sz = tgt.get_signature().size();
for(unsigned i=0; i<sz; i++) {
if(src.is_undefined(i)) {
continue;
}
app * curr_src = src.m_data.get(i);
if(tgt.is_undefined(i)) {
tgt.m_data.set(i, curr_src);
continue;
}
app * curr_tgt = tgt.m_data.get(i);
if(curr_tgt->get_decl()==m_union_decl.get()) {
if(curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) {
tgt.m_data.set(i, curr_src);
continue;
}
}
//the intersection is imprecise because we do nothing here, but it is good enough for
//the purpose of explanations
}
}
};
relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn(
const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt,
const unsigned * tgt_cols, const unsigned * src_cols) {
if(&tgt.get_plugin()!=this || &src.get_plugin()!=this) {
return 0;
}
//this checks the join is one to one on all columns
if(tgt.get_signature()!=src.get_signature()
|| joined_col_cnt!=tgt.get_signature().size()
|| !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) {
return 0;
}
counter ctr;
ctr.count(joined_col_cnt, tgt_cols);
if(ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) {
return 0;
}
return alloc(intersection_filter_fn, *this);
}
// -----------------------------------
//
// mk_explanations
//
// -----------------------------------
mk_explanations::mk_explanations(context & ctx, bool relation_level)
: plugin(50000),
m_manager(ctx.get_manager()),
m_context(ctx),
m_decl_util(ctx.get_decl_util()),
m_relation_level(relation_level),
m_pinned(m_manager) {
m_e_sort = m_decl_util.mk_rule_sort();
m_pinned.push_back(m_e_sort);
relation_manager & rmgr = ctx.get_rmanager();
symbol er_symbol = explanation_relation_plugin::get_name(relation_level);
m_er_plugin = static_cast<explanation_relation_plugin *>(rmgr.get_relation_plugin(er_symbol));
if(!m_er_plugin) {
m_er_plugin = alloc(explanation_relation_plugin, relation_level, rmgr);
rmgr.register_plugin(m_er_plugin);
if(!m_relation_level) {
DEBUG_CODE(
finite_product_relation_plugin * dummy;
SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
);
rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr));
}
}
DEBUG_CODE(
if(!m_relation_level) {
finite_product_relation_plugin * dummy;
SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy));
}
);
}
func_decl * mk_explanations::get_union_decl(context & ctx) {
ast_manager & m = ctx.get_manager();
sort_ref s(ctx.get_decl_util().mk_rule_sort(), m);
//can it happen that the function name would collide with some other symbol?
//if functions can be overloaded by their ranges, it should be fine.
return m.mk_func_decl(symbol("e_union"), s, s, s);
}
void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) {
SASSERT(m_relation_level);
relation_manager & rmgr = m_context.get_rmanager();
unsigned sz = e_decl->get_arity();
relation_signature sig;
rmgr.from_predicate(e_decl, sig);
svector<bool> inner_sieve(sz-1, true);
inner_sieve.push_back(false);
svector<bool> expl_sieve(sz-1, false);
expl_sieve.push_back(true);
sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr);
family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id
family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind);
family_id expl_kind = m_er_plugin->get_kind();
family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind);
product_relation_plugin::rel_spec product_spec;
product_spec.push_back(inner_sieve_kind);
product_spec.push_back(expl_sieve_kind);
family_id pred_kind =
product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec);
rmgr.set_predicate_kind(e_decl, pred_kind);
}
func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) {
decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0);
if(e->get_data().m_value==0) {
relation_signature e_domain;
e_domain.append(orig_decl->get_arity(), orig_decl->get_domain());
e_domain.push_back(m_e_sort);
func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"),
e_domain.size(), e_domain.c_ptr(), orig_decl);
m_pinned.push_back(new_decl);
e->get_data().m_value = new_decl;
if(m_relation_level) {
assign_rel_level_kind(new_decl, orig_decl);
}
}
return e->get_data().m_value;
}
app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) {
expr_ref_vector args(m_manager);
func_decl * e_decl = get_e_decl(lit->get_decl());
args.append(lit->get_num_args(), lit->get_args());
args.push_back(m_manager.mk_var(e_var_idx, m_e_sort));
return m_manager.mk_app(e_decl, args.c_ptr());
}
symbol mk_explanations::get_rule_symbol(rule * r) {
if (r->name() == symbol::null) {
std::stringstream sstm;
r->display(m_context, sstm);
std::string res = sstm.str();
res = res.substr(0, res.find_last_not_of('\n')+1);
return symbol(res.c_str());
}
else {
return r->name();
}
}
rule * mk_explanations::get_e_rule(rule * r) {
var_counter ctr;
ctr.count_vars(m_manager, r);
unsigned max_var;
unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0;
unsigned head_var = next_var++;
app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager);
app_ref_vector e_tail(m_manager);
svector<bool> neg_flags;
unsigned pos_tail_sz = r->get_positive_tail_size();
for(unsigned i=0; i<pos_tail_sz; i++) {
unsigned e_var = next_var++;
e_tail.push_back(get_e_lit(r->get_tail(i), e_var));
neg_flags.push_back(false);
}
unsigned tail_sz = r->get_tail_size();
for(unsigned i=pos_tail_sz; i<tail_sz; i++) {
e_tail.push_back(r->get_tail(i));
neg_flags.push_back(r->is_neg_tail(i));
}
symbol rule_repr = get_rule_symbol(r);
expr_ref_vector rule_expr_args(m_manager);
for(unsigned tail_idx=0; tail_idx<pos_tail_sz; tail_idx++) {
app * tail = e_tail.get(tail_idx);
if(true || m_relation_level) {
//this adds the explanation term of the tail
rule_expr_args.push_back(tail->get_arg(tail->get_num_args()-1));
}
else {
//this adds argument values and the explanation term
//(values will be substituted for variables at runtime by the finite_product_relation)
rule_expr_args.append(tail->get_num_args(), tail->get_args());
}
}
//rule_expr contains rule function with string representation of the rule as symbol and
//for each positive uninterpreted tail it contains its argument values and its explanation term
expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr());
app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager);
e_tail.push_back(e_record);
neg_flags.push_back(false);
SASSERT(e_tail.size()==neg_flags.size());
return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr());
}
void mk_explanations::transform_rules(const rule_set & orig, rule_set & tgt) {
rule_set::iterator rit = orig.begin();
rule_set::iterator rend = orig.end();
for(; rit!=rend; ++rit) {
rule * e_rule = get_e_rule(*rit);
tgt.add_rule(e_rule);
}
//add rules that will (for output predicates) copy facts from explained relations back to
//the original ones
expr_ref_vector lit_args(m_manager);
decl_set::iterator pit = m_original_preds.begin();
decl_set::iterator pend = m_original_preds.end();
for(; pit!=pend; ++pit) {
func_decl * orig_decl = *pit;
if(!m_context.is_output_predicate(orig_decl)) {
continue;
}
lit_args.reset();
unsigned arity = orig_decl->get_arity();
for(unsigned i=0; i<arity; i++) {
lit_args.push_back(m_manager.mk_var(i, orig_decl->get_domain(i)));
}
app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager);
app_ref e_lit(get_e_lit(orig_lit, arity), m_manager);
app * tail[] = { e_lit.get() };
tgt.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0));
}
}
void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig,
relation_base & e_rel) {
SASSERT(m_e_fact_relation);
SASSERT(e_rel.get_plugin().is_product_relation());
product_relation & prod_rel = static_cast<product_relation &>(e_rel);
SASSERT(prod_rel.size()==2);
SASSERT(prod_rel[0].get_plugin().is_sieve_relation());
SASSERT(prod_rel[1].get_plugin().is_sieve_relation());
sieve_relation * srels[] = {
static_cast<sieve_relation *>(&prod_rel[0]),
static_cast<sieve_relation *>(&prod_rel[1]) };
if(&srels[0]->get_inner().get_plugin()==m_er_plugin) {
std::swap(srels[0], srels[1]);
}
SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin());
SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin);
relation_base & new_orig = srels[0]->get_inner();
explanation_relation & expl_rel = static_cast<explanation_relation &>(srels[1]->get_inner());
{
scoped_ptr<relation_union_fn> orig_union_fun = rmgr.mk_union_fn(new_orig, orig);
SASSERT(orig_union_fun);
(*orig_union_fun)(new_orig, orig);
}
{
scoped_ptr<relation_union_fn> expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation);
SASSERT(expl_union_fun);
(*expl_union_fun)(expl_rel, *m_e_fact_relation);
}
}
void mk_explanations::transform_facts(relation_manager & rmgr) {
if(!m_e_fact_relation) {
relation_signature expl_singleton_sig;
expl_singleton_sig.push_back(m_e_sort);
relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind());
relation_fact es_fact(m_manager);
es_fact.push_back(m_decl_util.mk_fact(symbol("fact")));
expl_singleton->add_fact(es_fact);
SASSERT(&expl_singleton->get_plugin()==m_er_plugin);
m_e_fact_relation = static_cast<explanation_relation *>(expl_singleton);
}
decl_set::iterator it = m_original_preds.begin();
decl_set::iterator end = m_original_preds.end();
for(; it!=end; ++it) {
func_decl * orig_decl = *it;
func_decl * e_decl = get_e_decl(orig_decl);
if(m_context.is_output_predicate(orig_decl)) {
m_context.set_output_predicate(e_decl);
}
if(!rmgr.try_get_relation(orig_decl)) {
//there are no facts for this predicate
continue;
}
relation_base & orig_rel = rmgr.get_relation(orig_decl);
relation_base & e_rel = rmgr.get_relation(e_decl);
SASSERT(e_rel.empty()); //the e_rel should be a new relation
if(m_relation_level) {
translate_rel_level_relation(rmgr, orig_rel, e_rel);
}
else {
scoped_ptr<relation_join_fn> product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0);
SASSERT(product_fun);
scoped_rel<relation_base> aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation);
scoped_ptr<relation_union_fn> union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel);
SASSERT(union_fun);
(*union_fun)(e_rel, *aux_extended_rel);
}
}
}
rule_set * mk_explanations::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
SASSERT(!mc && !pc);
if(source.get_num_rules()==0) {
return 0;
}
m_context.collect_predicates(m_original_preds);
rule_set * res = alloc(rule_set, m_context);
transform_facts(m_context.get_rmanager());
transform_rules(source, *res);
return res;
}
};

View file

@ -1,92 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_explanations.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-11-08.
Revision History:
--*/
#ifndef _DL_MK_EXPLANATIONS_H_
#define _DL_MK_EXPLANATIONS_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
namespace datalog {
class explanation_relation;
class explanation_relation_plugin;
class mk_explanations : public rule_transformer::plugin {
typedef obj_map<func_decl, func_decl *> decl_map;
ast_manager & m_manager;
context & m_context;
dl_decl_util & m_decl_util;
bool m_relation_level;
decl_set m_original_preds;
ast_ref_vector m_pinned;
explanation_relation_plugin * m_er_plugin;
sort * m_e_sort;
scoped_rel<explanation_relation> m_e_fact_relation;
decl_map m_e_decl_map;
symbol get_rule_symbol(rule * r);
app * get_e_lit(app * lit, unsigned e_var_idx);
rule * get_e_rule(rule * r);
/**
If \c m_relation_level is true, ensure \c e_decl predicate will be represented by
the right relation object. \c orig is the predicate corresponding to \c e_decl without
the explanation column.
*/
void assign_rel_level_kind(func_decl * e_decl, func_decl * orig);
void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel);
void transform_rules(const rule_set & orig, rule_set & tgt);
void transform_facts(relation_manager & rmgr);
public:
/**
If relation_level is true, the explanation will not be stored for each fact,
but we will rather store history of the whole relation.
*/
mk_explanations(context & ctx, bool relation_level);
/**
\brief Return explanation predicate that corresponds to \c orig_decl.
*/
func_decl * get_e_decl(func_decl * orig_decl);
static func_decl * get_union_decl(context & ctx);
func_decl * get_union_decl() const {
return get_union_decl(m_context);
}
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
static expr* get_explanation(relation_base const& r);
};
};
#endif /* _DL_MK_EXPLANATIONS_H_ */

View file

@ -1,168 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_filter_rules.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-18.
Revision History:
--*/
#include"dl_mk_filter_rules.h"
#include"dl_context.h"
#include"for_each_expr.h"
#include"ast_pp.h"
namespace datalog {
mk_filter_rules::mk_filter_rules(context & ctx):
plugin(2000),
m_context(ctx),
m_manager(ctx.get_manager()),
m_result(0),
m_pinned(m_manager) {
}
mk_filter_rules::~mk_filter_rules() {
ptr_vector<filter_key> to_dealloc;
filter_cache::iterator it = m_tail2filter.begin();
filter_cache::iterator end = m_tail2filter.end();
for(; it!=end; ++it) {
to_dealloc.push_back(it->m_key);
}
m_tail2filter.reset();
ptr_vector<filter_key>::iterator dit = to_dealloc.begin();
ptr_vector<filter_key>::iterator dend = to_dealloc.end();
for(; dit!=dend; ++dit) {
dealloc(*dit);
}
}
/**
\brief Return true if \c pred is a cadidate for a "filter" rule.
*/
bool mk_filter_rules::is_candidate(app * pred) {
if (!m_context.get_rule_manager().is_predicate(pred)) {
TRACE("mk_filter_rules", tout << mk_pp(pred, m_manager) << "\nis not a candidate because it is interpreted.\n";);
return false;
}
var_idx_set used_vars;
unsigned n = pred->get_num_args();
for (unsigned i = 0; i < n; i++) {
expr * arg = pred->get_arg(i);
if (m_manager.is_value(arg))
return true;
SASSERT(is_var(arg));
unsigned vidx = to_var(arg)->get_idx();
if (used_vars.contains(vidx))
return true;
used_vars.insert(vidx);
}
return false;
}
/**
\brief Create a "filter" (if it doesn't exist already) for the given predicate.
*/
func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) {
sort_ref_buffer filter_domain(m_manager);
filter_key * key = alloc(filter_key, m_manager);
mk_new_rule_tail(m_manager, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred);
func_decl * filter_decl = 0;
if (!m_tail2filter.find(key, filter_decl)) {
filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"),
filter_domain.size(), filter_domain.c_ptr(), pred->get_decl());
m_pinned.push_back(filter_decl);
m_tail2filter.insert(key, filter_decl);
app_ref filter_head(m_manager);
filter_head = m_manager.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr());
app * filter_tail = key->new_pred;
rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0);
filter_rule->set_accounting_parent_object(m_context, m_current);
m_result->add_rule(filter_rule);
}
else {
dealloc(key);
}
SASSERT(filter_decl != 0);
SASSERT(filter_decl->get_arity()==filter_domain.size());
return filter_decl;
}
void mk_filter_rules::process(rule * r) {
m_current = r;
app * new_head = r->get_head();
app_ref_vector new_tail(m_manager);
svector<bool> new_is_negated;
unsigned sz = r->get_tail_size();
bool rule_modified = false;
for (unsigned i = 0; i < sz; i++) {
app * tail = r->get_tail(i);
if (is_candidate(tail)) {
TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m_manager) << "\n";);
var_idx_set non_local_vars;
collect_non_local_vars(m_manager, r, tail, non_local_vars);
func_decl * filter_decl = mk_filter_decl(tail, non_local_vars);
ptr_buffer<expr> new_args;
var_idx_set used_vars;
unsigned num_args = tail->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = tail->get_arg(i);
if (is_var(arg)) {
unsigned vidx = to_var(arg)->get_idx();
if (non_local_vars.contains(vidx) && !used_vars.contains(vidx)) {
new_args.push_back(arg);
used_vars.insert(vidx);
}
}
}
SASSERT(new_args.size() == filter_decl->get_arity());
new_tail.push_back(m_manager.mk_app(filter_decl, new_args.size(), new_args.c_ptr()));
rule_modified = true;
}
else {
new_tail.push_back(tail);
}
new_is_negated.push_back(r->is_neg_tail(i));
}
if(rule_modified) {
remove_duplicate_tails(new_tail, new_is_negated);
SASSERT(new_tail.size() == new_is_negated.size());
rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_is_negated.c_ptr());
new_rule->set_accounting_parent_object(m_context, m_current);
m_result->add_rule(new_rule);
m_modified = true;
}
else {
m_result->add_rule(r);
}
}
rule_set * mk_filter_rules::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
m_tail2filter.reset();
m_result = alloc(rule_set, m_context);
m_modified = false;
unsigned num_rules = source.get_num_rules();
for (unsigned i = 0; i < num_rules; i++) {
rule * r = source.get_rule(i);
process(r);
}
if(!m_modified) {
dealloc(m_result);
return static_cast<rule_set *>(0);
}
return m_result;
}
};

View file

@ -1,82 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_filter_rules.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-18.
Revision History:
--*/
#ifndef _DL_MK_FILTER_RULES_H_
#define _DL_MK_FILTER_RULES_H_
#include"map.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Functor for applying a rule set transformation that creates "filters".
A "filter" is a rule of the form:
Head(X_1, ..., X_n) :- Tail(...)
where X_1,...,X_n are distinct, and Tail contain repeated variables and/or values.
After applying this functor only "filter" rules will contain atoms with repeated variables and/or values.
*/
class mk_filter_rules : public rule_transformer::plugin {
struct filter_key {
app_ref new_pred;
expr_ref_buffer filter_args;
filter_key(ast_manager & m) : new_pred(m), filter_args(m) {}
unsigned hash() const {
return new_pred->hash() ^ int_vector_hash(filter_args);
}
bool operator==(const filter_key & o) const {
return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args);
}
};
typedef map<filter_key*, func_decl*, obj_ptr_hash<filter_key>, deref_eq<filter_key> > filter_cache;
context & m_context;
ast_manager & m_manager;
filter_cache m_tail2filter;
rule_set * m_result;
rule * m_current;
bool m_modified;
ast_ref_vector m_pinned;
bool is_candidate(app * pred);
func_decl * mk_filter_decl(app * pred, var_idx_set const & non_local_vars);
void process(rule * r);
public:
mk_filter_rules(context & ctx);
~mk_filter_rules();
/**
\brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values.
*/
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_FILTER_RULES_H_ */

View file

@ -1,569 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_interp_tail_simplifier.cpp
Abstract:
Rule transformer which simplifies interpreted tails
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#include <sstream>
#include"ast_pp.h"
#include"bool_rewriter.h"
#include"rewriter.h"
#include"rewriter_def.h"
#include"dl_mk_rule_inliner.h"
#include"dl_mk_interp_tail_simplifier.h"
namespace datalog {
// -----------------------------------
//
// mk_interp_tail_simplifier::rule_substitution
//
// -----------------------------------
void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) {
unsigned var_cnt = m_context.get_rule_manager().get_var_counter().get_max_var(*r)+1;
m_subst.reset();
m_subst.reserve(1, var_cnt);
m_rule = r;
}
bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) {
SASSERT(m_rule);
//we need to apply the current substitution in order to ensure the unifier
//works in an incremental way
expr_ref e1_s(m);
expr_ref e2_s(m);
m_subst.apply(e1,e1_s);
m_subst.apply(e2,e2_s);
//and we need to reset the cache as we're going to modify the substitution
m_subst.reset_cache();
return m_unif (e1_s, e2_s, m_subst, false);
}
void mk_interp_tail_simplifier::rule_substitution::apply(app * a, app_ref& res) {
SASSERT(m_rule);
expr_ref res_e(m);
m_subst.apply(a, res_e);
SASSERT(is_app(res_e.get()));
res = to_app(res_e.get());
}
void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) {
SASSERT(m_rule);
app_ref new_head(m);
apply(m_rule->get_head(), new_head);
app_ref_vector tail(m);
svector<bool> tail_neg;
unsigned tail_len = m_rule->get_tail_size();
for (unsigned i=0; i<tail_len; i++) {
app_ref new_tail_el(m);
apply(m_rule->get_tail(i), new_tail_el);
tail.push_back(new_tail_el);
tail_neg.push_back(m_rule->is_neg_tail(i));
}
mk_rule_inliner::remove_duplicate_tails(tail, tail_neg);
SASSERT(tail.size() == tail_neg.size());
res = m_context.get_rule_manager().mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
res->set_accounting_parent_object(m_context, m_rule);
res->norm_vars(res.get_manager());
}
// -----------------------------------
//
// mk_interp_tail_simplifier
//
// -----------------------------------
class mk_interp_tail_simplifier::normalizer_cfg : public default_rewriter_cfg
{
struct expr_cmp
{
ast_manager& m;
expr_cmp(ast_manager& m) : m(m) {}
bool operator()(expr * ae, expr * be) {
return cmp_expr(ae, be, 4) == -1;
}
template<typename T>
static int cmp(T a, T b) { return (a>b) ? 1 : ((a == b) ? 0 : -1); }
int cmp_expr(expr * ae, expr * be, int depth) {
if (ae == be) { return 0; }
//remove negations
bool a_neg = m.is_not(ae, ae);
bool b_neg = m.is_not(be, be);
if (ae==be) { return cmp(a_neg, b_neg); }
if (!is_app(ae) && !is_app(be)) { return cmp(ae->get_id(), be->get_id()); }
if (!is_app(ae)) { return -1; }
if (!is_app(be)) { return 1; }
app * a = to_app(ae);
app * b = to_app(be);
if (a->get_decl()!=b->get_decl()) {
return cmp(a->get_decl()->get_id(), b->get_decl()->get_id());
}
if (a->get_num_args()!=b->get_num_args()) {
return cmp(a->get_num_args(), b->get_num_args());
}
if (depth==0) {
return cmp(a->get_id(),b->get_id());
}
unsigned arg_cnt = a->get_num_args();
unsigned neg_comparison = 0;
for (unsigned i=0; i<arg_cnt; i++) {
expr * arg_a = a->get_arg(i);
expr * arg_b = b->get_arg(i);
//we normalize away negations
bool a_is_neg = m.is_not(arg_a, arg_a);
bool b_is_neg = m.is_not(arg_b, arg_b);
if (neg_comparison==0 && a_is_neg!=b_is_neg) {
neg_comparison = a_is_neg ? -1 : 1;
}
int res = cmp_expr(arg_a, arg_b, depth-1);
if (res!=0) {
return res;
}
}
if (neg_comparison!=0) {
return neg_comparison;
}
//by normalizing away negation we may have put non-equal terms to be equal, so here we check
return cmp(a->get_id(),b->get_id());
}
};
ast_manager& m;
bool_rewriter m_brwr;
//instead of a local variable
expr_ref_vector m_app_args;
expr_cmp m_expr_cmp;
public:
normalizer_cfg(ast_manager& m)
: m(m), m_brwr(m), m_app_args(m), m_expr_cmp(m)
{
}
static void remove_duplicates(expr_ref_vector& v)
{
expr * a = v[0].get();
unsigned read_idx = 1;
unsigned write_idx = 1;
for (;;) {
while(read_idx<v.size() && a==v[read_idx].get()) {
read_idx++;
}
if (read_idx==v.size()) {
break;
}
a = v[read_idx].get();
if (write_idx!=read_idx) {
v[write_idx] = a;
}
write_idx++;
read_idx++;
}
v.shrink(write_idx);
}
typedef std::pair<expr *,expr *> arg_pair;
bool match_arg_pair(expr * e, arg_pair& pair, bool seek_conjunction)
{
if (seek_conjunction) {
return m.is_and(e, pair.first, pair.second);
}
else {
return m.is_or(e, pair.first, pair.second);
}
}
/**
If inside_disjunction is false, we're inside a conjunction (and arg pairs
represent disjunctions).
*/
app * detect_equivalence(const arg_pair& p1, const arg_pair& p2, bool inside_disjunction)
{
if (m.is_not(p1.first)==m.is_not(p2.first)) { return 0; }
if (m.is_not(p1.second)==m.is_not(p2.second)) { return 0; }
expr * first_bare = 0;
if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return 0; }
if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return 0; }
SASSERT(first_bare);
expr * second_bare = 0;
if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return 0; }
if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return 0; }
SASSERT(second_bare);
if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return 0; }
//both negations are in the same pair
bool negs_together = m.is_not(p1.first)==m.is_not(p1.second);
if (negs_together==inside_disjunction) {
return m.mk_eq(first_bare, second_bare);
}
else {
return m.mk_eq(first_bare, m.mk_not(second_bare));
}
}
bool detect_equivalences(expr_ref_vector& v, bool inside_disjunction)
{
bool have_pair = false;
unsigned prev_pair_idx;
arg_pair ap;
unsigned read_idx = 0;
unsigned write_idx = 0;
while(read_idx<v.size()) {
expr * e = v[read_idx].get();
arg_pair new_ap;
if (match_arg_pair(e, new_ap, inside_disjunction)) {
app * neq = 0;
if (have_pair) {
neq = detect_equivalence(ap, new_ap, inside_disjunction);
}
if (neq) {
have_pair = false;
v[prev_pair_idx] = neq;
read_idx++;
continue;
}
else {
have_pair = true;
prev_pair_idx = write_idx;
ap = new_ap;
}
}
else {
have_pair = false;
}
if (write_idx!=read_idx) {
v[write_idx] = e;
}
read_idx++;
write_idx++;
}
v.shrink(write_idx);
return read_idx!=write_idx;
}
//bool detect_same_variable_conj_pairs
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result,
proof_ref & result_pr)
{
if (m.is_not(f)) {
SASSERT(num==1);
if (m.is_and(args[0]) || m.is_or(args[0])) {
expr_ref e(m.mk_not(args[0]),m);
if (push_toplevel_junction_negation_inside(e)) {
result = e;
return BR_REWRITE2;
}
}
}
if (!m.is_and(f) && !m.is_or(f)) { return BR_FAILED; }
if (num<2) { return BR_FAILED; }
m_app_args.reset();
m_app_args.append(num, args);
std::sort(m_app_args.c_ptr(), m_app_args.c_ptr()+m_app_args.size(), m_expr_cmp);
remove_duplicates(m_app_args);
bool have_rewritten_args = false;
if (m.is_or(f) || m.is_and(f)) {
have_rewritten_args = detect_equivalences(m_app_args, m.is_or(f));
#if 0
if (have_rewritten_args) {
std::sort(m_app_args.c_ptr(), m_app_args.c_ptr()+m_app_args.size(), m_expr_cmp);
app_ref orig(m.mk_app(f, num, args),m);
app_ref res(m.mk_app(f, m_app_args.size(), m_app_args.c_ptr()),m);
std::cout<<"s:"<<mk_pp(orig, m)<<"\n";
std::cout<<"t:"<<mk_pp(res, m)<<"\n";
}
#endif
}
if (m_app_args.size()==1) {
result = m_app_args[0].get();
}
else {
if (m.is_and(f)) {
m_brwr.mk_and(m_app_args.size(), m_app_args.c_ptr(), result);
}
else {
SASSERT(m.is_or(f));
m_brwr.mk_or(m_app_args.size(), m_app_args.c_ptr(), result);
}
}
if (have_rewritten_args) {
return BR_REWRITE1;
}
return BR_DONE;
}
};
void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res)
{
expr_ref simp1_res(m);
m_simp(a, simp1_res);
normalizer_cfg r_cfg(m);
rewriter_tpl<normalizer_cfg> rwr(m, false, r_cfg);
expr_ref dl_form_e(m);
rwr(simp1_res.get(), res);
/*if (simp1_res.get()!=res.get()) {
std::cout<<"pre norm:\n"<<mk_pp(simp1_res.get(),m)<<"post norm:\n"<<mk_pp(res.get(),m)<<"\n";
}*/
m_simp(res.get(), res);
}
bool mk_interp_tail_simplifier::propagate_variable_equivalences(rule * r, rule_ref& res) {
unsigned u_len = r->get_uninterpreted_tail_size();
unsigned len = r->get_tail_size();
if (u_len==len) {
return false;
}
ptr_vector<expr> todo;
for (unsigned i=u_len; i<len; i++) {
todo.push_back(r->get_tail(i));
SASSERT(!r->is_neg_tail(i));
}
m_rule_subst.reset(r);
bool found_something = false;
#define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; }
#define IS_FLEX(_x) (is_var(_x) || m.is_value(_x))
while (!todo.empty()) {
expr * arg1, *arg2;
expr * t0 = todo.back();
todo.pop_back();
expr* t = t0;
bool neg = m.is_not(t, t);
if (is_var(t)) {
TRY_UNIFY(t, neg ? m.mk_false() : m.mk_true());
}
else if (!neg && m.is_and(t)) {
app* a = to_app(t);
todo.append(a->get_num_args(), a->get_args());
}
else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) {
TRY_UNIFY(arg1, arg2);
}
else if (m.is_iff(t, arg1, arg2)) {
//determine the polarity of the equivalence and remove the negations
while (m.is_not(arg1, arg1)) neg = !neg;
while (m.is_not(arg2, arg2)) neg = !neg;
if (!is_var(arg1)) {
std::swap(arg1, arg2);
}
if (!IS_FLEX(arg1) || !IS_FLEX(arg2)) {
// no-op
}
else if (is_var(arg1) && !neg) {
TRY_UNIFY(arg1, arg2);
}
else if (is_var(arg1) && neg && m.is_true(arg2)) {
TRY_UNIFY(arg1, m.mk_false());
}
else if (is_var(arg1) && neg && m.is_false(arg2)) {
TRY_UNIFY(arg1, m.mk_true());
}
}
}
if (!found_something) {
return false;
}
TRACE("dl_interp_tail_simplifier_propagation_pre",
tout << "will propagate rule:\n";
r->display(m_context, tout);
);
m_rule_subst.get_result(res);
TRACE("dl_interp_tail_simplifier_propagation",
tout << "propagated equivalences of:\n";
r->display(m_context, tout);
tout << "into:\n";
res->display(m_context, tout);
);
return true;
}
bool mk_interp_tail_simplifier::transform_rule(rule * r0, rule_ref & res)
{
rule_ref r(r0, m_context.get_rule_manager());
start:
unsigned u_len = r->get_uninterpreted_tail_size();
unsigned len = r->get_tail_size();
if (u_len==len) {
res = r;
return true;
}
app_ref head(r->get_head(), m);
app_ref_vector tail(m);
svector<bool> tail_neg;
for (unsigned i=0; i<u_len; i++) {
tail.push_back(r->get_tail(i));
tail_neg.push_back(r->is_neg_tail(i));
}
bool modified = false;
app_ref itail(m);
if (u_len+1==len) {
//we have only one interpreted tail
itail = r->get_tail(u_len);
SASSERT(!r->is_neg_tail(u_len));
}
else {
expr_ref_vector itail_members(m);
for (unsigned i=u_len; i<len; i++) {
itail_members.push_back(r->get_tail(i));
SASSERT(!r->is_neg_tail(i));
}
itail = m.mk_and(itail_members.size(), itail_members.c_ptr());
modified = true;
}
expr_ref simp_res(m);
simplify_expr(itail.get(), simp_res);
modified |= itail.get()!=simp_res.get();
if (is_app(simp_res.get())) {
itail = to_app(simp_res.get());
}
else if (m.is_bool(simp_res)) {
itail = m.mk_eq(simp_res, m.mk_true());
}
else {
throw default_exception("simplification resulted in non-boolean non-function");
}
if (m.is_false(itail.get())) {
//the tail member is never true, so we may delete the rule
TRACE("dl", r->display(m_context, tout << "rule in infeasible\n"););
return false;
}
if (!m.is_true(itail.get())) {
//if the simplified tail is not a tautology, we add it to the rule
tail.push_back(itail);
tail_neg.push_back(false);
}
else {
modified = true;
}
SASSERT(tail.size() == tail_neg.size());
if (modified) {
res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
res->set_accounting_parent_object(m_context, r);
}
else {
res = r;
}
rule_ref pro_var_eq_result(m_context.get_rule_manager());
if (propagate_variable_equivalences(res, pro_var_eq_result)) {
SASSERT(var_counter().get_max_var(*r.get())==0 ||
var_counter().get_max_var(*r.get()) > var_counter().get_max_var(*pro_var_eq_result.get()));
r = pro_var_eq_result;
goto start;
}
CTRACE("dl", (res != r0), r0->display(m_context, tout << "old:\n"); res->display(m_context, tout << "new:\n"););
return true;
}
bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) {
bool modified = false;
rule_set::iterator rit = orig.begin();
rule_set::iterator rend = orig.end();
for (; rit!=rend; ++rit) {
rule_ref new_rule(m_context.get_rule_manager());
if (transform_rule(*rit, new_rule)) {
bool is_modified = *rit != new_rule;
modified |= is_modified;
tgt.add_rule(new_rule);
}
else {
modified = true;
}
}
return modified;
}
rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
if (source.get_num_rules() == 0) {
return 0;
}
rule_set * res = alloc(rule_set, m_context);
if (!transform_rules(source, *res)) {
dealloc(res);
res = 0;
}
return res;
}
};

View file

@ -1,101 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_interp_tail_simplifier.h
Abstract:
Rule transformer which simplifies interpreted tails
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#ifndef _DL_MK_INTERP_TAIL_SIMPLIFIER_H_
#define _DL_MK_INTERP_TAIL_SIMPLIFIER_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
#include "unifier.h"
#include "substitution.h"
namespace datalog {
class mk_interp_tail_simplifier : public rule_transformer::plugin {
class rule_substitution
{
ast_manager& m;
context& m_context;
substitution m_subst;
unifier m_unif;
rule * m_rule;
void apply(app * a, app_ref& res);
public:
rule_substitution(context & ctx)
: m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_rule(0) {}
/**
Reset substitution and get it ready for working with rule r.
As long as this object is used without a reset, the rule r must exist.
*/
void reset(rule * r);
/** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */
bool unify(expr * e1, expr * e2);
void get_result(rule_ref & res);
void display(std::ostream& stm) {
m_subst.display(stm);
}
};
ast_manager & m;
context & m_context;
th_rewriter & m_simp;
rule_substitution m_rule_subst;
class normalizer_cfg;
void simplify_expr(app * a, expr_ref& res);
/** return true if some propagation was done */
bool propagate_variable_equivalences(rule * r, rule_ref& res);
/** Return true if something was modified */
bool transform_rules(const rule_set & orig, rule_set & tgt);
public:
mk_interp_tail_simplifier(context & ctx, unsigned priority=40000)
: plugin(priority),
m(ctx.get_manager()),
m_context(ctx),
m_simp(ctx.get_rewriter()),
m_rule_subst(ctx) {}
/**If rule should be retained, assign transformed version to res and return true;
if rule can be deleted, return false.
This method is kind of useful, so it's public to allow other rules to use it,
e.g. on their intermediate results.
*/
bool transform_rule(rule * r, rule_ref& res);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */

View file

@ -1,395 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_magic_sets.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-04.
Revision History:
--*/
#include<utility>
#include<sstream>
#include"ast_pp.h"
#include"dl_mk_magic_sets.h"
namespace datalog {
mk_magic_sets::mk_magic_sets(context & ctx, rule * goal_rule) :
plugin(10000, true),
m_context(ctx),
m_manager(ctx.get_manager()),
m_rules(ctx.get_rule_manager()),
m_pinned(m_manager),
m_goal_rule(goal_rule, ctx.get_rule_manager()) {
}
void mk_magic_sets::reset() {
m_extentional.reset();
m_todo.reset();
m_adorned_preds.reset();
m_adornments.reset();
m_magic_preds.reset();
m_rules.reset();
m_pinned.reset();
}
void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) {
SASSERT(empty());
unsigned arity = lit->get_num_args();
for(unsigned i=0; i<arity; i++) {
const expr * arg = lit->get_arg(i);
bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx());
push_back(bound ? AD_BOUND : AD_FREE);
}
}
std::string mk_magic_sets::adornment::to_string() const {
std::string res;
const_iterator eit = begin();
const_iterator eend = end();
for(; eit!=eend; ++eit) {
switch(*eit) {
case AD_BOUND:
res+='b';
break;
case AD_FREE:
res+='f';
break;
default:
UNREACHABLE();
}
}
return res;
}
unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) {
unsigned res = 0;
unsigned n = lit->get_num_args();
for(unsigned i=0; i<n; i++) {
const expr * arg = lit->get_arg(i);
if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) {
continue;
}
SASSERT(is_var(arg) || is_app(arg));
SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0);
res++;
}
return res;
}
float mk_magic_sets::get_unbound_cost(app * lit, const var_idx_set & bound_vars) {
func_decl * pred = lit->get_decl();
float res = 1;
unsigned n = lit->get_num_args();
for(unsigned i=0; i<n; i++) {
const expr * arg = lit->get_arg(i);
if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) {
res*=m_context.get_sort_size_estimate(pred->get_domain(i));
}
//res-=1;
}
return res;
}
/**
\brief From \c cont which is list of indexes of tail literals of rule \c r, select
the index pointing to a literal with at least one bound variable that will be the next
bound literal in the process of creating an adorned rule. If all literals are unbound,
return -1.
*/
int mk_magic_sets::pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars) {
float best_cost;
int candidate_index = -1;
unsigned n = cont.size();
for(unsigned i=0; i<n; i++) {
app * lit = r->get_tail(cont[i]);
unsigned bound_cnt = get_bound_arg_count(lit, bound_vars);
if(bound_cnt==0) {
continue;
}
float cost = get_unbound_cost(lit, bound_vars);
if(candidate_index==-1 || cost<best_cost) {
best_cost = cost;
candidate_index = i;
}
}
if(candidate_index==-1) {
return -1;
}
if(candidate_index != static_cast<int>(n-1)) {
std::swap(cont[candidate_index], cont[n-1]);
}
unsigned res = cont.back();
cont.pop_back();
return res;
}
app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) {
SASSERT(!m_extentional.contains(lit->get_decl()));
func_decl * old_pred = lit->get_decl();
SASSERT(m_manager.is_bool(old_pred->get_range()));
adornment_desc adn(old_pred);
adn.m_adornment.populate(lit, bound_vars);
adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0);
func_decl * new_pred = e->get_data().m_value;
if(new_pred==0) {
std::string suffix = "ad_"+adn.m_adornment.to_string();
new_pred = m_context.mk_fresh_head_predicate(
old_pred->get_name(), symbol(suffix.c_str()),
old_pred->get_arity(), old_pred->get_domain(), old_pred);
m_pinned.push_back(new_pred);
e->get_data().m_value = new_pred;
m_todo.push_back(adn);
m_adornments.insert(new_pred, adn.m_adornment);
}
app * res = m_manager.mk_app(new_pred, lit->get_args());
m_pinned.push_back(res);
return res;
}
app * mk_magic_sets::create_magic_literal(app * l) {
func_decl * l_pred = l->get_decl();
SASSERT(m_manager.is_bool(l_pred->get_range()));
pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred);
SASSERT(ae);
const adornment & adn = ae->get_data().m_value;
unsigned l_arity = l->get_num_args();
ptr_vector<expr> bound_args;
for(unsigned i=0; i<l_arity; i++) {
if(adn[i]==AD_BOUND) {
bound_args.push_back(l->get_arg(i));
}
}
pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0);
func_decl * mag_pred = e->get_data().m_value;
if(mag_pred==0) {
unsigned mag_arity = bound_args.size();
ptr_vector<sort> mag_domain;
for(unsigned i=0; i<l_arity; i++) {
if(adn[i]==AD_BOUND) {
mag_domain.push_back(l_pred->get_domain(i));
}
}
mag_pred = m_context.mk_fresh_head_predicate(l_pred->get_name(), symbol("ms"),
mag_arity, mag_domain.c_ptr(), l_pred);
m_pinned.push_back(mag_pred);
e->get_data().m_value = mag_pred;
}
app * res = m_manager.mk_app(mag_pred, bound_args.c_ptr());
m_pinned.push_back(res);
return res;
}
void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated) {
//TODO: maybe include relevant interpreted predicates from the original rule
ptr_vector<app> new_tail;
svector<bool> negations;
new_tail.push_back(create_magic_literal(head));
new_tail.append(tail_cnt, tail);
negations.push_back(false);
negations.append(tail_cnt, negated);
for(unsigned i=0; i<tail_cnt; i++) {
if(m_extentional.contains(tail[i]->get_decl())) {
continue;
}
app * mag_head = create_magic_literal(tail[i]);
rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr());
TRACE("dl", r->display(m_context,tout); );
m_rules.push_back(r);
}
}
void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r) {
app * head = r->get_head();
unsigned head_len = head->get_num_args();
SASSERT(head_len==head_adornment.size());
var_idx_set bound_vars;
for(unsigned i=0; i<head_len; i++) {
expr * arg = head->get_arg(i);
if(head_adornment[i]==AD_BOUND && is_var(arg)) {
bound_vars.insert(to_var(arg)->get_idx());
}
}
unsigned processed_tail_len = r->get_uninterpreted_tail_size();
unsigned_vector exten_tails;
unsigned_vector inten_tails;
for(unsigned i=0; i<processed_tail_len; i++) {
app * t = r->get_tail(i);
if(m_extentional.contains(t->get_decl())) {
exten_tails.push_back(i);
}
else {
inten_tails.push_back(i);
}
}
ptr_vector<app> new_tail;
svector<bool> negations;
while(new_tail.size()!=processed_tail_len) {
bool intentional = false;
int curr_index = pop_bound(exten_tails, r, bound_vars);
if(curr_index==-1) {
curr_index = pop_bound(inten_tails, r,bound_vars);
if(curr_index!=-1) {
intentional = true;
}
}
if(curr_index==-1) {
if(!exten_tails.empty()) {
curr_index = exten_tails.back();
exten_tails.pop_back();
}
else {
SASSERT(!inten_tails.empty());
curr_index = inten_tails.back();
inten_tails.pop_back();
intentional = true;
}
}
SASSERT(curr_index!=-1);
app * curr = r->get_tail(curr_index);
if(intentional) {
curr = adorn_literal(curr, bound_vars);
}
new_tail.push_back(curr);
negations.push_back(r->is_neg_tail(curr_index));
collect_vars(m_manager, curr, bound_vars);
}
func_decl * new_head_pred;
VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) );
app * new_head = m_manager.mk_app(new_head_pred, head->get_args());
SASSERT(new_tail.size()==r->get_uninterpreted_tail_size());
create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr());
unsigned tail_len = r->get_tail_size();
for(unsigned i=processed_tail_len; i<tail_len; i++) {
new_tail.push_back(r->get_tail(i));
negations.push_back(r->is_neg_tail(i));
}
new_tail.push_back(create_magic_literal(new_head));
negations.push_back(false);
rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr());
m_rules.push_back(nr);
nr->set_accounting_parent_object(m_context, r);
}
void mk_magic_sets::create_transfer_rule(const adornment_desc & d) {
func_decl * adn_pred;
TRUSTME( m_adorned_preds.find(d, adn_pred) );
unsigned arity = adn_pred->get_arity();
SASSERT(arity==d.m_pred->get_arity());
ptr_vector<expr> args;
for(unsigned i=0; i<arity; i++) {
args.push_back(m_manager.mk_var(i, adn_pred->get_domain(i)));
}
app * lit = m_manager.mk_app(d.m_pred, args.c_ptr());
app * adn_lit = m_manager.mk_app(adn_pred, args.c_ptr());
app * mag_lit = create_magic_literal(adn_lit);
app * tail[] = {lit, mag_lit};
rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0);
m_rules.push_back(r);
}
rule_set * mk_magic_sets::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
SASSERT(!mc && !pc);
unsigned init_rule_cnt = source.get_num_rules();
{
func_decl_set intentional;
for(unsigned i=0; i<init_rule_cnt; i++) {
intentional.insert(source.get_rule(i)->get_head()->get_decl());
}
//now we iterate through all predicates and collect the set of extentional ones
const rule_dependencies * deps;
rule_dependencies computed_deps(m_context);
if(source.is_closed()) {
deps = &source.get_dependencies();
}
else {
computed_deps.populate(source);
deps = &computed_deps;
}
rule_dependencies::iterator it = deps->begin();
rule_dependencies::iterator end = deps->end();
for(; it!=end; ++it) {
func_decl * pred = it->m_key;
if(intentional.contains(pred)) {
continue;
}
SASSERT(it->m_value->empty());//extentional predicates have no dependency
m_extentional.insert(pred);
}
}
SASSERT(m_rules.empty());
app * goal_head = m_goal_rule->get_head();
//adornment goal_adn;
//goal_adn.populate(goal_head, );
var_idx_set empty_var_idx_set;
adorn_literal(goal_head, empty_var_idx_set);
while(!m_todo.empty()) {
adornment_desc task = m_todo.back();
m_todo.pop_back();
const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred);
rule_vector::const_iterator it = pred_rules.begin();
rule_vector::const_iterator end = pred_rules.end();
for(; it!=end; ++it) {
rule * r = *it;
transform_rule(task.m_adornment, r);
}
if(!m_context.get_relation(task.m_pred).empty()) {
//we need a rule to copy facts that are already in a relation into the adorned
//relation (since out intentional predicates can have facts, not only rules)
create_transfer_rule(task);
}
}
app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set);
app * mag_goal_head = create_magic_literal(adn_goal_head);
SASSERT(mag_goal_head->is_ground());
//SASSERT(is_fact(m_manager, mag_goal_head));
//m_context.add_fact(mag_goal_head);
rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0);
m_rules.push_back(mag_goal_rule);
rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0);
m_rules.push_back(back_to_goal_rule);
rule_set * result = static_cast<rule_set *>(0);
result = alloc(rule_set, m_context);
unsigned fin_rule_cnt = m_rules.size();
for(unsigned i=0; i<fin_rule_cnt; i++) {
result->add_rule(m_rules.get(i));
}
return result;
}
};

View file

@ -1,130 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_magic_sets.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-4.
Revision History:
--*/
#ifndef _DL_MK_MAGIC_SETS_H_
#define _DL_MK_MAGIC_SETS_H_
#include<utility>
#include"map.h"
#include"obj_pair_hashtable.h"
#include"dl_context.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Implements magic sets rule transformation.
According to A. Voronkov. Foundations of Deductive Databases.
The stratified negation is not in the book addressed wrt. magic sets, but it seems
that, for the purpose of magic sets, the negated literals should be treated just as
if they were non-negated (we are interested only in values of arguments, not in the
actual content of relations, at that point).
*/
class mk_magic_sets : public rule_transformer::plugin {
enum a_flag {
AD_FREE,
AD_BOUND
};
struct adornment : public svector<a_flag> {
void populate(app * lit, const var_idx_set & bound_vars);
bool operator==(const adornment & o) const {
return vectors_equal(*this, o);
}
std::string to_string() const;
};
struct adornment_desc {
func_decl * m_pred;
adornment m_adornment;
adornment_desc() {}
adornment_desc(func_decl * pred) : m_pred(pred) {}
adornment_desc(func_decl * pred, const adornment & a)
: m_pred(pred), m_adornment(a) {}
bool operator==(const adornment_desc & o) const {
//m_tail_adornment value is implied by the rule and the head adornment
return m_pred==o.m_pred && m_adornment==o.m_adornment;
}
unsigned hash() const {
return m_pred->hash()^int_vector_hash(m_adornment);
}
};
struct adorned_rule {
app * m_head;
adornment m_head_adornment;
ptr_vector<app> m_tail;
};
typedef hashtable<adornment_desc, obj_hash<adornment_desc>,
default_eq<adornment_desc> > adornment_set;
typedef map<adornment_desc, func_decl *, obj_hash<adornment_desc>,
default_eq<adornment_desc> > adornment_map;
typedef obj_map<func_decl, adornment> pred_adornment_map;
typedef obj_map<func_decl, func_decl *> pred2pred;
context & m_context;
ast_manager & m_manager;
rule_ref_vector m_rules;
ast_ref_vector m_pinned;
rule_ref m_goal_rule;
/**
\brief Predicates from the original set that appear in a head of a rule
*/
func_decl_set m_extentional;
//adornment_set m_processed;
vector<adornment_desc> m_todo;
adornment_map m_adorned_preds;
pred_adornment_map m_adornments;
pred2pred m_magic_preds;
void reset();
float get_unbound_cost(app * lit, const var_idx_set & bound_vars);
int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars);
app * create_magic_literal(app * l);
void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated);
app * adorn_literal(app * lit, const var_idx_set & bound_vars);
void transform_rule(const adornment & head_adornment, rule * r);
void create_transfer_rule(const adornment_desc & d);
public:
/**
\brief Create magic sets rule transformer for \c goal_rule. When applying the transformer,
the \c goal_rule must be present in the \c rule_set that is being transformed.
*/
mk_magic_sets(context & ctx, rule * goal_rule);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_MAGIC_SETS_H_ */

View file

@ -1,153 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_partial_equiv.cpp
Abstract:
Rule transformer which identifies predicates that are partial equivalence relations.
Author:
Nikolaj Bjorner (nbjorner) 2012-05-14
Revision History:
--*/
#include "dl_mk_partial_equiv.h"
#include "ast_pp.h"
namespace datalog {
bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) {
func_decl* p = r->get_head()->get_decl();
return
p->get_arity() == 2 &&
p->get_domain(0) == p->get_domain(1) &&
r->get_tail_size() == 1 &&
r->get_tail(0)->get_decl() == p &&
r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) &&
r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) &&
is_var(r->get_head()->get_arg(0)) &&
is_var(r->get_head()->get_arg(1)) &&
r->get_head()->get_arg(0) != r->get_head()->get_arg(1);
}
bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) {
func_decl* p = r->get_head()->get_decl();
if (p->get_arity() != 2 ||
p->get_domain(0) != p->get_domain(1) ||
r->get_tail_size() != 2 ||
r->get_tail(0)->get_decl() != p ||
r->get_tail(1)->get_decl() != p) {
return false;
}
app* h = r->get_head();
app* a = r->get_tail(0);
app* b = r->get_tail(1);
expr* x1 = h->get_arg(0);
expr* x2 = h->get_arg(1);
expr* a1 = a->get_arg(0);
expr* a2 = a->get_arg(1);
expr* b1 = b->get_arg(0);
expr* b2 = b->get_arg(1);
if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) {
return false;
}
if (x1 == x2 || a1 == a2 || b1 == b2) {
return false;
}
if (a2 == b1) {
if (x1 == b2 && x2 == a1) {
return true;
}
if (x1 == a1 && x2 == b2) {
return true;
}
return false;
}
if (a1 == b2) {
if (x1 == b1 && x2 == a2) {
return true;
}
if (x1 == a2 && x2 == b1) {
return true;
}
return false;
}
return false;
;
}
rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
if (source.get_num_rules() == 0) {
return 0;
}
if (m_context.get_engine() != DATALOG_ENGINE) {
return 0;
}
relation_manager & rm = m_context.get_rmanager();
rule_set::decl2rules::iterator it = source.begin_grouped_rules();
rule_set::decl2rules::iterator end = source.end_grouped_rules();
rule_set* res = alloc(rule_set, m_context);
for (; it != end; ++it) {
func_decl* p = it->m_key;
rule_vector const& rv = *(it->m_value);
bool has_symmetry = false;
bool has_transitivity = false;
unsigned i_symmetry, i_transitivity;
family_id kind = rm.get_requested_predicate_kind(p);
for (unsigned i = 0; i < rv.size(); ++i) {
if (kind != null_family_id) {
res->add_rule(rv[i]);
}
else if (is_symmetry(rv[i])) {
i_symmetry = i;
has_symmetry = true;
}
else if (is_transitivity(rv[i])) {
i_transitivity = i;
has_transitivity = true;
}
else {
res->add_rule(rv[i]);
}
}
if (has_symmetry && !has_transitivity) {
res->add_rule(rv[i_symmetry]);
}
else if (!has_symmetry && has_transitivity) {
res->add_rule(rv[i_transitivity]);
}
else if (has_symmetry && has_transitivity) {
TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";);
SASSERT(kind == null_family_id);
rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind());
}
}
if (res->get_num_rules() == source.get_num_rules()) {
dealloc(res);
return 0;
}
return res;
}
};

View file

@ -1,50 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_partial_equiv.h
Abstract:
Rule transformer which identifies predicates that are partial equivalence relations.
Author:
Nikolaj Bjorner (nbjorner) 2012-05-14
Revision History:
--*/
#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_
#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
namespace datalog {
class mk_partial_equivalence_transformer : public rule_transformer::plugin {
ast_manager & m;
context & m_context;
public:
mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000)
: plugin(priority),
m(ctx.get_manager()),
m_context(ctx) {}
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
private:
bool is_symmetry(rule const* r);
bool is_transitivity(rule const* r);
};
};
#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */

View file

@ -1,884 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_rule_inliner.cpp
Abstract:
Rule transformer which simplifies interpreted tails
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
Added linear_inline 2012-9-10 (nbjorner)
Notes:
Resolution transformation (resolve):
P(x) :- Q(y), phi(x,y) Q(y) :- R(z), psi(y,z)
--------------------------------------------------
P(x) :- R(z), phi(x,y), psi(y,z)
Proof converter:
replace assumption (*) by rule and upper assumptions.
Subsumption transformation (remove rule):
P(x) :- Q(y), phi(x,y) Rules
---------------------------------
Rules
Model converter:
P(x) := P(x) or (exists y . Q(y) & phi(x,y))
--*/
#include <sstream>
#include "ast_pp.h"
#include "dl_finite_product_relation.h"
#include "dl_product_relation.h"
#include "dl_sieve_relation.h"
#include "rewriter.h"
#include "rewriter_def.h"
#include "dl_mk_rule_inliner.h"
namespace datalog {
// -----------------------------------
//
// mk_rule_inliner::rule_unifier
//
// -----------------------------------
bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) {
var_counter& vc = m_rm.get_var_counter();
unsigned var_cnt = std::max(vc.get_max_var(tgt), vc.get_max_var(src))+1;
m_subst.reset();
m_subst.reserve(2, var_cnt);
m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst);
if (m_ready) {
m_deltas[0] = 0;
m_deltas[1] = var_cnt;
TRACE("dl",
output_predicate(m_context, src.get_head(), tout << "unify rules ");
output_predicate(m_context, tgt.get_head(), tout << "\n");
tout << "\n";);
}
return m_ready;
}
void rule_unifier::apply(app * a, bool is_tgt, app_ref& res) {
expr_ref res_e(m);
TRACE("dl", output_predicate(m_context, a, tout); tout << "\n";);
m_subst.apply(2, m_deltas, expr_offset(a, is_tgt ? 0 : 1), res_e);
SASSERT(is_app(res_e.get()));
res = to_app(res_e.get());
}
void rule_unifier::apply(
rule const& r, bool is_tgt, unsigned skipped_index,
app_ref_vector& res, svector<bool>& res_neg) {
unsigned rule_len = r.get_tail_size();
for (unsigned i = 0; i < rule_len; i++) {
if (i != skipped_index) { //i can never be UINT_MAX, so we'll never skip if we're not supposed to
app_ref new_tail_el(m);
apply(r.get_tail(i), is_tgt, new_tail_el);
res.push_back(new_tail_el);
res_neg.push_back(r.is_neg_tail(i));
}
}
}
bool rule_unifier::apply(rule const& tgt, unsigned tail_index, rule const& src, rule_ref& res) {
SASSERT(m_ready);
app_ref new_head(m);
app_ref_vector tail(m);
svector<bool> tail_neg;
rule_ref simpl_rule(m_rm);
apply(tgt.get_head(), true, new_head);
apply(tgt, true, tail_index, tail, tail_neg);
apply(src, false, UINT_MAX, tail, tail_neg);
mk_rule_inliner::remove_duplicate_tails(tail, tail_neg);
SASSERT(tail.size()==tail_neg.size());
res = m_rm.mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
res->set_accounting_parent_object(m_context, const_cast<rule*>(&tgt));
res->norm_vars(m_rm);
if (m_context.fix_unbound_vars()) {
m_rm.fix_unbound_vars(res, true);
}
if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) {
res = simpl_rule;
return true;
}
else {
return false;
}
}
expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) {
SASSERT(m_ready);
expr_ref_vector result(m);
sort_ref_vector sorts(m);
expr_ref v(m), w(m);
r.get_vars(sorts);
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i].get()) {
sorts[i] = m.mk_bool_sort();
}
v = m.mk_var(i, sorts[i].get());
m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w);
result.push_back(w);
}
return result;
}
// -----------------------------------
//
// mk_rule_inliner
//
// -----------------------------------
/**
Inline occurrences of rule src at tail_index in tgt and return the result in res.
*/
bool mk_rule_inliner::try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res)
{
SASSERT(tail_index<tgt.get_positive_tail_size());
SASSERT(!tgt.is_neg_tail(tail_index));
tgt.norm_vars(m_context.get_rule_manager());
if (has_quantifier(src)) {
return false;
}
if (!m_unifier.unify_rules(tgt, tail_index, src)) {
return false;
}
if (m_unifier.apply(tgt, tail_index, src, res)) {
TRACE("dl",
tgt.display(m_context, tout << "tgt (" << tail_index << "): \n");
src.display(m_context, tout << "src:\n");
res->display(m_context, tout << "res\n");
//m_unifier.display(tout << "subst:\n");
);
if (m_pc) {
expr_ref_vector s1 = m_unifier.get_rule_subst(tgt, true);
expr_ref_vector s2 = m_unifier.get_rule_subst(src, false);
datalog::resolve_rule(m_pc, tgt, src, tail_index, s1, s2, *res.get());
}
return true;
}
else {
TRACE("dl", res->display(m_context, tout << "interpreted tail is unsat\n"););
//the interpreted part is unsatisfiable
return false;
}
}
bool mk_rule_inliner::has_quantifier(rule const& r) const {
unsigned utsz = r.get_uninterpreted_tail_size();
for (unsigned i = utsz; i < r.get_tail_size(); ++i) {
if (r.get_tail(i)->has_quantifiers()) return true;
}
return false;
}
void mk_rule_inliner::count_pred_occurrences(rule_set const & orig)
{
m_context.get_rmanager().collect_non_empty_predicates(m_preds_with_facts);
rule_set::iterator rend = orig.end();
for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) {
rule * r = *rit;
func_decl * head_pred = r->get_decl();
m_head_pred_ctr.inc(head_pred);
if (r->get_tail_size()>0) {
m_head_pred_non_empty_tails_ctr.inc(head_pred);
}
unsigned ut_len = r->get_uninterpreted_tail_size();
for (unsigned i=0; i<ut_len; i++) {
func_decl * pred = r->get_decl(i);
m_tail_pred_ctr.inc(pred);
if (r->is_neg_tail(i)) {
m_preds_with_neg_occurrence.insert(pred);
}
}
}
}
bool mk_rule_inliner::inlining_allowed(func_decl * pred)
{
if (//these three conditions are important for soundness
m_context.is_output_predicate(pred) ||
m_preds_with_facts.contains(pred) ||
m_preds_with_neg_occurrence.contains(pred) ||
//this condition is used for breaking of cycles among inlined rules
m_forbidden_preds.contains(pred)) {
return false;
}
//these conditions are optional, they avoid possible exponential increase
//in the size of the problem
return
//m_head_pred_non_empty_tails_ctr.get(pred)<=1
m_head_pred_ctr.get(pred) <= 1
|| (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4)
;
}
/** Caller has to dealloc the returned object */
rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig)
{
rule_set * res = alloc(rule_set, m_context);
unsigned rcnt = orig.get_num_rules();
for (unsigned i=0; i<rcnt; i++) {
rule * r = orig.get_rule(i);
if (inlining_allowed(r->get_decl())) {
res->add_rule(r);
}
}
//the rule set should be stratified, since orig (which is its superset) is as well
VERIFY(res->close());
return res;
}
/**
Try to make the set of inlined predicates acyclic by forbidding inlining of one
predicate from each strongly connected component. Return true if we did forbide some
predicate, and false if the set of rules is already acyclic.
*/
bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r)
{
SASSERT(r.is_closed());
bool something_forbidden = false;
const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
if (stratum->size()==1) {
continue;
}
SASSERT(stratum->size()>1);
func_decl * first_stratum_pred = *stratum->begin();
//we're trying to break cycles by removing one predicate from each of them
m_forbidden_preds.insert(first_stratum_pred);
something_forbidden = true;
}
return something_forbidden;
}
bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig,
rule_set const & proposed_inlined_rules) {
bool something_forbidden = false;
const rule_stratifier::comp_vector& comps =
proposed_inlined_rules.get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
SASSERT(stratum->size()==1);
func_decl * head_pred = *stratum->begin();
bool is_multi_head_pred = m_head_pred_ctr.get(head_pred)>1;
bool is_multi_occurrence_pred = m_tail_pred_ctr.get(head_pred)>1;
const rule_vector& pred_rules = proposed_inlined_rules.get_predicate_rules(head_pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
rule * r = *iit;
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti<pt_len; ++ti) {
func_decl * tail_pred = r->get_decl(ti);
if (!inlining_allowed(tail_pred)) {
continue;
}
unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred);
if (tail_pred_head_cnt<=1) {
continue;
}
if (is_multi_head_pred) {
m_forbidden_preds.insert(head_pred);
something_forbidden = true;
goto process_next_pred;
}
if (is_multi_occurrence_pred) {
m_forbidden_preds.insert(tail_pred);
something_forbidden = true;
}
else {
is_multi_head_pred = true;
m_head_pred_ctr.get(head_pred) =
m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt;
}
}
}
process_next_pred:;
}
unsigned rule_cnt = orig.get_num_rules();
for (unsigned ri=0; ri<rule_cnt; ri++) {
rule * r = orig.get_rule(ri);
func_decl * head_pred = r->get_decl();
if (inlining_allowed(head_pred)) {
//we have already processed inlined rules
continue;
}
bool has_multi_head_pred = false;
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti<pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
if (!inlining_allowed(pred)) {
continue;
}
if (m_head_pred_ctr.get(pred)<=1) {
continue;
}
if (has_multi_head_pred) {
m_forbidden_preds.insert(pred);
something_forbidden = true;
}
else {
has_multi_head_pred = true;
}
}
}
return something_forbidden;
}
void mk_rule_inliner::plan_inlining(rule_set const & orig)
{
count_pred_occurrences(orig);
scoped_ptr<rule_set> candidate_inlined_set = create_allowed_rule_set(orig);
while (forbid_preds_from_cycles(*candidate_inlined_set)) {
candidate_inlined_set = create_allowed_rule_set(orig);
}
if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) {
candidate_inlined_set = create_allowed_rule_set(orig);
}
TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); );
// now we start filling in the set of the inlined rules in a topological order,
// so that we inline rules into other rules
SASSERT(m_inlined_rules.get_num_rules()==0);
const rule_stratifier::comp_vector& comps = candidate_inlined_set->get_stratifier().get_strats();
rule_stratifier::comp_vector::const_iterator cend = comps.end();
for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) {
rule_stratifier::item_set * stratum = *it;
SASSERT(stratum->size()==1);
func_decl * pred = *stratum->begin();
const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
transform_rule(*iit, m_inlined_rules);
}
}
TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; );
}
bool mk_rule_inliner::transform_rule(rule * r0, rule_set& tgt) {
bool modified = false;
rule_ref_vector todo(m_rm);
todo.push_back(r0);
while (!todo.empty()) {
rule_ref r(todo.back(), m_rm);
todo.pop_back();
unsigned pt_len = r->get_positive_tail_size();
unsigned i = 0;
for (; i < pt_len && !inlining_allowed(r->get_decl(i)); ++i) {};
if (has_quantifier(*r.get())) {
continue;
}
if (i == pt_len) {
//there's nothing we can inline in this rule
tgt.add_rule(r);
continue;
}
modified = true;
func_decl * pred = r->get_decl(i);
const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred);
rule_vector::const_iterator iend = pred_rules.end();
for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) {
rule * inl_rule = *iit;
rule_ref inl_result(m_rm);
if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) {
todo.push_back(inl_result);
}
}
}
return modified;
}
bool mk_rule_inliner::transform_rules(const rule_set & orig, rule_set & tgt) {
bool something_done = false;
rule_set::iterator rend = orig.end();
for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) {
rule_ref r(*rit, m_rm);
func_decl * pred = r->get_decl();
// if inlining is allowed, then we are eliminating
// this relation through inlining,
// so we don't add its rules to the result
something_done |= !inlining_allowed(pred) && transform_rule(r, tgt);
}
return something_done;
}
/**
Check whether rule r is oriented in a particular ordering.
This is to avoid infinite cycle of inlining in the eager inliner.
Out ordering is lexicographic, comparing atoms first on stratum they are in,
then on arity and then on ast ID of their func_decl.
*/
bool mk_rule_inliner::is_oriented_rewriter(rule * r, rule_stratifier const& strat) {
func_decl * head_pred = r->get_decl();
unsigned head_strat = strat.get_predicate_strat(head_pred);
unsigned head_arity = head_pred->get_arity();
//var_idx_set head_vars;
//var_idx_set same_strat_vars;
//collect_vars(m, r->get_head(), head_vars);
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti=0; ti<pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
unsigned pred_strat = strat.get_predicate_strat(pred);
SASSERT(pred_strat<=head_strat);
if (pred_strat==head_strat) {
//collect_vars(m, r->get_head(), same_strat_vars);
if (pred->get_arity()>head_arity
|| (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) {
return false;
}
}
}
return true;
}
bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) {
SASSERT(rules.is_closed());
const rule_stratifier& strat = rules.get_stratifier();
func_decl * head_pred = r->get_decl();
unsigned pt_len = r->get_positive_tail_size();
for (unsigned ti = 0; ti < pt_len; ++ti) {
func_decl * pred = r->get_decl(ti);
if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; }
const rule_vector& pred_rules = rules.get_predicate_rules(pred);
rule * inlining_candidate = 0;
unsigned rule_cnt = pred_rules.size();
if (rule_cnt == 0) {
inlining_candidate = 0;
}
else if (rule_cnt == 1) {
inlining_candidate = pred_rules[0];
}
else {
inlining_candidate = 0;
for (unsigned ri = 0; ri < rule_cnt; ++ri) {
rule * pred_rule = pred_rules[ri];
if (!m_unifier.unify_rules(*r, ti, *pred_rule)) {
//we skip rules which don't unify with the tail atom
continue;
}
if (inlining_candidate != 0) {
// We have two rules that can be inlined into the current
// tail predicate. In this situation we don't do inlinning
// on this tail atom, as we don't want the overall number
// of rules to increase.
goto process_next_tail;
}
inlining_candidate = pred_rule;
}
}
if (inlining_candidate == 0) {
// nothing unifies with the tail atom, therefore the rule is unsatisfiable
// (we can say this because relation pred doesn't have any ground facts either)
res = 0;
datalog::del_rule(m_mc, *r);
return true;
}
if (!is_oriented_rewriter(inlining_candidate, strat)) {
// The rule which should be used for inlining isn't oriented
// in a simplifying direction. Inlining with such rule might lead to
// infinite loops, so we don't do it.
goto process_next_tail;
}
if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) {
datalog::del_rule(m_mc, *r);
res = 0;
}
return true;
process_next_tail:;
}
return false;
}
bool mk_rule_inliner::do_eager_inlining(scoped_ptr<rule_set> & rules) {
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
bool done_something = false;
rule_set::iterator rend = rules->end();
for (rule_set::iterator rit = rules->begin(); rit!=rend; ++rit) {
rule_ref r(*rit, m_rm);
rule_ref replacement(m_rm);
while (r && do_eager_inlining(r, *rules, replacement)) {
r = replacement;
done_something = true;
}
if (!r) {
continue;
}
res->add_rule(r);
}
if (done_something) {
rules = res.detach();
}
return done_something;
}
/**
Inline predicates that are known to not be join-points.
P(1,x) :- P(0,y), phi(x,y)
P(0,x) :- P(1,z), psi(x,z)
->
P(1,x) :- P(1,z), phi(x,y), psi(y,z)
whenever P(0,x) is not unifiable with the
body of the rule where it appears (P(1,z))
and P(0,x) is unifiable with at most one (?)
other rule (and it does not occur negatively).
*/
bool mk_rule_inliner::visitor::operator()(expr* e) {
m_unifiers.append(m_positions.find(e));
TRACE("dl",
tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back());
tout << " num unifiers: " << m_unifiers.size();
tout << " num positions: " << m_positions.find(e).size() << "\n";
output_predicate(m_context, to_app(e), tout); tout << "\n";);
return true;
}
void mk_rule_inliner::visitor::reset(unsigned sz) {
m_unifiers.reset();
m_can_remove.reset();
m_can_remove.resize(sz, true);
m_can_expand.reset();
m_can_expand.resize(sz, true);
m_positions.reset();
}
unsigned_vector const& mk_rule_inliner::visitor::add_position(expr* e, unsigned j) {
obj_map<expr, unsigned_vector>::obj_map_entry * et = m_positions.insert_if_not_there2(e, unsigned_vector());
et->get_data().m_value.push_back(j);
return et->get_data().m_value;
}
unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) {
obj_map<expr, unsigned_vector>::obj_map_entry * et = m_positions.find_core(e);
SASSERT(et && et->get_data().m_value.contains(j));
et->get_data().m_value.erase(j);
return et->get_data().m_value;
}
void mk_rule_inliner::add_rule(rule* r, unsigned i) {
svector<bool>& can_remove = m_head_visitor.can_remove();
svector<bool>& can_expand = m_head_visitor.can_expand();
app* head = r->get_head();
func_decl* headd = head->get_decl();
m_head_visitor.add_position(head, i);
m_head_index.insert(head);
m_pinned.push_back(r);
if (m_context.is_output_predicate(headd) ||
m_preds_with_facts.contains(headd)) {
can_remove.set(i, false);
TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";);
}
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
app* tail = r->get_tail(j);
m_tail_visitor.add_position(tail, i);
m_tail_index.insert(tail);
}
bool can_exp =
tl_sz == 1
&& r->get_positive_tail_size() == 1
&& !m_preds_with_facts.contains(r->get_decl(0))
&& !m_context.is_output_predicate(r->get_decl(0));
can_expand.set(i, can_exp);
}
void mk_rule_inliner::del_rule(rule* r, unsigned i) {
app* head = r->get_head();
m_head_visitor.del_position(head, i);
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
app* tail = r->get_tail(j);
m_tail_visitor.del_position(tail, i);
}
}
#define PRT(_x_) ((_x_)?"T":"F")
bool mk_rule_inliner::inline_linear(scoped_ptr<rule_set>& rules) {
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
bool done_something = false;
unsigned sz = rules->get_num_rules();
m_head_visitor.reset(sz);
m_tail_visitor.reset(sz);
m_head_index.reset();
m_tail_index.reset();
TRACE("dl", rules->display(tout););
rule_ref_vector acc(m_rm);
for (unsigned i = 0; i < sz; ++i) {
acc.push_back(rules->get_rule(i));
}
// set up unification index.
svector<bool>& can_remove = m_head_visitor.can_remove();
svector<bool>& can_expand = m_head_visitor.can_expand();
for (unsigned i = 0; i < sz; ++i) {
add_rule(acc[i].get(), i);
}
// initialize substitution.
var_counter& vc = m_rm.get_var_counter();
unsigned max_var = 0;
for (unsigned i = 0; i < sz; ++i) {
rule* r = acc[i].get();
max_var = std::max(max_var, vc.get_max_var(r->get_head()));
unsigned tl_sz = r->get_uninterpreted_tail_size();
for (unsigned j = 0; j < tl_sz; ++j) {
max_var = std::max(max_var, vc.get_max_var(r->get_tail(j)));
}
}
m_subst.reset();
m_subst.reserve_vars(max_var+1);
m_subst.reserve_offsets(std::max(m_tail_index.get_approx_num_regs(), 2+m_head_index.get_approx_num_regs()));
svector<bool> valid;
valid.reset();
valid.resize(sz, true);
params_ref const& params = m_context.get_params();
bool allow_branching = params.get_bool(":inline-linear-branch", false);
for (unsigned i = 0; i < sz; ++i) {
while (true) {
rule_ref r(acc[i].get(), m_rm);
TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n"););
if (!valid.get(i)) {
TRACE("dl", tout << "invalid: " << i << "\n";);
break;
}
if (!can_expand.get(i)) {
TRACE("dl", tout << "cannot expand: " << i << "\n";);
break;
}
m_head_visitor.reset();
m_head_index.unify(r->get_tail(0), m_head_visitor);
unsigned num_head_unifiers = m_head_visitor.get_unifiers().size();
if (num_head_unifiers != 1) {
TRACE("dl", tout << "no unique unifier " << num_head_unifiers << "\n";);
break;
}
unsigned j = m_head_visitor.get_unifiers()[0];
if (!can_remove.get(j) || !valid.get(j) || i == j) {
TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";);
break;
}
rule* r2 = acc[j].get();
// check that the head of r2 only unifies with this single body position.
TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";);
m_tail_visitor.reset();
m_tail_index.unify(r2->get_head(), m_tail_visitor);
unsigned_vector const& tail_unifiers = m_tail_visitor.get_unifiers();
unsigned num_tail_unifiers = tail_unifiers.size();
SASSERT(!tail_unifiers.empty());
if (!allow_branching && num_tail_unifiers != 1) {
TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";);
break;
}
rule_ref rl_res(m_rm);
if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) {
TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); );
break;
}
done_something = true;
TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); );
del_rule(r, i);
add_rule(rl_res.get(), i);
r = rl_res;
acc[i] = r.get();
can_expand.set(i, can_expand.get(j));
if (num_tail_unifiers == 1) {
TRACE("dl", tout << "setting invalid: " << j << "\n";);
valid.set(j, false);
datalog::del_rule(m_mc, *r2);
del_rule(r2, j);
}
max_var = std::max(max_var, vc.get_max_var(*r.get()));
m_subst.reserve_vars(max_var+1);
}
}
if (done_something) {
rules = alloc(rule_set, m_context);
for (unsigned i = 0; i < sz; ++i) {
if (valid.get(i)) {
rules->add_rule(acc[i].get());
}
}
TRACE("dl", rules->display(tout););
}
return done_something;
}
rule_set * mk_rule_inliner::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
bool something_done = false;
ref<horn_subsume_model_converter> hsmc;
ref<replace_proof_converter> hpc;
if (source.get_num_rules() == 0) {
return 0;
}
if (mc) {
hsmc = alloc(horn_subsume_model_converter, m);
}
if (pc) {
hpc = alloc(replace_proof_converter, m);
}
m_mc = hsmc.get();
m_pc = hpc.get();
plan_inlining(source);
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
something_done = transform_rules(source, *res);
VERIFY(res->close()); //this transformation doesn't break the negation stratification
// try eager inlining
if (do_eager_inlining(res)) {
something_done = true;
}
params_ref const& params = m_context.get_params();
if (params.get_bool(":inline-linear", true) && inline_linear(res)) {
something_done = true;
}
if (!something_done) {
res = 0;
}
else {
if (mc) {
mc = concat(mc.get(), hsmc.get());
}
if (pc) {
pc = concat(pc.get(), hpc.get());
}
}
return res.detach();
}
};

View file

@ -1,199 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_interp_tail_simplifier.h
Abstract:
Rule transformer which inlines some of the rules
Author:
Krystof Hoder (t-khoder) 2011-10-02.
Revision History:
--*/
#ifndef _DL_MK_RULE_INLINER_H_
#define _DL_MK_RULE_INLINER_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
#include "dl_mk_interp_tail_simplifier.h"
#include "unifier.h"
#include "substitution.h"
#include "substitution_tree.h"
namespace datalog {
class rule_unifier {
ast_manager& m;
rule_manager& m_rm;
context& m_context;
/** We use this simplifier after inlining to get nicer intermediate rules */
mk_interp_tail_simplifier m_interp_simplifier;
substitution m_subst;
unifier m_unif;
bool m_ready;
unsigned m_deltas[2];
public:
rule_unifier(context& ctx)
: m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx),
m_interp_simplifier(ctx), m_subst(m), m_unif(m), m_ready(false) {}
/** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */
bool unify_rules(rule const& tgt, unsigned tgt_idx, rule const& src);
/**
\brief Apply unifier to rules.
Return false if the resulting rule is a tautology (the interpreted tail is unsat).
*/
bool apply(rule const& tgt, unsigned tgt_idx, rule const& src, rule_ref& result);
void display(std::ostream& stm) { m_subst.display(stm, 2, m_deltas); }
/**
Retrieve substitutions for src/tgt. (second argument of unify_rules).
*/
expr_ref_vector get_rule_subst(rule const& r, bool is_tgt);
private:
void apply(app * a, bool is_tgt, app_ref& res);
/**
Apply substitution to a rule tail. Tail with skipped_index is skipped,
unless skipped_index is equal to UINT_MAX
*/
void apply(rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res,
svector<bool>& res_neg);
};
class mk_rule_inliner : public rule_transformer::plugin {
class visitor : public st_visitor {
context& m_context;
unsigned_vector m_unifiers;
svector<bool> m_can_remove, m_can_expand;
obj_map<expr, unsigned_vector> m_positions;
public:
visitor(context& c, substitution & s): st_visitor(s), m_context(c) {}
virtual bool operator()(expr* e);
void reset() { m_unifiers.reset(); }
void reset(unsigned sz);
svector<bool>& can_remove() { return m_can_remove; }
svector<bool>& can_expand() { return m_can_expand; }
unsigned_vector const& add_position(expr* e, unsigned j);
unsigned_vector const& del_position(expr* e, unsigned j);
unsigned_vector const& get_unifiers() { return m_unifiers; }
};
typedef obj_map<func_decl, func_decl *> decl_map;
ast_manager & m;
rule_manager & m_rm;
context & m_context;
th_rewriter& m_simp;
rule_ref_vector m_pinned;
decl_set m_forbidden_preds;
decl_set m_preds_with_facts;
decl_set m_preds_with_neg_occurrence;
ast_counter m_head_pred_ctr;
ast_counter m_head_pred_non_empty_tails_ctr;
ast_counter m_tail_pred_ctr;
rule_set m_inlined_rules;
horn_subsume_model_converter* m_mc;
replace_proof_converter* m_pc;
//used in try_to_inline_rule and do_eager_inlining
rule_unifier m_unifier;
substitution_tree m_head_index; // for straight-line relation inlining.
substitution_tree m_tail_index;
substitution m_subst;
visitor m_head_visitor;
visitor m_tail_visitor;
bool tail_matches_head(app * tail, app* head);
bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res);
bool inlining_allowed(func_decl * pred);
void count_pred_occurrences(rule_set const & orig);
void plan_inlining(rule_set const & orig);
rule_set * create_allowed_rule_set(rule_set const & orig);
bool forbid_preds_from_cycles(rule_set const & r);
/** Ensure we don't inline two multi-head rules that would appear together in some tail */
bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules);
/** Return true if the rule was modified */
bool transform_rule(rule * r, rule_set& tgt);
/** Return true if some transformation was performed */
bool transform_rules(const rule_set & orig, rule_set & tgt);
bool is_oriented_rewriter(rule * r, rule_stratifier const& strat);
/**
Return false if nothing was done with the rule.
res may be set to zero if we managed to prove the rule unsatisfiable.
*/
bool do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res);
/**
Inline rules even if it doesn't lead to elimination of the whole predicate.
The inlining is done as long as it doesn't increase the number of rules
(i.e. when only one rule defining a predicate can replace tail atom).
The original rule-set must be closed before passing t this function
*/
bool do_eager_inlining(scoped_ptr<rule_set> & rules);
bool has_quantifier(rule const& r) const;
/**
Inline predicates that are known to not be join-points.
*/
bool inline_linear(scoped_ptr<rule_set>& rules);
void add_rule(rule* r, unsigned i);
void del_rule(rule* r, unsigned i);
public:
mk_rule_inliner(context & ctx, unsigned priority=35000)
: plugin(priority),
m(ctx.get_manager()),
m_rm(ctx.get_rule_manager()),
m_context(ctx),
m_simp(m_context.get_rewriter()),
m_pinned(m_rm),
m_inlined_rules(m_context),
m_unifier(ctx),
m_mc(0),
m_pc(0),
m_head_index(m),
m_tail_index(m),
m_subst(m),
m_head_visitor(ctx, m_subst),
m_tail_visitor(ctx, m_subst)
{}
virtual ~mk_rule_inliner() { }
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */

View file

@ -1,537 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_similarity_compressor.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-22.
Revision History:
--*/
#include<utility>
#include<sstream>
#include"dl_mk_similarity_compressor.h"
namespace datalog {
mk_similarity_compressor::mk_similarity_compressor(context & ctx, unsigned threshold_count) :
plugin(5000),
m_context(ctx),
m_manager(ctx.get_manager()),
m_threshold_count(threshold_count),
m_result_rules(ctx.get_rule_manager()),
m_pinned(m_manager) {
SASSERT(threshold_count>1);
}
void mk_similarity_compressor::reset() {
m_rules.reset();
m_result_rules.reset();
m_pinned.reset();
}
/**
Allows to traverse head and positive tails in a single for loop starting from -1
*/
app * get_by_tail_index(rule * r, int idx) {
if(idx==-1) {
return r->get_head();
}
SASSERT(idx<static_cast<int>(r->get_positive_tail_size()));
return r->get_tail(idx);
}
template<typename T>
int aux_compare(T a, T b) {
return (a>b) ? 1 : ( (a==b) ? 0 : -1);
}
int compare_var_args(app* t1, app* t2) {
SASSERT(t1->get_num_args()==t2->get_num_args());
int res;
unsigned n = t1->get_num_args();
for(unsigned i=0; i<n; i++) {
expr * a1 = t1->get_arg(i);
expr * a2 = t2->get_arg(i);
res = aux_compare(is_var(a1), is_var(a2));
if(res!=0) { return res; }
if(is_var(a1)) {
res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx());
if(res!=0) { return res; }
}
}
return 0;
}
int compare_args(app* t1, app* t2, int & skip_countdown) {
SASSERT(t1->get_num_args()==t2->get_num_args());
int res;
unsigned n = t1->get_num_args();
for(unsigned i=0; i<n; i++) {
if(is_var(t1->get_arg(i))) {
SASSERT(t1->get_arg(i)==t2->get_arg(i));
continue;
}
if((skip_countdown--)==0) {
continue;
}
res = aux_compare(t1->get_arg(i), t2->get_arg(i));
if(res!=0) { return res; }
}
return 0;
}
/**
\brief Return 0 if r1 and r2 could be similar. If the rough similarity
equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1.
Two rules are in the same rough similarity class if they differ only in constant arguments
of positive uninterpreted predicates.
*/
int rough_compare(rule * r1, rule * r2) {
int res = aux_compare(r1->get_tail_size(), r2->get_tail_size());
if(res!=0) { return res; }
res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size());
if(res!=0) { return res; }
res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size());
if(res!=0) { return res; }
int pos_tail_sz = r1->get_positive_tail_size();
for(int i=-1; i<pos_tail_sz; i++) {
app * t1 = get_by_tail_index(r1, i);
app * t2 = get_by_tail_index(r2, i);
res = aux_compare(t1->get_decl(), t2->get_decl());
if(res!=0) { return res; }
res = compare_var_args(t1, t2);
if(res!=0) { return res; }
}
unsigned tail_sz = r1->get_tail_size();
for(unsigned i=pos_tail_sz; i<tail_sz; i++) {
res = aux_compare(r1->get_tail(i), r2->get_tail(i));
if(res!=0) { return res; }
}
return 0;
}
/**
\c r1 and \c r2 must be equal according to the \c rough_compare function for this function
to be called.
*/
int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) {
SASSERT(rough_compare(r1, r2)==0);
int pos_tail_sz = r1->get_positive_tail_size();
for(int i=-1; i<pos_tail_sz; i++) {
int res = compare_args(get_by_tail_index(r1, i), get_by_tail_index(r2, i), skipped_arg_index);
if(res!=0) { return res; }
}
return 0;
}
class const_info {
int m_tail_index;
unsigned m_arg_index;
bool m_has_parent;
/** Parent is a constant that appears earlier in the rule and has always the same value
as this constant. */
unsigned m_parent_index;
public:
const_info(int tail_index, unsigned arg_index)
: m_tail_index(tail_index), m_arg_index(arg_index), m_has_parent(false) {}
int tail_index() const { return m_tail_index; }
unsigned arg_index() const { return m_arg_index; }
bool has_parent() const { return m_has_parent; }
unsigned parent_index() const { SASSERT(has_parent()); return m_parent_index; }
void set_parent_index(unsigned idx) {
SASSERT(!m_has_parent);
m_has_parent = true;
m_parent_index = idx;
}
};
typedef svector<const_info> info_vector;
void collect_const_indexes(app * t, int tail_index, info_vector & res) {
unsigned n = t->get_num_args();
for(unsigned i=0; i<n; i++) {
if(is_var(t->get_arg(i))) {
continue;
}
res.push_back(const_info(tail_index, i));
}
}
void collect_const_indexes(rule * r, info_vector & res) {
collect_const_indexes(r->get_head(), -1, res);
unsigned pos_tail_sz = r->get_positive_tail_size();
for(unsigned i=0; i<pos_tail_sz; i++) {
collect_const_indexes(r->get_tail(i), i, res);
}
}
template<class T>
void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) {
unsigned const_cnt = const_infos.size();
tgt.reset();
for(unsigned i=0; i<const_cnt; i++) {
const_info inf = const_infos[i];
if(inf.has_parent()) {
continue;
}
app * pred = get_by_tail_index(r, inf.tail_index());
tgt.push_back(to_app(pred->get_arg(inf.arg_index())));
SASSERT(tgt.back()->get_num_args()==0);
}
}
template<class T>
void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) {
unsigned const_cnt = const_infos.size();
tgt.reset();
for(unsigned i=0; i<const_cnt; i++) {
const_info inf = const_infos[i];
if(inf.has_parent()) {
continue;
}
app * pred = get_by_tail_index(r, inf.tail_index());
tgt.push_back(pred->get_decl()->get_domain(inf.arg_index()));
}
}
/**
\brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants
that are the same in rules \c *first ... \c *(after_last-1).
*/
void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last,
info_vector & const_infos) {
SASSERT(after_last-first>1);
unsigned const_cnt = const_infos.size();
ptr_vector<app> vals;
rule * r = *(first++);
collect_orphan_consts(r, const_infos, vals);
SASSERT(vals.size()==const_cnt);
rule_vector::iterator it = first;
for(; it!=after_last; ++it) {
for(unsigned i=0; i<const_cnt; i++) {
app * pred = get_by_tail_index(*it, const_infos[i].tail_index());
app * val = to_app(pred->get_arg(const_infos[i].arg_index()));
if(vals[i]!=val) {
vals[i] = 0;
}
}
}
unsigned removed_cnt = 0;
for(unsigned i=0; i<const_cnt; i++) {
if(vals[i]!=0) {
removed_cnt++;
}
else if(removed_cnt!=0) {
const_infos[i-removed_cnt] = const_infos[i];
}
}
if(removed_cnt!=0) {
const_infos.shrink(const_cnt-removed_cnt);
}
}
/**
\brief When function returns, \c parents will contain for each constant the index of the
first constant that is equal to it in all the rules. If there is no such, it will contain
its own index.
*/
void detect_equal_constants(rule_vector::iterator first, rule_vector::iterator after_last,
info_vector & const_infos) {
SASSERT(first!=after_last);
unsigned const_cnt = const_infos.size();
ptr_vector<app> vals;
ptr_vector<sort> sorts;
rule * r = *(first++);
collect_orphan_consts(r, const_infos, vals);
collect_orphan_sorts(r, const_infos, sorts);
SASSERT(vals.size()==const_cnt);
vector<unsigned_vector> possible_parents(const_cnt);
for(unsigned i=1; i<const_cnt; i++) {
for(unsigned j=0; j<i; j++) {
if(vals[i]==vals[j] && sorts[i]==sorts[j]) {
possible_parents[i].push_back(j);
}
}
}
rule_vector::iterator it = first;
for(; it!=after_last; ++it) {
collect_orphan_consts(*it, const_infos, vals);
for(unsigned i=1; i<const_cnt; i++) {
unsigned_vector & ppars = possible_parents[i];
unsigned j=0;
while(j<ppars.size()) {
if(vals[i]!=vals[ppars[j]]) {
ppars[j] = ppars.back();
ppars.pop_back();
}
else {
j++;
}
}
}
}
for(unsigned i=0; i<const_cnt; i++) {
unsigned parent = i;
unsigned_vector & ppars = possible_parents[i];
unsigned ppars_sz = ppars.size();
for(unsigned j=0; j<ppars_sz; j++) {
if(ppars[j]<parent) {
parent = ppars[j];
}
}
if(parent!=i) {
const_infos[i].set_parent_index(parent);
}
}
}
unsigned get_constant_count(rule * r) {
unsigned res = r->get_head()->get_num_args() - count_variable_arguments(r->get_head());
unsigned pos_tail_sz = r->get_positive_tail_size();
for(unsigned i=0; i<pos_tail_sz; i++) {
res+= r->get_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i));
}
return res;
}
bool initial_comparator(rule * r1, rule * r2) {
int res = rough_compare(r1, r2);
if(res!=0) { return res>0; }
return total_compare(r1, r2)>0;
}
class arg_ignoring_comparator {
unsigned m_ignored_index;
public:
arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {}
bool operator()(rule * r1, rule * r2) const {
return total_compare(r1, r2, m_ignored_index)>0;
}
bool eq(rule * r1, rule * r2) const {
return total_compare(r1, r2, m_ignored_index)==0;
}
};
void mk_similarity_compressor::merge_class(rule_vector::iterator first,
rule_vector::iterator after_last) {
SASSERT(after_last-first>1);
info_vector const_infos;
rule * r = *first; //an arbitrary representative of the class
collect_const_indexes(r, const_infos);
remove_stable_constants(first, after_last, const_infos);
unsigned const_cnt = const_infos.size();
SASSERT(const_cnt>0);
detect_equal_constants(first, after_last, const_infos);
//The aux relation contains column for each constant which does not have an earlier constant
//that it is equal to (i.e. only has no parent)
ptr_vector<sort> aux_domain;
collect_orphan_sorts(r, const_infos, aux_domain);
func_decl* head_pred = r->get_head()->get_decl();
symbol const& name_prefix = head_pred->get_name();
std::string name_suffix = "sc_" + to_string(const_cnt);
func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()),
aux_domain.size(), aux_domain.c_ptr(), head_pred);
m_pinned.push_back(aux_pred);
relation_fact val_fact(m_manager, const_cnt);
rule_vector::iterator it = first;
for(; it!=after_last; ++it) {
collect_orphan_consts(*it, const_infos, val_fact);
m_context.add_fact(aux_pred, val_fact);
}
m_context.get_rmanager().mark_saturated(aux_pred);
app * new_head = r->get_head();
ptr_vector<app> new_tail;
svector<bool> new_negs;
unsigned tail_sz = r->get_tail_size();
for(unsigned i=0; i<tail_sz; i++) {
new_tail.push_back(r->get_tail(i));
new_negs.push_back(r->is_neg_tail(i));
}
var_counter var_ctr;
var_ctr.count_vars(m_manager, r);
unsigned max_var_idx, new_var_idx_base;
if(var_ctr.get_max_positive(max_var_idx)) {
new_var_idx_base = max_var_idx+1;
}
else {
new_var_idx_base = 0;
}
ptr_vector<var> const_vars; //variables at indexes of their corresponding constants
expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate
unsigned aux_column_index = 0;
for(unsigned i=0; i<const_cnt; ) {
int tail_idx = const_infos[i].tail_index();
app * & mod_tail = (tail_idx==-1) ? new_head : new_tail[tail_idx];
ptr_vector<expr> mod_args(mod_tail->get_num_args(), mod_tail->get_args());
for(; i<const_cnt && const_infos[i].tail_index()==tail_idx; i++) { //here the outer loop counter is modified
const_info & inf = const_infos[i];
var * mod_var;
if(!inf.has_parent()) {
mod_var = m_manager.mk_var(new_var_idx_base+aux_column_index,
aux_domain[aux_column_index]);
aux_column_index++;
aux_vars.push_back(mod_var);
}
else {
mod_var = const_vars[inf.parent_index()];
}
const_vars.push_back(mod_var);
mod_args[inf.arg_index()] = mod_var;
}
app * upd_tail = m_manager.mk_app(mod_tail->get_decl(), mod_args.c_ptr());
m_pinned.push_back(upd_tail);
mod_tail = upd_tail;
}
app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager);
new_tail.push_back(aux_tail);
new_negs.push_back(false);
rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(),
new_negs.c_ptr());
m_result_rules.push_back(new_rule);
//TODO: allow for a rule to have multiple parent objects
new_rule->set_accounting_parent_object(m_context, r);
m_modified = true;
}
void mk_similarity_compressor::process_class(rule_vector::iterator first,
rule_vector::iterator after_last) {
SASSERT(first!=after_last);
//remove duplicates
{
rule_vector::iterator it = first;
rule_vector::iterator prev = it;
++it;
while(it!=after_last) {
if(it!=after_last && total_compare(*prev, *it)==0) {
--after_last;
std::swap(*it, *after_last);
m_modified = true;
}
else {
prev = it;
++it;
}
}
}
SASSERT(first!=after_last);
unsigned const_cnt = get_constant_count(*first);
#if 0
for(unsigned ignored_index=0; ignored_index<const_cnt; ignored_index++) {
arg_ignoring_comparator comparator(ignored_index);
std::sort(first, after_last, comparator);
rule_vector::iterator it = first;
rule_vector::iterator grp_begin = it;
unsigned grp_size=0;
while(it!=after_last) {
rule_vector::iterator prev = it;
++it;
grp_size++;
if(it==after_last || !comparator.eq(*prev, *it)) {
if(grp_size>m_threshold_count) {
merge_class(grp_begin, it);
//group was processed, so we remove it from the class
if(it==after_last) {
after_last=grp_begin;
it=after_last;
}
else {
while(it!=grp_begin) {
std::swap(*--it, *--after_last);
}
}
}
grp_begin = it;
grp_size = 0;
}
}
}
#endif
//TODO: compress also rules with pairs (or tuples) of equal constants
#if 1
if(const_cnt>0) {
unsigned rule_cnt = static_cast<unsigned>(after_last-first);
if(rule_cnt>m_threshold_count) {
merge_class(first, after_last);
return;
}
}
#endif
//put rules which weren't merged into result
rule_vector::iterator it = first;
for(; it!=after_last; ++it) {
m_result_rules.push_back(*it);
}
}
rule_set * mk_similarity_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
m_modified = false;
unsigned init_rule_cnt = source.get_num_rules();
SASSERT(m_rules.empty());
for(unsigned i=0; i<init_rule_cnt; i++) {
m_rules.push_back(source.get_rule(i));
}
std::sort(m_rules.begin(), m_rules.end(), initial_comparator);
rule_vector::iterator it = m_rules.begin();
rule_vector::iterator end = m_rules.end();
rule_vector::iterator cl_begin = it;
while(it!=end) {
rule_vector::iterator prev = it;
++it;
if(it==end || rough_compare(*prev, *it)!=0) {
process_class(cl_begin, it);
cl_begin = it;
}
}
rule_set * result = static_cast<rule_set *>(0);
if(m_modified) {
result = alloc(rule_set, m_context);
unsigned fin_rule_cnt = m_result_rules.size();
for(unsigned i=0; i<fin_rule_cnt; i++) {
result->add_rule(m_result_rules.get(i));
}
}
reset();
return result;
}
};

View file

@ -1,78 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_similarity_compressor.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-22.
Revision History:
--*/
#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_
#define _DL_MK_SIMILARITY_COMPRESSOR_H_
#include<utility>
#include"map.h"
#include"obj_pair_hashtable.h"
#include"dl_context.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Functor for merging groups of similar rules.
A rule sequence
P("1",x):-Q(x).
...
P("N",x):-Q(x).
will be replaced by
P(y,x):-Q(x), Aux(y).
and a set of facts
Aux("1").
...
Aux("N").
Similar transformation is performed when the varying constant appears in the positive tail.
*/
class mk_similarity_compressor : public rule_transformer::plugin {
context & m_context;
ast_manager & m_manager;
/** number of similar rules necessary for a group to be introduced */
unsigned m_threshold_count;
rule_vector m_rules;
rule_ref_vector m_result_rules;
bool m_modified;
ast_ref_vector m_pinned;
void merge_class(rule_vector::iterator first, rule_vector::iterator after_last);
void process_class(rule_vector::iterator first, rule_vector::iterator after_last);
void reset();
public:
mk_similarity_compressor(context & ctx, unsigned threshold_count);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */

View file

@ -1,734 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_simple_joins.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-20.
Revision History:
--*/
#include<utility>
#include<sstream>
#include<limits>
#include"dl_mk_simple_joins.h"
#include"ast_pp.h"
#include"trace.h"
namespace datalog {
mk_simple_joins::mk_simple_joins(context & ctx):
plugin(1000),
m_context(ctx) {
}
class join_planner {
typedef float cost;
class pair_info {
cost m_total_cost;
/**
\brief Number of rules longer than two that contain this pair.
This number is being updated by \c add_rule and \remove rule. Even though between
adding a rule and removing it, the length of a rule can decrease without this pair
being notified about it, it will surely see the decrease from length 3 to 2 which
the threshold for rule being counted in this counter.
*/
unsigned m_consumers;
bool m_stratified;
unsigned m_src_stratum;
public:
var_idx_set m_all_nonlocal_vars;
rule_vector m_rules;
pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {}
bool can_be_joined() const {
return m_consumers>0;
}
cost get_cost() const {
/*if(m_instantiated) {
return std::numeric_limits<cost>::min();
}*/
SASSERT(m_consumers>0);
cost amortized = m_total_cost/m_consumers;
if(m_stratified) {
return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f);
}
else {
return amortized;
}
}
/**
\brief Add rule \c r among rules interested in current predicate pair.
The \c pl.m_rule_content entry of the rule has to be properly filled in
by the time of a call to this function
*/
void add_rule(join_planner & pl, app * t1, app * t2, rule * r,
const var_idx_set & non_local_vars_normalized) {
if(m_rules.empty()) {
m_total_cost = pl.compute_cost(t1, t2);
m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl()));
}
m_rules.push_back(r);
if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) {
m_consumers++;
}
if(m_stratified) {
unsigned head_stratum = pl.get_stratum(r->get_head()->get_decl());
SASSERT(head_stratum>=m_src_stratum);
if(head_stratum==m_src_stratum) {
m_stratified = false;
}
}
idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized);
}
/**
\brief Remove rule from the pair record. Return true if no rules remain
in the pair, and so it should be removed.
*/
bool remove_rule(rule * r, unsigned original_length) {
TRUSTME( remove_from_vector(m_rules, r) );
if(original_length>2) {
SASSERT(m_consumers>0);
m_consumers--;
}
SASSERT(!m_rules.empty() || m_consumers==0);
return m_rules.empty();
}
private:
pair_info & operator=(const pair_info &); //to avoid the implicit one
};
typedef std::pair<app*, app*> app_pair;
typedef map<app_pair, pair_info *,
pair_hash<obj_ptr_hash<app>, obj_ptr_hash<app> >, default_eq<app_pair> > cost_map;
typedef map<rule *, ptr_vector<app>, ptr_hash<rule>, ptr_eq<rule> > rule_pred_map;
context & m_context;
ast_manager & m;
var_subst & m_var_subst;
rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels
cost_map m_costs;
ptr_vector<app> m_interpreted;
rule_pred_map m_rules_content;
rule_ref_vector m_introduced_rules;
ptr_hashtable<rule, ptr_hash<rule>, ptr_eq<rule> > m_modified_rules;
ast_ref_vector m_pinned;
public:
join_planner(context & ctx, rule_set & rs_aux_copy)
: m_context(ctx), m(ctx.get_manager()), m_var_subst(ctx.get_var_subst()),
m_rs_aux_copy(rs_aux_copy),
m_introduced_rules(ctx.get_rule_manager()),
m_pinned(ctx.get_manager())
{
}
~join_planner()
{
cost_map::iterator it = m_costs.begin();
cost_map::iterator end = m_costs.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
m_costs.reset();
}
private:
void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const {
SASSERT(result.size()>0);
unsigned res_ofs = result.size()-1;
unsigned n=t->get_num_args();
for(unsigned i=0; i<n; i++) {
SASSERT(is_var(t->get_arg(i)));
var * v = to_var(t->get_arg(i));
unsigned var_idx = v->get_idx();
if(result[res_ofs-var_idx]==0) {
result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort());
next_var++;
}
}
}
void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const {
SASSERT(result.empty());
if(t1->get_num_args()==0 && t2->get_num_args()==0) {
return; //nothing to normalize
}
SASSERT(!t1->is_ground() || !t2->is_ground());
unsigned max_var_idx = 0;
{
var_idx_set orig_var_set;
collect_vars(m, t1, orig_var_set);
collect_vars(m, t2, orig_var_set);
var_idx_set::iterator ovit = orig_var_set.begin();
var_idx_set::iterator ovend = orig_var_set.end();
for(; ovit!=ovend; ++ovit) {
unsigned var_idx = *ovit;
if(var_idx>max_var_idx) {
max_var_idx = var_idx;
}
}
}
if(t1->get_decl()!=t2->get_decl()) {
if(t1->get_decl()->get_id()<t2->get_decl()->get_id()) {
std::swap(t1, t2);
}
}
else {
int_vector norm1(max_var_idx+1, -1);
int_vector norm2(max_var_idx+1, -1);
unsigned n=t1->get_num_args();
SASSERT(n==t2->get_num_args());
for(unsigned i=0; i<n; i++) {
//We assume that the mk_simple_joins transformer is applied after mk_filter_rules,
//so the only literals which appear in pairs are the ones that contain only variables.
var * v1 = to_var(t1->get_arg(i));
var * v2 = to_var(t2->get_arg(i));
if(v1->get_sort()!=v2->get_sort()) {
//different sorts mean we can distinguish the two terms
if(v1->get_sort()->get_id()<v2->get_sort()->get_id()) {
std::swap(t1, t2);
}
break;
}
unsigned v1_idx = v1->get_idx();
unsigned v2_idx = v2->get_idx();
//since the rules already went through the mk_filter_rules transformer,
//variables must be linear
SASSERT(norm1[v1_idx]==-1);
SASSERT(norm2[v2_idx]==-1);
if(norm2[v1_idx]!=norm1[v2_idx]) {
//now we can distinguish the two terms
if(norm2[v1_idx]<norm1[v2_idx]) {
std::swap(t1, t2);
}
break;
}
norm1[v1_idx]=i;
norm2[v2_idx]=i;
}
//if we did not exit the loop prematurely, the two terms are indistinguishable,
//so the order should not matter
}
result.resize(max_var_idx+1, static_cast<expr *>(0));
unsigned next_var = 0;
get_normalizer(t1, next_var, result);
get_normalizer(t2, next_var, result);
}
app_pair get_key(app * t1, app * t2) {
expr_ref_vector norm_subst(m);
get_normalizer(t1, t2, norm_subst);
expr_ref t1n_ref(m);
expr_ref t2n_ref(m);
m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref);
m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref);
app * t1n = to_app(t1n_ref);
app * t2n = to_app(t2n_ref);
if(t1n>t2n) {
std::swap(t1n, t2n);
}
m_pinned.push_back(t1n);
m_pinned.push_back(t2n);
/*
IF_VERBOSE(0,
print_renaming(norm_subst, verbose_stream());
display_predicate(m_context, t1, verbose_stream());
display_predicate(m_context, t2, verbose_stream());
display_predicate(m_context, t1n, verbose_stream());
display_predicate(m_context, t2n, verbose_stream()););
*/
return app_pair(t1n, t2n);
}
/**
\brief Add rule \c r among rules interested in predicate pair \c t1, \c t2.
The \c m_rule_content entry of the rule \c r has to be properly filled in
by the time of a call to this function
*/
void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) {
TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n";
r->display(m_context, tout); tout << "\n";);
SASSERT(t1!=t2);
cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0);
pair_info * & ptr_inf = e->get_data().m_value;
if(ptr_inf==0) {
ptr_inf = alloc(pair_info);
}
pair_info & inf = *ptr_inf;
expr_ref_vector normalizer(m);
get_normalizer(t1, t2, normalizer);
unsigned norm_ofs = normalizer.size()-1;
var_idx_set normalized_vars;
var_idx_set::iterator vit = non_local_vars.begin();
var_idx_set::iterator vend = non_local_vars.end();
for(; vit!=vend; ++vit) {
unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx();
normalized_vars.insert(norm_var);
}
inf.add_rule(*this, t1, t2, r, normalized_vars);
}
pair_info & get_pair(app_pair key) const {
cost_map::entry * e = m_costs.find_core(key);
SASSERT(e);
return *e->get_data().m_value;
}
void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) {
pair_info * ptr = &get_pair(key);
if(ptr->remove_rule(r, original_len)) {
SASSERT(ptr->m_rules.empty());
m_costs.remove(key);
dealloc(ptr);
}
}
void register_rule(rule * r) {
var_counter counter;
counter.count_vars(m, r, 1);
ptr_vector<app> & rule_content =
m_rules_content.insert_if_not_there2(r, ptr_vector<app>())->get_data().m_value;
SASSERT(rule_content.empty());
unsigned pos_tail_size=r->get_positive_tail_size();
for(unsigned i=0; i<pos_tail_size; i++) {
rule_content.push_back(r->get_tail(i));
}
for(unsigned i=0; i<pos_tail_size; i++) {
app * t1 = r->get_tail(i);
var_idx_set t1_vars;
collect_vars(m, t1, t1_vars);
counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter
for(unsigned j=i+1; j<pos_tail_size; j++) {
app * t2 = r->get_tail(j);
counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter
var_idx_set scope_vars(t1_vars);
collect_vars(m, t2, scope_vars);
var_idx_set non_local_vars;
counter.collect_positive(non_local_vars);
counter.count_vars(m, t2, 1); //restore t2 variables in counter
set_intersection(non_local_vars, scope_vars);
register_pair(t1, t2, r, non_local_vars);
}
counter.count_vars(m, t1, 1); //restore t1 variables in counter
}
}
bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args,
ptr_vector<sort> & domain) {
unsigned n=t->get_num_args();
for(unsigned i=0; i<n; i++) {
var * v=to_var(t->get_arg(i));
if(v->get_idx()==var_idx) {
args.push_back(v);
domain.push_back(m.get_sort(v));
return true;
}
}
return false;
}
void join_pair(app_pair pair_key) {
app * t1 = pair_key.first;
app * t2 = pair_key.second;
pair_info & inf = get_pair(pair_key);
SASSERT(!inf.m_rules.empty());
var_idx_set & output_vars = inf.m_all_nonlocal_vars;
expr_ref_vector args(m);
ptr_vector<sort> domain;
unsigned arity = output_vars.num_elems();
idx_set::iterator ovit=output_vars.begin();
idx_set::iterator ovend=output_vars.end();
//TODO: improve quadratic complexity
for(;ovit!=ovend;++ovit) {
unsigned var_idx=*ovit;
bool found=extract_argument_info(var_idx, t1, args, domain);
if(!found) {
found=extract_argument_info(var_idx, t2, args, domain);
}
SASSERT(found);
}
SASSERT(args.size()==arity);
SASSERT(domain.size()==arity);
rule * one_parent = inf.m_rules.back();
func_decl* parent_head = one_parent->get_head()->get_decl();
const char * one_parent_name = parent_head->get_name().bare_str();
std::string parent_name;
if(inf.m_rules.size()>1) {
parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1);
}
else {
parent_name = one_parent_name;
}
func_decl * decl = m_context.mk_fresh_head_predicate(
symbol(parent_name.c_str()), symbol("split"),
arity, domain.c_ptr(), parent_head);
app_ref head(m.mk_app(decl, arity, args.c_ptr()), m);
app * tail[] = {t1, t2};
rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0);
//TODO: update accounting so that it can handle multiple parents
new_rule->set_accounting_parent_object(m_context, one_parent);
m_introduced_rules.push_back(new_rule);
//here we copy the inf.m_rules vector because inf.m_rules will get changed
//in the iteration. Also we use hashtable instead of vector because we do
//not want to process one rule twice.
typedef ptr_hashtable<rule, ptr_hash<rule>, default_eq<rule *> > rule_hashtable;
rule_hashtable relevant_rules;
insert_into_set(relevant_rules, inf.m_rules);
rule_hashtable::iterator rit = relevant_rules.begin();
rule_hashtable::iterator rend = relevant_rules.end();
for(; rit!=rend; ++rit) {
apply_binary_rule(*rit, pair_key, head);
}
// SASSERT(!m_costs.contains(pair_key));
}
void replace_edges(rule * r, const ptr_vector<app> & removed_tails,
const ptr_vector<app> & added_tails0, const ptr_vector<app> & rule_content) {
SASSERT(removed_tails.size()>=added_tails0.size());
unsigned len = rule_content.size();
unsigned original_len = len+removed_tails.size()-added_tails0.size();
ptr_vector<app> added_tails(added_tails0); //we need a copy since we'll be modifying it
unsigned rt_sz = removed_tails.size();
//remove edges between removed tails
for(unsigned i=0; i<rt_sz; i++) {
for(unsigned j=i+1; j<rt_sz; j++) {
app_pair pair_key = get_key(removed_tails[i], removed_tails[j]);
remove_rule_from_pair(pair_key, r, original_len);
}
}
//remove edges between surviving tails and removed tails
for(unsigned i=0; i<len; i++) {
if(added_tails.contains(rule_content[i])) {
continue;
}
for(unsigned ri=0; ri<rt_sz; ri++) {
app_pair pair_key = get_key(rule_content[i], removed_tails[ri]);
remove_rule_from_pair(pair_key, r, original_len);
}
}
if(len==1) {
return;
}
app * head = r->get_head();
var_counter counter;
counter.count_vars(m, head, 1);
unsigned tail_size=r->get_tail_size();
unsigned pos_tail_size=r->get_positive_tail_size();
for(unsigned i=pos_tail_size; i<tail_size; i++) {
counter.count_vars(m, r->get_tail(i), 1);
}
for(unsigned i=0; i<len; i++) {
counter.count_vars(m, rule_content[i], 1);
}
//add edges that contain added tails
while(!added_tails.empty()) {
app * a_tail = added_tails.back(); //added tail
var_idx_set a_tail_vars;
collect_vars(m, a_tail, a_tail_vars);
counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter
for(unsigned i=0; i<len; i++) {
app * o_tail = rule_content[i]; //other tail
if(added_tails.contains(o_tail)) {
//this avoids adding edges between new tails twice
continue;
}
counter.count_vars(m, o_tail, -1); //temporarily remove o_tail variables from counter
var_idx_set scope_vars(a_tail_vars);
collect_vars(m, o_tail, scope_vars);
var_idx_set non_local_vars;
counter.collect_positive(non_local_vars);
counter.count_vars(m, o_tail, 1); //restore o_tail variables in counter
set_intersection(non_local_vars, scope_vars);
register_pair(o_tail, a_tail, r, non_local_vars);
}
counter.count_vars(m, a_tail, 1); //restore t1 variables in counter
added_tails.pop_back();
}
}
void apply_binary_rule(rule * r, app_pair pair_key, app * t_new) {
app * t1 = pair_key.first;
app * t2 = pair_key.second;
ptr_vector<app> & rule_content = m_rules_content.find_core(r)->get_data().m_value;
unsigned len = rule_content.size();
if(len==1) {
return;
}
func_decl * t1_pred = t1->get_decl();
func_decl * t2_pred = t2->get_decl();
ptr_vector<app> removed_tails;
ptr_vector<app> added_tails;
for(unsigned i1=0; i1<len; i1++) {
app * rt1 = rule_content[i1];
if(rt1->get_decl()!=t1_pred) {
continue;
}
unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0;
for(unsigned i2=i2start; i2<len; i2++) {
app * rt2 = rule_content[i2];
if(i1==i2 || rt2->get_decl()!=t2_pred) {
continue;
}
if(get_key(rt1, rt2)!=pair_key) {
continue;
}
expr_ref_vector normalizer(m);
get_normalizer(rt1, rt2, normalizer);
expr_ref_vector denormalizer(m);
reverse_renaming(m, normalizer, denormalizer);
expr_ref new_transf(m);
m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf);
app * new_lit = to_app(new_transf);
m_pinned.push_back(new_lit);
rule_content[i1]=new_lit;
rule_content[i2]=rule_content.back();
rule_content.pop_back();
len--; //here the bound of both loops changes!!!
removed_tails.push_back(rt1);
removed_tails.push_back(rt2);
added_tails.push_back(new_lit);
//this exits the inner loop, the outer one continues in case there will
//be other matches
break;
}
}
SASSERT(!removed_tails.empty());
SASSERT(!added_tails.empty());
m_modified_rules.insert(r);
replace_edges(r, removed_tails, added_tails, rule_content);
}
cost get_domain_size(func_decl * pred, unsigned arg_index) const {
relation_sort sort = pred->get_domain(arg_index);
return static_cast<cost>(m_context.get_sort_size_estimate(sort));
//unsigned sz;
//if(!m_context.get_sort_size(sort, sz)) {
// sz=UINT_MAX;
//}
//return static_cast<cost>(sz);
}
unsigned get_stratum(func_decl * pred) const {
return m_rs_aux_copy.get_predicate_strat(pred);
}
cost estimate_size(app * t) const {
func_decl * pred = t->get_decl();
unsigned n=pred->get_arity();
if( (m_context.saturation_was_run() && m_context.get_rmanager().try_get_relation(pred))
|| m_context.get_rmanager().is_saturated(pred)) {
SASSERT(m_context.get_rmanager().try_get_relation(pred)); //if it is saturated, it should exist
unsigned rel_size_int = m_context.get_relation(pred).get_size_estimate_rows();
if(rel_size_int!=0) {
cost rel_size = static_cast<cost>(rel_size_int);
cost curr_size = rel_size;
for(unsigned i=0; i<n; i++) {
if(!is_var(t->get_arg(i))) {
curr_size /= get_domain_size(pred, i);
}
}
return curr_size;
}
}
cost res = 1;
for(unsigned i=0; i<n; i++) {
if(is_var(t->get_arg(i))) {
res *= get_domain_size(pred, i);
}
}
return res;
}
cost compute_cost(app * t1, app * t2) const {
func_decl * t1_pred = t1->get_decl();
func_decl * t2_pred = t2->get_decl();
cost inters_size = 1;
variable_intersection vi(m_context.get_manager());
vi.populate(t1, t2);
unsigned n = vi.size();
for(unsigned i=0; i<n; i++) {
unsigned arg_index1, arg_index2;
vi.get(i, arg_index1, arg_index2);
inters_size *= get_domain_size(t1_pred, arg_index1);
//joined arguments must have the same domain
SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2));
}
cost res = estimate_size(t1)*estimate_size(t2)/(inters_size*inters_size);
//cost res = -inters_size;
/*unsigned t1_strat = get_stratum(t1_pred);
SASSERT(t1_strat<=m_head_stratum);
if(t1_strat<m_head_stratum) {
unsigned t2_strat = get_stratum(t2_pred);
SASSERT(t2_strat<=m_head_stratum);
if(t2_strat<m_head_stratum) {
//the rule of this predicates would depend on predicates
//in lower stratum than the head, which is a good thing, since
//then the rule code will not need to appear in a loop
if(res>0) {
res /= 2;
}
else {
res *= 2;
}
}
}*/
TRACE("report_costs",
display_predicate(m_context, t1, tout);
display_predicate(m_context, t2, tout);
tout << res << "\n";);
return res;
}
bool pick_best_pair(app_pair & p) {
app_pair best;
bool found = false;
cost best_cost;
cost_map::iterator it = m_costs.begin();
cost_map::iterator end = m_costs.end();
for(; it!=end; ++it) {
app_pair key = it->m_key;
pair_info & inf = *it->m_value;
if(!inf.can_be_joined()) {
continue;
}
cost c = inf.get_cost();
if(!found || c<best_cost) {
found = true;
best_cost = c;
best = key;
}
}
if(!found) {
return false;
}
p=best;
return true;
}
public:
rule_set * run(rule_set const & source) {
unsigned num_rules = source.get_num_rules();
for (unsigned i = 0; i < num_rules; i++) {
register_rule(source.get_rule(i));
}
app_pair selected;
while(pick_best_pair(selected)) {
join_pair(selected);
}
if(m_modified_rules.empty()) {
return 0;
}
rule_set * result = alloc(rule_set, m_context);
rule_pred_map::iterator rcit = m_rules_content.begin();
rule_pred_map::iterator rcend = m_rules_content.end();
for(; rcit!=rcend; ++rcit) {
rule * orig_r = rcit->m_key;
ptr_vector<app> content = rcit->m_value;
SASSERT(content.size()<=2);
if(content.size()==orig_r->get_positive_tail_size()) {
//rule did not change
result->add_rule(orig_r);
continue;
}
ptr_vector<app> tail(content);
svector<bool> negs(tail.size(), false);
unsigned or_len = orig_r->get_tail_size();
for(unsigned i=orig_r->get_positive_tail_size(); i<or_len; i++) {
tail.push_back(orig_r->get_tail(i));
negs.push_back(orig_r->is_neg_tail(i));
}
rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(),
negs.c_ptr());
new_rule->set_accounting_parent_object(m_context, orig_r);
result->add_rule(new_rule);
}
while(!m_introduced_rules.empty()) {
result->add_rule(m_introduced_rules.back());
m_introduced_rules.pop_back();
}
return result;
}
};
rule_set * mk_simple_joins::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
rule_set rs_aux_copy(m_context);
rs_aux_copy.add_rules(source);
if(!rs_aux_copy.is_closed()) {
rs_aux_copy.close();
}
join_planner planner(m_context, rs_aux_copy);
return planner.run(source);
}
};

View file

@ -1,62 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_simple_joins.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-20.
Revision History:
--*/
#ifndef _DL_MK_SIMPLE_JOINS_H_
#define _DL_MK_SIMPLE_JOINS_H_
#include"map.h"
#include"obj_pair_hashtable.h"
#include"dl_context.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Functor for creating rules that contain simple joins.
A simple join is the join of two tables.
After applying this transformation, every rule has at most one join.
So, the rules will have the form
HEAD :- TAIL.
HEAD :- TAIL_1, TAIL_2.
We also assume a rule may contain interpreted expressions that work as filtering conditions.
So, we may also have:
HEAD :- TAIL, C_1, ..., C_n.
HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n.
Where the C_i's are interpreted expressions.
We say that a rule containing C_i's is a rule with a "big tail".
*/
class mk_simple_joins : public rule_transformer::plugin {
context & m_context;
public:
mk_simple_joins(context & ctx);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_SIMPLE_JOINS_H_ */

View file

@ -1,839 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_slice.cpp
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2012-9-12.
Revision History:
Consider a rule:
P(x,y) :- R(x,z), phi(x,y,z)
input: x, z
output: x, y
Let x_i, y_i, z_i be incides into the vectors x, y, z.
Suppose that positions in P and R are annotated with what is
slicable.
Sufficient conditions for sliceability:
x_i is sliceable if x_i does not appear in phi(x,y,z)
and the positions where x_i is used in P and R are sliceable
y_i is sliceable if y_i does not occur in phi(x,y,z), or
if it occurs in phi(x,y,z) it is only in one conjunct of the form
y_i = t[x_j,y_j,z_j]
and the positions where y_i is used in P and R are sliceable
z_i is sliceable if z_i does not occur in phi(x,y,z), or
if it occurs in phi(x,y,z) it is only in one conjunct of the form
y_i = t[x_j,y_j,z_i] where y_i is sliceable
and the positions where z_i is used in P and R are sliceable
A more refined approach may be using Gaussean elimination based
on x,z and eliminating variables from x,y (expressing them in terms
of a disjoint subeset of x,z).
--*/
#include "dl_mk_slice.h"
#include "ast_pp.h"
#include "expr_functors.h"
#include "dl_mk_rule_inliner.h"
#include "model_smt2_pp.h"
namespace datalog {
/**
Convert from sliced proofs to original proofs.
Given sliced rule
fml0: forall x y z u. p(x,y) & z = f(x,y) & phi(x,u) => p(u, z)
into
fml1: forall a b . q(a) & phi(a,b) => q(b)
It induces mappings:
theta: a |-> x, b |-> u
vars: x y z u.
predicates: -q(a) |-> p(x,y)
+q(b) |-> z = f(x,y) => p(u,z)
fml1 |-> fml0
The mapping theta is an injective function from variable indices
to variable indices. We can apply it as a substitution on expressions,
but we can also apply it as a transformation on substitutions. We
write theta[subst] when applying theta on substitution 'subst' such
that if [x |-> t] is in subst, then [theta(x) |-> theta(t)] is in
the result.
Given hyper-resolvent: fml1 subst1 fml2 subst2 |- fml3
where fml1 |-> fml1' with theta1
fml2 |-> fml2' with theta2
Perform the following steps:
1. [Convert fml1' fml2' to datalog rules because we have resolution routines]
2. Create subst1' := theta1[subst1]
subst2' := theta2[subst2]
3. Set fml1'' := subst1'(fml1')
fml2'' := subst2'(fml2')
4. Resolve fml1'' and fml2''
extract subst1'', subst2'' from resolvents.
extract goal fml3'
5. Create subst1''' := subst1'' o subst1'
subst2''' := subst2'' o subst2'
6. Return fml1'' subst1''' fml2'' subst2''' |- fml3'
7. Attach to fml3' the transformation ...?
*/
class mk_slice::slice_proof_converter : public proof_converter {
context& m_ctx;
ast_manager& m;
rule_manager& rm;
rule_ref_vector m_pinned_rules;
expr_ref_vector m_pinned_exprs;
obj_map<rule, rule*> m_rule2slice; // rule to sliced rule
obj_map<rule, unsigned_vector> m_renaming; // rule to renaming
obj_map<expr, rule*> m_sliceform2rule; // sliced formula to rule.
ptr_vector<proof> m_todo;
obj_map<proof, proof*> m_new_proof;
rule_unifier m_unifier;
slice_proof_converter(slice_proof_converter const& other);
void init_form2rule() {
if (!m_sliceform2rule.empty()) {
return;
}
obj_map<rule, rule*>::iterator it = m_rule2slice.begin();
obj_map<rule, rule*>::iterator end = m_rule2slice.end();
expr_ref fml(m);
for (; it != end; ++it) {
TRACE("dl",
it->m_key->display(m_ctx, tout << "orig:\n");
it->m_value->display(m_ctx, tout << "new:\n"););
it->m_value->to_formula(fml);
m_pinned_exprs.push_back(fml);
m_sliceform2rule.insert(fml, it->m_key);
}
}
void translate_proof(proof_ref& pr) {
m_todo.reset();
m_new_proof.reset();
m_todo.push_back(pr);
while (!m_todo.empty()) {
proof* p = m_todo.back();
if (m_new_proof.contains(p)) {
m_todo.pop_back();
}
else if (translate_asserted(p)) {
// done
}
else if (translate_hyper_res(p)) {
// done
}
else {
m_new_proof.insert(p, p);
m_todo.pop_back();
TRACE("dl", tout << "unhandled proof term\n" << mk_pp(p, m) << "\n";);
}
}
pr = m_new_proof.find(pr);
}
bool translate_asserted(proof* p) {
expr* fact = 0;
rule* r = 0;
if (!m.is_asserted(p, fact)) {
return false;
}
if (!m_sliceform2rule.find(fact, r)) {
TRACE("dl", tout << "does not have fact\n" << mk_pp(fact, m) << "\n";);
return false;
}
expr_ref fml(m);
proof_ref new_p(m);
r->to_formula(fml);
new_p = m.mk_asserted(fml);
m_pinned_exprs.push_back(new_p);
m_todo.pop_back();
m_new_proof.insert(p, new_p);
return true;
}
bool translate_hyper_res(proof* p) {
dl_decl_util util(m);
svector<std::pair<unsigned, unsigned> > positions;
expr_ref concl(m), slice_concl(m);
proof_ref_vector premises0(m);
vector<expr_ref_vector> substs, substs0;
if (!m.is_hyper_resolve(p, premises0, slice_concl, positions, substs0)) {
return false;
}
unsigned num_args = p->get_num_args();
SASSERT(num_args >= 2);
bool all_found = true;
for (unsigned i = 0; i < num_args-1; ++i) {
proof* arg = to_app(p->get_arg(i));
SASSERT(m.is_proof(arg));
if (!m_new_proof.contains(arg)) {
m_todo.push_back(arg);
all_found = false;
}
}
if (!all_found) {
return true;
}
ptr_vector<proof> premises;
proof* p0 = to_app(p->get_arg(0));
proof* p0_new = m_new_proof.find(p0);
expr* fact0 = m.get_fact(p0);
TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";);
rule* orig0 = m_sliceform2rule.find(fact0);
rule* slice0 = m_rule2slice.find(orig0);
unsigned_vector const& renaming0 = m_renaming.find(orig0);
premises.push_back(p0_new);
rule_ref r1(rm), r2(rm), r3(rm);
r1 = orig0;
substs.push_back(expr_ref_vector(m));
for (unsigned i = 1; i < num_args-1; ++i) {
proof* p1 = to_app(p->get_arg(i));
proof* p1_new = m_new_proof.find(p1);
expr* fact1 = m.get_fact(p1);
TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";);
rule* orig1 = m_sliceform2rule.find(fact1);
rule* slice1 = m_rule2slice.find(orig1);
unsigned_vector const& renaming1 = m_renaming.find(orig1); //TBD
premises.push_back(p1_new);
// TODO: work with substitutions.
r2 = orig1;
unsigned idx = 0; // brittle. TBD get index from positions.
VERIFY(m_unifier.unify_rules(*r1, idx, *r2));
m_unifier.apply(*r1.get(), idx, *r2.get(), r3);
expr_ref_vector const sub1 = m_unifier.get_rule_subst(*r1.get(), true);
for (unsigned j = 0; j < substs.size(); ++j) {
apply_subst(substs[j], sub1);
// size of substitutions may have grown...substs[j].resize(num_args[j]);
}
substs.push_back(m_unifier.get_rule_subst(*r2.get(), false));
TRACE("dl",
r1->display(m_ctx, tout << "rule1:");
r2->display(m_ctx, tout << "rule2:");
r3->display(m_ctx, tout << "res:"););
r1 = r3;
}
r1->to_formula(concl);
proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs);
m_pinned_exprs.push_back(new_p);
m_pinned_rules.push_back(r1.get());
m_sliceform2rule.insert(slice_concl, r1.get());
m_rule2slice.insert(r1.get(), 0);
m_renaming.insert(r1.get(), unsigned_vector());
m_new_proof.insert(p, new_p);
m_todo.pop_back();
TRACE("dl", tout << "translated:\n" << mk_pp(p, m) << "\nto\n" << mk_pp(new_p, m) << "\n";);
return true;
}
public:
slice_proof_converter(context& ctx):
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_pinned_rules(rm),
m_pinned_exprs(m),
m_unifier(ctx) {}
void insert(rule* orig_rule, rule* slice_rule, unsigned sz, unsigned const* renaming) {
m_rule2slice.insert(orig_rule, slice_rule);
m_pinned_rules.push_back(orig_rule);
m_pinned_rules.push_back(slice_rule);
m_renaming.insert(orig_rule, unsigned_vector(sz, renaming));
}
virtual void operator()(ast_manager& m, unsigned num_source, proof * const * source, proof_ref & result) {
SASSERT(num_source == 1);
result = source[0];
init_form2rule();
translate_proof(result);
}
virtual proof_converter * translate(ast_translation & translator) {
UNREACHABLE();
// this would require implementing translation for the dl_context.
return 0;
}
};
class mk_slice::slice_model_converter : public model_converter {
ast_manager& m;
obj_map<func_decl, func_decl*> m_slice2old;
obj_map<func_decl, bit_vector> m_sliceable;
ast_ref_vector m_pinned;
public:
slice_model_converter(mk_slice& parent, ast_manager& m): m(m), m_pinned(m) {}
void add_predicate(func_decl* old_f, func_decl* slice_f) {
m_pinned.push_back(old_f);
m_pinned.push_back(slice_f);
m_slice2old.insert(slice_f, old_f);
}
void add_sliceable(func_decl* f, bit_vector const& bv) {
m_pinned.push_back(f);
m_sliceable.insert(f, bv);
}
virtual void operator()(model_ref & md) {
if (m_slice2old.empty()) {
return;
}
TRACE("dl", model_smt2_pp(tout, m, *md, 0); );
model_ref old_model = alloc(model, m);
obj_map<func_decl, func_decl*>::iterator it = m_slice2old.begin();
obj_map<func_decl, func_decl*>::iterator end = m_slice2old.end();
for (; it != end; ++it) {
func_decl* old_p = it->m_value;
func_decl* new_p = it->m_key;
bit_vector const& is_sliced = m_sliceable.find(old_p);
SASSERT(is_sliced.size() == old_p->get_arity());
SASSERT(is_sliced.size() > new_p->get_arity());
func_interp* old_fi = alloc(func_interp, m, is_sliced.size());
TRACE("dl", tout << mk_pp(old_p, m) << " " << mk_pp(new_p, m) << "\n";
for (unsigned j = 0; j < is_sliced.size(); ++j) {
tout << (is_sliced.get(j)?"1":"0");
}
tout << "\n";);
if (new_p->get_arity() == 0) {
old_fi->set_else(md->get_const_interp(new_p));
}
else {
expr_ref_vector subst(m);
expr_ref tmp(m);
var_subst vs(m, false);
for (unsigned i = 0; i < is_sliced.size(); ++i) {
if (!is_sliced.get(i)) {
subst.push_back(m.mk_var(i, old_p->get_domain(i)));
}
}
func_interp* new_fi = md->get_func_interp(new_p);
if (!new_fi) {
TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";);
dealloc(old_fi);
continue;
}
if (!new_fi->is_partial()) {
TRACE("dl", tout << mk_pp(new_fi->get_else(), m) << "\n";);
vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp);
old_fi->set_else(tmp);
}
unsigned num_entries = new_fi->num_entries();
for (unsigned j = 0; j < num_entries; ++j) {
expr_ref res(m);
expr_ref_vector args(m);
func_entry const* e = new_fi->get_entry(j);
for (unsigned k = 0, l = 0; k < old_p->get_arity(); ++k) {
if (!is_sliced.get(k)) {
vs(e->get_arg(l++), subst.size(), subst.c_ptr(), tmp);
args.push_back(tmp);
}
else {
args.push_back(m.mk_var(k, old_p->get_domain(k)));
}
SASSERT(l <= new_p->get_arity());
}
vs(e->get_result(), subst.size(), subst.c_ptr(), res);
old_fi->insert_entry(args.c_ptr(), res.get());
}
old_model->register_decl(old_p, old_fi);
}
}
// register values that have not been sliced.
unsigned sz = md->get_num_constants();
for (unsigned i = 0; i < sz; ++i) {
func_decl* c = md->get_constant(i);
if (!m_slice2old.contains(c)) {
old_model->register_decl(c, md->get_const_interp(c));
}
}
sz = md->get_num_functions();
for (unsigned i = 0; i < sz; ++i) {
func_decl* f = md->get_function(i);
if (!m_slice2old.contains(f)) {
func_interp* fi = md->get_func_interp(f);
old_model->register_decl(f, fi->copy());
}
}
md = old_model;
TRACE("dl", model_smt2_pp(tout, m, *md, 0); );
}
virtual model_converter * translate(ast_translation & translator) {
UNREACHABLE();
return 0;
}
};
mk_slice::mk_slice(context & ctx):
plugin(1),
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_solved_vars(m),
m_pinned(m),
m_pc(0),
m_mc(0)
{}
bit_vector& mk_slice::get_predicate_slice(func_decl* h) {
if (!m_sliceable.contains(h)) {
bit_vector bv;
bv.resize(h->get_arity(), true);
m_sliceable.insert(h, bv);
}
return m_sliceable.find(h);
}
/**
\brief Saturate set of rules with respect to slicing criteria.
*/
void mk_slice::saturate(rule_set const& src) {
bool change = true;
while (change) {
change = false;
for (unsigned i = 0; i < src.get_num_rules(); ++i) {
change = prune_rule(*src.get_rule(i)) || change;
}
}
}
bool mk_slice::prune_rule(rule& r) {
TRACE("dl", r.display(m_ctx, tout << "prune:\n"); );
bool change = false;
init_vars(r);
//
// if a predicate in the body takes a constant as argument,
// the corresponding position is not sliceable.
//
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
app* p = r.get_tail(j);
bit_vector& bv = get_predicate_slice(p);
for (unsigned i = 0; i < p->get_num_args(); ++i) {
if (!is_var(p->get_arg(i)) && bv.get(i)) {
bv.unset(i);
change = true;
TRACE("dl", tout << "argument " << i << " is not a variable " << p->get_decl()->get_name() << "\n";);
}
}
}
//
// Collect the set of variables that are solved.
// Collect the occurrence count of the variables per conjunct.
//
expr_ref_vector conjs = get_tail_conjs(r);
uint_set used_vars, parameter_vars;
for (unsigned j = 0; j < conjs.size(); ++j) {
expr* e = conjs[j].get();
expr_ref r(m);
unsigned v;
if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) {
TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";);
add_var(v);
if (!m_solved_vars[v].get()) {
add_free_vars(parameter_vars, r);
m_solved_vars[v] = r;
}
else {
// variables can only be solved once.
add_free_vars(used_vars, e);
add_free_vars(used_vars, m_solved_vars[v].get());
used_vars.insert(v);
}
}
else {
add_free_vars(used_vars, e);
}
}
uint_set::iterator it = used_vars.begin(), end = used_vars.end();
for (; it != end; ++it) {
if (*it < m_var_is_sliceable.size()) {
m_var_is_sliceable[*it] = false;
}
}
//
// Check if sliceable variables are either solved
// or are used to solve output sliceable variables, or
// don't occur in interpreted tail.
//
for (unsigned i = 0; i < num_vars(); ++i) {
if (!m_var_is_sliceable[i]) {
continue;
}
if (used_vars.contains(i)) {
m_var_is_sliceable[i] = false;
continue;
}
bool is_input = m_input[i];
bool is_output = m_output[i];
if (is_input && is_output) {
if (m_solved_vars[i].get()) {
m_var_is_sliceable[i] = false;
}
}
else if (is_output) {
if (parameter_vars.contains(i)) {
m_var_is_sliceable[i] = false;
}
}
else if (is_input) {
// I can be a parameter var, but not in used_vars.
}
else {
// variable does not correspond to
// any position in predicates.
}
}
//
// Update sliceable predicates based on slicing information of variables.
//
change = finalize_vars(r.get_head()) || change;
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
change = finalize_vars(r.get_tail(j)) || change;
}
return change;
}
bool mk_slice::is_eq(expr* e, unsigned& v, expr_ref& r) {
expr* c, *th, *el, *e1, *e2;
unsigned v1, v2;
expr_ref r1(m), r2(m);
if (m.is_ite(e, c, th, el)) {
if (is_eq(th, v1, r1) && is_eq(el, v2, r2) && v1 == v2) {
v = v1;
r = m.mk_ite(c, r1, r2);
return true;
}
}
if (is_var(e)) {
v = to_var(e)->get_idx();
r = m.mk_true();
return true;
}
if (m.is_not(e,e) && is_var(e)) {
v = to_var(e)->get_idx();
r = m.mk_false();
return true;
}
if (m.is_eq(e, e1, e2) && is_var(e1)) {
v = to_var(e1)->get_idx();
r = e2;
return true;
}
if (m.is_eq(e, e1, e2) && is_var(e2)) {
v = to_var(e2)->get_idx();
r = e1;
return true;
}
return false;
}
bool mk_slice::is_output(unsigned idx) {
return idx < m_output.size() && m_output[idx] && !m_input[idx];
}
bool mk_slice::is_output(expr* e) {
if (is_var(e)) {
return is_output(to_var(e)->get_idx());
}
else {
return false;
}
}
void mk_slice::init_vars(rule& r) {
m_input.reset();
m_output.reset();
m_var_is_sliceable.reset();
m_solved_vars.reset();
init_vars(r.get_head(), true, false);
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
init_vars(r.get_tail(j), false, r.is_neg_tail(j));
}
}
expr_ref_vector mk_slice::get_tail_conjs(rule const& r) {
expr_ref_vector conjs(m);
for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) {
conjs.push_back(r.get_tail(j));
}
datalog::flatten_and(conjs);
return conjs;
}
void mk_slice::add_var(unsigned idx) {
if (idx >= m_input.size()) {
m_input.resize(idx+1, false);
m_output.resize(idx+1, false);
m_var_is_sliceable.resize(idx+1, true);
m_solved_vars.resize(idx+1);
}
}
void mk_slice::init_vars(app* p, bool is_output, bool is_neg_tail) {
bit_vector& bv = get_predicate_slice(p);
for (unsigned i = 0; i < p->get_num_args(); ++i) {
if (is_neg_tail) {
TRACE("dl", tout << "negated " << i << " in " << p->get_decl()->get_name() << "\n";);
bv.unset(i);
}
expr* arg = p->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
add_var(idx);
if (is_output) {
m_output[idx] = true;
}
else {
m_input[idx] = true;
}
m_var_is_sliceable[idx] &= bv.get(i);
}
else {
SASSERT(m.is_value(arg));
if (!is_output) {
TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";);
bv.unset(i);
}
}
}
}
bool mk_slice::finalize_vars(app* p) {
bool change = false;
bit_vector& bv = get_predicate_slice(p);
for (unsigned i = 0; i < p->get_num_args(); ++i) {
expr* arg = p->get_arg(i);
if (is_var(arg) && !m_var_is_sliceable[to_var(arg)->get_idx()] && bv.get(i)) {
bv.unset(i);
change = true;
TRACE("dl", tout << "variable is unslicable " << mk_pp(arg, m) << " for index " << i << " in " << p->get_decl()->get_name() << "\n";);
}
}
return change;
}
void mk_slice::add_free_vars(uint_set& result, expr* e) {
ptr_vector<sort> sorts;
get_free_vars(e, sorts);
for (unsigned i = 0; i < sorts.size(); ++i) {
if (sorts[i]) {
result.insert(i);
}
}
}
void mk_slice::display(std::ostream& out) {
obj_map<func_decl, bit_vector>::iterator it = m_sliceable.begin();
obj_map<func_decl, bit_vector>::iterator end = m_sliceable.end();
for (; it != end; ++it) {
out << it->m_key->get_name() << " ";
bit_vector const& bv = it->m_value;
for (unsigned i = 0; i < bv.size(); ++i) {
out << (bv.get(i)?"1":"0");
}
out << "\n";
}
}
void mk_slice::reset() {
m_input.reset();
m_output.reset();
m_var_is_sliceable.reset();
m_solved_vars.reset();
m_predicates.reset();
m_pinned.reset();
}
void mk_slice::declare_predicates() {
obj_map<func_decl, bit_vector>::iterator it = m_sliceable.begin(), end = m_sliceable.end();
ptr_vector<sort> domain;
func_decl* f;
for (; it != end; ++it) {
domain.reset();
func_decl* p = it->m_key;
bit_vector const& bv = it->m_value;
for (unsigned i = 0; i < bv.size(); ++i) {
if (!bv.get(i)) {
domain.push_back(p->get_domain(i));
}
}
if (domain.size() < bv.size()) {
f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p);
m_pinned.push_back(f);
m_predicates.insert(p, f);
if (m_mc) {
m_mc->add_predicate(p, f);
}
}
}
}
bool mk_slice::rule_updated(rule const& r) {
if (m_predicates.contains(r.get_decl())) return true;
for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) {
if (m_predicates.contains(r.get_decl(i))) return true;
}
return false;
}
void mk_slice::update_predicate(app* p, app_ref& q) {
func_decl* qd;
if (m_predicates.find(p->get_decl(), qd)) {
bit_vector const& bv = get_predicate_slice(p->get_decl());
ptr_vector<expr> args;
for (unsigned i = 0; i < bv.size(); ++i) {
if (!bv.get(i)) {
args.push_back(p->get_arg(i));
}
}
q = m.mk_app(qd, args.size(), args.c_ptr());
}
else {
q = p;
}
}
void mk_slice::update_rule(rule& r, rule_set& dst) {
rule* new_rule;
if (rule_updated(r)) {
app_ref_vector tail(m);
app_ref head(m);
ptr_vector<sort> sorts;
update_predicate(r.get_head(), head);
get_free_vars(head.get(), sorts);
for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) {
app_ref t(m);
update_predicate(r.get_tail(i), t);
tail.push_back(t);
get_free_vars(t, sorts);
}
expr_ref_vector conjs = get_tail_conjs(r);
m_solved_vars.reset();
uint_set used_vars;
unsigned v;
expr_ref b(m);
for (unsigned i = 0; i < conjs.size(); ++i) {
expr* e = conjs[i].get();
if (is_eq(e, v, b)) {
if (v >= m_solved_vars.size()) {
m_solved_vars.resize(v+1);
}
if (v < sorts.size() && sorts[v]) {
TRACE("dl", tout << "already bound " << mk_pp(e, m) << "\n";);
add_free_vars(used_vars, e);
}
else if (m_solved_vars[v].get()) {
TRACE("dl", tout << "already solved " << mk_pp(e, m) << "\n";);
add_free_vars(used_vars, e);
add_free_vars(used_vars, m_solved_vars[v].get());
used_vars.insert(v);
}
else {
TRACE("dl", tout << "new solution " << mk_pp(e, m) << "\n";);
m_solved_vars[v] = b;
}
}
else {
TRACE("dl", tout << "not solved " << mk_pp(e, m) << "\n";);
add_free_vars(used_vars, e);
}
}
for (unsigned i = 0; i < conjs.size(); ++i) {
expr* e = conjs[i].get();
if (is_eq(e, v, b) && m_solved_vars[v].get() && !used_vars.contains(v)) {
TRACE("dl", tout << "removing conjunct: " << mk_pp(e, m) << "\n";);
// skip conjunct
}
else {
tail.push_back(to_app(e));
}
}
new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) 0);
TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n"););
}
else {
new_rule = &r;
}
dst.add_rule(new_rule);
if (m_pc) {
m_pc->insert(&r, new_rule, 0, 0);
}
}
void mk_slice::update_rules(rule_set const& src, rule_set& dst) {
for (unsigned i = 0; i < src.get_num_rules(); ++i) {
update_rule(*src.get_rule(i), dst);
}
}
rule_set * mk_slice::operator()(rule_set const & src, model_converter_ref& mc, proof_converter_ref& pc) {
ref<slice_proof_converter> spc;
ref<slice_model_converter> smc;
if (pc) {
spc = alloc(slice_proof_converter, m_ctx);
}
if (mc) {
smc = alloc(slice_model_converter, *this, m);
}
m_pc = spc.get();
m_mc = smc.get();
reset();
saturate(src);
declare_predicates();
if (m_predicates.empty()) {
// nothing could be sliced.
return 0;
}
TRACE("dl", display(tout););
rule_set* result = alloc(rule_set, m_ctx);
update_rules(src, *result);
TRACE("dl", result->display(tout););
if (m_mc) {
obj_map<func_decl, bit_vector>::iterator it = m_sliceable.begin(), end = m_sliceable.end();
for (; it != end; ++it) {
m_mc->add_sliceable(it->m_key, it->m_value);
}
}
pc = concat(pc.get(), spc.get());
mc = concat(mc.get(), smc.get());
return result;
}
};

View file

@ -1,107 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_slice.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-4.
Revision History:
--*/
#ifndef _DL_MK_SLICE_H_
#define _DL_MK_SLICE_H_
#include"dl_context.h"
#include"dl_rule_set.h"
#include"uint_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Implements a slicing rule transformation.
*/
class mk_slice : public rule_transformer::plugin {
class slice_proof_converter;
class slice_model_converter;
context& m_ctx;
ast_manager& m;
rule_manager& rm;
svector<bool> m_input;
svector<bool> m_output;
expr_ref_vector m_solved_vars;
svector<bool> m_var_is_sliceable;
obj_map<func_decl, func_decl*> m_predicates;
obj_map<func_decl, bit_vector> m_sliceable;
ast_ref_vector m_pinned;
slice_proof_converter* m_pc;
slice_model_converter* m_mc;
void reset();
void init(rule_set const& source);
void saturate(rule_set const& source);
void display(std::ostream& out);
bool prune_rule(rule& r);
void init_vars(rule& r);
void init_vars(app* p, bool is_output, bool is_neg_tail);
bool finalize_vars(app* p);
unsigned num_vars() const { return m_input.size(); }
bit_vector& get_predicate_slice(func_decl* p);
bit_vector& get_predicate_slice(app* p) { return get_predicate_slice(p->get_decl()); }
bool is_eq(expr* e, unsigned& v, expr_ref& r);
void add_free_vars(uint_set& s, expr* e);
void add_var(unsigned idx);
bool is_output(expr* e);
bool is_output(unsigned idx);
void update_rules(rule_set const& src, rule_set& dst);
void update_rule(rule& r, rule_set& dst);
expr_ref_vector get_tail_conjs(rule const& r);
void declare_predicates();
bool rule_updated(rule const& r);
void update_predicate(app* p, app_ref& q);
public:
/**
\brief Create slice rule transformer for \c goal predicate. When applying the transformer,
the \c goal must be present in the \c rule_set that is being transformed.
*/
mk_slice(context & ctx);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; }
};
};
#endif /* _DL_MK_SLICE_H_ */

View file

@ -1,367 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_subsumption_checker.cpp
Abstract:
Rule transformer which checks for subsumption
(currently just for subsumption with total relations)
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#include <sstream>
#include"ast_pp.h"
#include "rewriter.h"
#include "rewriter_def.h"
#include"dl_mk_subsumption_checker.h"
#include"dl_table_relation.h"
namespace datalog {
// -----------------------------------
//
// mk_subsumption_checker
//
// -----------------------------------
bool mk_subsumption_checker::is_total_rule(const rule * r) {
if(r->get_tail_size()!=0) { return false; }
unsigned pt_len = r->get_positive_tail_size();
if(pt_len!=r->get_uninterpreted_tail_size()) {
//we dont' expect rules with negative tails to be total
return false;
}
for(unsigned i=0; i<pt_len; i++) {
func_decl * tail_pred = r->get_tail(i)->get_decl();
if(!m_total_relations.contains(tail_pred)) {
//this rule has a non-total predicate in the tail
return false;
}
}
unsigned t_len = r->get_positive_tail_size();
for(unsigned i=pt_len; i<t_len; i++) {
SASSERT(!r->is_neg_tail(i)); //we assume interpreted tail not to be negated
if(!m.is_true(r->get_tail(i))) {
//this rule has an interpreted tail which is not constant true
return false;
}
}
var_idx_set head_vars;
app * head = r->get_head();
unsigned arity = head->get_num_args();
for(unsigned i=0; i<arity; i++) {
expr * arg = head->get_arg(i);
if(!is_var(arg)) { return false; }
unsigned idx = to_var(arg)->get_idx();
if(head_vars.contains(idx)) { return false; }
head_vars.insert(idx);
}
SASSERT(head_vars.num_elems()==arity);
return true;
}
void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) {
//this should be rule marking a new relation as total
SASSERT(!m_total_relations.contains(pred));
SASSERT(!r || pred==r->get_head()->get_decl());
SASSERT(!r || is_total_rule(r));
m_total_relations.insert(pred);
m_total_relation_defining_rules.insert(pred, r);
m_have_new_total_rule = true;
if(r) {
m_ref_holder.push_back(r);
}
}
void mk_subsumption_checker::scan_for_total_rules(const rule_set & rules) {
bool new_discovered;
//we cycle through the rules until we keep discovering new total relations
//(discovering a total relation migh reveal other total relations)
do {
new_discovered = false;
rule_set::iterator rend = rules.end();
for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) {
rule * r = *rit;
func_decl * head_pred = r->get_head()->get_decl();
if(is_total_rule(r) && !m_total_relations.contains(head_pred)) {
on_discovered_total_relation(head_pred, r);
new_discovered = true;
}
}
} while(new_discovered);
}
bool mk_subsumption_checker::transform_rule(rule * r,
rule_subsumption_index& subs_index, rule_ref & res)
{
unsigned u_len = r->get_uninterpreted_tail_size();
unsigned len = r->get_tail_size();
if(u_len==0) {
res = r;
return true;
}
app_ref head(r->get_head(), m);
app_ref_vector tail(m);
svector<bool> tail_neg;
for(unsigned i=0; i<u_len; i++) {
app * tail_atom = r->get_tail(i);
bool neg = r->is_neg_tail(i);
if(m_total_relations.contains(tail_atom->get_decl())
|| subs_index.is_subsumed(tail_atom)) {
if(neg) {
//rule contains negated total relation, this means that it is unsatisfiable
//and can be removed
return false;
}
else {
//we remove total relations from the tail
continue;
}
}
if(!neg && head.get()==tail_atom) {
//rule contains its head positively in the tail, therefore
//it will never add any new facts to the relation, so it
//can be removed
return false;
}
tail.push_back(tail_atom);
tail_neg.push_back(neg);
}
if(tail.size()==u_len) {
res = r;
return true;
}
//we just copy the interpreted part of the tail
for(unsigned i=u_len; i<len; i++) {
tail.push_back(r->get_tail(i));
tail_neg.push_back(r->is_neg_tail(i));
}
SASSERT(tail.size()==tail_neg.size());
res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr());
res->set_accounting_parent_object(m_context, r);
m_context.get_rule_manager().fix_unbound_vars(res, true);
return true;
}
bool rule_size_comparator(rule * r1, rule * r2) {
return r1->get_tail_size() < r2->get_tail_size();
}
bool mk_subsumption_checker::transform_rules(const rule_set & orig, rule_set & tgt) {
bool modified = false;
func_decl_set total_relations_with_included_rules;
rule_subsumption_index subs_index(m_context);
rule_ref_vector orig_rules(m_context.get_rule_manager());
orig_rules.append(orig.get_num_rules(), orig.begin());
rule * * rbegin = orig_rules.c_ptr();
rule * * rend = rbegin + orig_rules.size();
//before traversing we sort rules so that the shortest are in the beginning.
//this will help make subsumption checks more efficient
std::sort(rbegin, rend, rule_size_comparator);
for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) {
rule * r = *rit;
func_decl * head_pred = r->get_head()->get_decl();
if(m_total_relations.contains(head_pred)) {
if(!m_context.is_output_predicate(head_pred) ||
total_relations_with_included_rules.contains(head_pred)) {
//We just skip definitions of total non-output relations as
//we'll eliminate them from the problem.
//We also skip rules of total output relations for which we have
//already output the rule which implies their totality.
modified = true;
continue;
}
rule * defining_rule;
TRUSTME(m_total_relation_defining_rules.find(head_pred, defining_rule));
if(defining_rule) {
rule_ref totality_rule(m_context.get_rule_manager());
TRUSTME(transform_rule(defining_rule, subs_index, totality_rule));
if(defining_rule!=totality_rule) {
modified = true;
}
tgt.add_rule(totality_rule);
SASSERT(totality_rule->get_head()->get_decl()==head_pred);
}
else {
modified = true;
}
total_relations_with_included_rules.insert(head_pred);
continue;
}
rule_ref new_rule(m_context.get_rule_manager());
if(!transform_rule(r, subs_index, new_rule)) {
modified = true;
continue;
}
if(m_new_total_relation_discovery_during_transformation && is_total_rule(new_rule)) {
on_discovered_total_relation(head_pred, new_rule.get());
}
if(subs_index.is_subsumed(new_rule)) {
modified = true;
continue;
}
if(new_rule.get()!=r) {
modified = true;
}
tgt.add_rule(new_rule);
subs_index.add(new_rule);
}
TRACE("dl",
tout << "original set size: "<<orig.get_num_rules()<<"\n"
<< "reduced set size: "<<tgt.get_num_rules()<<"\n"; );
return modified;
}
void mk_subsumption_checker::scan_for_relations_total_due_to_facts() {
relation_manager& rm = m_context.get_rmanager();
decl_set candidate_preds;
m_context.collect_predicates(candidate_preds);
decl_set::iterator end = candidate_preds.end();
for(decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) {
func_decl * pred = *it;
if(m_total_relations.contains(pred)) { continue; } //already total
relation_base * rel = rm.try_get_relation(pred);
if(!rel || !rel->knows_exact_size()) { continue; }
unsigned arity = pred->get_arity();
if(arity>30) { continue; }
//for now we only check booleans domains
for(unsigned i=0; i<arity; i++) {
if(!m.is_bool(pred->get_domain(i))) {
goto next_pred;
}
}
{
unsigned total_size = 1<<arity;
//by calling rel.knows_exact_size() we got assured that the estimate is exact
unsigned rel_sz = rel->get_size_estimate_rows();
obj_hashtable<app> * head_store;
if(m_ground_unconditional_rule_heads.find(pred, head_store)) {
//Some relations may receive facts by ground unconditioned rules.
//We scanned for those earlier, so now we check whether we cannot get a
//better estimate of relation size from these.
unsigned gnd_rule_cnt = head_store->size();
if(gnd_rule_cnt>rel_sz) {
rel_sz = gnd_rule_cnt;
}
}
SASSERT(total_size>=rel_sz);
if(total_size==rel_sz) {
on_discovered_total_relation(pred, 0);
}
}
next_pred:;
}
}
void mk_subsumption_checker::collect_ground_unconditional_rule_heads(const rule_set & rules)
{
rule_set::iterator rend = rules.end();
for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) {
rule * r = *rit;
func_decl * pred = r->get_head()->get_decl();
if(r->get_tail_size()!=0) { continue; }
app * head = r->get_head();
unsigned arity = pred->get_arity();
for(unsigned i=0; i<arity; i++) {
expr * arg = head->get_arg(i);
if(!is_app(arg)) {
goto next_rule;
}
}
if(!m_ground_unconditional_rule_heads.contains(pred)) {
m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable<app>));
}
obj_hashtable<app> * head_store;
m_ground_unconditional_rule_heads.find(pred, head_store);
head_store->insert(head);
next_rule:;
}
}
rule_set * mk_subsumption_checker::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
m_have_new_total_rule = false;
collect_ground_unconditional_rule_heads(source);
scan_for_relations_total_due_to_facts();
scan_for_total_rules(source);
m_have_new_total_rule = false;
rule_set * res = alloc(rule_set, m_context);
bool modified = transform_rules(source, *res);
if(!m_have_new_total_rule && !modified) {
dealloc(res);
return 0;
}
//During the construction of the new set we may discover new total relations
//(by quantifier elimination on the uninterpreted tails).
SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule);
while(m_have_new_total_rule) {
m_have_new_total_rule = false;
rule_set * old = res;
res = alloc(rule_set, m_context);
transform_rules(*old, *res);
dealloc(old);
}
return res;
}
};

View file

@ -1,93 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mk_subsumption_checker.h
Abstract:
Rule transformer which checks for subsumption
(currently just for subsumption with total relations)
Author:
Krystof Hoder (t-khoder) 2011-10-01.
Revision History:
--*/
#ifndef _DL_MK_SUBSUMPTION_CHECKER_H_
#define _DL_MK_SUBSUMPTION_CHECKER_H_
#include "dl_context.h"
#include "dl_rule_transformer.h"
#include "dl_rule_subsumption_index.h"
namespace datalog {
class mk_subsumption_checker : public rule_transformer::plugin {
ast_manager & m;
context & m_context;
rule_ref_vector m_ref_holder;
func_decl_set m_total_relations;
/** Map that for each relation contains the rule which implies its totality.
If the totality is due to the relation containing all facts, the rule stored
here is zero*/
obj_map<func_decl, rule *> m_total_relation_defining_rules;
/**
Contains heads of rules of shape
R(c1,c2,...cN).
grouped by their predicate.
This information helps to improve the results of the
scan_for_relations_total_due_to_facts() function.
*/
obj_map<func_decl, obj_hashtable<app> *> m_ground_unconditional_rule_heads;
bool m_have_new_total_rule;
bool m_new_total_relation_discovery_during_transformation;
bool is_total_rule(const rule * r);
/** Function to be called when a new total relation is discovered */
void on_discovered_total_relation(func_decl * pred, rule * r);
void scan_for_total_rules(const rule_set & rules);
void scan_for_relations_total_due_to_facts();
void collect_ground_unconditional_rule_heads(const rule_set & rules);
/** Return false if rule is unsatisfiable */
bool transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res);
/** Return false if the rule set hasn't changed */
bool transform_rules(const rule_set & orig, rule_set & tgt);
public:
mk_subsumption_checker(context & ctx, unsigned priority=31000)
: plugin(priority),
m(ctx.get_manager()),
m_context(ctx),
m_ref_holder(ctx.get_rule_manager()),
m_new_total_relation_discovery_during_transformation(true) {}
~mk_subsumption_checker() {
reset_dealloc_values(m_ground_unconditional_rule_heads);
}
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_SUBSUMPTION_CHECKER_H_ */

View file

@ -1,383 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_unbound_compressor.cpp
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-04.
Revision History:
--*/
#include<utility>
#include<sstream>
#include"dl_mk_unbound_compressor.h"
namespace datalog {
mk_unbound_compressor::mk_unbound_compressor(context & ctx) :
plugin(500),
m_context(ctx),
m_manager(ctx.get_manager()),
m_rules(ctx.get_rule_manager()),
m_pinned(m_manager) {
}
void mk_unbound_compressor::reset() {
m_rules.reset();
m_todo.reset();
m_in_progress.reset();
m_map.reset();
m_pinned.reset();
}
bool mk_unbound_compressor::is_unbound_argument(rule * r, unsigned head_index) {
app * head = r->get_head();
expr * head_arg = head->get_arg(head_index);
if (!is_var(head_arg)) {
return false;
}
unsigned var_idx = to_var(head_arg)->get_idx();
var_idx_set tail_vars;
collect_tail_vars(m_manager, r, tail_vars);
return tail_vars.contains(var_idx);
}
void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) {
c_info ci = c_info(pred, arg_index);
if (m_map.contains(ci)) {
return; //this task was already added
}
unsigned parent_arity = pred->get_arity();
sort * const * parent_domain = pred->get_domain();
symbol const& parent_name = pred->get_name();
unsigned arity = parent_arity-1;
ptr_vector<sort> domain;
for (unsigned i = 0; i < parent_arity; i++) {
if (i != arg_index) {
domain.push_back(parent_domain[i]);
}
}
std::stringstream name_suffix;
name_suffix << "compr_arg_" << arg_index;
func_decl * cpred = m_context.mk_fresh_head_predicate(parent_name, symbol(name_suffix.str().c_str()),
arity, domain.c_ptr(), pred);
m_pinned.push_back(cpred);
m_todo.push_back(ci);
m_map.insert(ci, cpred);
}
void mk_unbound_compressor::detect_tasks(unsigned rule_index) {
rule * r = m_rules.get(rule_index);
var_idx_set tail_vars;
collect_tail_vars(m_manager, r, tail_vars);
app * head = r->get_head();
func_decl * head_pred = head->get_decl();
if (m_context.is_output_predicate(head_pred)) {
//we don't compress output predicates
return;
}
unsigned n = head_pred->get_arity();
var_counter head_var_counter;
head_var_counter.count_vars(m_manager, head, 1);
for (unsigned i=0; i<n; i++) {
expr * arg = head->get_arg(i);
if (!is_var(arg)) {
continue;
}
unsigned var_idx = to_var(arg)->get_idx();
if (!tail_vars.contains(var_idx)) {
//unbound
unsigned occurence_cnt = head_var_counter.get(var_idx);
SASSERT(occurence_cnt>0);
if (occurence_cnt == 1) {
TRACE("dl", r->display(m_context, tout << "Compress: "););
add_task(head_pred, i);
return; //we compress out the unbound arguments one by one
}
}
}
}
void mk_unbound_compressor::try_compress(unsigned rule_index) {
start:
rule * r = m_rules.get(rule_index);
var_idx_set tail_vars;
collect_tail_vars(m_manager, r, tail_vars);
app * head = r->get_head();
func_decl * head_pred = head->get_decl();
unsigned head_arity = head_pred->get_arity();
var_counter head_var_counter;
head_var_counter.count_vars(m_manager, head);
unsigned arg_index;
for (arg_index = 0; arg_index < head_arity; arg_index++) {
expr * arg = head->get_arg(arg_index);
if (!is_var(arg)) {
continue;
}
unsigned var_idx = to_var(arg)->get_idx();
if (!tail_vars.contains(var_idx)) {
//unbound
unsigned occurence_cnt = head_var_counter.get(var_idx);
SASSERT(occurence_cnt>0);
if ( occurence_cnt==1 && m_in_progress.contains(c_info(head_pred, arg_index)) ) {
//we have found what to compress
break;
}
}
}
if (arg_index == head_arity) {
//we didn't find anything to compress
return;
}
SASSERT(arg_index<head_arity);
c_info ci(head_pred, arg_index);
func_decl * cpred;
TRUSTME( m_map.find(ci, cpred) );
ptr_vector<expr> cargs;
for (unsigned i=0; i<head_arity; i++) {
if (i != arg_index) {
cargs.push_back(head->get_arg(i));
}
}
app_ref chead(m_manager.mk_app(cpred, head_arity-1, cargs.c_ptr()), m_manager);
if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) {
m_non_empty_rels.insert(cpred);
m_context.add_fact(chead);
//remove the rule that became fact by placing the last rule on its place
m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl());
m_rules.set(rule_index, m_rules.get(m_rules.size()-1));
m_rules.shrink(m_rules.size()-1);
//since we moved the last rule to rule_index, we have to try to compress it as well
if (rule_index<m_rules.size()) {
goto start;
}
}
else {
rule_ref new_rule(m_context.get_rule_manager().mk(r, chead), m_context.get_rule_manager());
new_rule->set_accounting_parent_object(m_context, r);
m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl());
m_rules.set(rule_index, new_rule);
m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_head()->get_decl());
detect_tasks(rule_index);
}
m_modified = true;
}
void mk_unbound_compressor::mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index,
rule_ref& res)
{
app * orig_dtail = r->get_tail(tail_index); //dtail ~ decompressed tail
c_info ci(orig_dtail->get_decl(), arg_index);
func_decl * dtail_pred;
TRUSTME( m_map.find(ci, dtail_pred) );
ptr_vector<expr> dtail_args;
unsigned orig_dtail_arity = orig_dtail->get_num_args();
for (unsigned i=0;i<orig_dtail_arity;i++) {
if (i != arg_index) {
dtail_args.push_back(orig_dtail->get_arg(i));
}
}
SASSERT(dtail_args.size()==dtail_pred->get_arity());
app_ref dtail(m_manager.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m_manager);
svector<bool> tails_negated;
app_ref_vector tails(m_manager);
unsigned tail_len = r->get_tail_size();
for (unsigned i=0; i<tail_len; i++) {
tails_negated.push_back(r->is_neg_tail(i));
if (i==tail_index && !r->is_neg_tail(i)) {
tails.push_back(dtail);
}
else {
tails.push_back(r->get_tail(i));
}
}
// Accumulate negated filtered rule instead
// of replacing the original predicate.
if (r->is_neg_tail(tail_index)) {
tails_negated.push_back(true);
tails.push_back(dtail);
}
res = m_context.get_rule_manager().mk( r->get_head(), tails.size(), tails.c_ptr(), tails_negated.c_ptr());
res->set_accounting_parent_object(m_context, r);
m_context.get_rule_manager().fix_unbound_vars(res, true);
}
void mk_unbound_compressor::add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index) {
rule_ref new_rule(m_context.get_rule_manager());
mk_decompression_rule(r, tail_index, arg_index, new_rule);
unsigned new_rule_index = m_rules.size();
m_rules.push_back(new_rule);
m_head_occurrence_ctr.inc(new_rule->get_head()->get_decl());
detect_tasks(new_rule_index);
m_modified = true;
//TODO: avoid rule duplicity
//If two predicates are compressed in a rule, applying decompression
//to the results can cause a rule being added multiple times:
//P:- R(x,y), S(x,y)
//is decompressed into rules
//P:- R1(x), S(x,y)
//P:- R(x,y), S1(x)
//and each of these rules is again decompressed giving the same rule
//P:- R1(x), S1(x)
//P:- R1(x), S1(x)
}
void mk_unbound_compressor::replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index)
{
rule * r = m_rules.get(rule_index);
rule_ref new_rule(m_context.get_rule_manager());
mk_decompression_rule(r, tail_index, arg_index, new_rule);
m_rules.set(rule_index, new_rule);
//we don't update the m_head_occurrence_ctr because the head predicate doesn't change
detect_tasks(rule_index);
m_modified = true;
}
void mk_unbound_compressor::add_decompression_rules(unsigned rule_index) {
unsigned_vector compressed_tail_pred_arg_indexes;
//this value is updated inside the loop if replace_by_decompression_rule is called
rule_ref r(m_rules.get(rule_index), m_context.get_rule_manager());
unsigned utail_len = r->get_uninterpreted_tail_size();
unsigned tail_index=0;
while (tail_index<utail_len) {
app * t = r->get_tail(tail_index);
func_decl * t_pred = t->get_decl();
unsigned t_arity = t_pred->get_arity();
bool is_negated_predicate = r->is_neg_tail(tail_index);
compressed_tail_pred_arg_indexes.reset();
for (unsigned arg_index=0; arg_index<t_arity; arg_index++) {
c_info ci(t_pred, arg_index);
if (m_in_progress.contains(ci)) {
compressed_tail_pred_arg_indexes.push_back(arg_index);
}
}
bool orig_rule_replaced = false;
while (!compressed_tail_pred_arg_indexes.empty()) {
unsigned arg_index = compressed_tail_pred_arg_indexes.back();
compressed_tail_pred_arg_indexes.pop_back();
bool can_remove_orig_rule =
compressed_tail_pred_arg_indexes.empty() &&
!m_non_empty_rels.contains(t_pred) &&
m_head_occurrence_ctr.get(t_pred)==0;
if (can_remove_orig_rule || is_negated_predicate) {
replace_by_decompression_rule(rule_index, tail_index, arg_index);
orig_rule_replaced = true;
}
else {
add_decompression_rule(r, tail_index, arg_index);
}
}
if (orig_rule_replaced) {
//update r with the new rule
rule * new_rule = m_rules.get(rule_index);
SASSERT(new_rule->get_uninterpreted_tail_size() >= utail_len);
//here we check that the rule replacement didn't affect other uninterpreted literals
//in the tail (aside of variable renaming)
SASSERT(tail_index==0 ||
new_rule->get_tail(tail_index-1)->get_decl()==r->get_tail(tail_index-1)->get_decl());
r = new_rule;
//we have replaced the original rule, with one that has different
//content of the tail_index -th tail. we will therefore not do
//tail_index++, so that we examine the new tail literal as well
}
else {
tail_index++;
}
}
}
rule_set * mk_unbound_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
// TODO mc, pc
m_modified = false;
m_context.get_rmanager().collect_non_empty_predicates(m_non_empty_rels);
unsigned init_rule_cnt = source.get_num_rules();
SASSERT(m_rules.empty());
for (unsigned i=0; i<init_rule_cnt; i++) {
rule * r = source.get_rule(i);
m_rules.push_back(r);
m_head_occurrence_ctr.inc(r->get_head()->get_decl());
}
for (unsigned i=0; i<init_rule_cnt; i++) {
detect_tasks(i);
}
while (!m_todo.empty()) {
m_in_progress.reset();
while (!m_todo.empty()) {
m_in_progress.insert(m_todo.back());
m_todo.pop_back();
}
unsigned rule_index = 0;
while (rule_index<m_rules.size()) {
try_compress(rule_index); //m_rules.size() can change here
if (rule_index<m_rules.size()) {
add_decompression_rules(rule_index); //m_rules.size() can change here
}
rule_index++;
}
}
rule_set * result = static_cast<rule_set *>(0);
if (m_modified) {
result = alloc(rule_set, m_context);
unsigned fin_rule_cnt = m_rules.size();
for (unsigned i=0; i<fin_rule_cnt; i++) {
result->add_rule(m_rules.get(i));
}
}
reset();
return result;
}
};

View file

@ -1,91 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_mk_unbound_compressor.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-10-4.
Revision History:
--*/
#ifndef _DL_MK_UNBOUND_COMPRESSOR_H_
#define _DL_MK_UNBOUND_COMPRESSOR_H_
#include<utility>
#include"map.h"
#include"obj_pair_hashtable.h"
#include"dl_context.h"
#include"dl_rule_set.h"
#include"dl_rule_transformer.h"
namespace datalog {
/**
\brief Functor for introducing auxiliary predicates to avoid unbound variables in
rule heads.
A rule
P(x,_) :- T(x).
is replaced by
P1(x) :- T(x).
and for each occurrence of P in a tail of a rule, a new rule is added with P1 in
its place.
*/
class mk_unbound_compressor : public rule_transformer::plugin {
/** predicate and index of compressed argument */
typedef std::pair<func_decl*,unsigned> c_info;
typedef pair_hash<ptr_hash<func_decl>,unsigned_hash> c_info_hash;
/** predicates that are results of compression */
typedef map<c_info, func_decl*, c_info_hash, default_eq<c_info> > c_map;
typedef hashtable<c_info, c_info_hash, default_eq<c_info> > in_progress_table;
typedef svector<c_info> todo_stack;
context & m_context;
ast_manager & m_manager;
rule_ref_vector m_rules;
bool m_modified;
todo_stack m_todo;
in_progress_table m_in_progress;
c_map m_map;
/**
Relations that contain facts
*/
decl_set m_non_empty_rels;
ast_counter m_head_occurrence_ctr;
ast_ref_vector m_pinned;
bool is_unbound_argument(rule * r, unsigned head_index);
bool has_unbound_head_var(rule * r);
void detect_tasks(unsigned rule_index);
void add_task(func_decl * pred, unsigned arg_index);
void try_compress(unsigned rule_index);
void add_decompression_rules(unsigned rule_index);
void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res);
void add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index);
void replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index);
void reset();
public:
mk_unbound_compressor(context & ctx);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_UNBOUND_COMPRESSOR_H_ */

View file

@ -1,72 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_unfold.cpp
Abstract:
Unfold rules once, return the unfolded set of rules.
Author:
Nikolaj Bjorner (nbjorner) 2012-10-15
Revision History:
--*/
#include "dl_mk_unfold.h"
namespace datalog {
mk_unfold::mk_unfold(context& ctx):
rule_transformer::plugin(100, false),
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_unify(ctx)
{}
void mk_unfold::expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst) {
SASSERT(tail_idx <= r.get_uninterpreted_tail_size());
if (tail_idx == r.get_uninterpreted_tail_size()) {
dst.add_rule(&r);
}
else {
func_decl* p = r.get_decl(tail_idx);
rule_vector const& p_rules = src.get_predicate_rules(p);
rule_ref new_rule(rm);
for (unsigned i = 0; i < p_rules.size(); ++i) {
rule const& r2 = *p_rules[i];
if (m_unify.unify_rules(r, tail_idx, r2) &&
m_unify.apply(r, tail_idx, r2, new_rule)) {
expr_ref_vector s1 = m_unify.get_rule_subst(r, true);
expr_ref_vector s2 = m_unify.get_rule_subst(r2, false);
resolve_rule(m_pc, r, r2, tail_idx, s1, s2, *new_rule.get());
expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst);
}
}
}
}
rule_set * mk_unfold::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) {
m_pc = 0;
ref<replace_proof_converter> rpc;
if (pc) {
rpc = alloc(replace_proof_converter, m);
m_pc = rpc.get();
}
rule_set* rules = alloc(rule_set, m_ctx);
rule_set::iterator it = source.begin(), end = source.end();
for (; it != end; ++it) {
expand_tail(**it, 0, source, *rules);
}
if (pc) {
pc = concat(pc.get(), rpc.get());
}
return rules;
}
};

View file

@ -1,54 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
dl_mk_unfold.h
Abstract:
Unfold rules once, return the unfolded set of rules.
Author:
Nikolaj Bjorner (nbjorner) 2012-10-15
Revision History:
--*/
#ifndef _DL_MK_UNFOLD_H_
#define _DL_MK_UNFOLD_H_
#include"dl_context.h"
#include"dl_rule_set.h"
#include"uint_set.h"
#include"dl_rule_transformer.h"
#include"dl_mk_rule_inliner.h"
namespace datalog {
/**
\brief Implements an unfolding transformation.
*/
class mk_unfold : public rule_transformer::plugin {
context& m_ctx;
ast_manager& m;
rule_manager& rm;
rule_unifier m_unify;
replace_proof_converter* m_pc;
void expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst);
public:
/**
\brief Create unfold rule transformer.
*/
mk_unfold(context & ctx);
rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc);
};
};
#endif /* _DL_MK_UNFOLD_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,176 +0,0 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
dl_product_relation.h
Abstract:
A Relation relation combinator.
Author:
Nikolaj Bjorner (nbjorner) 2010-4-11
Revision History:
--*/
#ifndef _DL_PRODUCT_RELATION_H_
#define _DL_PRODUCT_RELATION_H_
#include "dl_context.h"
namespace datalog {
class product_relation;
class product_relation_plugin : public relation_plugin {
friend class product_relation;
public:
typedef svector<family_id> rel_spec;
private:
class join_fn;
class transform_fn;
class mutator_fn;
class aligned_union_fn;
class unaligned_union_fn;
class single_non_transparent_src_union_fn;
class filter_equal_fn;
class filter_identical_fn;
class filter_interpreted_fn;
rel_spec_store<rel_spec> m_spec_store;
family_id get_relation_kind(const product_relation & r);
bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); }
public:
static product_relation_plugin& get_plugin(relation_manager & rmgr);
product_relation_plugin(relation_manager& m);
virtual void initialize(family_id fid);
virtual bool can_handle_signature(const relation_signature & s);
virtual bool can_handle_signature(const relation_signature & s, family_id kind);
static symbol get_name() { return symbol("product_relation"); }
family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec);
virtual relation_base * mk_empty(const relation_signature & s);
virtual relation_base * mk_empty(const relation_signature & s, family_id kind);
virtual relation_base * mk_full(func_decl* p, const relation_signature & s);
virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind);
protected:
virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
static bool is_product_relation(relation_base const& r);
private:
static product_relation& get(relation_base& r);
static product_relation const & get(relation_base const& r);
static product_relation* get(relation_base* r);
static product_relation const* get(relation_base const* r);
relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta, bool is_widen);
bool are_aligned(const product_relation& r1, const product_relation& r2);
static void get_common_spec(const ptr_vector<const product_relation> & rels, rel_spec & res);
};
class product_relation : public relation_base {
friend class product_relation_plugin;
friend class product_relation_plugin::join_fn;
friend class product_relation_plugin::transform_fn;
friend class product_relation_plugin::mutator_fn;
friend class product_relation_plugin::aligned_union_fn;
friend class product_relation_plugin::unaligned_union_fn;
friend class product_relation_plugin::single_non_transparent_src_union_fn;
friend class product_relation_plugin::filter_equal_fn;
friend class product_relation_plugin::filter_identical_fn;
friend class product_relation_plugin::filter_interpreted_fn;
typedef product_relation_plugin::rel_spec rel_spec;
/**
If m_relations is empty, value of this determines whether the relation is empty or full.
*/
bool m_default_empty;
/**
There must not be two relations of the same kind
*/
ptr_vector<relation_base> m_relations;
/**
Array of kinds of inner relations.
If two product relations have equal signature and specification, their
m_relations arrays contain corresponding relations at the same indexes.
The value returned by get_kind() depends uniquely on the specification.
*/
rel_spec m_spec;
/**
\brief Ensure the kind assigned to this relation reflects the types of inner relations.
*/
void ensure_correct_kind();
/**
The current specification must be a subset of the new one.
*/
void convert_spec(const rel_spec & spec);
public:
product_relation(product_relation_plugin& p, relation_signature const& s);
product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations);
~product_relation();
virtual bool empty() const;
virtual void add_fact(const relation_fact & f);
virtual bool contains_fact(const relation_fact & f) const;
virtual product_relation * clone() const;
virtual product_relation * complement(func_decl* p) const;
virtual void display(std::ostream & out) const;
virtual void to_formula(expr_ref& fml) const;
product_relation_plugin& get_plugin() const;
unsigned size() const { return m_relations.size(); }
relation_base& operator[](unsigned i) const { return *m_relations[i]; }
/**
If all relations except one are sieve_relations with no inner columns,
return true and into \c idx assign index of that relation. Otherwise return
false.
*/
bool try_get_single_non_transparent(unsigned & idx) const;
};
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,683 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_remation_manager.h
Abstract:
<abstract>
Author:
Krystof Hoder (t-khoder) 2010-09-24.
Revision History:
--*/
#ifndef _DL_RELATION_MANAGER_H_
#define _DL_RELATION_MANAGER_H_
#include"map.h"
#include"vector.h"
#include"dl_base.h"
namespace datalog {
class context;
class dl_decl_util;
class table_relation;
class table_relation_plugin;
class finite_product_relation;
class finite_product_relation_plugin;
class sieve_relation;
class sieve_relation_plugin;
typedef hashtable<func_decl * , ptr_hash<func_decl>, ptr_eq<func_decl> > decl_set;
class relation_manager {
class empty_signature_relation_join_fn;
class default_relation_join_project_fn;
class default_relation_select_equal_and_project_fn;
class default_relation_intersection_filter_fn;
class auxiliary_table_transformer_fn;
class auxiliary_table_filter_fn;
class default_table_join_fn;
class default_table_project_fn;
class null_signature_table_project_fn;
class default_table_join_project_fn;
class default_table_rename_fn;
class default_table_union_fn;
class default_table_filter_equal_fn;
class default_table_filter_identical_fn;
class default_table_filter_interpreted_fn;
class default_table_negation_filter_fn;
class default_table_filter_not_equal_fn;
class default_table_select_equal_and_project_fn;
class default_table_map_fn;
class default_table_project_with_reduce_fn;
typedef obj_map<func_decl, family_id> decl2kind_map;
typedef u_map<relation_plugin *> kind2plugin_map;
typedef map<const table_plugin *, table_relation_plugin *, ptr_hash<const table_plugin>,
ptr_eq<const table_plugin> > tp2trp_map;
typedef map<const relation_plugin *, finite_product_relation_plugin *, ptr_hash<const relation_plugin>,
ptr_eq<const relation_plugin> > rp2fprp_map;
typedef map<func_decl *, relation_base *, ptr_hash<func_decl>, ptr_eq<func_decl> > relation_map;
typedef ptr_vector<table_plugin> table_plugin_vector;
typedef ptr_vector<relation_plugin> relation_plugin_vector;
context & m_context;
table_plugin_vector m_table_plugins;
relation_plugin_vector m_relation_plugins;
//table_relation_plugins corresponding to table_plugins
tp2trp_map m_table_relation_plugins;
rp2fprp_map m_finite_product_relation_plugins;
kind2plugin_map m_kind2plugin;
table_plugin * m_favourite_table_plugin;
relation_plugin * m_favourite_relation_plugin;
relation_map m_relations;
decl_set m_saturated_rels;
family_id m_next_table_fid;
family_id m_next_relation_fid;
/**
Map specifying what kind of relation should be used to represent particular predicate.
*/
decl2kind_map m_pred_kinds;
void register_relation_plugin_impl(relation_plugin * plugin);
relation_manager(const relation_manager &); //private and undefined copy constructor
relation_manager & operator=(const relation_manager &); //private and undefined operator=
public:
relation_manager(context & ctx) :
m_context(ctx),
m_favourite_table_plugin(0),
m_favourite_relation_plugin(0),
m_next_table_fid(0),
m_next_relation_fid(0) {}
virtual ~relation_manager();
void reset();
void reset_relations();
context & get_context() const { return m_context; }
dl_decl_util & get_decl_util() const;
family_id get_next_table_fid() { return m_next_table_fid++; }
family_id get_next_relation_fid(relation_plugin & claimer);
/**
Set what kind of relation is going to be used to represent the predicate \c pred.
This function can be called only before the relation object for \c pred is created
(i.e. before the \c get_relation function is called with \c pred as argument for the
first time).
*/
void set_predicate_kind(func_decl * pred, family_id kind);
/**
Return the relation kind that was requested to represent the predicate \c pred by
\c set_predicate_kind. If there was no such request, return \c null_family_id.
*/
family_id get_requested_predicate_kind(func_decl * pred);
relation_base & get_relation(func_decl * pred);
relation_base * try_get_relation(func_decl * pred) const;
/**
\brief Store the relation \c rel under the predicate \c pred. The \c relation_manager
takes over the relation object.
*/
void store_relation(func_decl * pred, relation_base * rel);
bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); }
void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); }
void reset_saturated_marks() {
if(!m_saturated_rels.empty()) {
m_saturated_rels.reset();
}
}
void collect_predicates(decl_set & res) const;
void collect_non_empty_predicates(decl_set & res) const;
void restrict_predicates(const decl_set & preds);
void register_plugin(table_plugin * plugin);
/**
table_relation_plugins should not be passed to this function since they are
created automatically when registering a table plugin.
*/
void register_plugin(relation_plugin * plugin) {
SASSERT(!plugin->from_table());
register_relation_plugin_impl(plugin);
}
table_plugin & get_appropriate_plugin(const table_signature & t);
relation_plugin & get_appropriate_plugin(const relation_signature & t);
table_plugin * try_get_appropriate_plugin(const table_signature & t);
relation_plugin * try_get_appropriate_plugin(const relation_signature & t);
table_plugin * get_table_plugin(symbol const& s);
relation_plugin * get_relation_plugin(symbol const& s);
relation_plugin & get_relation_plugin(family_id kind);
table_relation_plugin & get_table_relation_plugin(table_plugin & tp);
bool try_get_finite_product_relation_plugin(const relation_plugin & inner,
finite_product_relation_plugin * & res);
table_base * mk_empty_table(const table_signature & s);
relation_base * mk_implicit_relation(const relation_signature & s, app * expr);
relation_base * mk_empty_relation(const relation_signature & s, family_id kind);
relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred);
relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind);
relation_base * mk_full_relation(const relation_signature & s, func_decl* pred);
relation_base * mk_table_relation(const relation_signature & s, table_base * table);
bool mk_empty_table_relation(const relation_signature & s, relation_base * & result);
bool is_non_explanation(relation_signature const& s) const;
/**
\brief Convert relation value to table one.
This function can be called only for the relation sorts that have a table counterpart.
*/
void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to);
void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to);
void table_to_relation(const relation_sort & sort, const table_element & from,
const relation_fact::el_proxy & to);
void table_to_relation(const relation_sort & sort, const table_element & from,
relation_element_ref & to);
bool relation_sort_to_table(const relation_sort & from, table_sort & to);
void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result);
void from_predicate(func_decl * pred, relation_signature & result);
/**
\brief Convert relation signature to table signature and return true if successful. If false
is returned, the value of \c to is undefined.
*/
bool relation_signature_to_table(const relation_signature & from, table_signature & to);
void relation_fact_to_table(const relation_signature & s, const relation_fact & from,
table_fact & to);
void table_fact_to_relation(const relation_signature & s, const table_fact & from,
relation_fact & to);
// -----------------------------------
//
// relation operations
//
// -----------------------------------
//TODO: If multiple operation implementations are available, we may want to do something to
//select the best one here.
/**
If \c allow_product_relation is true, we will create a join that builds a product relation,
if there is no other way to do the join. If \c allow_product_relation is false, we will return
zero in that case.
*/
relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true);
relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2,
const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) {
SASSERT(cols1.size()==cols2.size());
return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation);
}
/**
\brief Return functor that transforms a table into one that lacks columns listed in
\c removed_cols array.
The \c removed_cols cotains columns of table \c t in strictly ascending order.
*/
relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt,
const unsigned * removed_cols);
relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) {
return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr());
}
/**
\brief Return an operation that is a composition of a join an a project operation.
*/
relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2,
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2,
unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true);
relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2,
const unsigned_vector & cols1, const unsigned_vector & cols2,
const unsigned_vector & removed_cols, bool allow_product_relation_join=true) {
return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(),
removed_cols.c_ptr(), allow_product_relation_join);
}
relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) {
return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr());
}
/**
Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array
of column number.
*/
relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t,
const unsigned * permutation);
relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t,
const unsigned_vector permutation) {
SASSERT(t.get_signature().size()==permutation.size());
return mk_permutation_rename_fn(t, permutation.c_ptr());
}
/**
The post-condition for an ideal union operation is be
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 )
A required post-condition is
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
tgt_1==tgt_0 => delta_1==delta_0
delta_0 \subset delta_1
delta_1 \subset (delta_0 \union tgt_1)
( tgt_1 \setminus tgt_0 ) \subset delta_1
So that a sufficient implementation is
Union(tgt, src, delta) {
oldTgt:=tgt.clone();
tgt:=tgt \union src
if(tgt!=oldTgt) {
delta:=delta \union src //also “delta \union tgt” would work
}
}
If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient
post-condition is
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
(tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty
*/
relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) {
return mk_union_fn(tgt, src, static_cast<relation_base *>(0));
}
/**
Similar to union, but this one should be used inside loops to allow for abstract
domain convergence.
*/
relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta);
relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt,
const unsigned * identical_cols);
relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) {
return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr());
}
relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value,
unsigned col);
relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition);
/**
\brief Operations that returns all rows of \c t for which is column \c col equal to \c value
with the column \c col removed.
This operation can often be efficiently implemented and is useful for evaluating rules
of the form
F(x):-P("c",x).
*/
relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t,
const relation_element & value, unsigned col);
relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt,
const relation_base & src, unsigned joined_col_cnt,
const unsigned * tgt_cols, const unsigned * src_cols);
relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt,
const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) {
SASSERT(tgt_cols.size()==src_cols.size());
return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr());
}
relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt,
const relation_base & src);
/**
The filter_by_negation postcondition:
filter_by_negation(tgt, neg, columns in tgt: c1,...,cN,
corresponding columns in neg: d1,...,dN):
tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) }
*/
relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * negated_cols);
relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t,
const relation_base & negated_obj, const unsigned_vector & t_cols,
const unsigned_vector & negated_cols) {
SASSERT(t_cols.size()==negated_cols.size());
return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr());
}
// -----------------------------------
//
// table operations
//
// -----------------------------------
table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2);
table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2,
const unsigned_vector & cols1, const unsigned_vector & cols2) {
SASSERT(cols1.size()==cols2.size());
return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr());
}
/**
\brief Return functor that transforms a table into one that lacks columns listed in
\c removed_cols array.
The \c removed_cols cotains columns of table \c t in strictly ascending order.
If a project operation removes a non-functional column, all functional columns become
non-functional (so that none of the values in functional columns are lost)
*/
table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt,
const unsigned * removed_cols);
table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) {
return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr());
}
/**
\brief Return an operation that is a composition of a join an a project operation.
This operation is equivalent to the two operations performed separately, unless functional
columns are involved.
The ordinary project would make all of the functional columns into non-functional if any
non-functional column was removed. In function, however, we group columns into equivalence
classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional
only if some equivalence class of non-functional columns would have no non-functional columns
remain after the removal.
This behavior is implemented in the \c table_signature::from_join_project function.
*/
table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2,
unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2,
unsigned removed_col_cnt, const unsigned * removed_cols);
table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2,
const unsigned_vector & cols1, const unsigned_vector & cols2,
const unsigned_vector & removed_cols) {
return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(),
removed_cols.c_ptr());
}
table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len,
const unsigned * permutation_cycle);
table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) {
return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr());
}
/**
Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array
of column number.
*/
table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation);
table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) {
SASSERT(t.get_signature().size()==permutation.size());
return mk_permutation_rename_fn(t, permutation.c_ptr());
}
/**
The post-condition for an ideal union operation is be
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 )
A required post-condition is
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
tgt_1==tgt_0 => delta_1==delta_0
delta_0 \subset delta_1
delta_1 \subset (delta_0 \union tgt_1)
( tgt_1 \setminus tgt_0 ) \subset delta_1
So that a sufficient implementation is
Union(tgt, src, delta) {
oldTgt:=tgt.clone();
tgt:=tgt \union src
if(tgt!=oldTgt) {
delta:=delta \union src //also “delta \union tgt” would work
}
}
If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient
post-condition is
Union(tgt, src, delta):
tgt_1==tgt_0 \union src
(tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty
*/
table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src,
const table_base * delta);
table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) {
return mk_union_fn(tgt, src, static_cast<table_base *>(0));
}
/**
Similar to union, but this one should be used inside loops to allow for abstract
domain convergence.
*/
table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src,
const table_base * delta);
table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt,
const unsigned * identical_cols);
table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) {
return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr());
}
table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value,
unsigned col);
table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition);
/**
\brief Operations that returns all rows of \c t for which is column \c col equal to \c value
with the column \c col removed.
This operation can often be efficiently implemented and is useful for evaluating rules
of the form
F(x):-P("c",x).
*/
table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t,
const table_element & value, unsigned col);
table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t,
const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols);
table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t,
const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) {
SASSERT(t_cols.size()==src_cols.size());
return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr());
}
/**
The filter_by_negation postcondition:
filter_by_negation(tgt, neg, columns in tgt: c1,...,cN,
corresponding columns in neg: d1,...,dN):
tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) }
*/
table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj,
unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols);
table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj,
const unsigned_vector & t_cols, const unsigned_vector & negated_cols) {
SASSERT(t_cols.size()==negated_cols.size());
return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr());
}
/**
\c t must contain at least one functional column.
Created object takes ownership of the \c mapper object.
*/
virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper);
/**
\c t must contain at least one functional column.
Created object takes ownership of the \c mapper object.
*/
virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt,
const unsigned * removed_cols, table_row_pair_reduce_fn * reducer);
// -----------------------------------
//
// output functions
//
// -----------------------------------
std::string to_nice_string(const relation_element & el) const;
/**
This one may give a nicer representation of \c el than the
\c to_nice_string(const relation_element & el) function, by unsing the information about the sort
of the element.
*/
std::string to_nice_string(const relation_sort & s, const relation_element & el) const;
std::string to_nice_string(const relation_sort & s) const;
std::string to_nice_string(const relation_signature & s) const;
void display(std::ostream & out) const;
void display_relation_sizes(std::ostream & out) const;
void display_output_tables(std::ostream & out) const;
private:
relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t,
const relation_base & src, unsigned joined_col_cnt,
const unsigned * t_cols, const unsigned * src_cols);
};
/**
This is a helper class for relation_plugins whose relations can be of various kinds.
*/
template<class Spec, class Hash=int_vector_hash_proc<Spec>, class Eq=vector_eq_proc<Spec> >
class rel_spec_store {
typedef relation_signature::hash r_hash;
typedef relation_signature::eq r_eq;
typedef map<Spec, unsigned, Hash, Eq > family_id_idx_store;
typedef map<relation_signature, family_id_idx_store *, r_hash, r_eq> sig2store;
typedef u_map<Spec> family_id2spec;
typedef map<relation_signature, family_id2spec *, r_hash, r_eq> sig2spec_store;
relation_plugin & m_parent;
svector<family_id> m_allocated_kinds;
sig2store m_kind_assignment;
sig2spec_store m_kind_specs;
relation_manager & get_manager() { return m_parent.get_manager(); }
void add_new_kind() {
add_available_kind(get_manager().get_next_relation_fid(m_parent));
}
public:
rel_spec_store(relation_plugin & parent) : m_parent(parent) {}
~rel_spec_store() {
reset_dealloc_values(m_kind_assignment);
reset_dealloc_values(m_kind_specs);
}
void add_available_kind(family_id k) {
m_allocated_kinds.push_back(k);
}
bool contains_signature(relation_signature const& sig) const {
return m_kind_assignment.contains(sig);
}
family_id get_relation_kind(const relation_signature & sig, const Spec & spec) {
typename sig2store::entry * e = m_kind_assignment.find_core(sig);
if(!e) {
e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store));
m_kind_specs.insert(sig, alloc(family_id2spec));
}
family_id_idx_store & ids = *e->get_data().m_value;
unsigned res_idx;
if(!ids.find(spec, res_idx)) {
res_idx = ids.size();
if(res_idx==m_allocated_kinds.size()) {
add_new_kind();
}
SASSERT(res_idx<m_allocated_kinds.size());
ids.insert(spec, res_idx);
family_id2spec * idspecs;
VERIFY( m_kind_specs.find(sig, idspecs) );
idspecs->insert(m_allocated_kinds[res_idx], spec);
}
return m_allocated_kinds[res_idx];
}
void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) {
family_id2spec * idspecs;
VERIFY( m_kind_specs.find(sig, idspecs) );
VERIFY( idspecs->find(kind, spec) );
}
};
};
#endif /* _DL_RELATION_MANAGER_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,265 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-17.
Revision History:
--*/
#ifndef _DL_RULE_H_
#define _DL_RULE_H_
#include"ast.h"
#include"dl_costs.h"
#include"dl_util.h"
#include"used_vars.h"
namespace datalog {
class rule;
class rule_manager;
class table;
class context;
typedef obj_ref<rule, rule_manager> rule_ref;
typedef ref_vector<rule, rule_manager> rule_ref_vector;
typedef ptr_vector<rule> rule_vector;
/**
\brief Manager for the \c rule class
\remark \c rule_manager objects are interchangable as long as they
contain the same \c ast_manager object.
*/
class rule_manager
{
ast_manager& m;
context& m_ctx;
var_counter m_var_counter;
obj_map<expr, app*> m_memoize_disj;
expr_ref_vector m_refs;
// only the context can create a rule_manager
friend class context;
explicit rule_manager(context& ctx);
/**
\brief Move functions from predicate tails into the interpreted tail by introducing new variables.
*/
void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body);
void flatten_body(app_ref_vector& body);
void remove_labels(expr_ref& fml);
void eliminate_disjunctions(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name);
void eliminate_disjunctions(app_ref_vector& body, rule_ref_vector& rules, symbol const& name);
void eliminate_quantifier_body(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name);
void eliminate_quantifier_body(app_ref_vector& body, rule_ref_vector& rules, symbol const& name);
app_ref ensure_app(expr* e);
void check_app(expr* e);
bool contains_predicate(expr* fml) const;
void bind_variables(expr* fml, bool is_forall, expr_ref& result);
void mk_rule_core(expr* fml, rule_ref_vector& rules, symbol const& name);
unsigned hoist_quantifier(bool is_forall, expr_ref& fml, svector<symbol>* names);
public:
ast_manager& get_manager() const { return m; }
void inc_ref(rule * r);
void dec_ref(rule * r);
/**
\brief Create a Datalog rule from a Horn formula.
The formula is of the form (forall (...) (forall (...) (=> (and ...) head)))
*/
void mk_rule(expr* fml, rule_ref_vector& rules, symbol const& name = symbol::null);
/**
\brief Create a Datalog query from an expression.
The formula is of the form (exists (...) (exists (...) (and ...))
*/
void mk_query(expr* query, func_decl_ref& query_pred, rule_ref_vector& query_rules, rule_ref& query_rule);
/**
\brief Create a Datalog rule head :- tail[0], ..., tail[n-1].
Return 0 if it is not a valid rule.
\remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true
*/
rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = 0,
symbol const& name = symbol::null);
/**
\brief Create a rule with the same tail as \c source and with a specified head.
*/
rule * mk(rule const * source, app * new_head, symbol const& name = symbol::null);
/**
\brief Create a copy of the given rule.
*/
rule * mk(rule const * source, symbol const& name = symbol::null);
/** make sure there are not non-quantified variables that occur only in interpreted predicates */
void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination);
/**
\brief apply substitution to variables of rule.
*/
void substitute(rule_ref& r, unsigned sz, expr*const* es);
/**
\brief Check that head :- tail[0], ..., tail[n-1]
is a valid Datalog rule.
*/
void check_valid_rule(app * head, unsigned n, app * const * tail) const;
/**
\brief Check that \c head may occur as a Datalog rule head.
*/
void check_valid_head(expr * head) const;
/**
\brief Return true if \c head may occur as a fact.
*/
bool is_fact(app * head) const;
bool is_predicate(func_decl * f) const;
bool is_predicate(expr * e) const {
return is_app(e) && is_predicate(to_app(e)->get_decl());
}
static bool is_forall(ast_manager& m, expr* e, quantifier*& q);
var_counter& get_var_counter() { return m_var_counter; }
};
class rule : public accounted_object {
friend class rule_manager;
app * m_head;
unsigned m_tail_size:20;
// unsigned m_reserve:12;
unsigned m_ref_cnt;
unsigned m_positive_cnt;
unsigned m_uninterp_cnt;
symbol m_name;
/**
The following field is an array of tagged pointers.
- Tag 0: the atom is not negated
- Tag 1: the atom is negated.
The order of tail formulas is the following:
uninterpreted positive,
uninterpreted negative,
interpreted.
The negated flag is never set for interpreted tails.
*/
app * m_tail[0];
static unsigned get_obj_size(unsigned n) { return sizeof(rule) + n * sizeof(app *); }
rule() : m_ref_cnt(0) {}
~rule() {}
void deallocate(ast_manager & m);
void get_used_vars(used_vars& uv) const;
public:
app * get_head() const { return m_head; }
func_decl* get_decl() const { return get_head()->get_decl(); }
unsigned get_tail_size() const { return m_tail_size; }
/**
\brief Return number of positive uninterpreted predicates in the tail.
These predicates are the first in the tail.
*/
unsigned get_positive_tail_size() const { return m_positive_cnt; }
unsigned get_uninterpreted_tail_size() const { return m_uninterp_cnt; }
/**
\brief Return i-th tail atom. The first \c get_uninterpreted_tail_size()
atoms are uninterpreted and the first \c get_positive_tail_size() are
uninterpreted and non-negated.
*/
app * get_tail(unsigned i) const { SASSERT(i < m_tail_size); return UNTAG(app *, m_tail[i]); }
func_decl* get_decl(unsigned i) const { SASSERT(i < get_uninterpreted_tail_size()); return get_tail(i)->get_decl(); }
bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; }
/**
Check whether predicate p is in the interpreted tail.
If only_positive is true, only the positive predicate tail atoms are checked.
*/
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
bool has_uninterpreted_non_predicates(func_decl*& f) const;
void has_quantifiers(bool& existential, bool& universal) const;
bool has_quantifiers() const;
/**
\brief Store in d the (direct) dependencies of the given rule.
*/
void norm_vars(rule_manager & rm);
void get_vars(sort_ref_vector& sorts) const;
void to_formula(expr_ref& result) const;
void display(context & ctx, std::ostream & out) const;
std::ostream& display_smt2(ast_manager& m, std::ostream & out) const;
symbol const& name() { return m_name; }
unsigned hash() const;
};
struct rule_eq_proc {
bool operator()(const rule * r1, const rule * r2) const;
};
struct rule_hash_proc {
unsigned operator()(const rule * r) const;
};
};
#endif /* _DL_RULE_H_ */

View file

@ -1,691 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_rule_set.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-17.
Revision History:
--*/
#include<algorithm>
#include<functional>
#include"dl_context.h"
#include"dl_rule_set.h"
#include"ast_pp.h"
namespace datalog {
rule_dependencies::rule_dependencies(context& ctx): m_context(ctx) {
}
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
m_context(o.m_context) {
if(reversed) {
iterator oit = o.begin();
iterator oend = o.end();
for(; oit!=oend; ++oit) {
func_decl * pred = oit->m_key;
item_set & orig_items = *oit->get_value();
ensure_key(pred);
item_set::iterator dit = orig_items.begin();
item_set::iterator dend = orig_items.end();
for(; dit!=dend; ++dit) {
func_decl * master_pred = *dit;
insert(master_pred, pred);
}
}
}
else {
iterator oit = o.begin();
iterator oend = o.end();
for(; oit!=oend; ++oit) {
func_decl * pred = oit->m_key;
item_set & orig_items = *oit->get_value();
m_data.insert(pred, alloc(item_set, orig_items));
}
}
}
rule_dependencies::~rule_dependencies() {
reset();
}
void rule_dependencies::reset() {
reset_dealloc_values(m_data);
}
void rule_dependencies::remove_m_data_entry(func_decl * key)
{
item_set * itm_set = m_data.find(key);
dealloc(itm_set);
m_data.remove(key);
}
rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) {
deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0);
if(!e->get_data().m_value) {
e->get_data().m_value = alloc(item_set);
}
return *e->get_data().m_value;
}
void rule_dependencies::insert(func_decl * depending, func_decl * master) {
SASSERT(m_data.contains(master)); //see m_data documentation
item_set & s = ensure_key(depending);
s.insert(master);
}
void rule_dependencies::populate(const rule_set & rules) {
SASSERT(m_data.empty());
rule_set::decl2rules::iterator it = rules.m_head2rules.begin();
rule_set::decl2rules::iterator end = rules.m_head2rules.end();
for (; it != end; ++it) {
ptr_vector<rule> * rules = it->m_value;
ptr_vector<rule>::iterator it2 = rules->begin();
ptr_vector<rule>::iterator end2 = rules->end();
for (; it2 != end2; ++it2) {
populate(*it2);
}
}
}
void rule_dependencies::populate(unsigned n, rule * const * rules) {
SASSERT(m_data.empty());
for(unsigned i=0; i<n; i++) {
populate(rules[i]);
}
}
void rule_dependencies::populate(rule const* r) {
m_visited.reset();
func_decl * d = r->get_head()->get_decl();
func_decl_set & s = ensure_key(d);
for (unsigned i = 0; i < r->get_tail_size(); ++i) {
m_todo.push_back(r->get_tail(i));
}
while (!m_todo.empty()) {
expr* e = m_todo.back();
m_todo.pop_back();
if (m_visited.is_marked(e)) {
continue;
}
m_visited.mark(e, true);
if (is_app(e)) {
app* a = to_app(e);
d = a->get_decl();
if (m_context.is_predicate(d)) {
// insert d and ensure the invariant
// that every predicate is present as
// a key in m_data
s.insert(d);
ensure_key(d);
}
m_todo.append(a->get_num_args(), a->get_args());
}
else if (is_quantifier(e)) {
m_todo.push_back(to_quantifier(e)->get_expr());
}
}
}
const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const {
deps_type::obj_map_entry * e = m_data.find_core(f);
if(!e) {
return m_empty_item_set;
}
SASSERT(e->get_data().get_value());
return *e->get_data().get_value();
}
void rule_dependencies::restrict(const item_set & allowed) {
ptr_vector<func_decl> to_remove;
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
func_decl * pred = pit->m_key;
if(!allowed.contains(pred)) {
to_remove.insert(pred);
continue;
}
item_set& itms = *pit->get_value();
set_intersection(itms, allowed);
}
ptr_vector<func_decl>::iterator rit = to_remove.begin();
ptr_vector<func_decl>::iterator rend = to_remove.end();
for(; rit!=rend; ++rit) {
remove_m_data_entry(*rit);
}
}
void rule_dependencies::remove(func_decl * itm) {
remove_m_data_entry(itm);
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
item_set & itms = *pit->get_value();
itms.remove(itm);
}
}
void rule_dependencies::remove(const item_set & to_remove) {
item_set::iterator rit = to_remove.begin();
item_set::iterator rend = to_remove.end();
for(; rit!=rend; ++rit) {
remove_m_data_entry(*rit);
}
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
item_set * itms = pit->get_value();
set_difference(*itms, to_remove);
}
}
unsigned rule_dependencies::out_degree(func_decl * f) const {
unsigned res = 0;
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
item_set & itms = *pit->get_value();
if(itms.contains(f)) {
res++;
}
}
return res;
}
bool rule_dependencies::sort_deps(ptr_vector<func_decl> & res) {
typedef obj_map<func_decl, unsigned> deg_map;
unsigned init_len = res.size();
deg_map degs;
unsigned curr_index = init_len;
rule_dependencies reversed(*this, true);
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
func_decl * pred = pit->m_key;
unsigned deg = in_degree(pred);
if(deg==0) {
res.push_back(pred);
}
else {
degs.insert(pred, deg);
}
}
while(curr_index<res.size()) { //res.size() can change in the loop iteration
func_decl * curr = res[curr_index];
const item_set & children = reversed.get_deps(curr);
item_set::iterator cit = children.begin();
item_set::iterator cend = children.end();
for(; cit!=cend; ++cit) {
func_decl * child = *cit;
deg_map::obj_map_entry * e = degs.find_core(child);
SASSERT(e);
unsigned & child_deg = e->get_data().m_value;
SASSERT(child_deg>0);
child_deg--;
if(child_deg==0) {
res.push_back(child);
}
}
curr_index++;
}
if(res.size()<init_len+m_data.size()) {
res.shrink(init_len);
return false;
}
SASSERT(res.size()==init_len+m_data.size());
return true;
}
void rule_dependencies::display( std::ostream & out ) const
{
iterator pit = begin();
iterator pend = end();
for(; pit!=pend; ++pit) {
func_decl * pred = pit->m_key;
const item_set & deps = *pit->m_value;
item_set::iterator dit=deps.begin();
item_set::iterator dend=deps.end();
if(dit==dend) {
out<<pred->get_name()<<" - <none>\n";
}
for(; dit!=dend; ++dit) {
func_decl * dep = *dit;
out<<pred->get_name()<<" -> "<<dep->get_name()<<"\n";
}
}
}
// -----------------------------------
//
// rule_set
//
// -----------------------------------
rule_set::rule_set(context & ctx)
: m_context(ctx),
m_rule_manager(ctx.get_rule_manager()),
m_rules(m_rule_manager),
m_deps(ctx),
m_stratifier(0) {
}
rule_set::rule_set(const rule_set & rs)
: m_context(rs.m_context),
m_rule_manager(rs.m_rule_manager),
m_rules(m_rule_manager),
m_deps(rs.m_context),
m_stratifier(0) {
add_rules(rs);
if(rs.m_stratifier) {
TRUSTME(close());
}
}
rule_set::~rule_set() {
reset();
}
void rule_set::reset() {
if(m_stratifier) {
m_stratifier = 0;
}
reset_dealloc_values(m_head2rules);
m_deps.reset();
m_rules.reset();
}
ast_manager & rule_set::get_manager() const {
return m_context.get_manager();
}
void rule_set::add_rule(rule * r) {
TRACE("dl_verbose", r->display(m_context, tout << "add:"););
SASSERT(!is_closed());
m_rules.push_back(r);
app * head = r->get_head();
SASSERT(head != 0);
func_decl * d = head->get_decl();
decl2rules::obj_map_entry* e = m_head2rules.insert_if_not_there2(d, 0);
if (!e->get_data().m_value) e->get_data().m_value = alloc(ptr_vector<rule>);
e->get_data().m_value->push_back(r);
}
void rule_set::del_rule(rule * r) {
TRACE("dl", r->display(m_context, tout << "del:"););
func_decl* d = r->get_head()->get_decl();
rule_vector* rules = m_head2rules.find(d);
#define DEL_VECTOR(_v) \
for (unsigned i = (_v).size(); i > 0; ) { \
--i; \
if ((_v)[i] == r) { \
(_v)[i] = (_v).back(); \
(_v).pop_back(); \
break; \
} \
} \
DEL_VECTOR(*rules);
DEL_VECTOR(m_rules);
}
void rule_set::ensure_closed()
{
if(!is_closed()) {
TRUSTME(close());
}
}
bool rule_set::close() {
SASSERT(!is_closed()); //the rule_set is not already closed
m_deps.populate(*this);
m_stratifier = alloc(rule_stratifier, m_deps);
if(!stratified_negation()) {
m_stratifier = 0;
m_deps.reset();
return false;
}
return true;
}
void rule_set::reopen() {
SASSERT(is_closed());
m_stratifier = 0;
m_deps.reset();
}
/**
\brief Return true if the negation is indeed stratified.
*/
bool rule_set::stratified_negation() {
ptr_vector<rule>::const_iterator it = m_rules.c_ptr();
ptr_vector<rule>::const_iterator end = m_rules.c_ptr()+m_rules.size();
for (; it != end; it++) {
rule * r = *it;
app * head = r->get_head();
func_decl * head_decl = head->get_decl();
unsigned n = r->get_uninterpreted_tail_size();
for (unsigned i = r->get_positive_tail_size(); i < n; i++) {
SASSERT(r->is_neg_tail(i));
func_decl * tail_decl = r->get_tail(i)->get_decl();
unsigned neg_strat = get_predicate_strat(tail_decl);
unsigned head_strat = get_predicate_strat(head_decl);
SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail
if(head_strat==neg_strat) {
return false;
}
}
}
return true;
}
void rule_set::add_rules(const rule_set & src) {
SASSERT(!is_closed());
unsigned n = src.get_num_rules();
for (unsigned i=0; i<n; i++) {
add_rule(src.get_rule(i));
}
}
void rule_set::add_rules(unsigned sz, rule * const * rules) {
for (unsigned i=0; i<sz; i++) {
add_rule(rules[i]);
}
}
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
if(!e) {
return m_empty_rule_vector;
}
return *e->get_data().m_value;
}
const rule_set::pred_set_vector & rule_set::get_strats() const {
SASSERT(m_stratifier);
return m_stratifier->get_strats();
}
unsigned rule_set::get_predicate_strat(func_decl * pred) const {
SASSERT(m_stratifier);
return m_stratifier->get_predicate_strat(pred);
}
void rule_set::display(std::ostream & out) const {
out << "; rule count: " << get_num_rules() << "\n";
out << "; predicate count: " << m_head2rules.size() << "\n";
decl2rules::iterator it = m_head2rules.begin();
decl2rules::iterator end = m_head2rules.end();
for (; it != end; ++it) {
ptr_vector<rule> * rules = it->m_value;
ptr_vector<rule>::iterator it2 = rules->begin();
ptr_vector<rule>::iterator end2 = rules->end();
for (; it2 != end2; ++it2) {
rule * r = *it2;
if(!r->passes_output_thresholds(m_context)) {
continue;
}
r->display(m_context, out);
}
}
#if 0 //print dependencies
out<<"##\n";
out<<m_deps.size()<<"\n";
#endif
#if 0 //print strats
out<<"##\n";
stratifier strat(m_deps);
#endif
}
void rule_set::display_deps( std::ostream & out ) const
{
const pred_set_vector & strats = get_strats();
pred_set_vector::const_iterator sit = strats.begin();
pred_set_vector::const_iterator send = strats.end();
for(; sit!=send; ++sit) {
func_decl_set & strat = **sit;
func_decl_set::iterator fit=strat.begin();
func_decl_set::iterator fend=strat.end();
bool non_empty = false;
for(; fit!=fend; ++fit) {
func_decl * first = *fit;
const func_decl_set & deps = m_deps.get_deps(first);
func_decl_set::iterator dit=deps.begin();
func_decl_set::iterator dend=deps.end();
for(; dit!=dend; ++dit) {
non_empty = true;
func_decl * dep = *dit;
out<<first->get_name()<<" -> "<<dep->get_name()<<"\n";
}
}
if(non_empty && sit!=send) {
out << "\n";
}
}
}
// -----------------------------------
//
// rule_stratifier
//
// -----------------------------------
rule_stratifier::~rule_stratifier() {
comp_vector::iterator it = m_strats.begin();
comp_vector::iterator end = m_strats.end();
for(; it!=end; ++it) {
SASSERT(*it);
dealloc(*it);
}
}
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
unsigned num;
if(!m_pred_strat_nums.find(pred, num)) {
//the number of the predicate is not stored, therefore it did not appear
//in the algorithm and therefore it does not depend on anything and nothing
//depends on it. So it is safe to assign zero strate to it, although it is
//not strictly true.
num = 0;
}
return num;
}
void rule_stratifier::traverse(T* el) {
unsigned p_num;
if(m_preorder_nums.find(el, p_num)) {
if(p_num<m_first_preorder) {
//traversed in a previous sweep
return;
}
if(m_component_nums.contains(el)) {
//we already assigned a component for el
return;
}
while(!m_stack_P.empty()) {
unsigned on_stack_num;
TRUSTME( m_preorder_nums.find(m_stack_P.back(), on_stack_num) );
if(on_stack_num <= p_num) {
break;
}
m_stack_P.pop_back();
}
}
else {
p_num=m_next_preorder++;
m_preorder_nums.insert(el, p_num);
m_stack_S.push_back(el);
m_stack_P.push_back(el);
const item_set & children = m_deps.get_deps(el);
item_set::iterator cit=children.begin();
item_set::iterator cend=children.end();
for(; cit!=cend; ++cit) {
traverse(*cit);
}
if(el==m_stack_P.back()) {
unsigned comp_num = m_components.size();
item_set * new_comp = alloc(item_set);
m_components.push_back(new_comp);
T* s_el;
do {
s_el=m_stack_S.back();
m_stack_S.pop_back();
new_comp->insert(s_el);
m_component_nums.insert(s_el, comp_num);
} while(s_el!=el);
m_stack_P.pop_back();
}
}
}
void rule_stratifier::process() {
if(m_deps.empty()) {
return;
}
//detect strong components
rule_dependencies::iterator it = m_deps.begin();
rule_dependencies::iterator end = m_deps.end();
for(; it!=end; ++it) {
T * el = it->m_key;
//we take a note of the preorder number with which this sweep started
m_first_preorder = m_next_preorder;
traverse(el);
}
//do topological sorting
//degres of components (number of inter-component edges ending up in the component)
svector<unsigned> in_degrees;
in_degrees.resize(m_components.size());
//init in_degrees
it = m_deps.begin();
end = m_deps.end();
for(; it!=end; ++it) {
T * el = it->m_key;
item_set * out_edges = it->m_value;
unsigned el_comp;
TRUSTME( m_component_nums.find(el, el_comp) );
item_set::iterator eit=out_edges->begin();
item_set::iterator eend=out_edges->end();
for(; eit!=eend; ++eit) {
T * tgt = *eit;
unsigned tgt_comp = m_component_nums.find(tgt);
if(el_comp!=tgt_comp) {
in_degrees[tgt_comp]++;
}
}
}
//We put components whose indegree is zero to m_strats and assign its
//m_components entry to zero.
unsigned comp_cnt = m_components.size();
for(unsigned i=0; i<comp_cnt; i++) {
if(in_degrees[i]==0) {
m_strats.push_back(m_components[i]);
m_components[i] = 0;
}
}
SASSERT(!m_strats.empty()); //the component graph is acyclic and non-empty
//we remove edges from components with zero indegre building the topological ordering
unsigned strats_index = 0;
while(strats_index < m_strats.size()) { //m_strats.size() changes inside the loop!
item_set * comp = m_strats[strats_index];
item_set::iterator cit=comp->begin();
item_set::iterator cend=comp->end();
for(; cit!=cend; ++cit) {
T * el = *cit;
const item_set & deps = m_deps.get_deps(el);
item_set::iterator eit=deps.begin();
item_set::iterator eend=deps.end();
for(; eit!=eend; ++eit) {
T * tgt = *eit;
unsigned tgt_comp;
TRUSTME( m_component_nums.find(tgt, tgt_comp) );
//m_components[tgt_comp]==0 means the edge is intra-component.
//Otherwise it would go to another component, but it is not possible, since
//as m_components[tgt_comp]==0, its indegree has already reached zero.
if(m_components[tgt_comp]) {
SASSERT(in_degrees[tgt_comp]>0);
in_degrees[tgt_comp]--;
if(in_degrees[tgt_comp]==0) {
m_strats.push_back(m_components[tgt_comp]);
m_components[tgt_comp] = 0;
}
}
traverse(*cit);
}
}
strats_index++;
}
//we have managed to topologicaly order all the components
SASSERT(std::find_if(m_components.begin(), m_components.end(),
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
//reverse the strats array, so that the only the later components would depend on earlier ones
std::reverse(m_strats.begin(), m_strats.end());
SASSERT(m_pred_strat_nums.empty());
unsigned strat_cnt = m_strats.size();
for(unsigned strat_index=0; strat_index<strat_cnt; strat_index++) {
item_set * comp = m_strats[strat_index];
item_set::iterator cit=comp->begin();
item_set::iterator cend=comp->end();
for(; cit!=cend; ++cit) {
T * el = *cit;
m_pred_strat_nums.insert(el, strat_index);
}
}
//finalize structures that are not needed anymore
m_preorder_nums.finalize();
m_stack_S.finalize();
m_stack_P.finalize();
m_component_nums.finalize();
m_components.finalize();
}
};

Some files were not shown because too many files have changed in this diff Show more