mirror of
https://github.com/Z3Prover/z3
synced 2026-02-14 12:51:48 +00:00
checkpoint
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
4722fdfca5
commit
add684d8e9
377 changed files with 204 additions and 62 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
421
lib/bit2int.cpp
421
lib/bit2int.cpp
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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) {
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
113
lib/bv_elim.cpp
113
lib/bv_elim.cpp
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
||||
1650
lib/diff_logic.h
1650
lib/diff_logic.h
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
490
lib/dl_base.cpp
490
lib/dl_base.cpp
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
1250
lib/dl_base.h
1250
lib/dl_base.h
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
437
lib/dl_cmds.cpp
437
lib/dl_cmds.cpp
|
|
@ -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)););
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
1190
lib/dl_compiler.cpp
1190
lib/dl_compiler.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -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_ */
|
||||
|
||||
1705
lib/dl_context.cpp
1705
lib/dl_context.cpp
File diff suppressed because it is too large
Load diff
468
lib/dl_context.h
468
lib/dl_context.h
|
|
@ -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_ */
|
||||
|
||||
160
lib/dl_costs.cpp
160
lib/dl_costs.cpp
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
115
lib/dl_costs.h
115
lib/dl_costs.h
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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, ¶m, 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, ¶m, 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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_ */
|
||||
|
||||
1084
lib/dl_rule.cpp
1084
lib/dl_rule.cpp
File diff suppressed because it is too large
Load diff
265
lib/dl_rule.h
265
lib/dl_rule.h
|
|
@ -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_ */
|
||||
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue