mirror of
https://github.com/Z3Prover/z3
synced 2025-09-06 01:48:02 +00:00
re-organize muz_qe into separate units
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
4597872be8
commit
0d56499e2d
131 changed files with 994 additions and 20069 deletions
280
src/muz/transforms/dl_mk_array_blast.cpp
Normal file
280
src/muz/transforms/dl_mk_array_blast.cpp
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_array_blast.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Remove array stores from rules.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-11-23
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "dl_mk_array_blast.h"
|
||||
#include "qe_util.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
||||
mk_array_blast::mk_array_blast(context & ctx, unsigned priority) :
|
||||
rule_transformer::plugin(priority, false),
|
||||
m_ctx(ctx),
|
||||
m(ctx.get_manager()),
|
||||
a(m),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_rewriter(m, m_params),
|
||||
m_simplifier(ctx),
|
||||
m_sub(m),
|
||||
m_next_var(0) {
|
||||
m_params.set_bool("expand_select_store",true);
|
||||
m_rewriter.updt_params(m_params);
|
||||
}
|
||||
|
||||
mk_array_blast::~mk_array_blast() {
|
||||
}
|
||||
|
||||
bool mk_array_blast::is_store_def(expr* e, expr*& x, expr*& y) {
|
||||
if (m.is_iff(e, x, y) || m.is_eq(e, x, y)) {
|
||||
if (!a.is_store(y)) {
|
||||
std::swap(x,y);
|
||||
}
|
||||
if (is_var(x) && a.is_store(y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expr* mk_array_blast::get_select(expr* e) const {
|
||||
while (a.is_select(e)) {
|
||||
e = to_app(e)->get_arg(0);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
void mk_array_blast::get_select_args(expr* e, ptr_vector<expr>& args) const {
|
||||
while (a.is_select(e)) {
|
||||
app* ap = to_app(e);
|
||||
for (unsigned i = 1; i < ap->get_num_args(); ++i) {
|
||||
args.push_back(ap->get_arg(i));
|
||||
}
|
||||
e = ap->get_arg(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool mk_array_blast::insert_def(rule const& r, app* e, var* v) {
|
||||
//
|
||||
// For the Ackermann reduction we would like the arrays
|
||||
// to be variables, so that variables can be
|
||||
// assumed to represent difference (alias)
|
||||
// classes. Ehm., Soundness of this approach depends on
|
||||
// if the arrays are finite domains...
|
||||
//
|
||||
|
||||
if (!is_var(get_select(e))) {
|
||||
return false;
|
||||
}
|
||||
if (v) {
|
||||
m_sub.insert(e, v);
|
||||
m_defs.insert(e, to_var(v));
|
||||
}
|
||||
else {
|
||||
if (m_next_var == 0) {
|
||||
ptr_vector<sort> vars;
|
||||
r.get_vars(vars);
|
||||
m_next_var = vars.size() + 1;
|
||||
}
|
||||
v = m.mk_var(m_next_var, m.get_sort(e));
|
||||
m_sub.insert(e, v);
|
||||
m_defs.insert(e, v);
|
||||
++m_next_var;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) {
|
||||
expr_ref_vector conjs(m);
|
||||
qe::flatten_and(body, conjs);
|
||||
m_defs.reset();
|
||||
m_sub.reset();
|
||||
m_next_var = 0;
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(head);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr* e = conjs[i].get();
|
||||
expr* x, *y;
|
||||
if (m.is_eq(e, x, y) || m.is_iff(e, x, y)) {
|
||||
if (a.is_select(y)) {
|
||||
std::swap(x,y);
|
||||
}
|
||||
if (a.is_select(x) && is_var(y)) {
|
||||
if (!insert_def(r, to_app(x), to_var(y))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (a.is_select(e) && !insert_def(r, to_app(e), 0)) {
|
||||
return false;
|
||||
}
|
||||
todo.push_back(e);
|
||||
}
|
||||
// now make sure to cover all occurrences.
|
||||
ast_mark mark;
|
||||
while (!todo.empty()) {
|
||||
expr* e = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark.is_marked(e)) {
|
||||
continue;
|
||||
}
|
||||
mark.mark(e, true);
|
||||
if (is_var(e)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_app(e)) {
|
||||
return false;
|
||||
}
|
||||
app* ap = to_app(e);
|
||||
if (a.is_select(ap) && !m_defs.contains(ap)) {
|
||||
if (!insert_def(r, ap, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (a.is_select(e)) {
|
||||
get_select_args(e, todo);
|
||||
continue;
|
||||
}
|
||||
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
|
||||
todo.push_back(ap->get_arg(i));
|
||||
}
|
||||
}
|
||||
m_sub(body);
|
||||
m_sub(head);
|
||||
conjs.reset();
|
||||
|
||||
// perform the Ackermann reduction by creating implications
|
||||
// i1 = i2 => val1 = val2 for each equality pair:
|
||||
// (= val1 (select a_i i1))
|
||||
// (= val2 (select a_i i2))
|
||||
defs_t::iterator it1 = m_defs.begin(), end = m_defs.end();
|
||||
for (; it1 != end; ++it1) {
|
||||
app* a1 = it1->m_key;
|
||||
var* v1 = it1->m_value;
|
||||
defs_t::iterator it2 = it1;
|
||||
++it2;
|
||||
for (; it2 != end; ++it2) {
|
||||
app* a2 = it2->m_key;
|
||||
var* v2 = it2->m_value;
|
||||
if (get_select(a1) != get_select(a2)) {
|
||||
continue;
|
||||
}
|
||||
expr_ref_vector eqs(m);
|
||||
ptr_vector<expr> args1, args2;
|
||||
get_select_args(a1, args1);
|
||||
get_select_args(a2, args2);
|
||||
for (unsigned j = 0; j < args1.size(); ++j) {
|
||||
eqs.push_back(m.mk_eq(args1[j], args2[j]));
|
||||
}
|
||||
conjs.push_back(m.mk_implies(m.mk_and(eqs.size(), eqs.c_ptr()), m.mk_eq(v1, v2)));
|
||||
}
|
||||
}
|
||||
if (!conjs.empty()) {
|
||||
conjs.push_back(body);
|
||||
body = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
}
|
||||
m_rewriter(body);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mk_array_blast::blast(rule& r, rule_set& rules) {
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
expr_ref_vector conjs(m), new_conjs(m);
|
||||
expr_ref tmp(m);
|
||||
expr_safe_replace sub(m);
|
||||
bool change = false;
|
||||
bool inserted = false;
|
||||
|
||||
for (unsigned i = 0; i < utsz; ++i) {
|
||||
new_conjs.push_back(r.get_tail(i));
|
||||
}
|
||||
for (unsigned i = utsz; i < tsz; ++i) {
|
||||
conjs.push_back(r.get_tail(i));
|
||||
}
|
||||
qe::flatten_and(conjs);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr* x, *y, *e = conjs[i].get();
|
||||
|
||||
if (is_store_def(e, x, y)) {
|
||||
// enforce topological order consistency:
|
||||
uint_set lhs = rm.collect_vars(x);
|
||||
uint_set rhs_vars = rm.collect_vars(y);
|
||||
lhs &= rhs_vars;
|
||||
if (!lhs.empty()) {
|
||||
TRACE("dl", tout << "unusable equality " << mk_pp(e, m) << "\n";);
|
||||
new_conjs.push_back(e);
|
||||
}
|
||||
else {
|
||||
sub.insert(x, y);
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_rewriter(e, tmp);
|
||||
change = change || (tmp != e);
|
||||
new_conjs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref fml2(m), body(m), head(m);
|
||||
body = m.mk_and(new_conjs.size(), new_conjs.c_ptr());
|
||||
head = r.get_head();
|
||||
sub(body);
|
||||
m_rewriter(body);
|
||||
sub(head);
|
||||
m_rewriter(head);
|
||||
change = ackermanize(r, body, head) || change;
|
||||
if (!inserted && !change) {
|
||||
rules.add_rule(&r);
|
||||
return false;
|
||||
}
|
||||
|
||||
fml2 = m.mk_implies(body, head);
|
||||
proof_ref p(m);
|
||||
rule_set new_rules(m_ctx);
|
||||
rm.mk_rule(fml2, p, new_rules, r.name());
|
||||
|
||||
rule_ref new_rule(rm);
|
||||
if (m_simplifier.transform_rule(new_rules.last(), new_rule)) {
|
||||
rules.add_rule(new_rule.get());
|
||||
rm.mk_rule_rewrite_proof(r, *new_rule.get());
|
||||
TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n"););
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rule_set * mk_array_blast::operator()(rule_set const & source) {
|
||||
|
||||
rule_set* rules = alloc(rule_set, m_ctx);
|
||||
rules->inherit_predicates(source);
|
||||
rule_set::iterator it = source.begin(), end = source.end();
|
||||
bool change = false;
|
||||
for (; !m_ctx.canceled() && it != end; ++it) {
|
||||
change = blast(**it, *rules) || change;
|
||||
}
|
||||
if (!change) {
|
||||
dealloc(rules);
|
||||
rules = 0;
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
77
src/muz/transforms/dl_mk_array_blast.h
Normal file
77
src/muz/transforms/dl_mk_array_blast.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_array_blast.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Remove array variables from rules.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-11-23
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_ARRAY_BLAST_H_
|
||||
#define _DL_MK_ARRAY_BLAST_H_
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_set.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"dl_mk_interp_tail_simplifier.h"
|
||||
#include "equiv_proof_converter.h"
|
||||
#include "array_decl_plugin.h"
|
||||
#include "expr_safe_replace.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
\brief Blast occurrences of arrays in rules
|
||||
*/
|
||||
class mk_array_blast : public rule_transformer::plugin {
|
||||
typedef obj_map<app, var*> defs_t;
|
||||
|
||||
context& m_ctx;
|
||||
ast_manager& m;
|
||||
array_util a;
|
||||
rule_manager& rm;
|
||||
params_ref m_params;
|
||||
th_rewriter m_rewriter;
|
||||
mk_interp_tail_simplifier m_simplifier;
|
||||
|
||||
defs_t m_defs;
|
||||
expr_safe_replace m_sub;
|
||||
unsigned m_next_var;
|
||||
|
||||
bool blast(rule& r, rule_set& new_rules);
|
||||
|
||||
bool is_store_def(expr* e, expr*& x, expr*& y);
|
||||
|
||||
bool ackermanize(rule const& r, expr_ref& body, expr_ref& head);
|
||||
|
||||
expr* get_select(expr* e) const;
|
||||
|
||||
void get_select_args(expr* e, ptr_vector<expr>& args) const;
|
||||
|
||||
bool insert_def(rule const& r, app* e, var* v);
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Create rule transformer that removes array stores and selects by ackermannization.
|
||||
*/
|
||||
mk_array_blast(context & ctx, unsigned priority);
|
||||
|
||||
virtual ~mk_array_blast();
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_ARRAY_BLAST_H_ */
|
||||
|
78
src/muz/transforms/dl_mk_backwards.cpp
Normal file
78
src/muz/transforms/dl_mk_backwards.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_backwards.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Create Horn clauses for backwards flow.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-17
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_mk_backwards.h"
|
||||
#include"dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
mk_backwards::mk_backwards(context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx) {
|
||||
}
|
||||
|
||||
mk_backwards::~mk_backwards() { }
|
||||
|
||||
rule_set * mk_backwards::operator()(rule_set const & source) {
|
||||
context& ctx = source.get_context();
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_set * result = alloc(rule_set, ctx);
|
||||
unsigned sz = source.get_num_rules();
|
||||
rule_ref new_rule(rm);
|
||||
app_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
svector<bool> neg;
|
||||
app_ref query(m);
|
||||
query = m.mk_fresh_const("Q", m.mk_bool_sort());
|
||||
result->set_output_predicate(query->get_decl());
|
||||
m_ctx.register_predicate(query->get_decl(), false);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tail.reset();
|
||||
neg.reset();
|
||||
rule & r = *source.get_rule(i);
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
if (!source.is_output_predicate(r.get_decl())) {
|
||||
tail.push_back(r.get_head());
|
||||
neg.push_back(false);
|
||||
}
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(r.get_tail(j));
|
||||
neg.push_back(false);
|
||||
}
|
||||
for (unsigned j = 0; j <= utsz; ++j) {
|
||||
if (j == utsz && j > 0) {
|
||||
break;
|
||||
}
|
||||
if (j == utsz) {
|
||||
head = query;
|
||||
}
|
||||
else {
|
||||
head = r.get_tail(j);
|
||||
}
|
||||
new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
}
|
||||
TRACE("dl", result->display(tout););
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
38
src/muz/transforms/dl_mk_backwards.h
Normal file
38
src/muz/transforms/dl_mk_backwards.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_backwards.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Create Horn clauses for backwards flow.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-17
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_BACKWARDS_H_
|
||||
#define _DL_MK_BACKWARDS_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_backwards : public rule_transformer::plugin {
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
public:
|
||||
mk_backwards(context & ctx, unsigned priority = 33000);
|
||||
~mk_backwards();
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_BACKWARDS_H_ */
|
||||
|
318
src/muz/transforms/dl_mk_bit_blast.cpp
Normal file
318
src/muz/transforms/dl_mk_bit_blast.cpp
Normal file
|
@ -0,0 +1,318 @@
|
|||
/*++
|
||||
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"
|
||||
#include "expr_safe_replace.h"
|
||||
#include "filter_model_converter.h"
|
||||
#include "dl_mk_interp_tail_simplifier.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
|
||||
|
||||
// this model converter should be composed with a filter converter
|
||||
// that gets rid of the new functions.
|
||||
class bit_blast_model_converter : public model_converter {
|
||||
ast_manager& m;
|
||||
bv_util m_bv;
|
||||
func_decl_ref_vector m_old_funcs;
|
||||
func_decl_ref_vector m_new_funcs;
|
||||
public:
|
||||
bit_blast_model_converter(ast_manager& m):
|
||||
m(m),
|
||||
m_bv(m),
|
||||
m_old_funcs(m),
|
||||
m_new_funcs(m) {}
|
||||
|
||||
void insert(func_decl* old_f, func_decl* new_f) {
|
||||
m_old_funcs.push_back(old_f);
|
||||
m_new_funcs.push_back(new_f);
|
||||
}
|
||||
|
||||
virtual model_converter * translate(ast_translation & translator) {
|
||||
return alloc(bit_blast_model_converter, m);
|
||||
}
|
||||
|
||||
virtual void operator()(model_ref & model) {
|
||||
for (unsigned i = 0; i < m_new_funcs.size(); ++i) {
|
||||
func_decl* p = m_new_funcs[i].get();
|
||||
func_decl* q = m_old_funcs[i].get();
|
||||
func_interp* f = model->get_func_interp(p);
|
||||
expr_ref body(m);
|
||||
unsigned arity_p = p->get_arity();
|
||||
unsigned arity_q = q->get_arity();
|
||||
SASSERT(0 < arity_p);
|
||||
model->register_decl(p, f);
|
||||
func_interp* g = alloc(func_interp, m, arity_q);
|
||||
|
||||
if (f) {
|
||||
body = f->get_interp();
|
||||
SASSERT(!f->is_partial());
|
||||
SASSERT(body);
|
||||
}
|
||||
else {
|
||||
body = m.mk_false();
|
||||
}
|
||||
unsigned idx = 0;
|
||||
expr_ref arg(m), proj(m);
|
||||
expr_safe_replace sub(m);
|
||||
for (unsigned j = 0; j < arity_q; ++j) {
|
||||
sort* s = q->get_domain(j);
|
||||
arg = m.mk_var(j, s);
|
||||
if (m_bv.is_bv_sort(s)) {
|
||||
expr* args[1] = { arg };
|
||||
unsigned sz = m_bv.get_bv_size(s);
|
||||
for (unsigned k = 0; k < sz; ++k) {
|
||||
proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, args);
|
||||
sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sub.insert(m.mk_var(idx++, s), arg);
|
||||
}
|
||||
}
|
||||
sub(body);
|
||||
g->set_else(body);
|
||||
model->register_decl(q, g);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class expand_mkbv_cfg : public default_rewriter_cfg {
|
||||
|
||||
context& m_context;
|
||||
ast_manager& m;
|
||||
bv_util m_util;
|
||||
expr_ref_vector m_args, m_f_vars, m_g_vars;
|
||||
func_decl_ref_vector m_old_funcs;
|
||||
func_decl_ref_vector m_new_funcs;
|
||||
rule_set const* m_src;
|
||||
rule_set* m_dst;
|
||||
obj_map<func_decl,func_decl*> m_pred2blast;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
expand_mkbv_cfg(context& ctx):
|
||||
m_context(ctx),
|
||||
m(ctx.get_manager()),
|
||||
m_util(m),
|
||||
m_args(m),
|
||||
m_f_vars(m),
|
||||
m_g_vars(m),
|
||||
m_old_funcs(m),
|
||||
m_new_funcs(m),
|
||||
m_src(0),
|
||||
m_dst(0)
|
||||
{}
|
||||
|
||||
~expand_mkbv_cfg() {}
|
||||
|
||||
void set_src(rule_set const* src) { m_src = src; }
|
||||
void set_dst(rule_set* dst) { m_dst = dst; }
|
||||
func_decl_ref_vector const& old_funcs() const { return m_old_funcs; }
|
||||
func_decl_ref_vector const& new_funcs() const { return m_new_funcs; }
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (num == 0) {
|
||||
if (m_src->is_output_predicate(f))
|
||||
m_dst->set_output_predicate(f);
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
if (!m_util.is_mkbv(args[i]))
|
||||
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_old_funcs.push_back(f);
|
||||
m_new_funcs.push_back(g);
|
||||
m_pred2blast.insert(f, g);
|
||||
|
||||
m_dst->inherit_predicate(*m_src, f, g);
|
||||
}
|
||||
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):
|
||||
rewriter_tpl<expand_mkbv_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class mk_bit_blast::impl {
|
||||
|
||||
context & m_context;
|
||||
ast_manager & m;
|
||||
params_ref m_params;
|
||||
mk_interp_tail_simplifier m_simplifier;
|
||||
bit_blaster_rewriter m_blaster;
|
||||
expand_mkbv m_rewriter;
|
||||
|
||||
|
||||
bool blast(rule *r, expr_ref& fml) {
|
||||
proof_ref pr(m);
|
||||
expr_ref fml1(m), fml2(m), fml3(m);
|
||||
rule_ref r2(m_context.get_rule_manager());
|
||||
// We need to simplify rule before bit-blasting.
|
||||
if (!m_simplifier.transform_rule(r, r2)) {
|
||||
r2 = r;
|
||||
}
|
||||
r2->to_formula(fml1);
|
||||
m_blaster(fml1, fml2, pr);
|
||||
m_rewriter(fml2, fml3);
|
||||
TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";);
|
||||
if (fml3 != fml) {
|
||||
fml = fml3;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
impl(context& ctx):
|
||||
m_context(ctx),
|
||||
m(ctx.get_manager()),
|
||||
m_params(ctx.get_params().p),
|
||||
m_simplifier(ctx),
|
||||
m_blaster(ctx.get_manager(), m_params),
|
||||
m_rewriter(ctx.get_manager(), ctx) {
|
||||
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) {
|
||||
// TODO pc
|
||||
if (!m_context.get_params().bit_blast()) {
|
||||
return 0;
|
||||
}
|
||||
rule_manager& rm = m_context.get_rule_manager();
|
||||
unsigned sz = source.get_num_rules();
|
||||
expr_ref fml(m);
|
||||
rule_set * result = alloc(rule_set, m_context);
|
||||
m_rewriter.m_cfg.set_src(&source);
|
||||
m_rewriter.m_cfg.set_dst(result);
|
||||
for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) {
|
||||
rule * r = source.get_rule(i);
|
||||
r->to_formula(fml);
|
||||
if (blast(r, fml)) {
|
||||
proof_ref pr(m);
|
||||
if (m_context.generate_proof_trace()) {
|
||||
pr = m.mk_asserted(fml); // loses original proof of r.
|
||||
}
|
||||
// TODO add logic for pc:
|
||||
// 1. replace fresh predicates by non-bit-blasted predicates
|
||||
// 2. replace pr by the proof of r.
|
||||
rm.mk_rule(fml, pr, *result, r->name());
|
||||
}
|
||||
else {
|
||||
result->add_rule(r);
|
||||
}
|
||||
}
|
||||
|
||||
// copy output predicates without any rule (bit-blasting not really needed)
|
||||
const func_decl_set& decls = source.get_output_predicates();
|
||||
for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) {
|
||||
if (!source.contains(*I))
|
||||
result->set_output_predicate(*I);
|
||||
}
|
||||
|
||||
if (m_context.get_model_converter()) {
|
||||
filter_model_converter* fmc = alloc(filter_model_converter, m);
|
||||
bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m);
|
||||
func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs();
|
||||
func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs();
|
||||
for (unsigned i = 0; i < old_funcs.size(); ++i) {
|
||||
fmc->insert(new_funcs[i]);
|
||||
bvmc->insert(old_funcs[i], new_funcs[i]);
|
||||
}
|
||||
m_context.add_model_converter(concat(bvmc, fmc));
|
||||
}
|
||||
|
||||
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) {
|
||||
return (*m_impl)(source);
|
||||
}
|
||||
|
||||
};
|
38
src/muz/transforms/dl_mk_bit_blast.h
Normal file
38
src/muz/transforms/dl_mk_bit_blast.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_bit_blast.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Functor for bit-blasting a rule set
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-08-30
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_BIT_BLAST_H_
|
||||
#define _DL_MK_BIT_BLAST_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_bit_blast : public rule_transformer::plugin {
|
||||
class impl;
|
||||
impl* m_impl;
|
||||
public:
|
||||
mk_bit_blast(context & ctx, unsigned priority = 35000);
|
||||
~mk_bit_blast();
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_BIT_BLAST_H_ */
|
||||
|
199
src/muz/transforms/dl_mk_coalesce.cpp
Normal file
199
src/muz/transforms/dl_mk_coalesce.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*++
|
||||
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);
|
||||
ptr_vector<sort> sorts;
|
||||
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]) {
|
||||
SASSERT(s == sorts[v]);
|
||||
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] && !revsub[i].get()) {
|
||||
revsub[i] = m.mk_var(m_idx++, sorts[i]);
|
||||
}
|
||||
}
|
||||
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);
|
||||
ptr_vector<sort> sorts1, sorts2;
|
||||
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_ctx.generate_proof_trace()) {
|
||||
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 = src.get_proof();
|
||||
p = m.mk_hyper_resolve(1, &p, fml, pos, substs);
|
||||
res->set_proof(m, p);
|
||||
#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) {
|
||||
rule_set* rules = alloc(rule_set, m_ctx);
|
||||
rules->inherit_predicates(source);
|
||||
rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules();
|
||||
for (; it != end; ++it) {
|
||||
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();
|
||||
--j;
|
||||
}
|
||||
}
|
||||
rules->add_rule(r1.get());
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
61
src/muz/transforms/dl_mk_coalesce.h
Normal file
61
src/muz/transforms/dl_mk_coalesce.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
/*++
|
||||
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;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_COALESCE_H_ */
|
||||
|
201
src/muz/transforms/dl_mk_coi_filter.cpp
Normal file
201
src/muz/transforms/dl_mk_coi_filter.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*++
|
||||
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:
|
||||
|
||||
Andrey Rybalchenko (rybal) 2013-8-8
|
||||
Added bottom_up pruning.
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include"ast_pp.h"
|
||||
#include"dl_mk_coi_filter.h"
|
||||
#include"extension_model_converter.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// mk_coi_filter
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
rule_set * mk_coi_filter::operator()(rule_set const & source) {
|
||||
if (source.empty()) {
|
||||
return 0;
|
||||
}
|
||||
scoped_ptr<rule_set> result1 = top_down(source);
|
||||
scoped_ptr<rule_set> result2 = bottom_up(result1?*result1:source);
|
||||
if (!result2) {
|
||||
result2 = result1.detach();
|
||||
}
|
||||
return result2.detach();
|
||||
}
|
||||
|
||||
rule_set * mk_coi_filter::bottom_up(rule_set const & source) {
|
||||
func_decl_set all, reached;
|
||||
ptr_vector<func_decl> todo;
|
||||
rule_set::decl2rules body2rules;
|
||||
// initialization for reachability
|
||||
for (rule_set::iterator it = source.begin(); it != source.end(); ++it) {
|
||||
rule * r = *it;
|
||||
all.insert(r->get_decl());
|
||||
if (r->get_uninterpreted_tail_size() == 0) {
|
||||
if (!reached.contains(r->get_decl())) {
|
||||
reached.insert(r->get_decl());
|
||||
todo.insert(r->get_decl());
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) {
|
||||
func_decl * d = r->get_tail(i)->get_decl();
|
||||
all.insert(d);
|
||||
rule_set::decl2rules::obj_map_entry * e = body2rules.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// reachability computation
|
||||
while (!todo.empty()) {
|
||||
func_decl * d = todo.back();
|
||||
todo.pop_back();
|
||||
ptr_vector<rule> * rules;
|
||||
if (!body2rules.find(d, rules)) continue;
|
||||
for (ptr_vector<rule>::iterator it = rules->begin(); it != rules->end(); ++it) {
|
||||
rule * r = *it;
|
||||
if (reached.contains(r->get_decl())) continue;
|
||||
bool contained = true;
|
||||
for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) {
|
||||
contained = reached.contains(r->get_tail(i)->get_decl());
|
||||
}
|
||||
if (!contained) continue;
|
||||
reached.insert(r->get_decl());
|
||||
todo.insert(r->get_decl());
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate each rule when some body predicate is not reached
|
||||
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
|
||||
res->inherit_predicates(source);
|
||||
for (rule_set::iterator it = source.begin(); it != source.end(); ++it) {
|
||||
rule * r = *it;
|
||||
|
||||
bool contained = true;
|
||||
for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) {
|
||||
contained = reached.contains(r->get_tail(i)->get_decl());
|
||||
}
|
||||
if (contained) {
|
||||
res->add_rule(r);
|
||||
}
|
||||
}
|
||||
if (res->get_num_rules() == source.get_num_rules()) {
|
||||
TRACE("dl", tout << "No transformation\n";);
|
||||
res = 0;
|
||||
}
|
||||
else {
|
||||
res->close();
|
||||
}
|
||||
|
||||
// set to false each unreached predicate
|
||||
if (m_context.get_model_converter()) {
|
||||
extension_model_converter* mc0 = alloc(extension_model_converter, m);
|
||||
for (func_decl_set::iterator it = all.begin(); it != all.end(); ++it) {
|
||||
if (!reached.contains(*it)) {
|
||||
mc0->insert(*it, m.mk_false());
|
||||
}
|
||||
}
|
||||
m_context.add_model_converter(mc0);
|
||||
}
|
||||
// clean up body2rules range resources
|
||||
for (rule_set::decl2rules::iterator it = body2rules.begin(); it != body2rules.end(); ++it) {
|
||||
dealloc(it->m_value);
|
||||
}
|
||||
CTRACE("dl", 0 != res, res->display(tout););
|
||||
return res.detach();
|
||||
}
|
||||
|
||||
rule_set * mk_coi_filter::top_down(rule_set const & source) {
|
||||
|
||||
func_decl_set interesting_preds;
|
||||
func_decl_set pruned_preds;
|
||||
ptr_vector<func_decl> todo;
|
||||
{
|
||||
const func_decl_set& output_preds = source.get_output_predicates();
|
||||
func_decl_set::iterator oend = output_preds.end();
|
||||
for (func_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);
|
||||
res->inherit_predicates(source);
|
||||
|
||||
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 (m_context.get_model_converter()) {
|
||||
pruned_preds.insert(pred);
|
||||
}
|
||||
}
|
||||
|
||||
if (res->get_num_rules() == source.get_num_rules()) {
|
||||
TRACE("dl", tout << "No transformation\n";);
|
||||
res = 0;
|
||||
}
|
||||
|
||||
if (res && m_context.get_model_converter()) {
|
||||
func_decl_set::iterator end = pruned_preds.end();
|
||||
func_decl_set::iterator it = pruned_preds.begin();
|
||||
extension_model_converter* mc0 = alloc(extension_model_converter, m);
|
||||
for (; it != end; ++it) {
|
||||
mc0->insert(*it, m.mk_true());
|
||||
}
|
||||
m_context.add_model_converter(mc0);
|
||||
}
|
||||
CTRACE("dl", 0 != res, res->display(tout););
|
||||
return res.detach();
|
||||
}
|
||||
|
||||
};
|
||||
|
51
src/muz/transforms/dl_mk_coi_filter.h
Normal file
51
src/muz/transforms/dl_mk_coi_filter.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*++
|
||||
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;
|
||||
|
||||
rule_set * bottom_up(rule_set const & source);
|
||||
rule_set * top_down(rule_set const & source);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_COI_FILTER_H_ */
|
||||
|
38
src/muz/transforms/dl_mk_different.h
Normal file
38
src/muz/transforms/dl_mk_different.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_different_symbolic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Create Horn clauses for different symbolic transformation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-06-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_DIFFERENT_SYMBOLIC_H_
|
||||
#define _DL_MK_DIFFERENT_SYMBOLIC_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_different_symbolic : public rule_transformer::plugin {
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
public:
|
||||
mk_different_symbolic(context & ctx, unsigned priority = 33037);
|
||||
~mk_different_symbolic();
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_DIFFERENT_SYMBOLIC_H_ */
|
||||
|
170
src/muz/transforms/dl_mk_filter_rules.cpp
Normal file
170
src/muz/transforms/dl_mk_filter_rules.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*++
|
||||
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(ctx.get_manager()),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_result(0),
|
||||
m_pinned(m) {
|
||||
}
|
||||
|
||||
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.is_predicate(pred)) {
|
||||
TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\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.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);
|
||||
|
||||
filter_key * key = alloc(filter_key, m);
|
||||
mk_new_rule_tail(m, 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);
|
||||
filter_head = m.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);
|
||||
m_context.get_rule_manager().mk_rule_asserted_proof(*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);
|
||||
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) << "\n";);
|
||||
var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail);
|
||||
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.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_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule);
|
||||
m_modified = true;
|
||||
}
|
||||
else {
|
||||
m_result->add_rule(r);
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * mk_filter_rules::operator()(rule_set const & source) {
|
||||
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++) {
|
||||
process(source.get_rule(i));
|
||||
}
|
||||
if(!m_modified) {
|
||||
dealloc(m_result);
|
||||
return static_cast<rule_set *>(0);
|
||||
}
|
||||
m_result->inherit_predicates(source);
|
||||
return m_result;
|
||||
}
|
||||
|
||||
};
|
87
src/muz/transforms/dl_mk_filter_rules.h
Normal file
87
src/muz/transforms/dl_mk_filter_rules.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*++
|
||||
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 {
|
||||
unsigned r = new_pred->hash();
|
||||
for (unsigned i = 0; i < filter_args.size(); ++i) {
|
||||
r ^= filter_args[i]->hash();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
bool operator==(const filter_key & o) const {
|
||||
return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args);
|
||||
}
|
||||
};
|
||||
|
||||
typedef obj_map<filter_key, func_decl*> filter_cache;
|
||||
|
||||
context & m_context;
|
||||
ast_manager & m;
|
||||
rule_manager & rm;
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* _DL_MK_FILTER_RULES_H_ */
|
||||
|
618
src/muz/transforms/dl_mk_interp_tail_simplifier.cpp
Normal file
618
src/muz/transforms/dl_mk_interp_tail_simplifier.cpp
Normal file
|
@ -0,0 +1,618 @@
|
|||
/*++
|
||||
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_counter().get_max_rule_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);
|
||||
|
||||
apply(m_rule->get_head(), m_head);
|
||||
|
||||
m_tail.reset();
|
||||
m_neg.reset();
|
||||
|
||||
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);
|
||||
m_tail.push_back(new_tail_el);
|
||||
m_neg.push_back(m_rule->is_neg_tail(i));
|
||||
}
|
||||
|
||||
mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg);
|
||||
|
||||
SASSERT(m_tail.size() == m_neg.size());
|
||||
res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_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) && (m.is_and(args[0]) || m.is_or(args[0]))) {
|
||||
SASSERT(num==1);
|
||||
expr_ref tmp(m);
|
||||
app* a = to_app(args[0]);
|
||||
m_app_args.reset();
|
||||
for (unsigned i = 0; i < a->get_num_args(); ++i) {
|
||||
m_brwr.mk_not(a->get_arg(i), tmp);
|
||||
m_app_args.push_back(tmp);
|
||||
}
|
||||
if (m.is_and(args[0])) {
|
||||
result = m.mk_or(m_app_args.size(), m_app_args.c_ptr());
|
||||
}
|
||||
else {
|
||||
result = m.mk_and(m_app_args.size(), m_app_args.c_ptr());
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
if (!m.is_and(f) && !m.is_or(f)) {
|
||||
return BR_FAILED;
|
||||
}
|
||||
if (num == 0) {
|
||||
if (m.is_and(f)) {
|
||||
result = m.mk_true();
|
||||
}
|
||||
else {
|
||||
result = m.mk_false();
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
if (num == 1) {
|
||||
result = args[0];
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
have_rewritten_args = detect_equivalences(m_app_args, m.is_or(f));
|
||||
|
||||
if (m_app_args.size()==1) {
|
||||
result = m_app_args[0].get();
|
||||
}
|
||||
else {
|
||||
if (m.is_and(f)) {
|
||||
result = m.mk_and(m_app_args.size(), m_app_args.c_ptr());
|
||||
}
|
||||
else {
|
||||
SASSERT(m.is_or(f));
|
||||
result = m.mk_or(m_app_args.size(), m_app_args.c_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
if (have_rewritten_args) {
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
};
|
||||
|
||||
class mk_interp_tail_simplifier::normalizer_rw : public rewriter_tpl<normalizer_cfg> {
|
||||
public:
|
||||
normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl<normalizer_cfg>(m, false, cfg) {}
|
||||
};
|
||||
|
||||
|
||||
mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority)
|
||||
: plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_context(ctx),
|
||||
m_simp(ctx.get_rewriter()),
|
||||
a(m),
|
||||
m_rule_subst(ctx),
|
||||
m_tail(m),
|
||||
m_itail_members(m),
|
||||
m_conj(m) {
|
||||
m_cfg = alloc(normalizer_cfg, m);
|
||||
m_rw = alloc(normalizer_rw, m, *m_cfg);
|
||||
}
|
||||
|
||||
mk_interp_tail_simplifier::~mk_interp_tail_simplifier() {
|
||||
dealloc(m_rw);
|
||||
dealloc(m_cfg);
|
||||
}
|
||||
|
||||
|
||||
void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res)
|
||||
{
|
||||
expr_ref simp1_res(m);
|
||||
m_simp(a, simp1_res);
|
||||
(*m_rw)(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;
|
||||
}
|
||||
|
||||
m_todo.reset();
|
||||
m_leqs.reset();
|
||||
for (unsigned i = u_len; i < len; i++) {
|
||||
m_todo.push_back(r->get_tail(i));
|
||||
SASSERT(!r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
m_rule_subst.reset(r);
|
||||
|
||||
expr_ref_vector trail(m);
|
||||
expr_ref tmp1(m), tmp2(m);
|
||||
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 (!m_todo.empty()) {
|
||||
expr * arg1, *arg2;
|
||||
expr * t0 = m_todo.back();
|
||||
m_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);
|
||||
m_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());
|
||||
}
|
||||
}
|
||||
else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) {
|
||||
tmp1 = a.mk_sub(arg1, arg2);
|
||||
tmp2 = a.mk_sub(arg2, arg1);
|
||||
if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) {
|
||||
TRY_UNIFY(arg1, arg2);
|
||||
}
|
||||
else {
|
||||
trail.push_back(tmp1);
|
||||
m_leqs.insert(tmp1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if (r->has_quantifiers()) {
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
m_tail.reset();
|
||||
m_tail_neg.reset();
|
||||
|
||||
for (unsigned i=0; i<u_len; i++) {
|
||||
m_tail.push_back(r->get_tail(i));
|
||||
m_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 {
|
||||
m_itail_members.reset();
|
||||
for (unsigned i=u_len; i<len; i++) {
|
||||
m_itail_members.push_back(r->get_tail(i));
|
||||
SASSERT(!r->is_neg_tail(i));
|
||||
}
|
||||
itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr());
|
||||
modified = true;
|
||||
}
|
||||
|
||||
expr_ref simp_res(m);
|
||||
simplify_expr(itail.get(), simp_res);
|
||||
|
||||
modified |= itail.get() != simp_res.get();
|
||||
|
||||
if (m.is_false(simp_res)) {
|
||||
TRACE("dl", r->display(m_context, tout << "rule is infeasible\n"););
|
||||
return false;
|
||||
}
|
||||
SASSERT(m.is_bool(simp_res));
|
||||
|
||||
if (modified) {
|
||||
m_conj.reset();
|
||||
qe::flatten_and(simp_res, m_conj);
|
||||
for (unsigned i = 0; i < m_conj.size(); ++i) {
|
||||
expr* e = m_conj[i].get();
|
||||
if (is_app(e)) {
|
||||
m_tail.push_back(to_app(e));
|
||||
}
|
||||
else {
|
||||
m_tail.push_back(m.mk_eq(e, m.mk_true()));
|
||||
}
|
||||
m_tail_neg.push_back(false);
|
||||
}
|
||||
|
||||
SASSERT(m_tail.size() == m_tail_neg.size());
|
||||
res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_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(rule_counter().get_max_rule_var(*r.get())==0 ||
|
||||
rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_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_manager& rm = m_context.get_rule_manager();
|
||||
rule_set::iterator rit = orig.begin();
|
||||
rule_set::iterator rend = orig.end();
|
||||
for (; rit!=rend; ++rit) {
|
||||
rule_ref new_rule(rm);
|
||||
if (transform_rule(*rit, new_rule)) {
|
||||
rm.mk_rule_rewrite_proof(**rit, *new_rule.get());
|
||||
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) {
|
||||
if (source.get_num_rules() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rule_set * res = alloc(rule_set, m_context);
|
||||
if (transform_rules(source, *res)) {
|
||||
res->inherit_predicates(source);
|
||||
} else {
|
||||
dealloc(res);
|
||||
res = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
109
src/muz/transforms/dl_mk_interp_tail_simplifier.h
Normal file
109
src/muz/transforms/dl_mk_interp_tail_simplifier.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*++
|
||||
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"
|
||||
#include "arith_decl_plugin.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;
|
||||
app_ref m_head;
|
||||
app_ref_vector m_tail;
|
||||
svector<bool> m_neg;
|
||||
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_head(m), m_tail(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);
|
||||
}
|
||||
};
|
||||
|
||||
class normalizer_cfg;
|
||||
class normalizer_rw;
|
||||
|
||||
ast_manager & m;
|
||||
context & m_context;
|
||||
th_rewriter & m_simp;
|
||||
arith_util a;
|
||||
rule_substitution m_rule_subst;
|
||||
ptr_vector<expr> m_todo;
|
||||
obj_hashtable<expr> m_leqs;
|
||||
app_ref_vector m_tail;
|
||||
expr_ref_vector m_itail_members;
|
||||
expr_ref_vector m_conj;
|
||||
svector<bool> m_tail_neg;
|
||||
normalizer_cfg* m_cfg;
|
||||
normalizer_rw* m_rw;
|
||||
|
||||
|
||||
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);
|
||||
virtual ~mk_interp_tail_simplifier();
|
||||
|
||||
/**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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */
|
||||
|
325
src/muz/transforms/dl_mk_karr_invariants.cpp
Normal file
325
src/muz/transforms/dl_mk_karr_invariants.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_karr_invariants.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Extract integer linear invariants.
|
||||
|
||||
The linear invariants are extracted according to Karr's method.
|
||||
A short description is in
|
||||
Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation
|
||||
of Invariants and Intermediate Assertions, in CP 95.
|
||||
|
||||
The algorithm is here adapted to Horn clauses.
|
||||
The idea is to maintain two data-structures for each recursive relation.
|
||||
We call them R and RD
|
||||
- R - set of linear congruences that are true of R.
|
||||
- RD - the dual basis of of solutions for R.
|
||||
|
||||
RD is updated by accumulating basis vectors for solutions
|
||||
to R (the homogeneous dual of R)
|
||||
R is updated from the inhomogeneous dual of RD.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-03-09
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"expr_safe_replace.h"
|
||||
#include"bool_rewriter.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
#include"dl_mk_karr_invariants.h"
|
||||
#include"dl_mk_backwards.h"
|
||||
#include"dl_mk_loop_counter.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
||||
mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority):
|
||||
rule_transformer::plugin(priority, false),
|
||||
m_ctx(ctx),
|
||||
m(ctx.get_manager()),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_inner_ctx(m, ctx.get_register_engine(), ctx.get_fparams()),
|
||||
a(m),
|
||||
m_pinned(m),
|
||||
m_cancel(false) {
|
||||
params_ref params;
|
||||
params.set_sym("default_relation", symbol("karr_relation"));
|
||||
params.set_sym("engine", symbol("datalog"));
|
||||
params.set_bool("karr", false);
|
||||
m_inner_ctx.updt_params(params);
|
||||
}
|
||||
|
||||
mk_karr_invariants::~mk_karr_invariants() { }
|
||||
|
||||
matrix& matrix::operator=(matrix const& other) {
|
||||
reset();
|
||||
append(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void matrix::display_row(
|
||||
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq) {
|
||||
for (unsigned j = 0; j < row.size(); ++j) {
|
||||
out << row[j] << " ";
|
||||
}
|
||||
out << (is_eq?" = ":" >= ") << -b << "\n";
|
||||
}
|
||||
|
||||
void matrix::display_ineq(
|
||||
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq) {
|
||||
bool first = true;
|
||||
for (unsigned j = 0; j < row.size(); ++j) {
|
||||
if (!row[j].is_zero()) {
|
||||
if (!first && row[j].is_pos()) {
|
||||
out << "+ ";
|
||||
}
|
||||
if (row[j].is_minus_one()) {
|
||||
out << "- ";
|
||||
}
|
||||
if (row[j] > rational(1) || row[j] < rational(-1)) {
|
||||
out << row[j] << "*";
|
||||
}
|
||||
out << "x" << j << " ";
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
out << (is_eq?"= ":">= ") << -b << "\n";
|
||||
}
|
||||
|
||||
void matrix::display(std::ostream& out) const {
|
||||
for (unsigned i = 0; i < A.size(); ++i) {
|
||||
display_row(out, A[i], b[i], eq[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class mk_karr_invariants::add_invariant_model_converter : public model_converter {
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
func_decl_ref_vector m_funcs;
|
||||
expr_ref_vector m_invs;
|
||||
public:
|
||||
|
||||
add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {}
|
||||
|
||||
virtual ~add_invariant_model_converter() { }
|
||||
|
||||
void add(func_decl* p, expr* inv) {
|
||||
if (!m.is_true(inv)) {
|
||||
m_funcs.push_back(p);
|
||||
m_invs.push_back(inv);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void operator()(model_ref & mr) {
|
||||
for (unsigned i = 0; i < m_funcs.size(); ++i) {
|
||||
func_decl* p = m_funcs[i].get();
|
||||
func_interp* f = mr->get_func_interp(p);
|
||||
expr_ref body(m);
|
||||
unsigned arity = p->get_arity();
|
||||
SASSERT(0 < arity);
|
||||
if (f) {
|
||||
SASSERT(f->num_entries() == 0);
|
||||
if (!f->is_partial()) {
|
||||
bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body);
|
||||
}
|
||||
}
|
||||
else {
|
||||
f = alloc(func_interp, m, arity);
|
||||
mr->register_decl(p, f);
|
||||
body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible.
|
||||
}
|
||||
f->set_else(body);
|
||||
}
|
||||
}
|
||||
|
||||
virtual model_converter * translate(ast_translation & translator) {
|
||||
add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m);
|
||||
for (unsigned i = 0; i < m_funcs.size(); ++i) {
|
||||
mc->add(translator(m_funcs[i].get()), m_invs[i].get());
|
||||
}
|
||||
return mc;
|
||||
}
|
||||
|
||||
private:
|
||||
void mk_body(matrix const& M, expr_ref& body) {
|
||||
expr_ref_vector conj(m);
|
||||
for (unsigned i = 0; i < M.size(); ++i) {
|
||||
mk_body(M.A[i], M.b[i], M.eq[i], conj);
|
||||
}
|
||||
bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body);
|
||||
}
|
||||
|
||||
void mk_body(vector<rational> const& row, rational const& b, bool is_eq, expr_ref_vector& conj) {
|
||||
expr_ref_vector sum(m);
|
||||
expr_ref zero(m), lhs(m);
|
||||
zero = a.mk_numeral(rational(0), true);
|
||||
|
||||
for (unsigned i = 0; i < row.size(); ++i) {
|
||||
if (row[i].is_zero()) {
|
||||
continue;
|
||||
}
|
||||
var* var = m.mk_var(i, a.mk_int());
|
||||
if (row[i].is_one()) {
|
||||
sum.push_back(var);
|
||||
}
|
||||
else {
|
||||
sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var));
|
||||
}
|
||||
}
|
||||
if (!b.is_zero()) {
|
||||
sum.push_back(a.mk_numeral(b, true));
|
||||
}
|
||||
lhs = a.mk_add(sum.size(), sum.c_ptr());
|
||||
if (is_eq) {
|
||||
conj.push_back(m.mk_eq(lhs, zero));
|
||||
}
|
||||
else {
|
||||
conj.push_back(a.mk_ge(lhs, zero));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void mk_karr_invariants::cancel() {
|
||||
m_cancel = true;
|
||||
m_inner_ctx.cancel();
|
||||
}
|
||||
|
||||
rule_set * mk_karr_invariants::operator()(rule_set const & source) {
|
||||
if (!m_ctx.get_params().karr()) {
|
||||
return 0;
|
||||
}
|
||||
rule_set::iterator it = source.begin(), end = source.end();
|
||||
for (; it != end; ++it) {
|
||||
rule const& r = **it;
|
||||
if (r.has_negation()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mk_loop_counter lc(m_ctx);
|
||||
mk_backwards bwd(m_ctx);
|
||||
|
||||
scoped_ptr<rule_set> src_loop = lc(source);
|
||||
TRACE("dl", src_loop->display(tout << "source loop\n"););
|
||||
|
||||
get_invariants(*src_loop);
|
||||
|
||||
if (m_cancel) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// figure out whether to update same rules as used for saturation.
|
||||
scoped_ptr<rule_set> rev_source = bwd(*src_loop);
|
||||
get_invariants(*rev_source);
|
||||
scoped_ptr<rule_set> src_annot = update_rules(*src_loop);
|
||||
rule_set* rules = lc.revert(*src_annot);
|
||||
rules->inherit_predicates(source);
|
||||
TRACE("dl", rules->display(tout););
|
||||
m_pinned.reset();
|
||||
m_fun2inv.reset();
|
||||
return rules;
|
||||
}
|
||||
|
||||
void mk_karr_invariants::get_invariants(rule_set const& src) {
|
||||
m_inner_ctx.reset();
|
||||
rel_context_base& rctx = *m_inner_ctx.get_rel_context();
|
||||
ptr_vector<func_decl> heads;
|
||||
func_decl_set const& predicates = m_ctx.get_predicates();
|
||||
for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) {
|
||||
m_inner_ctx.register_predicate(*fit, false);
|
||||
}
|
||||
m_inner_ctx.ensure_opened();
|
||||
m_inner_ctx.replace_rules(src);
|
||||
m_inner_ctx.close();
|
||||
rule_set::decl2rules::iterator dit = src.begin_grouped_rules();
|
||||
rule_set::decl2rules::iterator dend = src.end_grouped_rules();
|
||||
for (; dit != dend; ++dit) {
|
||||
heads.push_back(dit->m_key);
|
||||
}
|
||||
m_inner_ctx.rel_query(heads.size(), heads.c_ptr());
|
||||
|
||||
// retrieve invariants.
|
||||
dit = src.begin_grouped_rules();
|
||||
for (; dit != dend; ++dit) {
|
||||
func_decl* p = dit->m_key;
|
||||
expr_ref fml = rctx.try_get_formula(p);
|
||||
if (fml && !m.is_true(fml)) {
|
||||
expr* inv = 0;
|
||||
if (m_fun2inv.find(p, inv)) {
|
||||
fml = m.mk_and(inv, fml);
|
||||
}
|
||||
m_pinned.push_back(fml);
|
||||
m_fun2inv.insert(p, fml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule_set* mk_karr_invariants::update_rules(rule_set const& src) {
|
||||
scoped_ptr<rule_set> dst = alloc(rule_set, m_ctx);
|
||||
rule_set::iterator it = src.begin(), end = src.end();
|
||||
for (; it != end; ++it) {
|
||||
update_body(*dst, **it);
|
||||
}
|
||||
if (m_ctx.get_model_converter()) {
|
||||
add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m);
|
||||
rule_set::decl2rules::iterator git = src.begin_grouped_rules();
|
||||
rule_set::decl2rules::iterator gend = src.end_grouped_rules();
|
||||
for (; git != gend; ++git) {
|
||||
func_decl* p = git->m_key;
|
||||
expr* fml = 0;
|
||||
if (m_fun2inv.find(p, fml)) {
|
||||
kmc->add(p, fml);
|
||||
}
|
||||
}
|
||||
m_ctx.add_model_converter(kmc);
|
||||
}
|
||||
|
||||
dst->inherit_predicates(src);
|
||||
return dst.detach();
|
||||
}
|
||||
|
||||
void mk_karr_invariants::update_body(rule_set& rules, rule& r) {
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
app_ref_vector tail(m);
|
||||
expr_ref fml(m);
|
||||
for (unsigned i = 0; i < tsz; ++i) {
|
||||
tail.push_back(r.get_tail(i));
|
||||
}
|
||||
for (unsigned i = 0; i < utsz; ++i) {
|
||||
func_decl* q = r.get_decl(i);
|
||||
expr* fml = 0;
|
||||
if (m_fun2inv.find(q, fml)) {
|
||||
expr_safe_replace rep(m);
|
||||
for (unsigned j = 0; j < q->get_arity(); ++j) {
|
||||
rep.insert(m.mk_var(j, q->get_domain(j)),
|
||||
r.get_tail(i)->get_arg(j));
|
||||
}
|
||||
expr_ref tmp(fml, m);
|
||||
rep(tmp);
|
||||
tail.push_back(to_app(tmp));
|
||||
}
|
||||
}
|
||||
rule* new_rule = &r;
|
||||
if (tail.size() != tsz) {
|
||||
new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name());
|
||||
}
|
||||
rules.add_rule(new_rule);
|
||||
rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
81
src/muz/transforms/dl_mk_karr_invariants.h
Normal file
81
src/muz/transforms/dl_mk_karr_invariants.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_karr_invariants.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Extract integer linear invariants.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-03-08
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_KARR_INVARIANTS_H_
|
||||
#define _DL_MK_KARR_INVARIANTS_H_
|
||||
|
||||
#include"dl_context.h"
|
||||
#include"dl_rule_set.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"hilbert_basis.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
/**
|
||||
\brief Rule transformer that strengthens bodies with invariants.
|
||||
*/
|
||||
|
||||
struct matrix {
|
||||
vector<vector<rational> > A;
|
||||
vector<rational> b;
|
||||
svector<bool> eq;
|
||||
unsigned size() const { return A.size(); }
|
||||
void reset() { A.reset(); b.reset(); eq.reset(); }
|
||||
matrix& operator=(matrix const& other);
|
||||
void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); }
|
||||
void display(std::ostream& out) const;
|
||||
static void display_row(
|
||||
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq);
|
||||
static void display_ineq(
|
||||
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq);
|
||||
};
|
||||
|
||||
class mk_karr_invariants : public rule_transformer::plugin {
|
||||
|
||||
class add_invariant_model_converter;
|
||||
|
||||
context& m_ctx;
|
||||
ast_manager& m;
|
||||
rule_manager& rm;
|
||||
context m_inner_ctx;
|
||||
arith_util a;
|
||||
obj_map<func_decl, expr*> m_fun2inv;
|
||||
ast_ref_vector m_pinned;
|
||||
volatile bool m_cancel;
|
||||
|
||||
void get_invariants(rule_set const& src);
|
||||
|
||||
void update_body(rule_set& result, rule& r);
|
||||
rule_set* update_rules(rule_set const& src);
|
||||
public:
|
||||
mk_karr_invariants(context & ctx, unsigned priority);
|
||||
|
||||
virtual ~mk_karr_invariants();
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_KARR_INVARIANTS_H_ */
|
||||
|
158
src/muz/transforms/dl_mk_loop_counter.cpp
Normal file
158
src/muz/transforms/dl_mk_loop_counter.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_loop_counter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Add loop counter argument to relations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-03-31
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_mk_loop_counter.h"
|
||||
#include"dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx),
|
||||
a(m),
|
||||
m_refs(m) {
|
||||
}
|
||||
|
||||
mk_loop_counter::~mk_loop_counter() { }
|
||||
|
||||
app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) {
|
||||
expr_ref_vector args(m);
|
||||
func_decl* new_fn, *old_fn = fn->get_decl();
|
||||
args.append(fn->get_num_args(), fn->get_args());
|
||||
args.push_back(m.mk_var(idx, a.mk_int()));
|
||||
|
||||
if (!m_old2new.find(old_fn, new_fn)) {
|
||||
ptr_vector<sort> domain;
|
||||
domain.append(fn->get_num_args(), old_fn->get_domain());
|
||||
domain.push_back(a.mk_int());
|
||||
new_fn = m.mk_func_decl(old_fn->get_name(), domain.size(), domain.c_ptr(), old_fn->get_range());
|
||||
m_old2new.insert(old_fn, new_fn);
|
||||
m_new2old.insert(new_fn, old_fn);
|
||||
m_refs.push_back(new_fn);
|
||||
m_ctx.register_predicate(new_fn, false);
|
||||
if (src.is_output_predicate(old_fn)) {
|
||||
dst.set_output_predicate(new_fn);
|
||||
}
|
||||
}
|
||||
return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m);
|
||||
}
|
||||
|
||||
app_ref mk_loop_counter::del_arg(app* fn) {
|
||||
expr_ref_vector args(m);
|
||||
func_decl* old_fn, *new_fn = fn->get_decl();
|
||||
SASSERT(fn->get_num_args() > 0);
|
||||
args.append(fn->get_num_args()-1, fn->get_args());
|
||||
VERIFY (m_new2old.find(new_fn, old_fn));
|
||||
return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m);
|
||||
}
|
||||
|
||||
rule_set * mk_loop_counter::operator()(rule_set const & source) {
|
||||
m_refs.reset();
|
||||
m_old2new.reset();
|
||||
m_new2old.reset();
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_set * result = alloc(rule_set, m_ctx);
|
||||
unsigned sz = source.get_num_rules();
|
||||
rule_ref new_rule(rm);
|
||||
app_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
svector<bool> neg;
|
||||
rule_counter& vc = rm.get_counter();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tail.reset();
|
||||
neg.reset();
|
||||
rule & r = *source.get_rule(i);
|
||||
unsigned cnt = vc.get_max_rule_var(r)+1;
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
for (unsigned j = 0; j < utsz; ++j, ++cnt) {
|
||||
tail.push_back(add_arg(source, *result, r.get_tail(j), cnt));
|
||||
neg.push_back(r.is_neg_tail(j));
|
||||
}
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(r.get_tail(j));
|
||||
neg.push_back(false);
|
||||
}
|
||||
head = add_arg(source, *result, r.get_head(), cnt);
|
||||
// set the loop counter to be an increment of the previous
|
||||
bool found = false;
|
||||
unsigned last = head->get_num_args()-1;
|
||||
for (unsigned j = 0; !found && j < utsz; ++j) {
|
||||
if (head->get_decl() == tail[j]->get_decl()) {
|
||||
tail.push_back(m.mk_eq(head->get_arg(last),
|
||||
a.mk_add(tail[j]->get_arg(last),
|
||||
a.mk_numeral(rational(1), true))));
|
||||
neg.push_back(false);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
// initialize loop counter to 0 if none was found.
|
||||
if (!found) {
|
||||
expr_ref_vector args(m);
|
||||
args.append(head->get_num_args(), head->get_args());
|
||||
args[last] = a.mk_numeral(rational(0), true);
|
||||
head = m.mk_app(head->get_decl(), args.size(), args.c_ptr());
|
||||
}
|
||||
|
||||
new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
|
||||
// model converter: remove references to extra argument.
|
||||
// proof converter: remove references to extra argument as well.
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
rule_set * mk_loop_counter::revert(rule_set const & source) {
|
||||
context& ctx = source.get_context();
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_set * result = alloc(rule_set, ctx);
|
||||
unsigned sz = source.get_num_rules();
|
||||
rule_ref new_rule(rm);
|
||||
app_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
svector<bool> neg;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tail.reset();
|
||||
neg.reset();
|
||||
rule & r = *source.get_rule(i);
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
for (unsigned j = 0; j < utsz; ++j) {
|
||||
tail.push_back(del_arg(r.get_tail(j)));
|
||||
neg.push_back(r.is_neg_tail(j));
|
||||
}
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(r.get_tail(j));
|
||||
neg.push_back(false);
|
||||
}
|
||||
head = del_arg(r.get_head());
|
||||
new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
|
||||
// model converter: ...
|
||||
// proof converter: ...
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
};
|
51
src/muz/transforms/dl_mk_loop_counter.h
Normal file
51
src/muz/transforms/dl_mk_loop_counter.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_loop_counter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Add loop counter argument to relations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-03-31
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_LOOP_COUNTER_H_
|
||||
#define _DL_MK_LOOP_COUNTER_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_loop_counter : public rule_transformer::plugin {
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
arith_util a;
|
||||
func_decl_ref_vector m_refs;
|
||||
obj_map<func_decl, func_decl*> m_new2old;
|
||||
obj_map<func_decl, func_decl*> m_old2new;
|
||||
|
||||
app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx);
|
||||
app_ref del_arg(app* fn);
|
||||
public:
|
||||
mk_loop_counter(context & ctx, unsigned priority = 33000);
|
||||
~mk_loop_counter();
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
func_decl* get_old(func_decl* f) const { return m_new2old.find(f); }
|
||||
|
||||
rule_set * revert(rule_set const& source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_LOOP_COUNTER_H_ */
|
||||
|
383
src/muz/transforms/dl_mk_magic_sets.cpp
Normal file
383
src/muz/transforms/dl_mk_magic_sets.cpp
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*++
|
||||
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, func_decl* goal) :
|
||||
plugin(10000, true),
|
||||
m_context(ctx),
|
||||
m(ctx.get_manager()),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_pinned(m),
|
||||
m_goal(goal, m) {
|
||||
}
|
||||
|
||||
void mk_magic_sets::reset() {
|
||||
m_extentional.reset();
|
||||
m_todo.reset();
|
||||
m_adorned_preds.reset();
|
||||
m_adornments.reset();
|
||||
m_magic_preds.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) {
|
||||
res += (*eit == AD_BOUND)?'b':'f';
|
||||
}
|
||||
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())) {
|
||||
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.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.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.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.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, rule_set& result) {
|
||||
//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); );
|
||||
result.add_rule(r);
|
||||
}
|
||||
}
|
||||
|
||||
void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) {
|
||||
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));
|
||||
bound_vars |= rm.collect_vars(curr);
|
||||
}
|
||||
|
||||
|
||||
func_decl * new_head_pred;
|
||||
VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) );
|
||||
app * new_head = m.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(), result);
|
||||
|
||||
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());
|
||||
result.add_rule(nr);
|
||||
nr->set_accounting_parent_object(m_context, r);
|
||||
}
|
||||
|
||||
void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) {
|
||||
func_decl * adn_pred = m_adorned_preds.find(d);
|
||||
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.mk_var(i, adn_pred->get_domain(i)));
|
||||
}
|
||||
|
||||
app * lit = m.mk_app(d.m_pred, args.c_ptr());
|
||||
app * adn_lit = m.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);
|
||||
result.add_rule(r);
|
||||
}
|
||||
|
||||
rule_set * mk_magic_sets::operator()(rule_set const & source) {
|
||||
|
||||
if (!m_context.magic_sets_for_queries()) {
|
||||
return 0;
|
||||
}
|
||||
SASSERT(source.contains(m_goal));
|
||||
SASSERT(source.get_predicate_rules(m_goal).size() == 1);
|
||||
|
||||
app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head();
|
||||
|
||||
unsigned init_rule_cnt = source.get_num_rules();
|
||||
{
|
||||
func_decl_set intentional;
|
||||
for (unsigned i=0; i<init_rule_cnt; i++) {
|
||||
func_decl* pred = source.get_rule(i)->get_decl();
|
||||
intentional.insert(pred);
|
||||
}
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
//adornment goal_adn;
|
||||
//goal_adn.populate(goal_head, );
|
||||
var_idx_set empty_var_idx_set;
|
||||
adorn_literal(goal_head, empty_var_idx_set);
|
||||
|
||||
rule_set * result = alloc(rule_set, m_context);
|
||||
result->inherit_predicates(source);
|
||||
|
||||
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, *result);
|
||||
}
|
||||
if (!m_context.get_rel_context()->is_empty_relation(task.m_pred)) {
|
||||
//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, *result);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0);
|
||||
result->add_rule(mag_goal_rule);
|
||||
|
||||
rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0);
|
||||
result->add_rule(back_to_goal_rule);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
135
src/muz/transforms/dl_mk_magic_sets.h
Normal file
135
src/muz/transforms/dl_mk_magic_sets.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
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 a_flag_hash {
|
||||
typedef a_flag data;
|
||||
unsigned operator()(a_flag x) const { return x; }
|
||||
};
|
||||
|
||||
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()^svector_hash<a_flag_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;
|
||||
rule_manager& rm;
|
||||
ast_ref_vector m_pinned;
|
||||
/**
|
||||
\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;
|
||||
func_decl_ref m_goal;
|
||||
|
||||
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, rule_set& result);
|
||||
app * adorn_literal(app * lit, const var_idx_set & bound_vars);
|
||||
void transform_rule(const adornment & head_adornment, rule * r, rule_set& result);
|
||||
void create_transfer_rule(const adornment_desc & d, rule_set& result);
|
||||
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, func_decl* goal);
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_MAGIC_SETS_H_ */
|
||||
|
135
src/muz/transforms/dl_mk_magic_symbolic.cpp
Normal file
135
src/muz/transforms/dl_mk_magic_symbolic.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_magic_symbolic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Create Horn clauses for magic symbolic flow.
|
||||
|
||||
Q(x) :- A(y), B(z), phi1(x,y,z).
|
||||
Q(x) :- C(y), phi2(x,y).
|
||||
A(x) :- C(y), phi3(x,y).
|
||||
A(x) :- A(y), phi3(x,y).
|
||||
B(x) :- C(y), A(z), phi4(x,y,z).
|
||||
C(x) :- phi5(x).
|
||||
|
||||
Transformed clauses:
|
||||
|
||||
Q_ans(x) :- Q_query(x), A_ans(y), B_ans(z), phi1(x,y,z).
|
||||
Q_ans(x) :- Q_query(x), C_ans(y), phi2(x,y).
|
||||
Q_query(x) :- true.
|
||||
|
||||
A_ans(x) :- A_query(x), C_ans(y), phi2(x,y)
|
||||
A_ans(x) :- A_query(x), A_ans(y), phi3(x,y).
|
||||
A_query(y) :- Q_query(x), phi1(x,y,z).
|
||||
A_query(y) :- A_query(x), phi3(x,y).
|
||||
A_query(z) :- B_query(x), C_ans(y), phi4(x,y,z).
|
||||
|
||||
B_ans(x) :- B_query(x), C_ans(y), A_ans(z), phi4(x,y,z).
|
||||
B_query(z) :- Q_query(x), A_ans(y), phi1(x,y,z).
|
||||
|
||||
C_ans(x) :- C_query(x), phi5(x).
|
||||
C_query(y) :- Q_query(x), phi2(x,y).
|
||||
C_query(y) :- Q_query(x), phi3(x,y).
|
||||
C_query(y) :- B_query(x), phi4(x,y,z).
|
||||
|
||||
General scheme:
|
||||
A(x) :- P1(x_1), ..., Pn(x_n), phi(x,x1,..,x_n).
|
||||
|
||||
P(x) :- Prefix(x,y,z), A(z) ...
|
||||
|
||||
A_ans(x) :- A_query(x), P_i_ans(x_i), phi(x,..).
|
||||
A_query(z) :- P_query(x), Prefix_ans(x,y,z).
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-06-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_mk_magic_symbolic.h"
|
||||
#include"dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
||||
mk_magic_symbolic::mk_magic_symbolic(context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx) {
|
||||
}
|
||||
|
||||
mk_magic_symbolic::~mk_magic_symbolic() { }
|
||||
|
||||
rule_set * mk_magic_symbolic::operator()(rule_set const & source) {
|
||||
if (!m_ctx.get_params().magic()) {
|
||||
return 0;
|
||||
}
|
||||
context& ctx = source.get_context();
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_set * result = alloc(rule_set, ctx);
|
||||
unsigned sz = source.get_num_rules();
|
||||
rule_ref new_rule(rm);
|
||||
app_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
svector<bool> neg;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
rule & r = *source.get_rule(i);
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
tail.reset();
|
||||
neg.reset();
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(r.get_tail(j));
|
||||
neg.push_back(false);
|
||||
}
|
||||
tail.push_back(mk_query(r.get_head()));
|
||||
neg.push_back(false);
|
||||
for (unsigned j = 0; j < utsz; ++j) {
|
||||
tail.push_back(mk_ans(r.get_tail(j)));
|
||||
neg.push_back(false);
|
||||
}
|
||||
new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
if (source.is_output_predicate(r.get_decl())) {
|
||||
result->set_output_predicate(new_rule->get_decl());
|
||||
new_rule = rm.mk(mk_query(r.get_head()), 0, 0, 0, r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < utsz; ++j) {
|
||||
new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
}
|
||||
|
||||
}
|
||||
TRACE("dl", result->display(tout););
|
||||
return result;
|
||||
}
|
||||
|
||||
app_ref mk_magic_symbolic::mk_query(app* q) {
|
||||
string_buffer<64> name;
|
||||
func_decl* f = q->get_decl();
|
||||
name << f->get_name() << "!query";
|
||||
func_decl_ref g(m);
|
||||
g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range());
|
||||
m_ctx.register_predicate(g, false);
|
||||
return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m);
|
||||
}
|
||||
|
||||
app_ref mk_magic_symbolic::mk_ans(app* q) {
|
||||
string_buffer<64> name;
|
||||
func_decl* f = q->get_decl();
|
||||
func_decl_ref g(m);
|
||||
name << f->get_name() << "!ans";
|
||||
g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range());
|
||||
m_ctx.register_predicate(g, false);
|
||||
return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m);
|
||||
}
|
||||
|
||||
};
|
40
src/muz/transforms/dl_mk_magic_symbolic.h
Normal file
40
src/muz/transforms/dl_mk_magic_symbolic.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_magic_symbolic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Create Horn clauses for magic symbolic transformation.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-06-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_MAGIC_SYMBOLIC_H_
|
||||
#define _DL_MK_MAGIC_SYMBOLIC_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_magic_symbolic : public rule_transformer::plugin {
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
app_ref mk_ans(app* q);
|
||||
app_ref mk_query(app* q);
|
||||
public:
|
||||
mk_magic_symbolic(context & ctx, unsigned priority = 33037);
|
||||
~mk_magic_symbolic();
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_MAGIC_SYMBOLIC_H_ */
|
||||
|
367
src/muz/transforms/dl_mk_quantifier_abstraction.cpp
Normal file
367
src/muz/transforms/dl_mk_quantifier_abstraction.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_quantifier_abstraction.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Create quantified Horn clauses from benchmarks with arrays.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan
|
||||
Andrey Rybalchenko
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-02
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "dl_mk_quantifier_abstraction.h"
|
||||
#include "dl_context.h"
|
||||
#include "expr_safe_replace.h"
|
||||
#include "expr_abstract.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
||||
// model converter:
|
||||
// Given model for P^(x, y, i, a[i])
|
||||
// create model: P(x,y,a) == forall i . P^(x,y,i,a[i])
|
||||
// requires substitution and list of bound variables.
|
||||
|
||||
class mk_quantifier_abstraction::qa_model_converter : public model_converter {
|
||||
ast_manager& m;
|
||||
func_decl_ref_vector m_old_funcs;
|
||||
func_decl_ref_vector m_new_funcs;
|
||||
vector<expr_ref_vector> m_subst;
|
||||
vector<sort_ref_vector> m_sorts;
|
||||
vector<svector<bool> > m_bound;
|
||||
|
||||
public:
|
||||
|
||||
qa_model_converter(ast_manager& m):
|
||||
m(m), m_old_funcs(m), m_new_funcs(m) {}
|
||||
|
||||
virtual ~qa_model_converter() {}
|
||||
|
||||
virtual model_converter * translate(ast_translation & translator) {
|
||||
return alloc(qa_model_converter, m);
|
||||
}
|
||||
|
||||
void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector<bool> const& bound) {
|
||||
m_old_funcs.push_back(old_p);
|
||||
m_new_funcs.push_back(new_p);
|
||||
m_subst.push_back(sub);
|
||||
m_bound.push_back(bound);
|
||||
m_sorts.push_back(sorts);
|
||||
}
|
||||
|
||||
virtual void operator()(model_ref & old_model) {
|
||||
model_ref new_model = alloc(model, m);
|
||||
for (unsigned i = 0; i < m_new_funcs.size(); ++i) {
|
||||
func_decl* p = m_new_funcs[i].get();
|
||||
func_decl* q = m_old_funcs[i].get();
|
||||
expr_ref_vector const& sub = m_subst[i];
|
||||
sort_ref_vector const& sorts = m_sorts[i];
|
||||
svector<bool> const& is_bound = m_bound[i];
|
||||
func_interp* f = old_model->get_func_interp(p);
|
||||
expr_ref body(m);
|
||||
unsigned arity_p = p->get_arity();
|
||||
unsigned arity_q = q->get_arity();
|
||||
SASSERT(0 < arity_p);
|
||||
func_interp* g = alloc(func_interp, m, arity_q);
|
||||
|
||||
if (f) {
|
||||
body = f->get_interp();
|
||||
SASSERT(!f->is_partial());
|
||||
SASSERT(body);
|
||||
}
|
||||
else {
|
||||
body = m.mk_false();
|
||||
}
|
||||
// Create quantifier wrapper around body.
|
||||
|
||||
TRACE("dl", tout << mk_pp(body, m) << "\n";);
|
||||
// 1. replace variables by the compound terms from
|
||||
// the original predicate.
|
||||
expr_safe_replace rep(m);
|
||||
for (unsigned i = 0; i < sub.size(); ++i) {
|
||||
rep.insert(m.mk_var(i, m.get_sort(sub[i])), sub[i]);
|
||||
}
|
||||
rep(body);
|
||||
rep.reset();
|
||||
|
||||
TRACE("dl", tout << mk_pp(body, m) << "\n";);
|
||||
// 2. replace bound variables by constants.
|
||||
expr_ref_vector consts(m), bound(m), free(m);
|
||||
svector<symbol> names;
|
||||
ptr_vector<sort> bound_sorts;
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
sort* s = sorts[i];
|
||||
consts.push_back(m.mk_fresh_const("C", s));
|
||||
rep.insert(m.mk_var(i, s), consts.back());
|
||||
if (is_bound[i]) {
|
||||
bound.push_back(consts.back());
|
||||
names.push_back(symbol(i));
|
||||
bound_sorts.push_back(s);
|
||||
}
|
||||
else {
|
||||
free.push_back(consts.back());
|
||||
}
|
||||
}
|
||||
rep(body);
|
||||
rep.reset();
|
||||
|
||||
TRACE("dl", tout << mk_pp(body, m) << "\n";);
|
||||
// 3. abstract and quantify those variables that should be bound.
|
||||
expr_abstract(m, 0, bound.size(), bound.c_ptr(), body, body);
|
||||
body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body);
|
||||
|
||||
TRACE("dl", tout << mk_pp(body, m) << "\n";);
|
||||
// 4. replace remaining constants by variables.
|
||||
for (unsigned i = 0; i < free.size(); ++i) {
|
||||
rep.insert(free[i].get(), m.mk_var(i, m.get_sort(free[i].get())));
|
||||
}
|
||||
rep(body);
|
||||
g->set_else(body);
|
||||
TRACE("dl", tout << mk_pp(body, m) << "\n";);
|
||||
|
||||
new_model->register_decl(q, g);
|
||||
}
|
||||
old_model = new_model;
|
||||
}
|
||||
};
|
||||
|
||||
mk_quantifier_abstraction::mk_quantifier_abstraction(
|
||||
context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx),
|
||||
a(m),
|
||||
m_refs(m) {
|
||||
}
|
||||
|
||||
mk_quantifier_abstraction::~mk_quantifier_abstraction() {
|
||||
}
|
||||
|
||||
func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) {
|
||||
|
||||
if (rules.is_output_predicate(old_p)) {
|
||||
dst.inherit_predicate(rules, old_p, old_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned sz = old_p->get_arity();
|
||||
unsigned num_arrays = 0;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (a.is_array(old_p->get_domain(i))) {
|
||||
num_arrays++;
|
||||
}
|
||||
}
|
||||
if (num_arrays == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
func_decl* new_p = 0;
|
||||
if (!m_old2new.find(old_p, new_p)) {
|
||||
expr_ref_vector sub(m), vars(m);
|
||||
svector<bool> bound;
|
||||
sort_ref_vector domain(m), sorts(m);
|
||||
expr_ref arg(m);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
sort* s0 = old_p->get_domain(i);
|
||||
unsigned lookahead = 0;
|
||||
sort* s = s0;
|
||||
while (a.is_array(s)) {
|
||||
lookahead += get_array_arity(s);
|
||||
s = get_array_range(s);
|
||||
}
|
||||
arg = m.mk_var(bound.size() + lookahead, s0);
|
||||
s = s0;
|
||||
while (a.is_array(s)) {
|
||||
unsigned arity = get_array_arity(s);
|
||||
expr_ref_vector args(m);
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
sort* s1 = get_array_domain(s, j);
|
||||
domain.push_back(s1);
|
||||
args.push_back(m.mk_var(bound.size(), s1));
|
||||
bound.push_back(true);
|
||||
sorts.push_back(s1);
|
||||
}
|
||||
arg = mk_select(arg, args.size(), args.c_ptr());
|
||||
s = get_array_range(s);
|
||||
}
|
||||
domain.push_back(s);
|
||||
bound.push_back(false);
|
||||
sub.push_back(arg);
|
||||
sorts.push_back(s0);
|
||||
}
|
||||
SASSERT(old_p->get_range() == m.mk_bool_sort());
|
||||
new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range());
|
||||
m_refs.push_back(new_p);
|
||||
m_ctx.register_predicate(new_p, false);
|
||||
if (m_mc) {
|
||||
m_mc->insert(old_p, new_p, sub, sorts, bound);
|
||||
}
|
||||
m_old2new.insert(old_p, new_p);
|
||||
}
|
||||
return new_p;
|
||||
}
|
||||
|
||||
app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) {
|
||||
func_decl* new_p = declare_pred(rules, dst, p->get_decl());
|
||||
if (!new_p) {
|
||||
return app_ref(p, m);
|
||||
}
|
||||
expr_ref_vector args(m);
|
||||
expr_ref arg(m);
|
||||
unsigned sz = p->get_num_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
arg = p->get_arg(i);
|
||||
sort* s = m.get_sort(arg);
|
||||
while (a.is_array(s)) {
|
||||
unsigned arity = get_array_arity(s);
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
args.push_back(m.mk_var(idx++, get_array_domain(s, j)));
|
||||
}
|
||||
arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity);
|
||||
s = get_array_range(s);
|
||||
}
|
||||
args.push_back(arg);
|
||||
}
|
||||
TRACE("dl",
|
||||
tout << mk_pp(new_p, m) << "\n";
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
tout << mk_pp(args[i].get(), m) << "\n";
|
||||
});
|
||||
return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m);
|
||||
}
|
||||
|
||||
app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) {
|
||||
func_decl* old_p = p->get_decl();
|
||||
func_decl* new_p = declare_pred(rules, dst, old_p);
|
||||
if (!new_p) {
|
||||
return app_ref(p, m);
|
||||
}
|
||||
SASSERT(new_p->get_arity() > old_p->get_arity());
|
||||
unsigned num_extra_args = new_p->get_arity() - old_p->get_arity();
|
||||
var_shifter shift(m);
|
||||
expr_ref p_shifted(m);
|
||||
shift(p, num_extra_args, p_shifted);
|
||||
app* ps = to_app(p_shifted);
|
||||
expr_ref_vector args(m);
|
||||
app_ref_vector pats(m);
|
||||
sort_ref_vector vars(m);
|
||||
svector<symbol> names;
|
||||
expr_ref arg(m);
|
||||
unsigned idx = 0;
|
||||
unsigned sz = p->get_num_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
arg = ps->get_arg(i);
|
||||
sort* s = m.get_sort(arg);
|
||||
bool is_pattern = false;
|
||||
while (a.is_array(s)) {
|
||||
is_pattern = true;
|
||||
unsigned arity = get_array_arity(s);
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
vars.push_back(get_array_domain(s, j));
|
||||
names.push_back(symbol(idx));
|
||||
args.push_back(m.mk_var(idx++, vars.back()));
|
||||
}
|
||||
arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity);
|
||||
s = get_array_range(s);
|
||||
}
|
||||
if (is_pattern) {
|
||||
pats.push_back(to_app(arg));
|
||||
}
|
||||
args.push_back(arg);
|
||||
}
|
||||
expr* pat = 0;
|
||||
expr_ref pattern(m);
|
||||
pattern = m.mk_pattern(pats.size(), pats.c_ptr());
|
||||
pat = pattern.get();
|
||||
app_ref result(m);
|
||||
symbol qid, skid;
|
||||
result = m.mk_app(new_p, args.size(), args.c_ptr());
|
||||
result = m.mk_eq(m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), result, 1, qid, skid, 1, &pat), m.mk_true());
|
||||
return result;
|
||||
}
|
||||
|
||||
expr * mk_quantifier_abstraction::mk_select(expr* arg, unsigned num_args, expr* const* args) {
|
||||
ptr_vector<expr> args2;
|
||||
args2.push_back(arg);
|
||||
args2.append(num_args, args);
|
||||
return a.mk_select(args2.size(), args2.c_ptr());
|
||||
}
|
||||
|
||||
rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) {
|
||||
TRACE("dl", tout << "quantify " << source.get_num_rules() << " " << m_ctx.get_params().quantify_arrays() << "\n";);
|
||||
if (!m_ctx.get_params().quantify_arrays()) {
|
||||
return 0;
|
||||
}
|
||||
unsigned sz = source.get_num_rules();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
rule& r = *source.get_rule(i);
|
||||
if (r.has_negation()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_refs.reset();
|
||||
m_old2new.reset();
|
||||
m_new2old.reset();
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_ref new_rule(rm);
|
||||
expr_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
expr_ref fml(m);
|
||||
rule_counter& vc = rm.get_counter();
|
||||
|
||||
if (m_ctx.get_model_converter()) {
|
||||
m_mc = alloc(qa_model_converter, m);
|
||||
}
|
||||
rule_set * result = alloc(rule_set, m_ctx);
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tail.reset();
|
||||
rule & r = *source.get_rule(i);
|
||||
TRACE("dl", r.display(m_ctx, tout); );
|
||||
unsigned cnt = vc.get_max_rule_var(r)+1;
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
for (unsigned j = 0; j < utsz; ++j) {
|
||||
tail.push_back(mk_tail(source, *result, r.get_tail(j)));
|
||||
}
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(r.get_tail(j));
|
||||
}
|
||||
head = mk_head(source, *result, r.get_head(), cnt);
|
||||
fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head);
|
||||
rule_ref_vector added_rules(rm);
|
||||
proof_ref pr(m);
|
||||
rm.mk_rule(fml, pr, *result);
|
||||
TRACE("dl", result->last()->display(m_ctx, tout););
|
||||
}
|
||||
|
||||
// proof converter: proofs are not necessarily preserved using this transformation.
|
||||
|
||||
if (m_old2new.empty()) {
|
||||
dealloc(result);
|
||||
dealloc(m_mc);
|
||||
result = 0;
|
||||
}
|
||||
else {
|
||||
m_ctx.add_model_converter(m_mc);
|
||||
}
|
||||
m_mc = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
64
src/muz/transforms/dl_mk_quantifier_abstraction.h
Normal file
64
src/muz/transforms/dl_mk_quantifier_abstraction.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_quantifier_abstraction.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert clauses with array arguments to predicates
|
||||
into Quantified Horn clauses.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan
|
||||
Andrey Rybalchenko
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-02
|
||||
|
||||
Revision History:
|
||||
|
||||
Based on approach suggested in SAS 2013 paper
|
||||
"On Solving Universally Quantified Horn Clauses"
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_QUANTIFIER_ABSTRACTION_H_
|
||||
#define _DL_MK_QUANTIFIER_ABSTRACTION_H_
|
||||
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"array_decl_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class context;
|
||||
|
||||
class mk_quantifier_abstraction : public rule_transformer::plugin {
|
||||
class qa_model_converter;
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
array_util a;
|
||||
func_decl_ref_vector m_refs;
|
||||
obj_map<func_decl, func_decl*> m_new2old;
|
||||
obj_map<func_decl, func_decl*> m_old2new;
|
||||
qa_model_converter* m_mc;
|
||||
|
||||
func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p);
|
||||
app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx);
|
||||
app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p);
|
||||
expr* mk_select(expr* a, unsigned num_args, expr* const* args);
|
||||
|
||||
public:
|
||||
mk_quantifier_abstraction(context & ctx, unsigned priority);
|
||||
|
||||
virtual ~mk_quantifier_abstraction();
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_QUANTIFIER_ABSTRACTION_H_ */
|
||||
|
302
src/muz/transforms/dl_mk_quantifier_instantiation.cpp
Normal file
302
src/muz/transforms/dl_mk_quantifier_instantiation.cpp
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_quantifier_instantiation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert Quantified Horn clauses into non-quantified clauses using
|
||||
instantiation.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan
|
||||
Andrey Rybalchenko
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-02
|
||||
|
||||
Revision History:
|
||||
|
||||
Based on approach suggested in the SAS 2013 paper
|
||||
"On Solving Universally Quantified Horn Clauses"
|
||||
|
||||
--*/
|
||||
|
||||
#include "dl_mk_quantifier_instantiation.h"
|
||||
#include "dl_context.h"
|
||||
#include "pattern_inference.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
mk_quantifier_instantiation::mk_quantifier_instantiation(
|
||||
context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx),
|
||||
m_var2cnst(m),
|
||||
m_cnst2var(m) {
|
||||
}
|
||||
|
||||
mk_quantifier_instantiation::~mk_quantifier_instantiation() {
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs) {
|
||||
conjs.reset();
|
||||
qs.reset();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
for (unsigned j = 0; j < tsz; ++j) {
|
||||
conjs.push_back(r.get_tail(j));
|
||||
}
|
||||
qe::flatten_and(conjs);
|
||||
for (unsigned j = 0; j < conjs.size(); ++j) {
|
||||
expr* e = conjs[j].get();
|
||||
quantifier* q;
|
||||
if (rule_manager::is_forall(m, e, q)) {
|
||||
qs.push_back(q);
|
||||
conjs[j] = conjs.back();
|
||||
conjs.pop_back();
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, expr_ref_vector & conjs) {
|
||||
expr_ref qe(m);
|
||||
qe = q;
|
||||
m_var2cnst(qe);
|
||||
q = to_quantifier(qe);
|
||||
if (q->get_num_patterns() == 0) {
|
||||
proof_ref new_pr(m);
|
||||
pattern_inference_params params;
|
||||
pattern_inference infer(m, params);
|
||||
infer(q, qe, new_pr);
|
||||
q = to_quantifier(qe);
|
||||
}
|
||||
unsigned num_patterns = q->get_num_patterns();
|
||||
for (unsigned i = 0; i < num_patterns; ++i) {
|
||||
expr * pat = q->get_pattern(i);
|
||||
SASSERT(m.is_pattern(pat));
|
||||
instantiate_quantifier(q, to_app(pat), conjs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs) {
|
||||
m_binding.reset();
|
||||
m_binding.resize(q->get_num_decls());
|
||||
term_pairs todo;
|
||||
match(0, pat, 0, todo, q, conjs);
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs) {
|
||||
TRACE("dl", tout << "match" << mk_pp(pat, m) << "\n";);
|
||||
while (j < todo.size()) {
|
||||
expr* p = todo[j].first;
|
||||
expr* t = todo[j].second;
|
||||
if (is_var(p)) {
|
||||
unsigned idx = to_var(p)->get_idx();
|
||||
if (!m_binding[idx]) {
|
||||
m_binding[idx] = t;
|
||||
match(i, pat, j + 1, todo, q, conjs);
|
||||
m_binding[idx] = 0;
|
||||
return;
|
||||
}
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
if (!is_app(p)) {
|
||||
return;
|
||||
}
|
||||
app* a1 = to_app(p);
|
||||
unsigned id = t->get_id();
|
||||
unsigned next_id = id;
|
||||
unsigned sz = todo.size();
|
||||
do {
|
||||
expr* t2 = m_terms[next_id];
|
||||
if (is_app(t2)) {
|
||||
app* a2 = to_app(t2);
|
||||
if (a1->get_decl() == a2->get_decl() &&
|
||||
a1->get_num_args() == a2->get_num_args()) {
|
||||
for (unsigned k = 0; k < a1->get_num_args(); ++k) {
|
||||
todo.push_back(std::make_pair(a1->get_arg(k), a2->get_arg(k)));
|
||||
}
|
||||
match(i, pat, j + 1, todo, q, conjs);
|
||||
todo.resize(sz);
|
||||
}
|
||||
}
|
||||
next_id = m_uf.next(next_id);
|
||||
}
|
||||
while (next_id != id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (i == pat->get_num_args()) {
|
||||
yield_binding(q, conjs);
|
||||
return;
|
||||
}
|
||||
expr* arg = pat->get_arg(i);
|
||||
ptr_vector<expr>* terms = 0;
|
||||
|
||||
if (m_funs.find(to_app(arg)->get_decl(), terms)) {
|
||||
for (unsigned k = 0; k < terms->size(); ++k) {
|
||||
todo.push_back(std::make_pair(arg, (*terms)[k]));
|
||||
match(i + 1, pat, j, todo, q, conjs);
|
||||
todo.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::yield_binding(quantifier* q, expr_ref_vector& conjs) {
|
||||
DEBUG_CODE(
|
||||
for (unsigned i = 0; i < m_binding.size(); ++i) {
|
||||
SASSERT(m_binding[i]);
|
||||
});
|
||||
m_binding.reverse();
|
||||
expr_ref res(m);
|
||||
instantiate(m, q, m_binding.c_ptr(), res);
|
||||
m_binding.reverse();
|
||||
m_cnst2var(res);
|
||||
conjs.push_back(res);
|
||||
TRACE("dl", tout << mk_pp(q, m) << "\n==>\n" << mk_pp(res, m) << "\n";);
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::collect_egraph(expr* e) {
|
||||
expr* e1, *e2;
|
||||
m_todo.push_back(e);
|
||||
expr_fast_mark1 visited;
|
||||
while (!m_todo.empty()) {
|
||||
e = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (visited.is_marked(e)) {
|
||||
continue;
|
||||
}
|
||||
unsigned n = e->get_id();
|
||||
if (n >= m_terms.size()) {
|
||||
m_terms.resize(n+1);
|
||||
}
|
||||
m_terms[n] = e;
|
||||
visited.mark(e);
|
||||
if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) {
|
||||
m_uf.merge(e1->get_id(), e2->get_id());
|
||||
}
|
||||
if (is_app(e)) {
|
||||
app* ap = to_app(e);
|
||||
ptr_vector<expr>* terms = 0;
|
||||
if (!m_funs.find(ap->get_decl(), terms)) {
|
||||
terms = alloc(ptr_vector<expr>);
|
||||
m_funs.insert(ap->get_decl(), terms);
|
||||
}
|
||||
terms->push_back(e);
|
||||
m_todo.append(ap->get_num_args(), ap->get_args());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_quantifier_instantiation::instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules) {
|
||||
rule_manager& rm = m_ctx.get_rule_manager();
|
||||
expr_ref fml(m), cnst(m);
|
||||
var_ref var(m);
|
||||
ptr_vector<sort> sorts;
|
||||
r.get_vars(sorts);
|
||||
m_uf.reset();
|
||||
m_terms.reset();
|
||||
m_var2cnst.reset();
|
||||
m_cnst2var.reset();
|
||||
fml = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
if (!sorts[i]) {
|
||||
sorts[i] = m.mk_bool_sort();
|
||||
}
|
||||
var = m.mk_var(i, sorts[i]);
|
||||
cnst = m.mk_fresh_const("C", sorts[i]);
|
||||
m_var2cnst.insert(var, cnst);
|
||||
m_cnst2var.insert(cnst, var);
|
||||
}
|
||||
|
||||
fml = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
m_var2cnst(fml);
|
||||
collect_egraph(fml);
|
||||
|
||||
for (unsigned i = 0; i < qs.size(); ++i) {
|
||||
instantiate_quantifier(qs[i].get(), conjs);
|
||||
}
|
||||
obj_map<func_decl, ptr_vector<expr>*>::iterator it = m_funs.begin(), end = m_funs.end();
|
||||
for (; it != end; ++it) {
|
||||
dealloc(it->m_value);
|
||||
}
|
||||
m_funs.reset();
|
||||
|
||||
fml = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
fml = m.mk_implies(fml, r.get_head());
|
||||
TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";);
|
||||
|
||||
rule_set added_rules(m_ctx);
|
||||
proof_ref pr(m);
|
||||
rm.mk_rule(fml, pr, added_rules);
|
||||
if (r.get_proof()) {
|
||||
// use def-axiom to encode that new rule is a weakening of the original.
|
||||
proof* p1 = r.get_proof();
|
||||
for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) {
|
||||
rule* r2 = added_rules.get_rule(i);
|
||||
r2->to_formula(fml);
|
||||
pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1);
|
||||
r2->set_proof(m, pr);
|
||||
}
|
||||
}
|
||||
rules.add_rules(added_rules);
|
||||
}
|
||||
|
||||
rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) {
|
||||
TRACE("dl", tout << m_ctx.get_params().instantiate_quantifiers() << "\n";);
|
||||
if (!m_ctx.get_params().instantiate_quantifiers()) {
|
||||
return 0;
|
||||
}
|
||||
bool has_quantifiers = false;
|
||||
unsigned sz = source.get_num_rules();
|
||||
for (unsigned i = 0; !has_quantifiers && i < sz; ++i) {
|
||||
rule& r = *source.get_rule(i);
|
||||
has_quantifiers = has_quantifiers || r.has_quantifiers();
|
||||
if (r.has_negation()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!has_quantifiers) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
expr_ref_vector conjs(m);
|
||||
quantifier_ref_vector qs(m);
|
||||
rule_set * result = alloc(rule_set, m_ctx);
|
||||
|
||||
bool instantiated = false;
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
rule * r = source.get_rule(i);
|
||||
extract_quantifiers(*r, conjs, qs);
|
||||
if (qs.empty()) {
|
||||
result->add_rule(r);
|
||||
}
|
||||
else {
|
||||
instantiate_rule(*r, conjs, qs, *result);
|
||||
instantiated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// model convertion: identity function.
|
||||
|
||||
if (instantiated) {
|
||||
result->inherit_predicates(source);
|
||||
}
|
||||
else {
|
||||
dealloc(result);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
136
src/muz/transforms/dl_mk_quantifier_instantiation.h
Normal file
136
src/muz/transforms/dl_mk_quantifier_instantiation.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_quantifier_instantiation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert Quantified Horn clauses into non-quantified clauses using
|
||||
instantiation.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan
|
||||
Andrey Rybalchenko
|
||||
Nikolaj Bjorner (nbjorner) 2013-04-02
|
||||
|
||||
Revision History:
|
||||
|
||||
Based on approach suggested in the SAS 2013 paper
|
||||
"On Solving Universally Quantified Horn Clauses"
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_QUANTIFIER_INSTANTIATION_H_
|
||||
#define _DL_MK_QUANTIFIER_INSTANTIATION_H_
|
||||
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"expr_safe_replace.h"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class context;
|
||||
|
||||
class mk_quantifier_instantiation : public rule_transformer::plugin {
|
||||
typedef svector<std::pair<expr*,expr*> > term_pairs;
|
||||
|
||||
class union_find {
|
||||
unsigned_vector m_find;
|
||||
unsigned_vector m_size;
|
||||
unsigned_vector m_next;
|
||||
|
||||
void ensure_size(unsigned v) {
|
||||
while (v >= get_num_vars()) {
|
||||
mk_var();
|
||||
}
|
||||
}
|
||||
public:
|
||||
unsigned mk_var() {
|
||||
unsigned r = m_find.size();
|
||||
m_find.push_back(r);
|
||||
m_size.push_back(1);
|
||||
m_next.push_back(r);
|
||||
return r;
|
||||
}
|
||||
unsigned get_num_vars() const { return m_find.size(); }
|
||||
|
||||
unsigned find(unsigned v) const {
|
||||
if (v >= get_num_vars()) {
|
||||
return v;
|
||||
}
|
||||
while (true) {
|
||||
unsigned new_v = m_find[v];
|
||||
if (new_v == v)
|
||||
return v;
|
||||
v = new_v;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned next(unsigned v) const {
|
||||
if (v >= get_num_vars()) {
|
||||
return v;
|
||||
}
|
||||
return m_next[v];
|
||||
}
|
||||
|
||||
bool is_root(unsigned v) const {
|
||||
return v >= get_num_vars() || m_find[v] == v;
|
||||
}
|
||||
|
||||
void merge(unsigned v1, unsigned v2) {
|
||||
unsigned r1 = find(v1);
|
||||
unsigned r2 = find(v2);
|
||||
if (r1 == r2)
|
||||
return;
|
||||
ensure_size(v1);
|
||||
ensure_size(v2);
|
||||
if (m_size[r1] > m_size[r2])
|
||||
std::swap(r1, r2);
|
||||
m_find[r1] = r2;
|
||||
m_size[r2] += m_size[r1];
|
||||
std::swap(m_next[r1], m_next[r2]);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_find.reset();
|
||||
m_next.reset();
|
||||
m_size.reset();
|
||||
}
|
||||
};
|
||||
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
expr_safe_replace m_var2cnst;
|
||||
expr_safe_replace m_cnst2var;
|
||||
union_find m_uf;
|
||||
ptr_vector<expr> m_todo;
|
||||
ptr_vector<expr> m_terms;
|
||||
ptr_vector<expr> m_binding;
|
||||
obj_map<func_decl, ptr_vector<expr>*> m_funs;
|
||||
|
||||
|
||||
void extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs);
|
||||
void collect_egraph(expr* e);
|
||||
void instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules);
|
||||
void instantiate_quantifier(quantifier* q, expr_ref_vector & conjs);
|
||||
void instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs);
|
||||
void match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs);
|
||||
void yield_binding(quantifier* q, expr_ref_vector& conjs);
|
||||
|
||||
public:
|
||||
mk_quantifier_instantiation(context & ctx, unsigned priority);
|
||||
|
||||
virtual ~mk_quantifier_instantiation();
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_QUANTIFIER_INSTANTIATION_H_ */
|
||||
|
896
src/muz/transforms/dl_mk_rule_inliner.cpp
Normal file
896
src/muz/transforms/dl_mk_rule_inliner.cpp
Normal file
|
@ -0,0 +1,896 @@
|
|||
/*++
|
||||
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)
|
||||
|
||||
Disable inliner for quantified rules 2012-10-31 (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 "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) {
|
||||
rule_counter& vc = m_rm.get_counter();
|
||||
unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_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(), tgt.name(), m_normalize);
|
||||
res->set_accounting_parent_object(m_context, const_cast<rule*>(&tgt));
|
||||
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_unif.display(tout << "subst:\n");
|
||||
);
|
||||
|
||||
if (m_normalize) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) {
|
||||
SASSERT(m_ready);
|
||||
expr_ref_vector result(m);
|
||||
ptr_vector<sort> sorts;
|
||||
expr_ref v(m), w(m);
|
||||
r.get_vars(sorts);
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
if (!sorts[i]) {
|
||||
sorts[i] = m.mk_bool_sort();
|
||||
}
|
||||
v = m.mk_var(i, sorts[i]);
|
||||
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());
|
||||
|
||||
SASSERT(!has_quantifier(src));
|
||||
|
||||
if (!m_unifier.unify_rules(tgt, tail_index, src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_unifier.apply(tgt, tail_index, src, res)) {
|
||||
if (m_context.generate_proof_trace()) {
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
|
||||
// TBD: replace by r.has_quantifiers() and test
|
||||
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)
|
||||
{
|
||||
rel_context_base* rel = m_context.get_rel_context();
|
||||
if (rel) {
|
||||
rel->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(rule_set const& source, func_decl * pred)
|
||||
{
|
||||
if (//these three conditions are important for soundness
|
||||
source.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(orig, 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(orig, 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(orig, 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(orig, 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(orig, *iit, m_inlined_rules);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; );
|
||||
}
|
||||
|
||||
bool mk_rule_inliner::transform_rule(rule_set const& orig, 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(orig, r->get_decl(i)); ++i) {};
|
||||
|
||||
SASSERT(!has_quantifier(*r.get()));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
datalog::del_rule(m_mc, *r0);
|
||||
}
|
||||
|
||||
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(orig, pred) && transform_rule(orig, r, tgt);
|
||||
}
|
||||
|
||||
if (something_done && m_mc) {
|
||||
for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) {
|
||||
if (inlining_allowed(orig, (*rit)->get_decl())) {
|
||||
datalog::del_rule(m_mc, **rit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
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) {
|
||||
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_set const& source, 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 (source.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))
|
||||
&& !source.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) {
|
||||
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(*rules, acc[i].get(), i);
|
||||
}
|
||||
|
||||
// initialize substitution.
|
||||
rule_counter& vc = m_rm.get_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);
|
||||
|
||||
bool allow_branching = m_context.get_params().inline_linear_branch();
|
||||
|
||||
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(*rules, 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_rule_var(*r.get()));
|
||||
m_subst.reserve_vars(max_var+1);
|
||||
|
||||
}
|
||||
}
|
||||
if (done_something) {
|
||||
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (valid.get(i)) {
|
||||
res->add_rule(acc[i].get());
|
||||
}
|
||||
}
|
||||
res->inherit_predicates(*rules);
|
||||
TRACE("dl", res->display(tout););
|
||||
rules = res.detach();
|
||||
}
|
||||
return done_something;
|
||||
}
|
||||
|
||||
rule_set * mk_rule_inliner::operator()(rule_set const & source) {
|
||||
|
||||
bool something_done = false;
|
||||
ref<horn_subsume_model_converter> hsmc;
|
||||
|
||||
if (source.get_num_rules() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rule_set::iterator end = source.end();
|
||||
for (rule_set::iterator it = source.begin(); it != end; ++ it) {
|
||||
if (has_quantifier(**it)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_context.get_model_converter()) {
|
||||
hsmc = alloc(horn_subsume_model_converter, m);
|
||||
}
|
||||
m_mc = hsmc.get();
|
||||
|
||||
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
|
||||
|
||||
if (m_context.get_params().inline_eager()) {
|
||||
TRACE("dl", source.display(tout << "before eager inlining\n"););
|
||||
plan_inlining(source);
|
||||
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;
|
||||
}
|
||||
TRACE("dl", res->display(tout << "after eager inlining\n"););
|
||||
}
|
||||
if (something_done) {
|
||||
res->inherit_predicates(source);
|
||||
}
|
||||
else {
|
||||
res = alloc(rule_set, source);
|
||||
}
|
||||
|
||||
if (m_context.get_params().inline_linear() && inline_linear(res)) {
|
||||
something_done = true;
|
||||
}
|
||||
|
||||
if (!something_done) {
|
||||
res = 0;
|
||||
}
|
||||
else {
|
||||
m_context.add_model_converter(hsmc.get());
|
||||
}
|
||||
|
||||
return res.detach();
|
||||
}
|
||||
|
||||
};
|
||||
|
205
src/muz/transforms/dl_mk_rule_inliner.h
Normal file
205
src/muz/transforms/dl_mk_rule_inliner.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*++
|
||||
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;
|
||||
bool m_normalize;
|
||||
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), m_normalize(true) {}
|
||||
|
||||
/** 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);
|
||||
|
||||
/**
|
||||
Control if bound variables are normalized after unification.
|
||||
The default is 'true': bound variables are re-mapped to an
|
||||
initial segment of de-Bruijn indices.
|
||||
*/
|
||||
void set_normalize(bool n) { m_normalize = n; }
|
||||
|
||||
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;
|
||||
func_decl_set m_forbidden_preds;
|
||||
func_decl_set m_preds_with_facts;
|
||||
func_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;
|
||||
|
||||
|
||||
//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(rule_set const& orig, 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_set const& orig, 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_set const& rule_set, 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_mc(0),
|
||||
m_unifier(ctx),
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */
|
||||
|
241
src/muz/transforms/dl_mk_scale.cpp
Normal file
241
src/muz/transforms/dl_mk_scale.cpp
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_scale.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_mk_scale.h"
|
||||
#include"dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_scale::scale_model_converter : public model_converter {
|
||||
ast_manager& m;
|
||||
func_decl_ref_vector m_trail;
|
||||
arith_util a;
|
||||
obj_map<func_decl, func_decl*> m_new2old;
|
||||
public:
|
||||
scale_model_converter(ast_manager& m): m(m), m_trail(m), a(m) {}
|
||||
|
||||
virtual ~scale_model_converter() {}
|
||||
|
||||
void add_new2old(func_decl* new_f, func_decl* old_f) {
|
||||
m_trail.push_back(old_f);
|
||||
m_trail.push_back(new_f);
|
||||
m_new2old.insert(new_f, old_f);
|
||||
}
|
||||
|
||||
virtual void operator()(model_ref& md) {
|
||||
model_ref old_model = alloc(model, m);
|
||||
obj_map<func_decl, func_decl*>::iterator it = m_new2old.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = m_new2old.end();
|
||||
for (; it != end; ++it) {
|
||||
func_decl* old_p = it->m_value;
|
||||
func_decl* new_p = it->m_key;
|
||||
func_interp* old_fi = alloc(func_interp, m, old_p->get_arity());
|
||||
|
||||
if (new_p->get_arity() == 0) {
|
||||
old_fi->set_else(md->get_const_interp(new_p));
|
||||
}
|
||||
else {
|
||||
func_interp* new_fi = md->get_func_interp(new_p);
|
||||
expr_ref_vector subst(m);
|
||||
var_subst vs(m, false);
|
||||
expr_ref tmp(m);
|
||||
|
||||
if (!new_fi) {
|
||||
TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";);
|
||||
dealloc(old_fi);
|
||||
continue;
|
||||
}
|
||||
for (unsigned i = 0; i < old_p->get_arity(); ++i) {
|
||||
subst.push_back(m.mk_var(i, old_p->get_domain(i)));
|
||||
}
|
||||
subst.push_back(a.mk_numeral(rational(1), a.mk_real()));
|
||||
|
||||
// Hedge that we don't have to handle the general case for models produced
|
||||
// by Horn clause solvers.
|
||||
SASSERT(!new_fi->is_partial() && new_fi->num_entries() == 0);
|
||||
vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp);
|
||||
old_fi->set_else(tmp);
|
||||
old_model->register_decl(old_p, old_fi);
|
||||
}
|
||||
}
|
||||
|
||||
// register values that have not been scaled.
|
||||
unsigned sz = md->get_num_constants();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
func_decl* c = md->get_constant(i);
|
||||
if (!m_new2old.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_new2old.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_scale::mk_scale(context & ctx, unsigned priority):
|
||||
plugin(priority),
|
||||
m(ctx.get_manager()),
|
||||
m_ctx(ctx),
|
||||
a(m),
|
||||
m_trail(m),
|
||||
m_eqs(m) {
|
||||
}
|
||||
|
||||
mk_scale::~mk_scale() {
|
||||
}
|
||||
|
||||
rule_set * mk_scale::operator()(rule_set const & source) {
|
||||
if (!m_ctx.get_params().scale()) {
|
||||
return 0;
|
||||
}
|
||||
rule_manager& rm = source.get_rule_manager();
|
||||
rule_set * result = alloc(rule_set, m_ctx);
|
||||
unsigned sz = source.get_num_rules();
|
||||
rule_ref new_rule(rm);
|
||||
app_ref_vector tail(m);
|
||||
app_ref head(m);
|
||||
svector<bool> neg;
|
||||
ptr_vector<sort> vars;
|
||||
ref<scale_model_converter> smc;
|
||||
if (m_ctx.get_model_converter()) {
|
||||
smc = alloc(scale_model_converter, m);
|
||||
}
|
||||
m_mc = smc.get();
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
rule & r = *source.get_rule(i);
|
||||
unsigned utsz = r.get_uninterpreted_tail_size();
|
||||
unsigned tsz = r.get_tail_size();
|
||||
tail.reset();
|
||||
vars.reset();
|
||||
m_cache.reset();
|
||||
m_trail.reset();
|
||||
m_eqs.reset();
|
||||
r.get_vars(vars);
|
||||
unsigned num_vars = vars.size();
|
||||
for (unsigned j = 0; j < utsz; ++j) {
|
||||
tail.push_back(mk_pred(num_vars, r.get_tail(j)));
|
||||
}
|
||||
for (unsigned j = utsz; j < tsz; ++j) {
|
||||
tail.push_back(mk_constraint(num_vars, r.get_tail(j)));
|
||||
}
|
||||
app_ref new_pred = mk_pred(num_vars, r.get_head());
|
||||
tail.append(m_eqs);
|
||||
tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false)));
|
||||
neg.resize(tail.size(), false);
|
||||
new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true);
|
||||
result->add_rule(new_rule);
|
||||
if (source.is_output_predicate(r.get_decl())) {
|
||||
result->set_output_predicate(new_rule->get_decl());
|
||||
}
|
||||
}
|
||||
TRACE("dl", result->display(tout););
|
||||
if (m_mc) {
|
||||
m_ctx.add_model_converter(m_mc);
|
||||
}
|
||||
m_trail.reset();
|
||||
m_cache.reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
app_ref mk_scale::mk_pred(unsigned sigma_idx, app* q) {
|
||||
func_decl* f = q->get_decl();
|
||||
ptr_vector<sort> domain(f->get_arity(), f->get_domain());
|
||||
domain.push_back(a.mk_real());
|
||||
func_decl_ref g(m);
|
||||
g = m.mk_func_decl(f->get_name(), f->get_arity() + 1, domain.c_ptr(), f->get_range());
|
||||
expr_ref_vector args(m);
|
||||
for (unsigned i = 0; i < q->get_num_args(); ++i) {
|
||||
expr* arg = q->get_arg(i);
|
||||
rational val;
|
||||
if (a.is_numeral(arg, val)) {
|
||||
if (val.is_zero()) {
|
||||
// arg is unchanged.
|
||||
}
|
||||
else if (val.is_one()) {
|
||||
arg = m.mk_var(sigma_idx, a.mk_real());
|
||||
}
|
||||
else {
|
||||
// create a fresh variable 'v', add 'v == sigma*arg'
|
||||
expr* v = m.mk_var(sigma_idx + 1 + m_eqs.size(), a.mk_real());
|
||||
m_eqs.push_back(m.mk_eq(v, a.mk_mul(arg, m.mk_var(sigma_idx, a.mk_real()))));
|
||||
arg = v;
|
||||
}
|
||||
}
|
||||
args.push_back(arg);
|
||||
}
|
||||
args.push_back(m.mk_var(sigma_idx, a.mk_real()));
|
||||
m_ctx.register_predicate(g, false);
|
||||
if (m_mc) {
|
||||
m_mc->add_new2old(g, f);
|
||||
}
|
||||
return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m);
|
||||
}
|
||||
|
||||
app_ref mk_scale::mk_constraint(unsigned sigma_idx, app* q) {
|
||||
expr* r = linearize(sigma_idx, q);
|
||||
SASSERT(is_app(r));
|
||||
return app_ref(to_app(r), m);
|
||||
}
|
||||
|
||||
expr* mk_scale::linearize(unsigned sigma_idx, expr* e) {
|
||||
expr* r;
|
||||
if (m_cache.find(e, r)) {
|
||||
return r;
|
||||
}
|
||||
if (!is_app(e)) {
|
||||
return e;
|
||||
}
|
||||
expr_ref result(m);
|
||||
app* ap = to_app(e);
|
||||
if (ap->get_family_id() == m.get_basic_family_id() ||
|
||||
a.is_add(e) || a.is_sub(e) ||
|
||||
a.is_le(e) || a.is_ge(e) ||
|
||||
a.is_lt(e) || a.is_gt(e)) {
|
||||
expr_ref_vector args(m);
|
||||
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
|
||||
args.push_back(linearize(sigma_idx, ap->get_arg(i)));
|
||||
}
|
||||
result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr());
|
||||
}
|
||||
else if (a.is_numeral(e)) {
|
||||
result = a.mk_mul(m.mk_var(sigma_idx, a.mk_real()), e);
|
||||
}
|
||||
else {
|
||||
result = e;
|
||||
}
|
||||
m_trail.push_back(result);
|
||||
m_cache.insert(e, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
53
src/muz/transforms/dl_mk_scale.h
Normal file
53
src/muz/transforms/dl_mk_scale.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_mk_scale.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Add scale factor to linear (Real) arithemetic Horn clauses.
|
||||
The transformation replaces occurrences of isolated constants by
|
||||
a scale multiplied to each constant.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-19
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_MK_SCALE_H_
|
||||
#define _DL_MK_SCALE_H_
|
||||
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class mk_scale : public rule_transformer::plugin {
|
||||
|
||||
class scale_model_converter;
|
||||
|
||||
ast_manager& m;
|
||||
context& m_ctx;
|
||||
arith_util a;
|
||||
expr_ref_vector m_trail;
|
||||
app_ref_vector m_eqs;
|
||||
obj_map<expr, expr*> m_cache;
|
||||
scale_model_converter* m_mc;
|
||||
|
||||
expr* linearize(unsigned num_vars, expr* e);
|
||||
app_ref mk_pred(unsigned num_vars, app* q);
|
||||
app_ref mk_constraint(unsigned num_vars, app* q);
|
||||
public:
|
||||
mk_scale(context & ctx, unsigned priority = 33039);
|
||||
virtual ~mk_scale();
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_SCALE_H_ */
|
||||
|
859
src/muz/transforms/dl_mk_slice.cpp
Normal file
859
src/muz/transforms/dl_mk_slice.cpp
Normal file
|
@ -0,0 +1,859 @@
|
|||
/*++
|
||||
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) {
|
||||
it->m_value->to_formula(fml);
|
||||
m_pinned_exprs.push_back(fml);
|
||||
TRACE("dl",
|
||||
tout << "orig: " << mk_pp(fml, m) << "\n";
|
||||
it->m_value->display(m_ctx, tout << "new:\n"););
|
||||
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;
|
||||
}
|
||||
proof_ref new_p(m);
|
||||
new_p = r->get_proof();
|
||||
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;
|
||||
if (!m_sliceform2rule.find(fact0, orig0)) {
|
||||
return false;
|
||||
}
|
||||
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 = 0;
|
||||
if (!m_sliceform2rule.find(fact1, orig1)) {
|
||||
return false;
|
||||
}
|
||||
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());
|
||||
TRACE("dl",
|
||||
tout << "orig: " << mk_pp(slice_concl, m) << "\n";
|
||||
r1->display(m_ctx, tout << "new:"););
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_slice::filter_unique_vars(rule& r) {
|
||||
//
|
||||
// Variables that occur in multiple uinterpreted predicates are not sliceable.
|
||||
//
|
||||
uint_set used_vars;
|
||||
for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) {
|
||||
app* p = r.get_tail(j);
|
||||
for (unsigned i = 0; i < p->get_num_args(); ++i) {
|
||||
expr* v = p->get_arg(i);
|
||||
if (is_var(v)) {
|
||||
unsigned vi = to_var(v)->get_idx();
|
||||
add_var(vi);
|
||||
if (used_vars.contains(vi)) {
|
||||
m_var_is_sliceable[vi] = false;
|
||||
}
|
||||
else {
|
||||
used_vars.insert(vi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) {
|
||||
expr_ref_vector conjs = get_tail_conjs(r);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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";);
|
||||
}
|
||||
}
|
||||
}
|
||||
filter_unique_vars(r);
|
||||
//
|
||||
// Collect the set of variables that are solved.
|
||||
// Collect the occurrence count of the variables per conjunct.
|
||||
//
|
||||
uint_set used_vars, parameter_vars;
|
||||
solve_vars(r, used_vars, parameter_vars);
|
||||
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));
|
||||
}
|
||||
qe::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(rule_set const& src, rule_set& dst) {
|
||||
obj_map<func_decl, bit_vector>::iterator it = m_sliceable.begin(), end = m_sliceable.end();
|
||||
ptr_vector<sort> domain;
|
||||
bool has_output = false;
|
||||
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);
|
||||
dst.inherit_predicate(src, p, f);
|
||||
if (m_mc) {
|
||||
m_mc->add_predicate(p, f);
|
||||
}
|
||||
}
|
||||
else if (src.is_output_predicate(p)) {
|
||||
dst.set_output_predicate(p);
|
||||
has_output = true;
|
||||
}
|
||||
}
|
||||
// disable slicing if the output predicates don't occur in rules.
|
||||
if (!has_output) {
|
||||
m_predicates.reset();
|
||||
}
|
||||
}
|
||||
|
||||
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_ref new_rule(rm);
|
||||
if (rule_updated(r)) {
|
||||
init_vars(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();
|
||||
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr* e = conjs[i].get();
|
||||
tail.push_back(to_app(e));
|
||||
}
|
||||
|
||||
new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) 0);
|
||||
|
||||
rm.fix_unbound_vars(new_rule, false);
|
||||
|
||||
TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n"););
|
||||
if (m_ctx.generate_proof_trace()) {
|
||||
rm.mk_rule_asserted_proof(*new_rule.get());
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_rule = &r;
|
||||
}
|
||||
dst.add_rule(new_rule.get());
|
||||
|
||||
if (m_pc) {
|
||||
m_pc->insert(&r, new_rule.get(), 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) {
|
||||
for (unsigned i = 0; i < src.get_num_rules(); ++i) {
|
||||
if (src.get_rule(i)->has_quantifiers()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ref<slice_proof_converter> spc;
|
||||
ref<slice_model_converter> smc;
|
||||
if (m_ctx.generate_proof_trace()) {
|
||||
spc = alloc(slice_proof_converter, m_ctx);
|
||||
}
|
||||
if (m_ctx.get_model_converter()) {
|
||||
smc = alloc(slice_model_converter, *this, m);
|
||||
}
|
||||
m_pc = spc.get();
|
||||
m_mc = smc.get();
|
||||
reset();
|
||||
saturate(src);
|
||||
rule_set* result = alloc(rule_set, m_ctx);
|
||||
declare_predicates(src, *result);
|
||||
if (m_predicates.empty()) {
|
||||
// nothing could be sliced.
|
||||
dealloc(result);
|
||||
return 0;
|
||||
}
|
||||
TRACE("dl", display(tout););
|
||||
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);
|
||||
}
|
||||
}
|
||||
m_ctx.add_proof_converter(spc.get());
|
||||
m_ctx.add_model_converter(smc.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
115
src/muz/transforms/dl_mk_slice.h
Normal file
115
src/muz/transforms/dl_mk_slice.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*++
|
||||
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(rule_set const& src, rule_set& dst);
|
||||
|
||||
bool rule_updated(rule const& r);
|
||||
|
||||
void update_predicate(app* p, app_ref& q);
|
||||
|
||||
void filter_unique_vars(rule& r);
|
||||
|
||||
void solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars);
|
||||
|
||||
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);
|
||||
|
||||
virtual ~mk_slice() { }
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
|
||||
func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; }
|
||||
|
||||
obj_map<func_decl, func_decl*> const& get_predicates() { return m_predicates; }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_SLICE_H_ */
|
||||
|
364
src/muz/transforms/dl_mk_subsumption_checker.cpp
Normal file
364
src/muz/transforms/dl_mk_subsumption_checker.cpp
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*++
|
||||
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"
|
||||
|
||||
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_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_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);
|
||||
m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *res.get());
|
||||
|
||||
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_decl();
|
||||
|
||||
if(m_total_relations.contains(head_pred)) {
|
||||
if(!orig.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;
|
||||
VERIFY(m_total_relation_defining_rules.find(head_pred, defining_rule));
|
||||
if (defining_rule) {
|
||||
rule_ref totality_rule(m_context.get_rule_manager());
|
||||
VERIFY(transform_rule(defining_rule, subs_index, totality_rule));
|
||||
if(defining_rule!=totality_rule) {
|
||||
modified = true;
|
||||
}
|
||||
tgt.add_rule(totality_rule);
|
||||
SASSERT(totality_rule->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);
|
||||
}
|
||||
tgt.inherit_predicates(orig);
|
||||
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(rule_set const& source) {
|
||||
rel_context_base* rel = m_context.get_rel_context();
|
||||
if (!rel) {
|
||||
return;
|
||||
}
|
||||
relation_manager& rm = rel->get_rmanager();
|
||||
|
||||
func_decl_set const& candidate_preds = m_context.get_predicates();
|
||||
|
||||
func_decl_set::iterator end = candidate_preds.end();
|
||||
for(func_decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) {
|
||||
func_decl * pred = *it;
|
||||
unsigned rel_sz;
|
||||
|
||||
if (m_total_relations.contains(pred)) { continue; } // already total
|
||||
if (!rel->try_get_size(pred, rel_sz)) { 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
|
||||
|
||||
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_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>));
|
||||
}
|
||||
m_ground_unconditional_rule_heads.find(pred)->insert(head);
|
||||
|
||||
next_rule:;
|
||||
}
|
||||
}
|
||||
|
||||
rule_set * mk_subsumption_checker::operator()(rule_set const & source) {
|
||||
// TODO mc
|
||||
|
||||
m_have_new_total_rule = false;
|
||||
collect_ground_unconditional_rule_heads(source);
|
||||
scan_for_relations_total_due_to_facts(source);
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
93
src/muz/transforms/dl_mk_subsumption_checker.h
Normal file
93
src/muz/transforms/dl_mk_subsumption_checker.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*++
|
||||
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(rule_set const& rules);
|
||||
void scan_for_relations_total_due_to_facts(rule_set const& rules);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_SUBSUMPTION_CHECKER_H_ */
|
||||
|
383
src/muz/transforms/dl_mk_unbound_compressor.cpp
Normal file
383
src/muz/transforms/dl_mk_unbound_compressor.cpp
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*++
|
||||
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(ctx.get_manager()),
|
||||
rm(ctx.get_rule_manager()),
|
||||
m_rules(rm),
|
||||
m_pinned(m) {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return rm.collect_tail_vars(r).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(rule_set const& source, unsigned rule_index) {
|
||||
rule * r = m_rules.get(rule_index);
|
||||
var_idx_set& tail_vars = rm.collect_tail_vars(r);
|
||||
|
||||
app * head = r->get_head();
|
||||
func_decl * head_pred = head->get_decl();
|
||||
if (source.is_output_predicate(head_pred)) {
|
||||
//we don't compress output predicates
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned n = head_pred->get_arity();
|
||||
|
||||
rm.get_counter().reset();
|
||||
rm.get_counter().count_vars(m, 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 = rm.get_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(rule_set const& source, unsigned rule_index) {
|
||||
start:
|
||||
rule * r = m_rules.get(rule_index);
|
||||
var_idx_set& tail_vars = rm.collect_tail_vars(r);
|
||||
|
||||
app * head = r->get_head();
|
||||
func_decl * head_pred = head->get_decl();
|
||||
unsigned head_arity = head_pred->get_arity();
|
||||
|
||||
rm.get_counter().reset();
|
||||
rm.get_counter().count_vars(m, 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 = rm.get_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.mk_app(cpred, head_arity-1, cargs.c_ptr()), m);
|
||||
|
||||
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_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_decl());
|
||||
m_rules.set(rule_index, new_rule);
|
||||
m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl());
|
||||
detect_tasks(source, 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.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m);
|
||||
|
||||
svector<bool> tails_negated;
|
||||
app_ref_vector tails(m);
|
||||
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_set const& source, 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_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get());
|
||||
m_head_occurrence_ctr.inc(new_rule->get_decl());
|
||||
|
||||
|
||||
detect_tasks(source, 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(rule_set const& source, 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(source, rule_index);
|
||||
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
void mk_unbound_compressor::add_decompression_rules(rule_set const& source, 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(source, rule_index, tail_index, arg_index);
|
||||
orig_rule_replaced = true;
|
||||
}
|
||||
else {
|
||||
add_decompression_rule(source, 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) {
|
||||
// TODO mc
|
||||
m_modified = false;
|
||||
|
||||
rel_context_base* rel = m_context.get_rel_context();
|
||||
if (rel) {
|
||||
rel->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_decl());
|
||||
}
|
||||
|
||||
for (unsigned i=0; i<init_rule_cnt; i++) {
|
||||
detect_tasks(source, 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(source, rule_index); //m_rules.size() can change here
|
||||
if (rule_index<m_rules.size()) {
|
||||
add_decompression_rules(source, 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));
|
||||
}
|
||||
result->inherit_predicates(source);
|
||||
}
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
};
|
92
src/muz/transforms/dl_mk_unbound_compressor.h
Normal file
92
src/muz/transforms/dl_mk_unbound_compressor.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*++
|
||||
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;
|
||||
rule_manager & rm;
|
||||
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
|
||||
*/
|
||||
func_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(rule_set const& source, unsigned rule_index);
|
||||
void add_task(func_decl * pred, unsigned arg_index);
|
||||
void try_compress(rule_set const& source, unsigned rule_index);
|
||||
void add_decompression_rules(rule_set const& source, unsigned rule_index);
|
||||
void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res);
|
||||
void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index);
|
||||
void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index);
|
||||
void reset();
|
||||
public:
|
||||
mk_unbound_compressor(context & ctx);
|
||||
|
||||
rule_set * operator()(rule_set const & source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_UNBOUND_COMPRESSOR_H_ */
|
||||
|
64
src/muz/transforms/dl_mk_unfold.cpp
Normal file
64
src/muz/transforms/dl_mk_unfold.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*++
|
||||
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(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) {
|
||||
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);
|
||||
}
|
||||
rules->inherit_predicates(source);
|
||||
return rules;
|
||||
}
|
||||
|
||||
};
|
||||
|
53
src/muz/transforms/dl_mk_unfold.h
Normal file
53
src/muz/transforms/dl_mk_unfold.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*++
|
||||
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;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_MK_UNFOLD_H_ */
|
||||
|
81
src/muz/transforms/dl_transforms.cpp
Normal file
81
src/muz/transforms/dl_transforms.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_transforms.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Default transformations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-28.
|
||||
|
||||
Revision History:
|
||||
|
||||
Extracted from dl_context
|
||||
|
||||
--*/
|
||||
|
||||
#include"dl_transforms.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"dl_mk_coi_filter.h"
|
||||
#include"dl_mk_filter_rules.h"
|
||||
#include"dl_mk_interp_tail_simplifier.h"
|
||||
#include"dl_mk_rule_inliner.h"
|
||||
#include"dl_mk_bit_blast.h"
|
||||
#include"dl_mk_array_blast.h"
|
||||
#include"dl_mk_karr_invariants.h"
|
||||
#include"dl_mk_magic_symbolic.h"
|
||||
#include"dl_mk_quantifier_abstraction.h"
|
||||
#include"dl_mk_quantifier_instantiation.h"
|
||||
#include"dl_mk_subsumption_checker.h"
|
||||
#include"dl_mk_scale.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
void apply_default_transformation(context& ctx) {
|
||||
rule_transformer transf(ctx);
|
||||
ctx.ensure_closed();
|
||||
transf.reset();
|
||||
transf.register_plugin(alloc(datalog::mk_coi_filter, ctx));
|
||||
transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx));
|
||||
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005));
|
||||
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000));
|
||||
transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990));
|
||||
transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34980));
|
||||
|
||||
//and another round of inlining
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975));
|
||||
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970));
|
||||
transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960));
|
||||
transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950));
|
||||
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940));
|
||||
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930));
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920));
|
||||
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910));
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900));
|
||||
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890));
|
||||
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880));
|
||||
|
||||
|
||||
if (ctx.get_params().quantify_arrays()) {
|
||||
transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 33000));
|
||||
transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 32500));
|
||||
}
|
||||
transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 32000));
|
||||
|
||||
transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000));
|
||||
transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000));
|
||||
transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010));
|
||||
if (ctx.get_params().magic()) {
|
||||
transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020));
|
||||
}
|
||||
transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030));
|
||||
ctx.transform_rules(transf);
|
||||
}
|
||||
}
|
30
src/muz/transforms/dl_transforms.h
Normal file
30
src/muz/transforms/dl_transforms.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_transforms.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Default transformations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-28.
|
||||
|
||||
Revision History:
|
||||
|
||||
Extracted from dl_context
|
||||
|
||||
--*/
|
||||
#ifndef _DL_TRANSFORMS_H_
|
||||
#define _DL_TRANSFORMS_H_
|
||||
|
||||
#include "dl_context.h"
|
||||
|
||||
namespace datalog {
|
||||
void apply_default_transformation(context& ctx);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue