3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-18 20:03:38 +00:00

checkpoint

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

40
src/api_headers/z3.h Normal file
View file

@ -0,0 +1,40 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
z3.h
Abstract:
Z3 API.
Author:
Nikolaj Bjorner (nbjorner)
Leonardo de Moura (leonardo) 2007-06-8
Notes:
--*/
#ifndef _Z3__H_
#define _Z3__H_
#include<stdio.h>
#include"z3_macros.h"
#include"z3_api.h"
#include"z3_internal_types.h"
#undef __in
#undef __out
#undef __inout
#undef __in_z
#undef __out_z
#undef __ecount
#undef __in_ecount
#undef __out_ecount
#undef __inout_ecount
#endif

7563
src/api_headers/z3_api.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
z3_internal_types.h
Abstract:
Z3 internal API type declarations.
Author:
Leonardo de Moura (leonardo) 2012-10-18
Notes:
--*/
#ifndef _Z3_INTERNAL_TYPES_H_
#define _Z3_INTERNAL_TYPES_H_
DEFINE_TYPE(Z3_polynomial_manager);
DEFINE_TYPE(Z3_polynomial);
DEFINE_TYPE(Z3_monomial);
/*
Definitions for update_api.py
def_Type('POLYNOMIAL_MANAGER', 'Z3_polynomial_manager', 'PolynomialManagerObj')
def_Type('POLYNOMIAL', 'Z3_polynomial', 'PolynomialObj')
def_Type('MONOMIAL', 'Z3_monomial', 'MonomialObj')
*/
#endif

View file

@ -0,0 +1,54 @@
#ifndef __in
#define __in
#endif
#ifndef __out
#define __out
#endif
#ifndef __out_opt
#define __out_opt __out
#endif
#ifndef __ecount
#define __ecount(num_args)
#endif
#ifndef __in_ecount
#define __in_ecount(num_args) __in __ecount(num_args)
#endif
#ifndef __out_ecount
#define __out_ecount(num_args) __out __ecount(num_args)
#endif
#ifndef __inout_ecount
#define __inout_ecount(num_args) __in __out __ecount(num_args)
#endif
#ifndef __inout
#define __inout __in __out
#endif
#ifndef Z3_bool_opt
#define Z3_bool_opt Z3_bool
#endif
#ifndef Z3_API
#define Z3_API
#endif
#ifndef DEFINE_TYPE
#define DEFINE_TYPE(T) typedef struct _ ## T *T
#endif
#ifndef DEFINE_VOID
#define DEFINE_VOID(T) typedef void* T
#endif
#ifndef BEGIN_MLAPI_EXCLUDE
#define BEGIN_MLAPI_EXCLUDE
#endif
#ifndef END_MLAPI_EXCLUDE
#define END_MLAPI_EXCLUDE
#endif

64
src/api_headers/z3_v1.h Normal file
View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
z3_v1.h
Abstract:
Z3 1.x backwards compatibility macros.
These macros are used to simulate the Z3 API using in the 1.x versions.
This file should only be used by users still using the Z3 1.x API.
Author:
Leonardo de Moura (leonardo) 2011-09-22
Notes:
--*/
#ifndef _Z3_V1_H_
#define _Z3_V1_H_
#include"z3.h"
// Backwards compatibility
#define Z3_type_ast Z3_sort
#define Z3_const_decl_ast Z3_func_decl
#define Z3_const Z3_app
#define Z3_pattern_ast Z3_pattern
#define Z3_UNINTERPRETED_TYPE Z3_UNINTERPRETED_SORT
#define Z3_BOOL_TYPE Z3_BOOL_SORT
#define Z3_INT_TYPE Z3_INT_SORT
#define Z3_REAL_TYPE Z3_REAL_SORT
#define Z3_BV_TYPE Z3_BV_SORT
#define Z3_ARRAY_TYPE Z3_ARRAY_SORT
#define Z3_TUPLE_TYPE Z3_DATATYPE_SORT
#define Z3_UNKNOWN_TYPE Z3_UNKNOWN_SORT
#define Z3_CONST_DECL_AST Z3_FUNC_DECL_AST
#define Z3_TYPE_AST Z3_SORT_AST
#define Z3_SORT_ERROR Z3_TYPE_ERROR
#define Z3_mk_uninterpreted_type Z3_mk_uninterpreted_sort
#define Z3_mk_bool_type Z3_mk_bool_sort
#define Z3_mk_int_type Z3_mk_int_sort
#define Z3_mk_real_type Z3_mk_real_sort
#define Z3_mk_bv_type Z3_mk_bv_sort
#define Z3_mk_array_type Z3_mk_array_sort
#define Z3_mk_tuple_type Z3_mk_tuple_sort
#define Z3_get_type Z3_get_sort
#define Z3_get_pattern_ast Z3_get_pattern
#define Z3_get_type_kind Z3_get_sort_kind
#define Z3_get_type_name Z3_get_sort_name
#define Z3_get_bv_type_size Z3_get_bv_sort_size
#define Z3_get_array_type_domain Z3_get_array_sort_domain
#define Z3_get_array_type_range Z3_get_array_sort_range
#define Z3_get_tuple_type_num_fields Z3_get_tuple_sort_num_fields
#define Z3_get_tuple_type_field_decl Z3_get_tuple_sort_field_decl
#define Z3_get_tuple_type_mk_decl Z3_get_tuple_sort_mk_decl
#define Z3_to_const_ast Z3_to_app
#define Z3_get_numeral_value_string Z3_get_numeral_string
#define Z3_get_const_ast_decl Z3_get_app_decl
#define Z3_get_value Z3_eval_func_decl
#endif

77
src/ast/expr2var.cpp Normal file
View file

@ -0,0 +1,77 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
expr2var.h
Abstract:
The mapping between Z3 expressions and (low level) variables.
Example of low level variables:
- SAT solver
- Polynomial
- etc.
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#include"expr2var.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
void expr2var::insert(expr * n, var v) {
if (!is_uninterp_const(n)) {
TRACE("expr2var", tout << "interpreted:\n" << mk_ismt2_pp(n, m()) << "\n";);
m_interpreted_vars = true;
}
m().inc_ref(n);
m_mapping.insert(n, v);
m_recent_exprs.push_back(n);
}
expr2var::expr2var(ast_manager & m):
m_manager(m),
m_interpreted_vars(false) {
}
expr2var::~expr2var() {
dec_ref_map_keys(m(), m_mapping);
}
expr2var::var expr2var::to_var(expr * n) const {
var v = UINT_MAX;
m_mapping.find(n, v);
return v;
}
void expr2var::display(std::ostream & out) const {
obj_map<expr, var>::iterator it = m_mapping.begin();
obj_map<expr, var>::iterator end = m_mapping.end();
for (; it != end; ++it) {
out << mk_ismt2_pp(it->m_key, m()) << " -> " << it->m_value << "\n";
}
}
void expr2var::mk_inv(expr_ref_vector & var2expr) const {
obj_map<expr, var>::iterator it = m_mapping.begin();
obj_map<expr, var>::iterator end = m_mapping.end();
for (; it != end; ++it) {
expr * t = it->m_key;
var x = it->m_value;
if (x >= var2expr.size())
var2expr.resize(x+1, 0);
var2expr.set(x, t);
}
}
void expr2var::reset() {
dec_ref_map_keys(m(), m_mapping);
SASSERT(m_mapping.empty());
m_recent_exprs.reset();
m_interpreted_vars = false;
}

75
src/ast/expr2var.h Normal file
View file

@ -0,0 +1,75 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
expr2var.h
Abstract:
The mapping between Z3 expressions and (low level) variables.
Example of low level variables:
- SAT solver
- Polynomial
- etc.
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#ifndef _EXPR2VAR_H_
#define _EXPR2VAR_H_
#include"ast.h"
#include"obj_hashtable.h"
/**
\brief The mapping between Z3 expressions and (low level) variables.
*/
class expr2var {
public:
typedef unsigned var;
typedef obj_map<expr, var> expr2var_mapping;
typedef expr2var_mapping::iterator iterator;
typedef ptr_vector<expr>::const_iterator recent_iterator;
protected:
ast_manager & m_manager;
expr2var_mapping m_mapping;
ptr_vector<expr> m_recent_exprs;
bool m_interpreted_vars;
public:
expr2var(ast_manager & m);
~expr2var();
ast_manager & m() const { return m_manager; }
void insert(expr * n, var v);
var to_var(expr * n) const;
bool is_var(expr * n) const { return m_mapping.contains(n); }
void display(std::ostream & out) const;
void mk_inv(expr_ref_vector & var2expr) const;
// return true if the mapping contains interpreted vars.
bool interpreted_vars() const { return m_interpreted_vars; }
iterator begin() const { return m_mapping.begin(); }
iterator end() const { return m_mapping.end(); }
void reset_recent() { m_recent_exprs.reset(); }
// Iterators for traversing the recently registered expressions.
// The set of recent registered expressions is reset by using reset_recent().
recent_iterator begin_recent() const { return m_recent_exprs.begin(); }
recent_iterator end_recent() const { return m_recent_exprs.end(); }
void reset();
};
#endif

97
src/ast/expr_abstract.cpp Normal file
View file

@ -0,0 +1,97 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
expr_abstract.h
Abstract:
Abstract occurrences of constants to bound variables.
Author:
Nikolaj Bjorner (nbjorner) 2008-03-08
Notes:
--*/
#include "expr_abstract.h"
#include "map.h"
void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) {
ast_ref_vector pinned(m);
ptr_vector<expr> stack;
obj_map<expr, expr*> map;
expr * curr = 0, *b = 0;
SASSERT(n->get_ref_count() > 0);
stack.push_back(n);
for (unsigned i = 0; i < num_bound; ++i) {
b = bound[i];
expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b));
pinned.push_back(v);
map.insert(b, v);
}
while(!stack.empty()) {
curr = stack.back();
if (map.contains(curr)) {
stack.pop_back();
continue;
}
switch(curr->get_kind()) {
case AST_VAR: {
map.insert(curr, curr);
stack.pop_back();
break;
}
case AST_APP: {
app* a = to_app(curr);
bool all_visited = true;
ptr_vector<expr> args;
for (unsigned i = 0; i < a->get_num_args(); ++i) {
if (!map.find(a->get_arg(i), b)) {
stack.push_back(a->get_arg(i));
all_visited = false;
}
else {
args.push_back(b);
}
}
if (all_visited) {
b = m.mk_app(a->get_decl(), args.size(), args.c_ptr());
pinned.push_back(b);
map.insert(curr, b);
stack.pop_back();
}
break;
}
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(curr);
expr_ref_buffer patterns(m);
expr_ref result1(m);
unsigned new_base = base + q->get_num_decls();
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
expr_abstract(m, new_base, num_bound, bound, q->get_pattern(i), result1);
patterns.push_back(result1.get());
}
expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1);
b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get());
pinned.push_back(b);
map.insert(curr, b);
stack.pop_back();
break;
}
default:
UNREACHABLE();
}
}
if (!map.find(n, b)) {
UNREACHABLE();
}
result = b;
}

28
src/ast/expr_abstract.h Normal file
View file

@ -0,0 +1,28 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
expr_abstract.h
Abstract:
Abstract occurrences of constants to bound variables.
Author:
Nikolaj Bjorner (nbjorner) 2008-03-08
Notes:
--*/
#ifndef _EXPR_ABSTRACT_H_
#define _EXPR_ABSTRACT_H_
#include"ast.h"
void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result);
#endif

142
src/ast/expr_functors.cpp Normal file
View file

@ -0,0 +1,142 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
expr_functors.cpp
Abstract:
Functors on expressions.
Author:
Nikolaj Bjorner (nbjorner) 2010-02-19
Revision History:
Hoisted from quant_elim.
--*/
#include "expr_functors.h"
// ----------
// check_pred
bool check_pred::operator()(expr* e) {
if (!m_visited.is_marked(e)) {
m_refs.push_back(e);
visit(e);
}
SASSERT(m_visited.is_marked(e));
return m_pred_holds.is_marked(e);
}
void check_pred::visit(expr* e) {
ptr_vector<expr> todo;
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
if (m_pred(e)) {
m_pred_holds.mark(e, true);
}
if (m_visited.is_marked(e)) {
todo.pop_back();
continue;
}
switch(e->get_kind()) {
case AST_APP: {
app* a = to_app(e);
bool all_visited = true;
unsigned num_args = a->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
expr* arg = a->get_arg(i);
if (!m_visited.is_marked(arg)) {
todo.push_back(arg);
all_visited = false;
}
else if (m_pred_holds.is_marked(arg)) {
m_pred_holds.mark(e, true);
}
}
if (all_visited) {
m_visited.mark(e, true);
todo.pop_back();
}
break;
}
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(e);
expr* arg = q->get_expr();
if (m_visited.is_marked(arg)) {
todo.pop_back();
if (m_pred_holds.is_marked(arg)) {
m_pred_holds.mark(e, true);
}
m_visited.mark(e, true);
}
else {
todo.push_back(arg);
}
break;
}
case AST_VAR:
todo.pop_back();
m_visited.mark(e, true);
break;
default:
UNREACHABLE();
break;
}
}
}
// ------------
// contains_app
bool contains_app::operator()(unsigned size, expr* const* es) {
for (unsigned i = 0; i < size; ++i) {
if ((*this)(es[i])) {
return true;
}
}
return false;
}
// -----------
// map_proc
void map_proc::reconstruct(app* a) {
m_args.reset();
bool is_new = false;
for (unsigned i = 0; i < a->get_num_args(); ++i) {
expr* e1 = a->get_arg(i);
expr* e2 = get_expr(e1);
m_args.push_back(e2);
if (e1 != e2) {
is_new = true;
}
}
if (is_new) {
expr* b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr());
m_map.insert(a, b, 0);
}
else {
m_map.insert(a, a, 0);
}
}
void map_proc::visit(quantifier* e) {
expr_ref q(m);
q = m.update_quantifier(e, get_expr(e->get_expr()));
m_map.insert(e, q, 0);
}
expr* map_proc::get_expr(expr* e) {
expr* result = 0;
proof* p = 0;
m_map.get(e, result, p);
return result;
}

121
src/ast/expr_functors.h Normal file
View file

@ -0,0 +1,121 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
expr_functors.h
Abstract:
Functors on expressions.
Author:
Nikolaj Bjorner (nbjorner) 2010-02-19
Revision History:
Hoisted from quant_elim.
--*/
#ifndef __EXPR_FUNCTORS_H__
#define __EXPR_FUNCTORS_H__
#include "ast.h"
#include "expr_map.h"
class i_expr_pred {
public:
virtual bool operator()(expr* e) = 0;
virtual ~i_expr_pred() {}
};
/**
\brief Memoizing predicate functor on sub-expressions.
The class is initialized with a predicate 'p' on expressions.
The class is memoizing.
*/
class check_pred {
i_expr_pred& m_pred;
ast_mark m_pred_holds;
ast_mark m_visited;
expr_ref_vector m_refs;
public:
check_pred(i_expr_pred& p, ast_manager& m) : m_pred(p), m_refs(m) {}
bool operator()(expr* e);
void reset() { m_pred_holds.reset(); m_visited.reset(); m_refs.reset(); }
private:
void visit(expr* e);
};
/**
\brief Determine if expression 'e' or vector of expressions 'v' contains the app x
*/
class contains_app {
class pred : public i_expr_pred {
app* m_x;
public:
pred(app* x) : m_x(x) {}
virtual bool operator()(expr* e) {
return m_x == e;
}
};
app_ref m_x;
pred m_pred;
check_pred m_check;
public:
contains_app(ast_manager& m, app* x) :
m_x(x, m), m_pred(x), m_check(m_pred, m) {}
bool operator()(expr* e) {
return m_check(e);
}
bool operator()(expr_ref_vector const& v) {
return (*this)(v.size(), v.c_ptr());
}
bool operator()(unsigned size, expr* const* es);
app* x() const { return m_x; }
};
/**
\brief Base class of functor that applies map to expressions.
*/
class map_proc {
protected:
ast_manager& m;
expr_map m_map;
ptr_vector<expr> m_args;
public:
map_proc(ast_manager& m):
m(m),
m_map(m)
{}
void reset() { m_map.reset(); }
void visit(var* e) { m_map.insert(e, e, 0); }
void visit(quantifier* e);
void reconstruct(app* a);
expr* get_expr(expr* e);
};
#endif

604
src/ast/static_features.cpp Normal file
View file

@ -0,0 +1,604 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
static_features.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-16.
Revision History:
--*/
#include"static_features.h"
#include"ast_pp.h"
static_features::static_features(ast_manager & m):
m_manager(m),
m_autil(m),
m_bfid(m.get_basic_family_id()),
m_afid(m.get_family_id("arith")),
m_lfid(m.get_family_id("label")),
m_label_sym("label"),
m_pattern_sym("pattern"),
m_expr_list_sym("expr-list") {
reset();
}
void static_features::reset() {
m_already_visited .reset();
m_cnf = true;
m_num_exprs = 0;
m_num_roots = 0;
m_max_depth = 0;
m_num_quantifiers = 0;
m_num_quantifiers_with_patterns = 0;
m_num_quantifiers_with_multi_patterns = 0;
m_num_clauses = 0;
m_num_bin_clauses = 0;
m_num_units = 0;
m_sum_clause_size = 0;
m_num_nested_formulas = 0;
m_num_bool_exprs = 0;
m_num_bool_constants = 0;
m_num_formula_trees = 0;
m_max_formula_depth = 0;
m_sum_formula_depth = 0;
m_num_or_and_trees = 0;
m_max_or_and_tree_depth = 0;
m_sum_or_and_tree_depth = 0;
m_num_ite_trees = 0;
m_max_ite_tree_depth = 0;
m_sum_ite_tree_depth = 0;
m_num_ors = 0;
m_num_ands = 0;
m_num_iffs = 0;
m_num_ite_formulas = 0;
m_num_ite_terms = 0;
m_num_sharing = 0;
m_num_interpreted_exprs = 0;
m_num_uninterpreted_exprs = 0;
m_num_interpreted_constants = 0;
m_num_uninterpreted_constants = 0;
m_num_uninterpreted_functions = 0;
m_num_eqs = 0;
m_has_rational = false;
m_has_int = false;
m_has_real = false;
m_arith_k_sum .reset();
m_num_arith_terms = 0;
m_num_arith_eqs = 0;
m_num_arith_ineqs = 0;
m_num_diff_terms = 0;
m_num_diff_eqs = 0;
m_num_diff_ineqs = 0;
m_num_simple_eqs = 0;
m_num_simple_ineqs = 0;
m_num_non_linear = 0;
m_num_apps .reset();
m_num_theory_terms .reset();
m_num_theory_atoms .reset();
m_num_theory_constants .reset();
m_num_theory_eqs .reset();
m_num_aliens = 0;
m_num_aliens_per_family .reset();
m_num_theories = 0;
m_theories .reset();
m_max_stack_depth = 500;
flush_cache();
}
void static_features::flush_cache() {
m_expr2depth.reset();
m_expr2or_and_depth.reset();
m_expr2ite_depth.reset();
m_expr2formula_depth.reset();
}
#if 0
bool static_features::is_non_linear(expr * e) const {
if (!is_arith_expr(e))
return false;
if (is_numeral(e))
return true;
if (m_autil.is_add(e))
return true; // the non
}
#endif
bool static_features::is_diff_term(expr const * e, rational & r) const {
// lhs can be 'x' or '(+ k x)'
if (!is_arith_expr(e)) {
r.reset();
return true;
}
if (is_numeral(e, r))
return true;
return m_autil.is_add(e) && to_app(e)->get_num_args() == 2 && is_numeral(to_app(e)->get_arg(0), r) && !is_arith_expr(to_app(e)->get_arg(1));
}
bool static_features::is_diff_atom(expr const * e) const {
if (!is_bool(e))
return false;
if (!m_manager.is_eq(e) && !is_arith_expr(e))
return false;
SASSERT(to_app(e)->get_num_args() == 2);
expr * lhs = to_app(e)->get_arg(0);
expr * rhs = to_app(e)->get_arg(1);
if (!is_arith_expr(lhs) && !is_arith_expr(rhs))
return true;
if (!is_numeral(rhs))
return false;
// lhs can be 'x' or '(+ x (* -1 y))'
if (!is_arith_expr(lhs))
return true;
expr* arg1, *arg2;
if (!m_autil.is_add(lhs, arg1, arg2))
return false;
// x
if (is_arith_expr(arg1))
return false;
// arg2: (* -1 y)
expr* m1, *m2;
return m_autil.is_mul(arg2, m1, m2) && is_minus_one(m1) && !is_arith_expr(m2);
}
bool static_features::is_gate(expr const * e) const {
if (is_basic_expr(e)) {
switch (to_app(e)->get_decl_kind()) {
case OP_ITE: case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_IMPLIES:
return true;
}
}
return false;
}
void static_features::update_core(expr * e) {
m_num_exprs++;
// even if a benchmark does not contain any theory interpreted function decls, we still have to install
// the theory if the benchmark contains constants or function applications of an interpreted sort.
sort * s = m_manager.get_sort(e);
mark_theory(s->get_family_id());
bool _is_gate = is_gate(e);
bool _is_eq = m_manager.is_eq(e);
if (_is_gate) {
m_cnf = false;
m_num_nested_formulas++;
switch (to_app(e)->get_decl_kind()) {
case OP_ITE:
if (is_bool(e))
m_num_ite_formulas++;
else {
m_num_ite_terms++;
// process then&else nodes
for (unsigned i = 1; i < 3; i++) {
expr * arg = to_app(e)->get_arg(i);
acc_num(arg);
// Must check whether arg is diff logic or not.
// Otherwise, problem can be incorrectly tagged as diff logic.
sort * arg_s = m_manager.get_sort(arg);
family_id fid_arg = arg_s->get_family_id();
if (fid_arg == m_afid) {
m_num_arith_terms++;
rational k;
TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m_manager) << "\n";);
if (is_diff_term(arg, k)) {
m_num_diff_terms++;
acc_num(k);
}
}
}
}
break;
case OP_AND:
m_num_ands++;
break;
case OP_OR:
m_num_ors++;
break;
case OP_IFF:
m_num_iffs++;
break;
}
}
if (is_bool(e)) {
m_num_bool_exprs++;
if (is_app(e) && to_app(e)->get_num_args() == 0)
m_num_bool_constants++;
}
if (is_quantifier(e)) {
m_num_quantifiers++;
unsigned num_patterns = to_quantifier(e)->get_num_patterns();
if (num_patterns > 0) {
m_num_quantifiers_with_patterns++;
for (unsigned i = 0; i < num_patterns; i++) {
expr * p = to_quantifier(e)->get_pattern(i);
if (is_app(p) && to_app(p)->get_num_args() > 1) {
m_num_quantifiers_with_multi_patterns++;
break;
}
}
}
}
bool _is_le_ge = m_autil.is_le(e) || m_autil.is_ge(e);
if (_is_le_ge) {
m_num_arith_ineqs++;
TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m_manager) << "\n";);
if (is_diff_atom(e))
m_num_diff_ineqs++;
if (!is_arith_expr(to_app(e)->get_arg(0)))
m_num_simple_ineqs++;
acc_num(to_app(e)->get_arg(1));
}
rational r;
if (is_numeral(e, r)) {
if (!r.is_int())
m_has_rational = true;
}
if (_is_eq) {
m_num_eqs++;
if (is_numeral(to_app(e)->get_arg(1))) {
acc_num(to_app(e)->get_arg(1));
m_num_arith_eqs++;
TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m_manager) << "\n";);
if (is_diff_atom(e))
m_num_diff_eqs++;
if (!is_arith_expr(to_app(e)->get_arg(0)))
m_num_simple_eqs++;
}
sort * s = m_manager.get_sort(to_app(e)->get_arg(0));
family_id fid = s->get_family_id();
if (fid != null_family_id && fid != m_bfid)
inc_theory_eqs(fid);
}
if (!m_has_int && m_autil.is_int(e))
m_has_int = true;
if (!m_has_real && m_autil.is_real(e))
m_has_real = true;
if (is_app(e)) {
family_id fid = to_app(e)->get_family_id();
mark_theory(fid);
if (fid != null_family_id && fid != m_bfid) {
m_num_interpreted_exprs++;
if (is_bool(e))
inc_theory_atoms(fid);
else
inc_theory_terms(fid);
if (to_app(e)->get_num_args() == 0)
m_num_interpreted_constants++;
}
if (fid == m_afid) {
switch (to_app(e)->get_decl_kind()) {
case OP_MUL:
if (!is_numeral(to_app(e)->get_arg(0)))
m_num_non_linear++;
break;
case OP_DIV:
case OP_IDIV:
case OP_REM:
case OP_MOD:
if (!is_numeral(to_app(e)->get_arg(1)))
m_num_non_linear++;
break;
}
}
if (fid == null_family_id) {
m_num_uninterpreted_exprs++;
if (to_app(e)->get_num_args() == 0) {
m_num_uninterpreted_constants++;
sort * s = m_manager.get_sort(e);
family_id fid = s->get_family_id();
if (fid != null_family_id && fid != m_bfid)
inc_theory_constants(fid);
}
}
func_decl * d = to_app(e)->get_decl();
inc_num_apps(d);
if (d->get_arity() > 0 && !is_marked(d)) {
mark(d);
if (fid == null_family_id)
m_num_uninterpreted_functions++;
}
if (!_is_eq && !_is_gate) {
unsigned num_args = to_app(e)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
sort * arg_s = m_manager.get_sort(arg);
family_id fid_arg = arg_s->get_family_id();
if (fid_arg != fid && fid_arg != null_family_id) {
m_num_aliens++;
inc_num_aliens(fid_arg);
if (fid_arg == m_afid) {
SASSERT(!_is_le_ge);
m_num_arith_terms++;
rational k;
TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m_manager) << "\n";);
if (is_diff_term(arg, k)) {
m_num_diff_terms++;
acc_num(k);
}
}
}
}
}
}
}
void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth) {
TRACE("static_features", tout << "processing\n" << mk_pp(e, m_manager) << "\n";);
if (is_var(e))
return;
if (is_marked(e)) {
m_num_sharing++;
return;
}
if (stack_depth > m_max_stack_depth) {
return;
}
mark(e);
update_core(e);
if (is_quantifier(e)) {
expr * body = to_quantifier(e)->get_expr();
process(body, false, false, false, stack_depth+1);
set_depth(e, get_depth(body)+1);
return;
}
bool form_ctx_new = false;
bool or_and_ctx_new = false;
bool ite_ctx_new = false;
if (is_basic_expr(e)) {
switch (to_app(e)->get_decl_kind()) {
case OP_ITE:
form_ctx_new = m_manager.is_bool(e);
ite_ctx_new = true;
break;
case OP_AND:
case OP_OR:
form_ctx_new = true;
or_and_ctx_new = true;
break;
case OP_IFF:
form_ctx_new = true;
break;
}
}
unsigned depth = 0;
unsigned form_depth = 0;
unsigned or_and_depth = 0;
unsigned ite_depth = 0;
unsigned num_args = to_app(e)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
if (m_manager.is_not(arg))
arg = to_app(arg)->get_arg(0);
process(arg, form_ctx_new, or_and_ctx_new, ite_ctx_new, stack_depth+1);
depth = std::max(depth, get_depth(arg));
if (form_ctx_new)
form_depth = std::max(form_depth, get_form_depth(arg));
if (or_and_ctx_new)
or_and_depth = std::max(or_and_depth, get_or_and_depth(arg));
if (ite_ctx_new)
ite_depth = std::max(ite_depth, get_ite_depth(arg));
}
depth++;
set_depth(e, depth);
if (depth > m_max_depth)
m_max_depth = depth;
if (form_ctx_new) {
form_depth++;
if (!form_ctx) {
m_num_formula_trees++;
m_sum_formula_depth += form_depth;
if (form_depth > m_max_formula_depth)
m_max_formula_depth = form_depth;
}
set_form_depth(e, form_depth);
}
if (or_and_ctx_new) {
or_and_depth++;
if (!or_and_ctx) {
m_num_or_and_trees++;
m_sum_or_and_tree_depth += or_and_depth;
if (or_and_depth > m_max_or_and_tree_depth)
m_max_or_and_tree_depth = or_and_depth;
}
set_or_and_depth(e, or_and_depth);
}
if (ite_ctx_new) {
ite_depth++;
if (!ite_ctx) {
m_num_ite_trees++;
m_sum_ite_tree_depth += ite_depth;
if (ite_depth >= m_max_ite_tree_depth)
m_max_ite_tree_depth = ite_depth;
}
set_ite_depth(e, ite_depth);
}
}
void static_features::process_root(expr * e) {
if (is_marked(e)) {
m_num_sharing++;
return;
}
m_num_roots++;
if (m_manager.is_or(e)) {
mark(e);
m_num_clauses++;
m_num_bool_exprs++;
unsigned num_args = to_app(e)->get_num_args();
m_sum_clause_size += num_args;
if (num_args == 2)
m_num_bin_clauses++;
unsigned depth = 0;
unsigned form_depth = 0;
unsigned or_and_depth = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
if (m_manager.is_not(arg))
arg = to_app(arg)->get_arg(0);
process(arg, true, true, false, 0);
depth = std::max(depth, get_depth(arg));
form_depth = std::max(form_depth, get_form_depth(arg));
or_and_depth = std::max(or_and_depth, get_or_and_depth(arg));
}
depth++;
set_depth(e, depth);
if (depth > m_max_depth)
m_max_depth = depth;
form_depth++;
m_num_formula_trees++;
m_sum_formula_depth += form_depth;
if (form_depth > m_max_formula_depth)
m_max_formula_depth = form_depth;
set_form_depth(e, form_depth);
or_and_depth++;
m_num_or_and_trees++;
m_sum_or_and_tree_depth += or_and_depth;
if (or_and_depth > m_max_or_and_tree_depth)
m_max_or_and_tree_depth = or_and_depth;
set_or_and_depth(e, or_and_depth);
return;
}
if (!is_gate(e)) {
m_sum_clause_size++;
m_num_units++;
m_num_clauses++;
}
process(e, false, false, false, 0);
}
void static_features::collect(unsigned num_formulas, expr * const * formulas) {
for (unsigned i = 0; i < num_formulas; i++)
process_root(formulas[i]);
}
bool static_features::internal_family(symbol const & f_name) const {
return f_name == m_label_sym || f_name == m_pattern_sym || f_name == m_expr_list_sym;
}
void static_features::display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const {
for (unsigned fid = 0; fid < data.size(); fid++) {
symbol const & n = m_manager.get_family_name(fid);
if (!internal_family(n))
out << prefix << "_" << n << " " << data[fid] << "\n";
}
}
bool static_features::has_uf() const {
return m_num_uninterpreted_functions > 0;
}
unsigned static_features::num_non_uf_theories() const {
return m_num_theories;
}
unsigned static_features::num_theories() const {
return (num_non_uf_theories() + (has_uf() ? 1 : 0));
}
void static_features::display_primitive(std::ostream & out) const {
out << "BEGIN_PRIMITIVE_STATIC_FEATURES" << "\n";
out << "CNF " << m_cnf << "\n";
out << "NUM_EXPRS " << m_num_exprs << "\n";
out << "NUM_ROOTS " << m_num_roots << "\n";
out << "MAX_DEPTH " << m_max_depth << "\n";
out << "NUM_QUANTIFIERS " << m_num_quantifiers << "\n";
out << "NUM_QUANTIFIERS_WITH_PATTERNS " << m_num_quantifiers_with_patterns << "\n";
out << "NUM_QUANTIFIERS_WITH_MULTI_PATTERNS " << m_num_quantifiers_with_multi_patterns << "\n";
out << "NUM_CLAUSES " << m_num_clauses << "\n";
out << "NUM_BIN_CLAUSES " << m_num_bin_clauses << "\n";
out << "NUM_UNITS " << m_num_units << "\n";
out << "SUM_CLAUSE_SIZE " << m_sum_clause_size << "\n";
out << "NUM_NESTED_FORMULAS " << m_num_nested_formulas << "\n";
out << "NUM_BOOL_EXPRS " << m_num_bool_exprs << "\n";
out << "NUM_BOOL_CONSTANTS " << m_num_bool_constants << "\n";
out << "NUM_FORMULA_TREES " << m_num_formula_trees << "\n";
out << "MAX_FORMULA_DEPTH " << m_max_formula_depth << "\n";
out << "SUM_FORMULA_DEPTH " << m_sum_formula_depth << "\n";
out << "NUM_OR_AND_TREES " << m_num_or_and_trees << "\n";
out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n";
out << "SUM_OR_AND_TREE_DEPTH " << m_sum_or_and_tree_depth << "\n";
out << "NUM_ITE_TREES " << m_num_ite_trees << "\n";
out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n";
out << "SUM_ITE_TREE_DEPTH " << m_sum_ite_tree_depth << "\n";
out << "NUM_ORS " << m_num_ors << "\n";
out << "NUM_ANDS " << m_num_ands << "\n";
out << "NUM_IFFS " << m_num_iffs << "\n";
out << "NUM_ITE_FORMULAS " << m_num_ite_formulas << "\n";
out << "NUM_ITE_TERMS " << m_num_ite_terms << "\n";
out << "NUM_SHARING " << m_num_sharing << "\n";
out << "NUM_INTERPRETED_EXPRS " << m_num_interpreted_exprs << "\n";
out << "NUM_UNINTERPRETED_EXPRS " << m_num_uninterpreted_exprs << "\n";
out << "NUM_INTERPRETED_CONSTANTS " << m_num_interpreted_constants << "\n";
out << "NUM_UNINTERPRETED_CONSTANTS " << m_num_uninterpreted_constants << "\n";
out << "NUM_UNINTERPRETED_FUNCTIONS " << m_num_uninterpreted_functions << "\n";
out << "NUM_EQS " << m_num_eqs << "\n";
out << "HAS_RATIONAL " << m_has_rational << "\n";
out << "HAS_INT " << m_has_int << "\n";
out << "HAS_REAL " << m_has_real << "\n";
out << "ARITH_K_SUM " << m_arith_k_sum << "\n";
out << "NUM_ARITH_TERMS " << m_num_arith_terms << "\n";
out << "NUM_ARITH_EQS " << m_num_arith_eqs << "\n";
out << "NUM_ARITH_INEQS " << m_num_arith_ineqs << "\n";
out << "NUM_DIFF_TERMS " << m_num_diff_terms << "\n";
out << "NUM_DIFF_EQS " << m_num_diff_eqs << "\n";
out << "NUM_DIFF_INEQS " << m_num_diff_ineqs << "\n";
out << "NUM_SIMPLE_EQS " << m_num_simple_eqs << "\n";
out << "NUM_SIMPLE_INEQS " << m_num_simple_ineqs << "\n";
out << "NUM_NON_LINEAR " << m_num_non_linear << "\n";
out << "NUM_ALIENS " << m_num_aliens << "\n";
display_family_data(out, "NUM_TERMS", m_num_theory_terms);
display_family_data(out, "NUM_ATOMS", m_num_theory_atoms);
display_family_data(out, "NUM_CONSTANTS", m_num_theory_constants);
display_family_data(out, "NUM_EQS", m_num_theory_eqs);
display_family_data(out, "NUM_ALIENS", m_num_aliens_per_family);
out << "NUM_THEORIES " << num_theories() << "\n";
out << "END_PRIMITIVE_STATIC_FEATURES" << "\n";
}
void static_features::display(std::ostream & out) const {
out << "BEGIN_STATIC_FEATURES" << "\n";
out << "CNF " << m_cnf << "\n";
out << "MAX_DEPTH " << m_max_depth << "\n";
out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n";
out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n";
out << "HAS_INT " << m_has_int << "\n";
out << "HAS_REAL " << m_has_real << "\n";
out << "HAS_QUANTIFIERS " << (m_num_quantifiers > 0) << "\n";
out << "PERC_QUANTIFIERS_WITH_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_patterns / (double) m_num_quantifiers : 0) << "\n";
out << "PERC_QUANTIFIERS_WITH_MULTI_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_multi_patterns / (double) m_num_quantifiers : 0) << "\n";
out << "IS_NON_LINEAR " << (m_num_non_linear > 0) << "\n";
out << "THEORY_COMBINATION " << (num_theories() > 1) << "\n";
out << "AVG_CLAUSE_SIZE " << (m_num_clauses > 0 ? (double) m_sum_clause_size / (double) m_num_clauses : 0) << "\n";
out << "PERC_BOOL_CONSTANTS " << (m_num_uninterpreted_constants > 0 ? (double) m_num_bool_constants / (double) m_num_uninterpreted_constants : 0) << "\n";
out << "PERC_NESTED_FORMULAS " << (m_num_bool_exprs > 0 ? (double) m_num_nested_formulas / (double) m_num_bool_exprs : 0) << "\n";
out << "IS_DIFF " << (m_num_arith_eqs == m_num_diff_eqs && m_num_arith_ineqs == m_num_diff_ineqs && m_num_arith_terms == m_num_diff_terms) << "\n";
out << "INEQ_EQ_RATIO " << (m_num_arith_eqs > 0 ? (double) m_num_arith_ineqs / (double) m_num_arith_eqs : 0) << "\n";
out << "PERC_ARITH_EQS " << (m_num_eqs > 0 ? (double) m_num_arith_eqs / (double) m_num_eqs : 0) << "\n";
out << "PERC_DIFF_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_diff_eqs / (double) m_num_arith_eqs : 0) << "\n";
out << "PERC_DIFF_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_diff_ineqs / (double) m_num_arith_ineqs : 0) << "\n";
out << "PERC_SIMPLE_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_simple_eqs / (double) m_num_arith_eqs : 0) << "\n";
out << "PERC_SIMPLE_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_simple_ineqs / (double) m_num_arith_ineqs : 0) << "\n";
out << "PERC_ALIENS " << (m_num_exprs > 0 ? (double) m_num_aliens / (double) m_num_exprs : 0) << "\n";
out << "END_STATIC_FEATURES" << "\n";
}
void static_features::get_feature_vector(vector<double> & result) {
}

169
src/ast/static_features.h Normal file
View file

@ -0,0 +1,169 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
static_features.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-05-16.
Revision History:
--*/
#ifndef _STATIC_FEATURES_H_
#define _STATIC_FEATURES_H_
#include"ast.h"
#include"arith_decl_plugin.h"
#include"map.h"
struct static_features {
ast_manager & m_manager;
arith_util m_autil;
family_id m_bfid;
family_id m_afid;
family_id m_lfid;
ast_mark m_already_visited;
bool m_cnf;
unsigned m_num_exprs; //
unsigned m_num_roots; //
unsigned m_max_depth;
unsigned m_num_quantifiers; //
unsigned m_num_quantifiers_with_patterns; //
unsigned m_num_quantifiers_with_multi_patterns; //
unsigned m_num_clauses;
unsigned m_num_bin_clauses; //
unsigned m_num_units; //
unsigned m_sum_clause_size;
unsigned m_num_nested_formulas; //
unsigned m_num_bool_exprs; //
unsigned m_num_bool_constants; //
unsigned m_num_formula_trees;
unsigned m_max_formula_depth;
unsigned m_sum_formula_depth;
unsigned m_num_or_and_trees;
unsigned m_max_or_and_tree_depth;
unsigned m_sum_or_and_tree_depth;
unsigned m_num_ite_trees;
unsigned m_max_ite_tree_depth;
unsigned m_sum_ite_tree_depth;
unsigned m_num_ands; //
unsigned m_num_ors; // num nested ors
unsigned m_num_iffs; //
unsigned m_num_ite_formulas; //
unsigned m_num_ite_terms; //
unsigned m_num_sharing;
unsigned m_num_interpreted_exprs; // doesn't include bool_exprs
unsigned m_num_uninterpreted_exprs; //
unsigned m_num_interpreted_constants; // doesn't include bool_consts
unsigned m_num_uninterpreted_constants; //
unsigned m_num_uninterpreted_functions; //
unsigned m_num_eqs; //
bool m_has_rational; //
bool m_has_int; //
bool m_has_real; //
rational m_arith_k_sum; // sum of the numerals in arith atoms.
unsigned m_num_arith_terms;
unsigned m_num_arith_eqs; // equalities of the form t = k where k is a numeral
unsigned m_num_arith_ineqs;
unsigned m_num_diff_terms; // <= m_num_arith_terms
unsigned m_num_diff_eqs; // <= m_num_arith_eqs
unsigned m_num_diff_ineqs; // <= m_num_arith_ineqs
unsigned m_num_simple_eqs; // eqs of the form x = k
unsigned m_num_simple_ineqs; // ineqs of the form x <= k or x >= k
unsigned m_num_non_linear;
unsigned_vector m_num_apps; // mapping decl_id -> num_apps;
unsigned_vector m_num_theory_terms; // mapping family_id -> num_terms
unsigned_vector m_num_theory_atoms; // mapping family_id -> num_atoms
unsigned_vector m_num_theory_constants; // mapping family_id -> num_exprs
unsigned_vector m_num_theory_eqs; // mapping family_id -> num_eqs
unsigned m_num_aliens; //
unsigned_vector m_num_aliens_per_family; // mapping family_id -> num_alies exprs
unsigned_vector m_expr2depth; // expr-id -> depth
unsigned m_max_stack_depth; // maximal depth of stack we are willing to walk.
u_map<unsigned> m_expr2or_and_depth;
u_map<unsigned> m_expr2ite_depth;
u_map<unsigned> m_expr2formula_depth;
unsigned m_num_theories;
svector<bool> m_theories; // mapping family_id -> bool
symbol m_label_sym;
symbol m_pattern_sym;
symbol m_expr_list_sym;
bool is_marked(ast * e) const { return m_already_visited.is_marked(e); }
void mark(ast * e) { m_already_visited.mark(e, true); }
bool is_bool(expr const * e) const { return m_manager.is_bool(e); }
bool is_basic_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_bfid; }
bool is_arith_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_afid; }
bool is_numeral(expr const * e) const { return m_autil.is_numeral(e); }
bool is_numeral(expr const * e, rational & r) const { return m_autil.is_numeral(e, r); }
bool is_minus_one(expr const * e) const { rational r; return m_autil.is_numeral(e, r) && r.is_minus_one(); }
bool is_diff_term(expr const * e, rational & r) const;
bool is_diff_atom(expr const * e) const;
bool is_gate(expr const * e) const;
void mark_theory(family_id fid) {
if (fid != null_family_id && !m_manager.is_builtin_family_id(fid) && !m_theories.get(fid, false)) {
m_theories.setx(fid, true, false);
m_num_theories++;
}
}
void acc_num(rational const & r) {
if (r.is_neg())
m_arith_k_sum -= r;
else
m_arith_k_sum += r;
}
void acc_num(expr const * e) {
rational r;
if (is_numeral(e, r)) {
acc_num(r);
}
}
void inc_num_apps(func_decl const * d) { unsigned id = d->get_decl_id(); m_num_apps.reserve(id+1, 0); m_num_apps[id]++; }
void inc_theory_terms(family_id fid) { m_num_theory_terms.reserve(fid+1, 0); m_num_theory_terms[fid]++; }
void inc_theory_atoms(family_id fid) { m_num_theory_atoms.reserve(fid+1, 0); m_num_theory_atoms[fid]++; }
void inc_theory_constants(family_id fid) { m_num_theory_constants.reserve(fid+1, 0); m_num_theory_constants[fid]++; }
void inc_theory_eqs(family_id fid) { m_num_theory_eqs.reserve(fid+1, 0); m_num_theory_eqs[fid]++; }
void inc_num_aliens(family_id fid) { m_num_aliens_per_family.reserve(fid+1, 0); m_num_aliens_per_family[fid]++; }
void update_core(expr * e);
void process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth);
void process_root(expr * e);
unsigned get_depth(expr const * e) const { return m_expr2depth.get(e->get_id(), 1); }
void set_depth(expr const * e, unsigned d) { m_expr2depth.setx(e->get_id(), d, 1); }
unsigned get_or_and_depth(expr const * e) const { unsigned d = 0; m_expr2or_and_depth.find(e->get_id(), d); return d; }
void set_or_and_depth(expr const * e, unsigned d) { m_expr2or_and_depth.insert(e->get_id(), d); }
unsigned get_ite_depth(expr const * e) const { unsigned d = 0; m_expr2ite_depth.find(e->get_id(), d); return d; }
void set_ite_depth(expr const * e, unsigned d) { m_expr2ite_depth.insert(e->get_id(), d); }
unsigned get_form_depth(expr const * e) const { unsigned d = 0; m_expr2formula_depth.find(e->get_id(), d); return d; }
void set_form_depth(expr const * e, unsigned d) { m_expr2formula_depth.insert(e->get_id(), d); }
static_features(ast_manager & m);
void reset();
void flush_cache();
void collect(unsigned num_formulas, expr * const * formulas);
void collect(expr * f) { process_root(f); }
bool internal_family(symbol const & f_name) const;
void display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const;
void display_primitive(std::ostream & out) const;
void display(std::ostream & out) const;
void get_feature_vector(vector<double> & result);
bool has_uf() const;
unsigned num_theories() const;
unsigned num_non_uf_theories() const;
};
#endif /* _STATIC_FEATURES_H_ */

404
src/ast/trail.h Normal file
View file

@ -0,0 +1,404 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
trail.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-02.
Revision History:
--*/
#ifndef _TRAIL_H_
#define _TRAIL_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"region.h"
template<typename Ctx>
class trail {
public:
virtual ~trail() {
}
virtual void undo(Ctx & ctx) = 0;
};
template<typename Ctx, typename T>
class value_trail : public trail<Ctx> {
T & m_value;
T m_old_value;
public:
value_trail(T & value):
m_value(value),
m_old_value(value) {
}
virtual ~value_trail() {
}
virtual void undo(Ctx & ctx) {
m_value = m_old_value;
}
};
template<typename Ctx>
class reset_flag_trail : public trail<Ctx> {
bool & m_value;
public:
reset_flag_trail(bool & value):
m_value(value) {
}
virtual ~reset_flag_trail() {
}
virtual void undo(Ctx & ctx) {
m_value = false;
}
};
template<typename Ctx, typename T>
class set_ptr_trail : public trail<Ctx> {
T * & m_ptr;
public:
set_ptr_trail(T * & ptr):
m_ptr(ptr) {
SASSERT(m_ptr == 0);
}
virtual void undo(Ctx & ctx) {
m_ptr = 0;
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class restore_size_trail : public trail<Ctx> {
vector<T, CallDestructors> & m_vector;
unsigned m_old_size;
public:
restore_size_trail(vector<T, CallDestructors> & v, unsigned sz):
m_vector(v),
m_old_size(sz) {
}
restore_size_trail(vector<T, CallDestructors> & v):
m_vector(v),
m_old_size(v.size()) {
}
virtual ~restore_size_trail() {
}
virtual void undo(Ctx & ctx) {
m_vector.shrink(m_old_size);
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class vector_value_trail : public trail<Ctx> {
vector<T, CallDestructors> & m_vector;
unsigned m_idx;
T m_old_value;
public:
vector_value_trail(vector<T, CallDestructors> & v, unsigned idx):
m_vector(v),
m_idx(idx),
m_old_value(v[idx]) {
}
virtual ~vector_value_trail() {
}
virtual void undo(Ctx & ctx) {
m_vector[m_idx] = m_old_value;
}
};
template<typename Ctx, typename D, typename R>
class insert_obj_map : public trail<Ctx> {
obj_map<D,R>& m_map;
D* m_obj;
public:
insert_obj_map(obj_map<D,R>& t, D* o) : m_map(t), m_obj(o) {}
virtual ~insert_obj_map() {}
virtual void undo(Ctx & ctx) { m_map.remove(m_obj); }
};
template<typename Ctx, typename M, typename D>
class insert_map : public trail<Ctx> {
M& m_map;
D m_obj;
public:
insert_map(M& t, D o) : m_map(t), m_obj(o) {}
virtual ~insert_map() {}
virtual void undo(Ctx & ctx) { m_map.remove(m_obj); }
};
template<typename Ctx, typename V>
class push_back_vector : public trail<Ctx> {
V & m_vector;
public:
push_back_vector(V & v):
m_vector(v) {
}
virtual void undo(Ctx & ctx) {
m_vector.pop_back();
}
};
template<typename Ctx, typename T>
class set_vector_idx_trail : public trail<Ctx> {
ptr_vector<T> & m_vector;
unsigned m_idx;
public:
set_vector_idx_trail(ptr_vector<T> & v, unsigned idx):
m_vector(v),
m_idx(idx) {
}
virtual ~set_vector_idx_trail() {
}
virtual void undo(Ctx & ctx) {
m_vector[m_idx] = 0;
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class pop_back_trail : public trail<Ctx> {
vector<T, CallDestructors> & m_vector;
T m_value;
public:
pop_back_trail(vector<T, CallDestructors> & v):
m_vector(v),
m_value(m_vector.back()) {
}
virtual void undo(Ctx & ctx) {
m_vector.push_back(m_value);
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class pop_back2_trail : public trail<Ctx> {
vector<T, CallDestructors> & m_vector;
typedef vector<vector<T, CallDestructors>, true> vector_t;
unsigned m_index;
T m_value;
public:
pop_back2_trail(vector<T, CallDestructors> & v, unsigned index):
m_vector(v),
m_index(index),
m_value(m_vector[index].back()) {
}
virtual void undo(Ctx & ctx) {
m_vector[m_index].push_back(m_value);
}
};
template<typename S, typename T>
class ast2ast_trailmap {
ref_vector<S, ast_manager> m_domain;
ref_vector<T, ast_manager> m_range;
obj_map<S, T*> m_map;
public:
ast2ast_trailmap(ast_manager& m):
m_domain(m),
m_range(m),
m_map()
{}
bool find(S* s, T*& t) {
return m_map.find(s,t);
}
void insert(S* s, T* t) {
SASSERT(!m_map.contains(s));
m_domain.push_back(s);
m_range.push_back(t);
m_map.insert(s,t);
}
void pop() {
SASSERT(!m_domain.empty());
m_map.remove(m_domain.back());
m_domain.pop_back();
m_range.pop_back();
}
};
template<typename Ctx, typename S, typename T>
class ast2ast_trail : public trail<Ctx> {
ast2ast_trailmap<S,T>& m_map;
public:
ast2ast_trail(ast2ast_trailmap<S,T>& m, S* s, T* t) :
m_map(m) {
m.insert(s,t);
}
virtual void undo(Ctx& ctx) {
m_map.pop();
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class push_back_trail : public trail<Ctx> {
vector<T, CallDestructors> & m_vector;
public:
push_back_trail(vector<T, CallDestructors> & v):
m_vector(v) {
}
virtual void undo(Ctx & ctx) {
m_vector.pop_back();
}
};
template<typename Ctx, typename T, bool CallDestructors=true>
class push_back2_trail : public trail<Ctx> {
typedef vector<vector<T, CallDestructors>, true> vector_t;
vector_t & m_vector;
unsigned m_index;
public:
push_back2_trail(vector_t & v, unsigned index) :
m_vector(v),
m_index(index) {
}
virtual void undo(Ctx & ctx) {
m_vector[m_index].pop_back();
}
};
template<typename Ctx>
class set_bitvector_trail : public trail<Ctx> {
svector<bool> & m_vector;
unsigned m_idx;
public:
set_bitvector_trail(svector<bool> & v, unsigned idx):
m_vector(v),
m_idx(idx) {
SASSERT(m_vector[m_idx] == false);
m_vector[m_idx] = true;
}
virtual void undo(Ctx & ctx) {
m_vector[m_idx] = false;
}
};
template<typename Ctx, typename T>
class new_obj_trail : public trail<Ctx> {
T * m_obj;
public:
new_obj_trail(T * obj):
m_obj(obj) {
}
virtual void undo(Ctx & ctx) {
dealloc(m_obj);
}
};
template<typename Ctx, typename M, typename T>
class obj_ref_trail : public trail<Ctx> {
obj_ref<T,M> m_obj;
public:
obj_ref_trail(obj_ref<T,M>& obj):
m_obj(obj) {
}
virtual void undo(Ctx & ctx) {
m_obj.reset();
}
};
template<typename Ctx, typename T>
class insert_obj_trail : public trail<Ctx> {
obj_hashtable<T>& m_table;
T* m_obj;
public:
insert_obj_trail(obj_hashtable<T>& t, T* o) : m_table(t), m_obj(o) {}
virtual ~insert_obj_trail() {}
virtual void undo(Ctx & ctx) { m_table.remove(m_obj); }
};
template<typename Ctx, typename T>
class remove_obj_trail : public trail<Ctx> {
obj_hashtable<T>& m_table;
T* m_obj;
public:
remove_obj_trail(obj_hashtable<T>& t, T* o) : m_table(t), m_obj(o) {}
virtual ~remove_obj_trail() {}
virtual void undo(Ctx & ctx) { m_table.insert(m_obj); }
};
template<typename Ctx>
void undo_trail_stack(Ctx & ctx, ptr_vector<trail<Ctx> > & s, unsigned old_size) {
SASSERT(old_size <= s.size());
typename ptr_vector<trail<Ctx> >::iterator begin = s.begin() + old_size;
typename ptr_vector<trail<Ctx> >::iterator it = s.end();
while (it != begin) {
--it;
(*it)->undo(ctx);
}
s.shrink(old_size);
}
template<typename Ctx>
class trail_stack {
Ctx & m_ctx;
ptr_vector<trail<Ctx> > m_trail_stack;
unsigned_vector m_scopes;
region m_region;
public:
trail_stack(Ctx & c):m_ctx(c) {}
~trail_stack() {}
region & get_region() { return m_region; }
void reset() {
pop_scope(m_scopes.size());
// Undo trail objects stored at lvl 0 (avoid memory leaks if lvl 0 contains new_obj_trail objects).
undo_trail_stack(m_ctx, m_trail_stack, 0);
}
void push_ptr(trail<Ctx> * t) { m_trail_stack.push_back(t); }
template<typename TrailObject>
void push(TrailObject const & obj) { m_trail_stack.push_back(new (m_region) TrailObject(obj)); }
unsigned get_num_scopes() const { return m_scopes.size(); }
void push_scope() { m_region.push_scope(); m_scopes.push_back(m_trail_stack.size()); }
void pop_scope(unsigned num_scopes) {
if (num_scopes == 0) return;
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_size = m_scopes[new_lvl];
undo_trail_stack(m_ctx, m_trail_stack, old_size);
m_scopes.shrink(new_lvl);
m_region.pop_scope(num_scopes);
}
};
#endif /* _TRAIL_H_ */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -0,0 +1,436 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
eager_bit_blaster.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-02.
Revision History:
--*/
#include"ast_ll_pp.h"
#include"eager_bit_blaster.h"
eager_bit_blaster::basic_plugin::basic_plugin(ast_manager & m, eager_bit_blaster::bv_plugin & p, basic_simplifier_plugin & s):
simplifier_plugin(symbol("basic"), m),
m_main(p),
m_s(s) {
}
eager_bit_blaster::basic_plugin::~basic_plugin() {
}
bool eager_bit_blaster::basic_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (f->get_decl_kind() == OP_ITE) {
SASSERT(num_args == 3);
return m_main.reduce_ite(args[0], args[1], args[2], result);
}
else if (f->get_decl_kind() == OP_NOT) {
// the internalizer assumes there is not double negation (not (not x))
SASSERT(num_args == 1);
m_s.mk_not(args[0], result);
return true;
}
return false;
}
eager_bit_blaster::bv_plugin::bv_plugin(ast_manager & m, bit_blaster_params const & p):
simplifier_plugin(symbol("bv"), m),
m_util(m),
m_bb(m, p),
m_s(m) {
}
eager_bit_blaster::bv_plugin::~bv_plugin() {
}
void eager_bit_blaster::bv_plugin::get_bits(expr * n, expr_ref_vector & out_bits) {
rational val;
unsigned bv_size;
if (m_util.is_numeral(n, val, bv_size)) {
TRACE("eager_bb_bug", tout << "bv_size: " << bv_size << "\n";);
m_bb.num2bits(val, bv_size, out_bits);
SASSERT(out_bits.size() == bv_size);
}
else if (m_util.is_mkbv(n)) {
out_bits.append(to_app(n)->get_num_args(), to_app(n)->get_args());
}
else {
unsigned bv_size = m_util.get_bv_size(n);
for (unsigned i = 0; i < bv_size; i++) {
parameter p(i);
out_bits.push_back(m_manager.mk_app(get_family_id(), OP_BIT2BOOL, 1, &p, 1, &n));
}
SASSERT(bv_size == out_bits.size());
}
}
inline app * eager_bit_blaster::bv_plugin::mk_mkbv(expr_ref_vector const & bits) {
#ifdef Z3DEBUG
for (unsigned i = 0; i < bits.size(); i++) {
expr * b = bits.get(i);
SASSERT(!m_manager.is_not(b) || !m_manager.is_not(to_app(b)->get_arg(0)));
}
#endif
return m_manager.mk_app(get_family_id(), OP_MKBV, bits.size(), bits.c_ptr());
}
#define MK_UNARY_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg, expr_ref & result) { \
expr_ref_vector bits(m_manager); \
get_bits(arg, bits); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits.size(), bits.c_ptr(), out_bits); \
result = mk_mkbv(out_bits); \
}
#define MK_BIN_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg1, expr * arg2, expr_ref & result) { \
expr_ref_vector bits1(m_manager); \
expr_ref_vector bits2(m_manager); \
get_bits(arg1, bits1); \
get_bits(arg2, bits2); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), out_bits); \
result = mk_mkbv(out_bits); \
}
#define MK_BIN_AC_FLAT_REDUCE(OP, BIN_OP, S_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void eager_bit_blaster::bv_plugin::OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
if (num_args == 2) { \
BIN_OP(args[0], args[1], result); \
return; \
} \
\
ptr_buffer<expr_ref_vector> args_bits; \
for (unsigned i = 0; i < num_args; i++) { \
expr_ref_vector * bits = alloc(expr_ref_vector, m_manager); \
get_bits(args[i], *bits); \
args_bits.push_back(bits); \
} \
\
unsigned bv_size = m_util.get_bv_size(args[0]); \
expr_ref_vector new_bits(m_manager); \
for (unsigned i = 0; i < bv_size; i++) { \
expr_ref_vector arg_bits(m_manager); \
for (unsigned j = 0; j < num_args; j++) \
arg_bits.push_back(args_bits[j]->get(i)); \
expr_ref new_bit(m_manager); \
m_s.S_OP(arg_bits.size(), arg_bits.c_ptr(), new_bit); \
new_bits.push_back(new_bit); \
} \
result = mk_mkbv(new_bits); \
std::for_each(args_bits.begin(), args_bits.end(), delete_proc<expr_ref_vector>()); \
}
#define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \
MK_BIN_REDUCE(BIN_OP, BB_OP); \
void eager_bit_blaster::bv_plugin::OP(unsigned num_args, expr * const * args, expr_ref & result) { \
SASSERT(num_args > 0); \
result = args[0]; \
for (unsigned i = 1; i < num_args; i++) { \
expr_ref new_result(m_manager); \
BIN_OP(result.get(), args[i], new_result); \
result = new_result; \
} \
}
#define MK_BIN_PRED_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg1, expr * arg2, expr_ref & result) { \
expr_ref_vector bits1(m_manager); \
expr_ref_vector bits2(m_manager); \
get_bits(arg1, bits1); \
get_bits(arg2, bits2); \
m_bb.BB_OP(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), result); \
}
#define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \
void eager_bit_blaster::bv_plugin::OP(expr * arg, unsigned n, expr_ref & result) { \
expr_ref_vector bits(m_manager); \
get_bits(arg, bits); \
expr_ref_vector out_bits(m_manager); \
m_bb.BB_OP(bits.size(), bits.c_ptr(), n, out_bits); \
result = mk_mkbv(out_bits); \
}
MK_UNARY_REDUCE(reduce_not, mk_not);
MK_BIN_AC_FLAT_REDUCE(reduce_or, reduce_bin_or, mk_or, mk_or);
MK_BIN_AC_FLAT_REDUCE(reduce_and, reduce_bin_and, mk_and, mk_and);
MK_BIN_AC_FLAT_REDUCE(reduce_nor, reduce_bin_nor, mk_nor, mk_nor);
MK_BIN_AC_FLAT_REDUCE(reduce_nand, reduce_bin_nand, mk_nand, mk_nand);
MK_BIN_REDUCE(reduce_xor, mk_xor);
MK_BIN_REDUCE(reduce_xnor, mk_xnor);
MK_UNARY_REDUCE(reduce_neg, mk_neg);
MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder);
MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier);
MK_BIN_PRED_REDUCE(reduce_sle, mk_sle);
MK_BIN_PRED_REDUCE(reduce_ule, mk_ule);
MK_PARAMETRIC_UNARY_REDUCE(reduce_rotate_left, mk_rotate_left);
MK_PARAMETRIC_UNARY_REDUCE(reduce_rotate_right, mk_rotate_right);
MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
MK_PARAMETRIC_UNARY_REDUCE(reduce_zero_extend, mk_zero_extend);
MK_UNARY_REDUCE(reduce_redor, mk_redor);
MK_UNARY_REDUCE(reduce_redand, mk_redand);
MK_BIN_REDUCE(reduce_shl, mk_shl);
MK_BIN_REDUCE(reduce_ashr, mk_ashr);
MK_BIN_REDUCE(reduce_lshr, mk_lshr);
MK_BIN_REDUCE(reduce_comp, mk_comp);
MK_BIN_REDUCE(reduce_udiv, mk_udiv);
MK_BIN_REDUCE(reduce_urem, mk_urem);
MK_BIN_REDUCE(reduce_sdiv, mk_sdiv);
MK_BIN_REDUCE(reduce_srem, mk_srem);
MK_BIN_REDUCE(reduce_smod, mk_smod);
void eager_bit_blaster::bv_plugin::reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) {
expr_ref_vector bits(m_manager);
get_bits(arg, bits);
expr_ref_vector out_bits(m_manager);
for (unsigned i = start; i <= end; ++i)
out_bits.push_back(bits.get(i));
result = mk_mkbv(out_bits);
}
void eager_bit_blaster::bv_plugin::reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_vector out_bits(m_manager);
unsigned i = num_args;
while (i > 0) {
i--;
expr_ref_vector bits(m_manager);
get_bits(args[i], bits);
out_bits.append(bits.size(), bits.c_ptr());
}
result = mk_mkbv(out_bits);
}
bool eager_bit_blaster::bv_plugin::reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) {
sort * s = m_manager.get_sort(arg2);
if (!m_util.is_bv_sort(s))
return false;
expr_ref_vector bits1(m_manager);
expr_ref_vector bits2(m_manager);
get_bits(arg2, bits1);
get_bits(arg3, bits2);
SASSERT(bits1.size() == bits2.size());
expr_ref_vector out_bits(m_manager);
unsigned bv_size = bits1.size();
for (unsigned i = 0; i < bv_size; i++) {
expr_ref new_bit(m_manager);
m_s.mk_ite(arg1, bits1.get(i), bits2.get(i), new_bit);
out_bits.push_back(new_bit);
}
result = mk_mkbv(out_bits);
return true;
}
bool eager_bit_blaster::bv_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
bv_op_kind k = static_cast<bv_op_kind>(f->get_decl_kind());
switch (k) {
case OP_BNOT:
SASSERT(num_args == 1);
reduce_not(args[0], result);
return true;
case OP_BOR:
reduce_or(num_args, args, result);
return true;
case OP_BAND:
reduce_and(num_args, args, result);
return true;
case OP_BNOR:
reduce_nor(num_args, args, result);
return true;
case OP_BNAND:
reduce_nand(num_args, args, result);
return true;
case OP_BXOR:
SASSERT(num_args == 2);
reduce_xor(args[0], args[1], result);
return true;
case OP_BXNOR:
SASSERT(num_args == 2);
reduce_xnor(args[0], args[1], result);
return true;
case OP_BNEG:
SASSERT(num_args == 1);
reduce_neg(args[0], result);
return true;
case OP_BADD:
reduce_add(num_args, args, result);
return true;
case OP_BMUL:
reduce_mul(num_args, args, result);
return true;
case OP_BIT1:
case OP_BIT0:
case OP_BSUB:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_BSDIV:
case OP_BUDIV:
case OP_BSREM:
case OP_BUREM:
case OP_BSMOD:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_BSDIV0:
case OP_BUDIV0:
case OP_BSREM0:
case OP_BUREM0:
case OP_BSMOD0:
// do nothing... these are uninterpreted
return true;
case OP_BSDIV_I:
SASSERT(num_args == 2);
reduce_sdiv(args[0], args[1], result);
return true;
case OP_BUDIV_I:
SASSERT(num_args == 2);
reduce_udiv(args[0], args[1], result);
return true;
case OP_BSREM_I:
SASSERT(num_args == 2);
reduce_srem(args[0], args[1], result);
return true;
case OP_BUREM_I:
SASSERT(num_args == 2);
reduce_urem(args[0], args[1], result);
return true;
case OP_BSMOD_I:
SASSERT(num_args == 2);
reduce_smod(args[0], args[1], result);
return true;
case OP_ULEQ:
SASSERT(num_args == 2);
reduce_ule(args[0], args[1], result);
return true;
case OP_SLEQ:
SASSERT(num_args == 2);
reduce_sle(args[0], args[1], result);
return true;
case OP_UGEQ:
case OP_SGEQ:
case OP_ULT:
case OP_SLT:
case OP_UGT:
case OP_SGT:
// I'm assuming the expressions were simplified before invoking this method.
UNREACHABLE();
return false;
case OP_EXTRACT:
SASSERT(num_args == 1);
reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result);
return true;
case OP_CONCAT:
reduce_concat(num_args, args, result);
return true;
case OP_SIGN_EXT:
SASSERT(num_args == 1);
reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_ZERO_EXT:
SASSERT(num_args == 1);
reduce_zero_extend(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_REPEAT:
UNREACHABLE();
return false;
case OP_BREDOR:
SASSERT(num_args == 1);
reduce_redor(args[0], result);
return true;
case OP_BREDAND:
SASSERT(num_args == 1);
reduce_redand(args[0], result);
return true;
case OP_BCOMP:
SASSERT(num_args == 2);
reduce_comp(args[0], args[1], result);
return true;
case OP_BSHL:
SASSERT(num_args == 2);
reduce_shl(args[0], args[1], result);
return true;
case OP_BLSHR:
SASSERT(num_args == 2);
reduce_lshr(args[0], args[1], result);
return true;
case OP_BASHR:
SASSERT(num_args == 2);
reduce_ashr(args[0], args[1], result);
return true;
case OP_ROTATE_LEFT:
SASSERT(num_args == 1);
reduce_rotate_left(args[0], f->get_parameter(0).get_int(), result);
return true;
case OP_ROTATE_RIGHT:
SASSERT(num_args == 1);
reduce_rotate_right(args[0], f->get_parameter(0).get_int(), result);
return true;
default:
return false;
}
}
bool eager_bit_blaster::bv_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
TRACE("eager_bb_eq", tout << mk_ll_pp(lhs, m_manager) << "\n" << mk_ll_pp(rhs, m_manager) << "\n";);
SASSERT(m_util.get_bv_size(lhs) == m_util.get_bv_size(rhs));
expr_ref_vector bits1(m_manager);
expr_ref_vector bits2(m_manager);
get_bits(lhs, bits1);
get_bits(rhs, bits2);
SASSERT(bits1.size() == bits2.size());
m_bb.mk_eq(bits1.size(), bits1.c_ptr(), bits2.c_ptr(), result);
return true;
}
bool eager_bit_blaster::bv_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args <= 1) {
result = m_manager.mk_true();
}
if (num_args == 2) {
expr_ref tmp(m_manager);
reduce_eq(args[0], args[1], tmp);
m_s.mk_not(tmp, result);
}
else {
expr_ref_vector new_args(m_manager);
for (unsigned i = 0; i < num_args - 1; i++) {
expr * a1 = args[i];
for (unsigned j = i + 1; j < num_args; j++) {
expr * a2 = args[j];
expr_ref tmp1(m_manager);
reduce_eq(a1, a2, tmp1);
expr_ref tmp2(m_manager);
m_s.mk_not(tmp1, tmp2);
new_args.push_back(tmp2);
}
}
m_s.mk_and(new_args.size(), new_args.c_ptr(), result);
}
return true;
}
eager_bit_blaster::eager_bit_blaster(ast_manager & m, bit_blaster_params const & p):
m_simplifier(m) {
m_simplifier.enable_ac_support(false);
bv_plugin * bv_p = alloc(bv_plugin, m, p);
m_simplifier.register_plugin(bv_p);
m_simplifier.register_plugin(alloc(basic_plugin, m, *bv_p, bv_p->get_basic_simplifier()));
}
void eager_bit_blaster::operator()(expr * s, expr_ref & r, proof_ref & p) {
m_simplifier.operator()(s, r, p);
}

View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
eager_bit_blaster.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-02.
Revision History:
--*/
#ifndef _EAGER_BIT_BLASTER_H_
#define _EAGER_BIT_BLASTER_H_
#include"bv_decl_plugin.h"
#include"bit_blaster.h"
#include"simplifier.h"
#include"basic_simplifier_plugin.h"
class eager_bit_blaster {
class bv_plugin : public simplifier_plugin {
bv_util m_util;
bit_blaster m_bb;
basic_simplifier_plugin m_s;
void get_bits(expr * n, expr_ref_vector & out_bits);
app * mk_mkbv(expr_ref_vector const & bits);
void reduce_not(expr * arg, expr_ref & result);
void reduce_bin_or(expr * arg1, expr * arg2, expr_ref & result);
void reduce_or(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_and(expr * arg1, expr * arg2, expr_ref & result);
void reduce_and(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_nor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_nor(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_nand(expr * arg1, expr * arg2, expr_ref & result);
void reduce_nand(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_xor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_xnor(expr * arg1, expr * arg2, expr_ref & result);
void reduce_neg(expr * arg, expr_ref & result);
void reduce_bin_add(expr * arg1, expr * arg2, expr_ref & result);
void reduce_add(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_bin_mul(expr * arg1, expr * arg2, expr_ref & result);
void reduce_mul(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_sdiv(expr * arg1, expr * arg2, expr_ref & result);
void reduce_udiv(expr * arg1, expr * arg2, expr_ref & result);
void reduce_srem(expr * arg1, expr * arg2, expr_ref & result);
void reduce_urem(expr * arg1, expr * arg2, expr_ref & result);
void reduce_smod(expr * arg1, expr * arg2, expr_ref & result);
void reduce_sle(expr * arg1, expr * arg2, expr_ref & result);
void reduce_ule(expr * arg1, expr * arg2, expr_ref & result);
void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result);
void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result);
void reduce_redor(expr * arg, expr_ref & result);
void reduce_redand(expr * arg, expr_ref & result);
void reduce_comp(expr * arg1, expr * arg2, expr_ref & result);
void reduce_shl(expr * arg1, expr * arg2, expr_ref & result);
void reduce_ashr(expr * arg1, expr * arg2, expr_ref & result);
void reduce_lshr(expr * arg1, expr * arg2, expr_ref & result);
void reduce_rotate_left(expr * arg, unsigned n, expr_ref & result);
void reduce_rotate_right(expr * arg, unsigned n, expr_ref & result);
void reduce_sign_extend(expr * arg, unsigned n, expr_ref & result);
void reduce_zero_extend(expr * arg, unsigned n, expr_ref & result);
public:
bv_plugin(ast_manager & m, bit_blaster_params const & p);
virtual ~bv_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
basic_simplifier_plugin & get_basic_simplifier() { return m_s; }
bool reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
};
/**
\brief Plugin for handling the term-ite.
*/
class basic_plugin : public simplifier_plugin {
bv_plugin & m_main;
basic_simplifier_plugin & m_s;
public:
basic_plugin(ast_manager & m, bv_plugin & p, basic_simplifier_plugin & s);
virtual ~basic_plugin();
virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
};
simplifier m_simplifier;
public:
eager_bit_blaster(ast_manager & m, bit_blaster_params const & p);
void operator()(expr * s, expr_ref & r, proof_ref & p);
};
#endif /* _EAGER_BIT_BLASTER_H_ */

32
src/dead/expr_weight.cpp Normal file
View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
expr_weight.cpp
Abstract:
Functor for computing the weight of an expression.
Author:
Leonardo (leonardo) 2008-01-11
Notes:
--*/
#include"expr_weight.h"
#include"recurse_expr_def.h"
template class recurse_expr<weight, expr_weight_visitor, true>;
weight expr_weight_visitor::visit(app const * n, weight const * args) {
weight r(1);
unsigned j = n->get_num_args();
while (j > 0) {
--j;
r += args[j];
}
return r;
}

35
src/dead/expr_weight.h Normal file
View file

@ -0,0 +1,35 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
expr_weight.h
Abstract:
Functor for computing the weight of an expression.
Author:
Leonardo (leonardo) 2008-01-11
Notes:
--*/
#ifndef _EXPR_WEIGHT_H_
#define _EXPR_WEIGHT_H_
#include"recurse_expr.h"
#include"approx_nat.h"
typedef approx_nat weight;
struct expr_weight_visitor {
weight visit(var * n) { return weight(1); }
weight visit(app const * n, weight const * args);
weight visit(quantifier * n, weight body, weight *, weight *) { body += 1; return body; }
};
typedef recurse_expr<weight, expr_weight_visitor, true> expr_weight;
#endif /* _EXPR_WEIGHT_H_ */

187
src/dead/smt_euf.cpp Normal file
View file

@ -0,0 +1,187 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
smt_euf.cpp
Abstract:
Equality and uninterpreted functions
Author:
Leonardo de Moura (leonardo) 2012-02-14.
Revision History:
--*/
#include"smt_euf.h"
#include"smt_context.h"
#include"ast_smt2_pp.h"
namespace smt {
struct euf_manager::imp {
context & m_context;
ast_manager & m_manager;
region & m_region;
expr_ref_vector m_e_internalized_stack; // stack of the expressions already internalized as enodes.
ptr_vector<enode> m_app2enode; // app -> enode
ptr_vector<enode> m_enodes;
vector<enode_vector> m_decl2enodes; // decl -> enode (for decls with arity > 0)
cg_table m_cg_table;
dyn_ack_manager m_dyn_ack_manager;
struct new_eq {
enode * m_lhs;
enode * m_rhs;
eq_justification m_justification;
new_eq() {}
new_eq(enode * lhs, enode * rhs, eq_justification const & js):
m_lhs(lhs), m_rhs(rhs), m_justification(js) {}
};
svector<new_eq> m_eq_propagation_queue;
struct new_th_eq {
theory_id m_th_id;
theory_var m_lhs;
theory_var m_rhs;
new_th_eq():m_th_id(null_theory_id), m_lhs(null_theory_var), m_rhs(null_theory_var) {}
new_th_eq(theory_id id, theory_var l, theory_var r):m_th_id(id), m_lhs(l), m_rhs(r) {}
};
svector<new_th_eq> m_th_eq_propagation_queue;
svector<new_th_eq> m_th_diseq_propagation_queue;
enode * m_is_diseq_tmp; // auxiliary enode used to find congruent equality atoms.
tmp_enode m_tmp_enode;
ptr_vector<almost_cg_table> m_almost_cg_tables; // temporary field for is_ext_diseq
obj_map<expr, unsigned> m_cached_generation;
obj_hashtable<expr> m_cache_generation_visited;
friend class mk_enode_trail;
class mk_enode_trail : public trail<context> {
imp & m_owner;
public:
mk_enode_trail(imp & o):m_owner(o) {}
virtual void undo(context & ctx) { m_owner.undo_mk_enode(); }
};
mk_enode_trail m_mk_enode_trail;
volatile bool m_cancel_flag;
// Statistics
unsigned m_num_mk_enode;
unsigned m_num_del_enode;
void push_eq(enode * lhs, enode * rhs, eq_justification const & js) {
SASSERT(lhs != rhs);
m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js));
}
void push_new_congruence(enode * n1, enode * n2, bool used_commutativity) {
SASSERT(n1->m_cg == n2);
push_eq(n1, n2, eq_justification::mk_cg(used_commutativity));
}
bool e_internalized(expr const * n) const {
return m_app2enode.get(n->get_id(), 0) != 0;
}
void set_app2enode(expr const * n, enode * e) {
m_app2enode.setx(n->get_id(), e, 0);
}
enode * mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled, unsigned generation) {
TRACE("mk_enode_detail",
tout << mk_ismt2_pp(n, m_manager) << "\n";
tout <<"suppress_args: " << suppress_args << ", merge_tf: " << merge_tf << ", cgc_enabled: " << cgc_enabled << "\n";);
SASSERT(!e_internalized(n));
unsigned scope_lvl = m_context.get_scope_level();
unsigned id = n->get_id();
unsigned _generation = 0;
if (!m_cached_generation.empty() && m_cached_generation.find(n, _generation)) {
generation = _generation;
}
enode * e = enode::mk(m_manager, m_region, m_app2enode, n, generation, suppress_args, merge_tf, scope_lvl, cgc_enabled, true);
TRACE("mk_enode_detail", tout << "e.get_num_args() = " << e->get_num_args() << "\n";);
if (n->get_num_args() == 0 && m_manager.is_value(n))
e->mark_as_interpreted();
TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";);
TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";);
set_app2enode(n, e);
m_e_internalized_stack.push_back(n);
m_context.push_trail_ptr(&m_mk_enode_trail);
m_enodes.push_back(e);
if (e->get_num_args() > 0) {
if (e->is_true_eq()) {
/*
bool_var v = enode2bool_var(e);
assign(literal(v), mk_justification(eq_propagation_justification(e->get_arg(0), e->get_arg(1))));
e->m_cg = e;
*/
}
else {
if (cgc_enabled) {
enode_bool_pair pair = m_cg_table.insert(e);
enode * e_prime = pair.first;
if (e != e_prime) {
e->m_cg = e_prime;
bool used_commutativity = pair.second;
push_new_congruence(e, e_prime, used_commutativity);
}
else {
e->m_cg = e;
}
}
else {
e->m_cg = e;
}
}
if (!e->is_eq()) {
unsigned decl_id = n->get_decl()->get_decl_id();
if (decl_id >= m_decl2enodes.size())
m_decl2enodes.resize(decl_id+1);
m_decl2enodes[decl_id].push_back(e);
}
}
SASSERT(e_internalized(n));
m_num_mk_enode++;
// #ifndef SMTCOMP
// if (m_params.m_trace_stream != NULL)
// *m_params.m_trace_stream << "[attach-enode] #" << n->get_id() << " " << m_generation << "\n";
// #endif
return e;
}
void undo_mk_enode() {
SASSERT(!m_e_internalized_stack.empty());
m_num_del_enode++;
expr * n = m_e_internalized_stack.back();
TRACE("undo_mk_enode", tout << "undo_enode: #" << n->get_id() << "\n" << mk_ismt2_pp(n, m_manager) << "\n";);
TRACE("mk_var_bug", tout << "undo_mk_enode: " << n->get_id() << "\n";);
unsigned n_id = n->get_id();
SASSERT(is_app(n));
enode * e = m_app2enode[n_id];
m_app2enode[n_id] = 0;
if (e->is_cgr() && !e->is_true_eq() && e->is_cgc_enabled()) {
SASSERT(m_cg_table.contains_ptr(e));
m_cg_table.erase(e);
}
if (e->get_num_args() > 0 && !e->is_eq()) {
unsigned decl_id = to_app(n)->get_decl()->get_decl_id();
SASSERT(decl_id < m_decl2enodes.size());
SASSERT(m_decl2enodes[decl_id].back() == e);
m_decl2enodes[decl_id].pop_back();
}
e->del_eh(m_manager);
SASSERT(m_e_internalized_stack.size() == m_enodes.size());
m_enodes.pop_back();
m_e_internalized_stack.pop_back();
}
};
euf_manager::euf_manager(context & ctx) {
}
euf_manager::~euf_manager() {
}
};

55
src/dead/smt_euf.h Normal file
View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
smt_euf.h
Abstract:
Equality and uninterpreted functions
Author:
Leonardo de Moura (leonardo) 2012-02-14.
Revision History:
--*/
#ifndef _SMT_EUF_H_
#define _SMT_EUF_H_
#include"ast.h"
#include"smt_enode.h"
#include"smt_eq_justification.h"
namespace smt {
class context;
class euf_manager {
struct imp;
imp * m_imp;
public:
euf_manager(context & ctx);
~euf_manager();
enode * mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled);
void add_eq(enode * n1, enode * n2, eq_justification js);
bool assume_eq(enode * lhs, enode * rhs);
void reset();
static bool is_eq(enode const * n1, enode const * n2) { return n1->get_root() == n2->get_root(); }
bool is_diseq(enode * n1, enode * n2) const;
bool is_ext_diseq(enode * n1, enode * n2, unsigned depth);
enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args);
bool is_shared(enode * n) const;
unsigned get_num_enodes_of(func_decl const * decl) const;
enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const;
enode_vector::const_iterator end_enodes_of(func_decl const * decl) const;
};
};
#endif

View file

@ -0,0 +1,855 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
euclidean_solver.cpp
Abstract:
Euclidean Solver with support for explanations.
Author:
Leonardo de Moura (leonardo) 2011-07-08.
Revision History:
--*/
#include"euclidean_solver.h"
#include"numeral_buffer.h"
#include"heap.h"
struct euclidean_solver::imp {
typedef unsigned var;
typedef unsigned justification;
typedef unsynch_mpq_manager numeral_manager;
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
typedef numeral_buffer<mpq, numeral_manager> mpq_buffer;
typedef svector<justification> justification_vector;
static const justification null_justification = UINT_MAX;
#define null_var UINT_MAX
#define null_eq_idx UINT_MAX
typedef svector<var> var_vector;
typedef svector<mpz> mpz_vector;
typedef svector<mpq> mpq_vector;
struct elim_order_lt {
unsigned_vector & m_solved;
elim_order_lt(unsigned_vector & s):m_solved(s) {}
bool operator()(var x1, var x2) const { return m_solved[x1] < m_solved[x2]; }
};
typedef heap<elim_order_lt> var_queue; // queue used for scheduling variables for applying substitution.
static unsigned pos(unsigned_vector const & xs, unsigned x_i) {
if (xs.empty())
return UINT_MAX;
int low = 0;
int high = xs.size() - 1;
while (true) {
int mid = low + ((high - low) / 2);
var x_mid = xs[mid];
if (x_i > x_mid) {
low = mid + 1;
if (low > high)
return UINT_MAX;
}
else if (x_i < x_mid) {
high = mid - 1;
if (low > high)
return UINT_MAX;
}
else {
return mid;
}
}
}
/**
Equation as[0]*xs[0] + ... + as[n-1]*xs[n-1] + c = 0 with justification bs[0]*js[0] + ... + bs[m-1]*js[m-1]
*/
struct equation {
mpz_vector m_as;
var_vector m_xs;
mpz m_c;
// justification
mpq_vector m_bs;
justification_vector m_js;
unsigned size() const { return m_xs.size(); }
unsigned js_size() const { return m_js.size(); }
var x(unsigned i) const { return m_xs[i]; }
var & x(unsigned i) { return m_xs[i]; }
mpz const & a(unsigned i) const { return m_as[i]; }
mpz & a(unsigned i) { return m_as[i]; }
mpz const & c() const { return m_c; }
mpz & c() { return m_c; }
var j(unsigned i) const { return m_js[i]; }
var & j(unsigned i) { return m_js[i]; }
mpq const & b(unsigned i) const { return m_bs[i]; }
mpq & b(unsigned i) { return m_bs[i]; }
unsigned pos_x(unsigned x_i) const { return pos(m_xs, x_i); }
};
typedef ptr_vector<equation> equations;
typedef svector<unsigned> occs;
numeral_manager * m_manager;
bool m_owns_m;
volatile bool m_cancel;
equations m_equations;
equations m_solution;
svector<bool> m_parameter;
unsigned_vector m_solved; // null_eq_idx if var is not solved, otherwise the position in m_solution
vector<occs> m_occs; // occurrences of the variable in m_equations.
unsigned m_inconsistent; // null_eq_idx if not inconsistent, otherwise it is the index of an unsatisfiable equality in m_equations.
unsigned m_next_justification;
mpz_buffer m_decompose_buffer;
mpz_buffer m_as_buffer;
mpq_buffer m_bs_buffer;
var_vector m_tmp_xs;
mpz_buffer m_tmp_as;
mpq_buffer m_tmp_bs;
var_vector m_norm_xs_vector;
mpz_vector m_norm_as_vector;
mpq_vector m_norm_bs_vector;
var_queue m_var_queue;
// next candidate
unsigned m_next_eq;
var m_next_x;
mpz m_next_a;
bool m_next_pos_a;
numeral_manager & m() const { return *m_manager; }
bool solved(var x) const { return m_solved[x] != null_eq_idx; }
template<typename Numeral>
void sort_core(svector<Numeral> & as, unsigned_vector & xs, numeral_buffer<Numeral, numeral_manager> & buffer) {
std::sort(xs.begin(), xs.end());
unsigned num = as.size();
for (unsigned i = 0; i < num; i++) {
m().swap(as[i], buffer[xs[i]]);
}
}
template<typename Numeral>
void sort(svector<Numeral> & as, unsigned_vector & xs, numeral_buffer<Numeral, numeral_manager> & buffer) {
unsigned num = as.size();
for (unsigned i = 0; i < num; i++) {
m().set(buffer[xs[i]], as[i]);
}
sort_core(as, xs, buffer);
}
equation * mk_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, unsigned num_js, mpq const * bs, justification const * js,
bool sort = true) {
equation * new_eq = alloc(equation);
for (unsigned i = 0; i < num; i++) {
m().set(m_as_buffer[xs[i]], as[i]);
new_eq->m_as.push_back(mpz());
new_eq->m_xs.push_back(xs[i]);
}
sort_core(new_eq->m_as, new_eq->m_xs, m_as_buffer);
m().set(new_eq->m_c, c);
for (unsigned i = 0; i < num_js; i++) {
m().set(m_bs_buffer[js[i]], bs[i]);
new_eq->m_bs.push_back(mpq());
new_eq->m_js.push_back(js[i]);
}
if (sort)
sort_core(new_eq->m_bs, new_eq->m_js, m_bs_buffer);
return new_eq;
}
template<typename Numeral>
void div(svector<Numeral> & as, mpz const & g) {
unsigned n = as.size();
for (unsigned i = 0; i < n; i++)
m().div(as[i], g, as[i]);
}
void normalize_eq(unsigned eq_idx) {
if (inconsistent())
return;
equation & eq = *(m_equations[eq_idx]);
TRACE("euclidean_solver", tout << "normalizing:\n"; display(tout, eq); tout << "\n";);
unsigned num = eq.size();
if (num == 0) {
// c == 0 inconsistency
if (!m().is_zero(eq.c())) {
TRACE("euclidean_solver", tout << "c = 0 inconsistency detected\n";);
m_inconsistent = eq_idx;
}
else {
del_eq(&eq);
m_equations[eq_idx] = 0;
}
return;
}
mpz g;
mpz a;
m().set(g, eq.a(0));
m().abs(g);
for (unsigned i = 1; i < num; i++) {
if (m().is_one(g))
break;
m().set(a, eq.a(i));
m().abs(a);
m().gcd(g, a, g);
}
if (m().is_one(g))
return;
if (!m().divides(g, eq.c())) {
// g does not divide c
TRACE("euclidean_solver", tout << "gcd inconsistency detected\n";);
m_inconsistent = eq_idx;
return;
}
div(eq.m_as, g);
div(eq.m_bs, g);
m().del(g);
m().del(a);
TRACE("euclidean_solver", tout << "after normalization:\n"; display(tout, eq); tout << "\n";);
}
bool is_better(mpz const & a, var x, unsigned eq_sz) {
SASSERT(m().is_pos(a));
if (m_next_x == null_var)
return true;
if (m().lt(a, m_next_a))
return true;
if (m().lt(m_next_a, a))
return false;
if (m_occs[x].size() < m_occs[m_next_x].size())
return true;
if (m_occs[x].size() > m_occs[m_next_x].size())
return false;
return eq_sz < m_equations[m_next_eq]->size();
}
void updt_next_candidate(unsigned eq_idx) {
if (!m_equations[eq_idx])
return;
mpz abs_a;
equation const & eq = *(m_equations[eq_idx]);
unsigned num = eq.size();
for (unsigned i = 0; i < num; i++) {
mpz const & a = eq.a(i);
m().set(abs_a, a);
m().abs(abs_a);
if (is_better(abs_a, eq.x(i), num)) {
m().set(m_next_a, abs_a);
m_next_x = eq.x(i);
m_next_eq = eq_idx;
m_next_pos_a = m().is_pos(a);
}
}
m().del(abs_a);
}
/**
\brief Select next variable to be eliminated.
Return false if there is not variable to eliminate.
The result is in
m_next_x variable to be eliminated
m_next_eq id of the equation containing x
m_next_a absolute value of the coefficient of x in eq.
m_next_pos_a true if the coefficient of x is positive in eq.
*/
bool select_next_var() {
while (!m_equations.empty() && m_equations.back() == 0)
m_equations.pop_back();
if (m_equations.empty())
return false;
SASSERT(!m_equations.empty() && m_equations.back() != 0);
m_next_x = null_var;
unsigned eq_idx = m_equations.size();
while (eq_idx > 0) {
--eq_idx;
updt_next_candidate(eq_idx);
// stop as soon as possible
// TODO: use heuristics
if (m_next_x != null_var && m().is_one(m_next_a))
return true;
}
CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout););
SASSERT(m_next_x != null_var);
return true;
}
template<typename Numeral>
void del_nums(svector<Numeral> & as) {
unsigned sz = as.size();
for (unsigned i = 0; i < sz; i++)
m().del(as[i]);
as.reset();
}
void del_eq(equation * eq) {
m().del(eq->c());
del_nums(eq->m_as);
del_nums(eq->m_bs);
dealloc(eq);
}
void del_equations(equations & eqs) {
unsigned sz = eqs.size();
for (unsigned i = 0; i < sz; i++) {
if (eqs[i])
del_eq(eqs[i]);
}
}
/**
\brief Store the "solved" variables in xs into m_var_queue.
*/
void schedule_var_subst(unsigned num, var const * xs) {
for (unsigned i = 0; i < num; i++) {
if (solved(xs[i]))
m_var_queue.insert(xs[i]);
}
}
void schedule_var_subst(var_vector const & xs) {
schedule_var_subst(xs.size(), xs.c_ptr());
}
/**
\brief Store as1*xs1 + k*as2*xs2 into new_as*new_xs
If UpdateOcc == true,
Then,
1) for each variable x occurring in xs2 but not in xs1:
- eq_idx is added to m_occs[x]
2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero,
- eq_idx is removed from m_occs[x] IF x != except_var
If UpdateQueue == true
Then,
1) for each variable x occurring in xs2 but not in xs1:
- if x is solved, then x is inserted into m_var_queue
2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero,
- if x is solved, then x is removed from m_var_queue
*/
template<typename Numeral, bool UpdateOcc, bool UpdateQueue>
void addmul(svector<Numeral> const & as1, var_vector const & xs1,
mpz const & k, svector<Numeral> const & as2, var_vector const & xs2,
numeral_buffer<Numeral, numeral_manager> & new_as, var_vector & new_xs,
unsigned eq_idx = null_eq_idx, var except_var = null_var) {
Numeral new_a;
SASSERT(as1.size() == xs1.size());
SASSERT(as2.size() == xs2.size());
new_as.reset();
new_xs.reset();
unsigned sz1 = xs1.size();
unsigned sz2 = xs2.size();
unsigned i1 = 0;
unsigned i2 = 0;
while (true) {
if (i1 == sz1) {
// copy remaining entries from as2*xs2
while (i2 < sz2) {
var x2 = xs2[i2];
if (UpdateOcc)
m_occs[x2].push_back(eq_idx);
if (UpdateQueue && solved(x2))
m_var_queue.insert(x2);
new_as.push_back(Numeral());
m().mul(k, as2[i2], new_as.back());
new_xs.push_back(x2);
i2++;
}
break;
}
if (i2 == sz2) {
// copy remaining entries from as1*xs1
while (i1 < sz1) {
new_as.push_back(as1[i1]);
new_xs.push_back(xs1[i1]);
i1++;
}
break;
}
var x1 = xs1[i1];
var x2 = xs2[i2];
if (x1 < x2) {
new_as.push_back(as1[i1]);
new_xs.push_back(xs1[i1]);
i1++;
}
else if (x1 > x2) {
if (UpdateOcc)
m_occs[x2].push_back(eq_idx);
if (UpdateQueue && solved(x2))
m_var_queue.insert(x2);
new_as.push_back(Numeral());
m().mul(k, as2[i2], new_as.back());
new_xs.push_back(x2);
i2++;
}
else {
m().addmul(as1[i1], k, as2[i2], new_a);
TRACE("euclidean_solver_add_mul", tout << "i1: " << i1 << ", i2: " << i2 << " new_a: " << m().to_string(new_a) << "\n";
tout << "as1: " << m().to_string(as1[i1]) << ", k: " << m().to_string(k) << ", as2: " << m().to_string(as2[i2]) << "\n";);
if (m().is_zero(new_a)) {
// variable was canceled
if (UpdateOcc && x1 != except_var)
m_occs[x1].erase(eq_idx);
if (UpdateQueue && solved(x1) && m_var_queue.contains(x1))
m_var_queue.erase(x1);
}
else {
new_as.push_back(new_a);
new_xs.push_back(x1);
}
i1++;
i2++;
}
}
m().del(new_a);
}
template<bool UpdateOcc, bool UpdateQueue>
void apply_solution(var x, mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js,
unsigned eq_idx = null_eq_idx, var except_var = null_var) {
SASSERT(solved(x));
unsigned idx = pos(xs, x);
if (idx == UINT_MAX)
return;
mpz const & a1 = as[idx];
SASSERT(!m().is_zero(a1));
equation const & eq2 = *(m_solution[m_solved[x]]);
SASSERT(eq2.pos_x(x) != UINT_MAX);
SASSERT(m().is_minus_one(eq2.a(eq2.pos_x(x))));
TRACE("euclidean_solver_apply",
tout << "applying: " << m().to_string(a1) << " * "; display(tout, eq2); tout << "\n";
for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
addmul<mpz, UpdateOcc, UpdateQueue>(as, xs, a1, eq2.m_as, eq2.m_xs, m_tmp_as, m_tmp_xs, eq_idx, except_var);
m().addmul(c, a1, eq2.m_c, c);
m_tmp_as.swap(as);
m_tmp_xs.swap(xs);
SASSERT(as.size() == xs.size());
TRACE("euclidean_solver_apply", for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
addmul<mpq, false, false>(bs, js, a1, eq2.m_bs, eq2.m_js, m_tmp_bs, m_tmp_xs);
m_tmp_bs.swap(bs);
m_tmp_xs.swap(js);
SASSERT(pos(xs, x) == UINT_MAX);
}
void apply_solution(mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js) {
m_var_queue.reset();
schedule_var_subst(xs);
while (!m_var_queue.empty()) {
var x = m_var_queue.erase_min();
apply_solution<false, true>(x, as, xs, c, bs, js);
}
}
void apply_solution(equation & eq) {
apply_solution(eq.m_as, eq.m_xs, eq.m_c, eq.m_bs, eq.m_js);
}
void display(std::ostream & out, equation const & eq) const {
unsigned num = eq.js_size();
for (unsigned i = 0; i < num; i ++) {
if (i > 0) out << " ";
out << m().to_string(eq.b(i)) << "*j" << eq.j(i);
}
if (num > 0) out << " ";
out << "|= ";
num = eq.size();
for (unsigned i = 0; i < num; i++) {
out << m().to_string(eq.a(i)) << "*x" << eq.x(i) << " + ";
}
out << m().to_string(eq.c()) << " = 0";
}
void display(std::ostream & out, equations const & eqs) const {
unsigned num = eqs.size();
for (unsigned i = 0; i < num; i++) {
if (eqs[i]) {
display(out, *(eqs[i]));
out << "\n";
}
}
}
void display(std::ostream & out) const {
if (inconsistent()) {
out << "inconsistent: ";
display(out, *(m_equations[m_inconsistent]));
out << "\n";
}
out << "solution set:\n";
display(out, m_solution);
out << "todo:\n";
display(out, m_equations);
}
void add_occs(unsigned eq_idx) {
equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++)
m_occs[eq.x(i)].push_back(eq_idx);
}
imp(numeral_manager * m):
m_manager(m == 0 ? alloc(numeral_manager) : m),
m_owns_m(m == 0),
m_decompose_buffer(*m_manager),
m_as_buffer(*m_manager),
m_bs_buffer(*m_manager),
m_tmp_as(*m_manager),
m_tmp_bs(*m_manager),
m_var_queue(16, elim_order_lt(m_solved)) {
m_inconsistent = null_eq_idx;
m_next_justification = 0;
m_cancel = false;
m_next_x = null_var;
m_next_eq = null_eq_idx;
}
~imp() {
m().del(m_next_a);
del_equations(m_equations);
del_equations(m_solution);
if (m_owns_m)
dealloc(m_manager);
}
var mk_var(bool parameter) {
var x = m_solved.size();
m_parameter.push_back(parameter);
m_solved.push_back(null_eq_idx);
m_occs.push_back(occs());
m_as_buffer.push_back(mpz());
m_var_queue.reserve(x+1);
return x;
}
justification mk_justification() {
justification r = m_next_justification;
m_bs_buffer.push_back(mpq());
m_next_justification++;
return r;
}
void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) {
if (inconsistent())
return;
equation * eq;
if (j == null_justification) {
eq = mk_eq(num, as, xs, c, 0, 0, 0);
}
else {
mpq one(1);
eq = mk_eq(num, as, xs, c, 1, &one, &j);
}
TRACE("euclidean_solver", tout << "new-eq:\n"; display(tout, *eq); tout << "\n";);
unsigned eq_idx = m_equations.size();
m_equations.push_back(eq);
apply_solution(*eq);
normalize_eq(eq_idx);
add_occs(eq_idx);
TRACE("euclidean_solver", tout << "asserted:\n"; display(tout, *eq); tout << "\n";);
}
justification_vector const & get_justification() const {
SASSERT(inconsistent());
return m_equations[m_inconsistent]->m_js;
}
template<typename Numeral>
void neg_coeffs(svector<Numeral> & as) {
unsigned sz = as.size();
for (unsigned i = 0; i < sz; i++) {
m().neg(as[i]);
}
}
void substitute_most_recent_solution(var x) {
SASSERT(!m_solution.empty());
equation & eq = *(m_solution.back());
TRACE("euclidean_solver", tout << "applying solution for x" << x << "\n"; display(tout, eq); tout << "\n";);
occs & use_list = m_occs[x];
occs::iterator it = use_list.begin();
occs::iterator end = use_list.end();
for (; it != end; ++it) {
unsigned eq_idx = *it;
// remark we don't want to update the use_list of x while we are traversing it.
equation & eq2 = *(m_equations[eq_idx]);
TRACE("euclidean_solver", tout << "eq before substituting x" << x << "\n"; display(tout, eq2); tout << "\n";);
apply_solution<true, false>(x, eq2.m_as, eq2.m_xs, eq2.m_c, eq2.m_bs, eq2.m_js, eq_idx, x);
TRACE("euclidean_solver", tout << "eq after substituting x" << x << "\n"; display(tout, eq2); tout << "\n";);
normalize_eq(eq_idx);
if (inconsistent())
break;
}
use_list.reset();
}
void elim_unit() {
SASSERT(m().is_one(m_next_a));
equation & eq = *(m_equations[m_next_eq]);
TRACE("euclidean_solver", tout << "eliminating equation with unit coefficient:\n"; display(tout, eq); tout << "\n";);
if (m_next_pos_a) {
// neg coeffs... to make sure that m_next_x is -1
neg_coeffs(eq.m_as);
neg_coeffs(eq.m_bs);
}
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
m_occs[eq.x(i)].erase(m_next_eq);
}
m_solved[m_next_x] = m_solution.size();
m_solution.push_back(&eq);
m_equations[m_next_eq] = 0;
substitute_most_recent_solution(m_next_x);
}
void decompose(bool pos_a, mpz const & abs_a, mpz const & a_i, mpz & new_a_i, mpz & r_i) {
mpz abs_a_i;
bool pos_a_i = m().is_pos(a_i);
m().set(abs_a_i, a_i);
if (!pos_a_i)
m().neg(abs_a_i);
bool new_pos_a_i = pos_a_i;
if (pos_a)
new_pos_a_i = !new_pos_a_i;
m().div(abs_a_i, abs_a, new_a_i);
if (m().divides(abs_a, a_i)) {
m().reset(r_i);
}
else {
if (pos_a_i)
m().submul(a_i, abs_a, new_a_i, r_i);
else
m().addmul(a_i, abs_a, new_a_i, r_i);
}
if (!new_pos_a_i)
m().neg(new_a_i);
m().del(abs_a_i);
}
void decompose_and_elim() {
m_tmp_xs.reset();
mpz_buffer & buffer = m_decompose_buffer;
buffer.reset();
var p = mk_var(true);
mpz new_a_i;
equation & eq = *(m_equations[m_next_eq]);
TRACE("euclidean_solver", tout << "decompositing equation for x" << m_next_x << "\n"; display(tout, eq); tout << "\n";);
unsigned sz = eq.size();
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
var x_i = eq.x(i);
if (x_i == m_next_x) {
m().set(new_a_i, -1);
buffer.push_back(new_a_i);
m_tmp_xs.push_back(m_next_x);
m_occs[x_i].erase(m_next_eq);
}
else {
decompose(m_next_pos_a, m_next_a, eq.a(i), new_a_i, eq.m_as[j]);
buffer.push_back(new_a_i);
m_tmp_xs.push_back(x_i);
if (m().is_zero(eq.m_as[j])) {
m_occs[x_i].erase(m_next_eq);
}
else {
eq.m_xs[j] = x_i;
j++;
}
}
}
SASSERT(j < sz);
// add parameter: p to new equality, and m_next_pos_a * m_next_a * p to current eq
m().set(new_a_i, 1);
buffer.push_back(new_a_i);
m_tmp_xs.push_back(p);
m().set(eq.m_as[j], m_next_a);
if (!m_next_pos_a)
m().neg(eq.m_as[j]);
eq.m_xs[j] = p;
j++;
unsigned new_sz = j;
// shrink current eq
for (; j < sz; j++)
m().del(eq.m_as[j]);
eq.m_as.shrink(new_sz);
eq.m_xs.shrink(new_sz);
// ajust c
mpz new_c;
decompose(m_next_pos_a, m_next_a, eq.m_c, new_c, eq.m_c);
// create auxiliary equation
equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, 0, 0, false);
// new_eq doesn't need to normalized, since it has unit coefficients
TRACE("euclidean_solver", tout << "decomposition: new parameter x" << p << " aux eq:\n";
display(tout, *new_eq); tout << "\n";
display(tout, eq); tout << "\n";);
m_solved[m_next_x] = m_solution.size();
m_solution.push_back(new_eq);
substitute_most_recent_solution(m_next_x);
m().del(new_a_i);
m().del(new_c);
}
bool solve() {
if (inconsistent()) return false;
TRACE("euclidean_solver", tout << "solving...\n"; display(tout););
while (select_next_var()) {
CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout););
TRACE("euclidean_solver", tout << "eliminating x" << m_next_x << "\n";);
if (m().is_one(m_next_a) || m().is_minus_one(m_next_a))
elim_unit();
else
decompose_and_elim();
TRACE("euclidean_solver_step", display(tout););
if (inconsistent()) return false;
}
return true;
}
bool inconsistent() const {
return m_inconsistent != null_eq_idx;
}
bool is_parameter(var x) const {
return m_parameter[x];
}
void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) {
TRACE("euclidean_solver", tout << "before applying solution set\n";
for (unsigned i = 0; i < num; i++) {
tout << m().to_string(as[i]) << "*x" << xs[i] << " ";
}
tout << "\n";);
m_norm_xs_vector.reset();
m_norm_as_vector.reset();
for (unsigned i = 0; i < num; i++) {
m_norm_xs_vector.push_back(xs[i]);
m_norm_as_vector.push_back(mpz());
m().set(m_norm_as_vector.back(), as[i]);
}
sort(m_norm_as_vector, m_norm_xs_vector, m_as_buffer);
m_norm_bs_vector.reset();
js.reset();
m().set(c_prime, c);
apply_solution(m_norm_as_vector, m_norm_xs_vector, c_prime, m_norm_bs_vector, js);
TRACE("euclidean_solver", tout << "after applying solution set\n";
for (unsigned i = 0; i < m_norm_as_vector.size(); i++) {
tout << m().to_string(m_norm_as_vector[i]) << "*x" << m_norm_xs_vector[i] << " ";
}
tout << "\n";);
// compute gcd of the result m_norm_as_vector
if (m_norm_as_vector.empty()) {
m().set(a_prime, 0);
}
else {
mpz a;
m().set(a_prime, m_norm_as_vector[0]);
m().abs(a_prime);
unsigned sz = m_norm_as_vector.size();
for (unsigned i = 1; i < sz; i++) {
if (m().is_one(a_prime))
break;
m().set(a, m_norm_as_vector[i]);
m().abs(a);
m().gcd(a_prime, a, a_prime);
}
m().del(a);
}
// REMARK: m_norm_bs_vector contains the linear combination of the justifications. It may be useful if we
// decided (one day) to generate detailed proofs for this step.
del_nums(m_norm_as_vector);
del_nums(m_norm_bs_vector);
}
void set_cancel(bool f) {
m_cancel = f;
}
};
euclidean_solver::euclidean_solver(numeral_manager * m):
m_imp(alloc(imp, m)) {
}
euclidean_solver::~euclidean_solver() {
dealloc(m_imp);
}
euclidean_solver::numeral_manager & euclidean_solver::m() const {
return m_imp->m();
}
void euclidean_solver::reset() {
numeral_manager * m = m_imp->m_manager;
bool owns_m = m_imp->m_owns_m;
m_imp->m_owns_m = false;
#pragma omp critical (euclidean_solver)
{
dealloc(m_imp);
m_imp = alloc(imp, m);
m_imp->m_owns_m = owns_m;
}
}
euclidean_solver::var euclidean_solver::mk_var() {
return m_imp->mk_var(false);
}
euclidean_solver::justification euclidean_solver::mk_justification() {
return m_imp->mk_justification();
}
void euclidean_solver::assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) {
m_imp->assert_eq(num, as, xs, c, j);
}
bool euclidean_solver::solve() {
return m_imp->solve();
}
euclidean_solver::justification_vector const & euclidean_solver::get_justification() const {
return m_imp->get_justification();
}
bool euclidean_solver::inconsistent() const {
return m_imp->inconsistent();
}
bool euclidean_solver::is_parameter(var x) const {
return m_imp->is_parameter(x);
}
void euclidean_solver::normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime,
justification_vector & js) {
return m_imp->normalize(num, as, xs, c, a_prime, c_prime, js);
}
void euclidean_solver::set_cancel(bool f) {
#pragma omp critical (euclidean_solver)
{
m_imp->set_cancel(f);
}
}
void euclidean_solver::display(std::ostream & out) const {
m_imp->display(out);
}

View file

@ -0,0 +1,106 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
euclidean_solver.h
Abstract:
Euclidean Solver with support for explanations.
Author:
Leonardo de Moura (leonardo) 2011-07-08.
Revision History:
--*/
#ifndef _EUCLIDEAN_SOLVER_H_
#define _EUCLIDEAN_SOLVER_H_
#include"mpq.h"
#include"vector.h"
class euclidean_solver {
struct imp;
imp * m_imp;
public:
typedef unsigned var;
typedef unsigned justification;
typedef unsynch_mpq_manager numeral_manager;
typedef svector<justification> justification_vector;
static const justification null_justification = UINT_MAX;
/**
\brief If m == 0, then the solver will create its own numeral manager.
*/
euclidean_solver(numeral_manager * m);
~euclidean_solver();
numeral_manager & m() const;
/**
\brief Reset the state of the euclidean solver.
*/
void reset();
/**
\brief Creates a integer variable.
*/
var mk_var();
/**
\brief Creates a fresh justification id.
*/
justification mk_justification();
/**
\brief Asserts an equation of the form as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c = 0 with justification j.
The numerals must be created using the numeral_manager m().
*/
void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j = null_justification);
/**
\brief Solve the current set of equations. Return false if it is inconsistent.
*/
bool solve();
/**
\brief Return a set of justifications (proof) for the inconsitency.
\pre inconsistent()
*/
justification_vector const & get_justification() const;
bool inconsistent() const;
/**
\brief Return true if the variable is a "parameter" created by the Euclidean solver.
*/
bool is_parameter(var x) const;
/**
Given a linear polynomial as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c and the current solution set,
It applies the solution set to produce a polynomial of the for a_prime * p + c_prime, where
a_prime * p represents a linear polynomial where the coefficient of every monomial is a multiple of
a_prime.
The justification is stored in js.
Note that, this function does not return the actual p.
The numerals must be created using the numeral_manager m().
*/
void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js);
/**
\brief Set/Reset the cancel flag.
*/
void set_cancel(bool f);
void display(std::ostream & out) const;
};
#endif

View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
goal_shared_occs.cpp
Abstract:
Functor for computing the set of shared occurrences in a goal.
Author:
Leonardo de Moura (leonardo) 2011-12-28
Revision History:
--*/
#include"goal_shared_occs.h"
void goal_shared_occs::operator()(goal const & g) {
m_occs.reset();
shared_occs_mark visited;
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++) {
expr * t = g.form(i);
m_occs(t, visited);
}
}

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
goal_shared_occs.h
Abstract:
Functor for computing the set of shared occurrences in a goal.
Author:
Leonardo de Moura (leonardo) 2011-12-28
Revision History:
--*/
#ifndef _GOAL_SHARED_OCCS_H_
#define _GOAL_SHARED_OCCS_H_
#include"goal.h"
#include"shared_occs.h"
/**
\brief Functor for computing the set of shared occurrences in a goal.
It is essentially a wrapper for shared_occs functor.
*/
class goal_shared_occs {
shared_occs m_occs;
public:
goal_shared_occs(ast_manager & m, bool track_atomic = false, bool visit_quantifiers = true, bool visit_patterns = false):
m_occs(m, track_atomic, visit_quantifiers, visit_patterns) {
}
void operator()(goal const & s);
bool is_shared(expr * t) { return m_occs.is_shared(t); }
unsigned num_shared() const { return m_occs.num_shared(); }
void reset() { return m_occs.reset(); }
void cleanup() { return m_occs.cleanup(); }
void display(std::ostream & out, ast_manager & m) const { m_occs.display(out, m); }
};
#endif

View file

@ -0,0 +1,485 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
strategic_solver.h
Abstract:
Strategies -> Solver
Author:
Leonardo (leonardo) 2011-05-19
Notes:
--*/
#include"strategic_solver.h"
#include"cmd_context.h"
#include"scoped_timer.h"
#include"params2front_end_params.h"
#include"ast_smt2_pp.h"
// minimum verbosity level for portfolio verbose messages
#define PS_VB_LVL 15
strategic_solver::strategic_solver():
m_manager(0),
m_fparams(0),
m_force_tactic(false),
m_inc_mode(false),
m_check_sat_executed(false),
m_inc_solver(0),
m_inc_solver_timeout(UINT_MAX),
m_inc_unknown_behavior(IUB_USE_TACTIC_IF_QF),
m_default_fct(0),
m_curr_tactic(0),
m_proof(0),
m_callback(0) {
m_use_inc_solver_results = false;
DEBUG_CODE(m_num_scopes = 0;);
m_produce_proofs = false;
m_produce_models = false;
m_produce_unsat_cores = false;
}
strategic_solver::~strategic_solver() {
SASSERT(!m_curr_tactic);
dictionary<tactic_factory*>::iterator it = m_logic2fct.begin();
dictionary<tactic_factory*>::iterator end = m_logic2fct.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
if (m_proof)
m().dec_ref(m_proof);
}
bool strategic_solver::has_quantifiers() const {
unsigned sz = get_num_assertions();
for (unsigned i = 0; i < sz; i++) {
if (::has_quantifiers(get_assertion(i)))
return true;
}
return false;
}
/**
\brief Return true if a tactic should be used when the incremental solver returns unknown.
*/
bool strategic_solver::use_tactic_when_undef() const {
switch (m_inc_unknown_behavior) {
case IUB_RETURN_UNDEF: return false;
case IUB_USE_TACTIC_IF_QF: return !has_quantifiers();
case IUB_USE_TACTIC: return true;
default:
UNREACHABLE();
return false;
}
}
void strategic_solver::set_inc_solver(solver * s) {
SASSERT(m_inc_solver == 0);
SASSERT(m_num_scopes == 0);
m_inc_solver = s;
if (m_callback)
m_inc_solver->set_progress_callback(m_callback);
}
void strategic_solver::updt_params(params_ref const & p) {
if (m_inc_solver)
m_inc_solver->updt_params(p);
if (m_fparams)
params2front_end_params(p, *m_fparams);
}
void strategic_solver::collect_param_descrs(param_descrs & r) {
if (m_inc_solver)
m_inc_solver->collect_param_descrs(r);
}
/**
\brief Set a timeout for each check_sat query that is processed by the inc_solver.
timeout == UINT_MAX means infinite
After the timeout a strategy is used.
*/
void strategic_solver::set_inc_solver_timeout(unsigned timeout) {
m_inc_solver_timeout = timeout;
}
/**
\brief Set the default tactic factory.
It is used if there is no tactic for a given logic.
*/
void strategic_solver::set_default_tactic(tactic_factory * fct) {
m_default_fct = fct;
}
/**
\brief Set a tactic factory for a given logic.
*/
void strategic_solver::set_tactic_for(symbol const & logic, tactic_factory * fct) {
tactic_factory * old_fct;
if (m_logic2fct.find(logic, old_fct)) {
dealloc(old_fct);
}
m_logic2fct.insert(logic, fct);
}
void strategic_solver::init(ast_manager & m, symbol const & logic) {
m_manager = &m;
m_logic = logic;
if (m_inc_mode) {
SASSERT(m_inc_solver);
m_inc_solver->init(m, logic);
}
}
// delayed inc solver initialization
void strategic_solver::init_inc_solver() {
if (m_inc_mode)
return; // solver was already initialized
if (!m_inc_solver)
return; // inc solver was not installed
m_inc_mode = true;
m_inc_solver->set_front_end_params(*m_fparams);
m_inc_solver->init(m(), m_logic);
unsigned sz = get_num_assertions();
for (unsigned i = 0; i < sz; i++) {
m_inc_solver->assert_expr(get_assertion(i));
}
}
void strategic_solver::collect_statistics(statistics & st) const {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
m_inc_solver->collect_statistics(st);
}
else {
if (m_curr_tactic)
m_curr_tactic->collect_statistics(st); // m_curr_tactic is still being executed.
else
st.copy(m_stats);
}
}
void strategic_solver::reset() {
m_logic = symbol::null;
m_inc_mode = false;
m_check_sat_executed = false;
if (m_inc_solver)
m_inc_solver->reset();
SASSERT(!m_curr_tactic);
m_use_inc_solver_results = false;
reset_results();
}
void strategic_solver::reset_results() {
m_use_inc_solver_results = false;
m_model = 0;
if (m_proof) {
m().dec_ref(m_proof);
m_proof = 0;
}
m_reason_unknown.clear();
m_stats.reset();
}
void strategic_solver::assert_expr(expr * t) {
if (m_check_sat_executed && !m_inc_mode) {
// a check sat was already executed --> switch to incremental mode
init_inc_solver();
SASSERT(m_inc_solver == 0 || m_inc_mode);
}
if (m_inc_mode) {
SASSERT(m_inc_solver);
m_inc_solver->assert_expr(t);
}
}
void strategic_solver::push() {
DEBUG_CODE(m_num_scopes++;);
init_inc_solver();
if (m_inc_solver)
m_inc_solver->push();
}
void strategic_solver::pop(unsigned n) {
DEBUG_CODE({
SASSERT(n <= m_num_scopes);
m_num_scopes -= n;
});
init_inc_solver();
if (m_inc_solver)
m_inc_solver->pop(n);
}
unsigned strategic_solver::get_scope_level() const {
if (m_inc_solver)
return m_inc_solver->get_scope_level();
else
return 0;
}
struct aux_timeout_eh : public event_handler {
solver * m_solver;
volatile bool m_canceled;
aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {}
virtual void operator()() {
m_solver->cancel();
m_canceled = true;
}
};
struct strategic_solver::mk_tactic {
strategic_solver * m_solver;
mk_tactic(strategic_solver * s, tactic_factory * f):m_solver(s) {
ast_manager & m = s->m();
params_ref p;
front_end_params2params(*s->m_fparams, p);
tactic * tct = (*f)(m, p);
tct->set_front_end_params(*s->m_fparams);
tct->set_logic(s->m_logic);
if (s->m_callback)
tct->set_progress_callback(s->m_callback);
#pragma omp critical (strategic_solver)
{
s->m_curr_tactic = tct;
}
}
~mk_tactic() {
#pragma omp critical (strategic_solver)
{
m_solver->m_curr_tactic = 0;
}
}
};
tactic_factory * strategic_solver::get_tactic_factory() const {
tactic_factory * f = 0;
if (m_logic2fct.find(m_logic, f))
return f;
return m_default_fct.get();
}
lbool strategic_solver::check_sat_with_assumptions(unsigned num_assumptions, expr * const * assumptions) {
if (!m_inc_solver) {
IF_VERBOSE(PS_VB_LVL, verbose_stream() << "incremental solver was not installed, returning unknown...\n";);
m_use_inc_solver_results = false;
m_reason_unknown = "incomplete";
return l_undef;
}
init_inc_solver();
m_use_inc_solver_results = true;
return m_inc_solver->check_sat(num_assumptions, assumptions);
}
lbool strategic_solver::check_sat(unsigned num_assumptions, expr * const * assumptions) {
reset_results();
m_check_sat_executed = true;
if (num_assumptions > 0 || // assumptions were provided
(!m_fparams->m_auto_config && !m_force_tactic) // auto config and force_tactic are turned off
) {
// must use incremental solver
return check_sat_with_assumptions(num_assumptions, assumptions);
}
tactic_factory * factory = get_tactic_factory();
if (factory == 0)
init_inc_solver(); // try to switch to incremental solver
if (m_inc_mode) {
SASSERT(m_inc_solver);
unsigned timeout = m_inc_solver_timeout;
if (factory == 0)
timeout = UINT_MAX; // there is no tactic available
if (timeout == UINT_MAX) {
IF_VERBOSE(PS_VB_LVL, verbose_stream() << "using incremental solver (without a timeout).\n";);
m_use_inc_solver_results = true;
lbool r = m_inc_solver->check_sat(0, 0);
if (r != l_undef || factory == 0 || !use_tactic_when_undef()) {
m_use_inc_solver_results = true;
return r;
}
}
else {
IF_VERBOSE(PS_VB_LVL, verbose_stream() << "using incremental solver (with timeout).\n";);
SASSERT(factory != 0);
aux_timeout_eh eh(m_inc_solver.get());
lbool r;
{
scoped_timer timer(m_inc_solver_timeout, &eh);
r = m_inc_solver->check_sat(0, 0);
}
if ((r != l_undef || !use_tactic_when_undef()) && !eh.m_canceled) {
m_use_inc_solver_results = true;
return r;
}
}
IF_VERBOSE(PS_VB_LVL, verbose_stream() << "incremental solver failed, trying tactic.\n";);
}
m_use_inc_solver_results = false;
if (factory == 0) {
IF_VERBOSE(PS_VB_LVL, verbose_stream() << "there is no tactic available for the current logic.\n";);
m_reason_unknown = "incomplete";
return l_undef;
}
goal_ref g = alloc(goal, m(), m_produce_proofs, m_produce_models, m_produce_unsat_cores);
unsigned sz = get_num_assertions();
for (unsigned i = 0; i < sz; i++) {
g->assert_expr(get_assertion(i));
}
expr_dependency_ref core(m());
mk_tactic tct_maker(this, factory);
SASSERT(m_curr_tactic);
proof_ref pr(m());
lbool r = ::check_sat(*(m_curr_tactic.get()), g, m_model, pr, core, m_reason_unknown);
m_curr_tactic->collect_statistics(m_stats);
if (pr) {
m_proof = pr;
m().inc_ref(m_proof);
}
return r;
}
void strategic_solver::set_cancel(bool f) {
if (m_inc_solver)
m_inc_solver->set_cancel(f);
#pragma omp critical (strategic_solver)
{
if (m_curr_tactic)
m_curr_tactic->set_cancel(f);
}
}
void strategic_solver::get_unsat_core(ptr_vector<expr> & r) {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
m_inc_solver->get_unsat_core(r);
}
}
void strategic_solver::get_model(model_ref & m) {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
m_inc_solver->get_model(m);
}
else {
m = m_model;
}
}
proof * strategic_solver::get_proof() {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
return m_inc_solver->get_proof();
}
else {
return m_proof;
}
}
std::string strategic_solver::reason_unknown() const {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
return m_inc_solver->reason_unknown();
}
return m_reason_unknown;
}
void strategic_solver::get_labels(svector<symbol> & r) {
if (m_use_inc_solver_results) {
SASSERT(m_inc_solver);
m_inc_solver->get_labels(r);
}
}
void strategic_solver::set_progress_callback(progress_callback * callback) {
m_callback = callback;
if (m_inc_solver)
m_inc_solver->set_progress_callback(callback);
}
void strategic_solver::display(std::ostream & out) const {
if (m_manager) {
unsigned num = get_num_assertions();
out << "(solver";
for (unsigned i = 0; i < num; i++) {
out << "\n " << mk_ismt2_pp(get_assertion(i), m(), 2);
}
out << ")";
}
else {
out << "(solver)";
}
}
strategic_solver_cmd::strategic_solver_cmd(cmd_context & ctx):
m_ctx(ctx) {
}
unsigned strategic_solver_cmd::get_num_assertions() const {
return static_cast<unsigned>(m_ctx.end_assertions() - m_ctx.begin_assertions());
}
expr * strategic_solver_cmd::get_assertion(unsigned idx) const {
SASSERT(idx < get_num_assertions());
return m_ctx.begin_assertions()[idx];
}
strategic_solver_api::ctx::ctx(ast_manager & m):m_assertions(m) {
}
void strategic_solver_api::init(ast_manager & m, symbol const & logic) {
strategic_solver::init(m, logic);
m_ctx = alloc(ctx, m);
}
unsigned strategic_solver_api::get_num_assertions() const {
if (m_ctx == 0)
return 0;
return m_ctx->m_assertions.size();
}
expr * strategic_solver_api::get_assertion(unsigned idx) const {
SASSERT(m_ctx);
return m_ctx->m_assertions.get(idx);
}
void strategic_solver_api::assert_expr(expr * t) {
SASSERT(m_ctx);
strategic_solver::assert_expr(t);
m_ctx->m_assertions.push_back(t);
}
void strategic_solver_api::push() {
SASSERT(m_ctx);
strategic_solver::push();
m_ctx->m_scopes.push_back(m_ctx->m_assertions.size());
}
void strategic_solver_api::pop(unsigned n) {
SASSERT(m_ctx);
unsigned new_lvl = m_ctx->m_scopes.size() - n;
unsigned old_sz = m_ctx->m_scopes[new_lvl];
m_ctx->m_assertions.shrink(old_sz);
m_ctx->m_scopes.shrink(new_lvl);
strategic_solver::pop(n);
}
void strategic_solver_api::reset() {
m_ctx = 0;
strategic_solver::reset();
}

View file

@ -0,0 +1,155 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
strategic_solver.h
Abstract:
Strategies -> Solver
Author:
Leonardo (leonardo) 2011-05-19
Notes:
--*/
#ifndef _STRATEGIC_SOLVER_H_
#define _STRATEGIC_SOLVER_H_
#include"solver.h"
#include"tactic.h"
class progress_callback;
struct front_end_params;
class strategic_solver : public solver {
public:
// Behavior when the incremental solver returns unknown.
enum inc_unknown_behavior {
IUB_RETURN_UNDEF, // just return unknown
IUB_USE_TACTIC_IF_QF, // invoke tactic if problem is quantifier free
IUB_USE_TACTIC // invoke tactic
};
private:
ast_manager * m_manager;
front_end_params * m_fparams;
symbol m_logic;
bool m_force_tactic; // use tactics even when auto_config = false
bool m_inc_mode;
bool m_check_sat_executed;
scoped_ptr<solver> m_inc_solver;
unsigned m_inc_solver_timeout;
inc_unknown_behavior m_inc_unknown_behavior;
scoped_ptr<tactic_factory> m_default_fct;
dictionary<tactic_factory*> m_logic2fct;
ref<tactic> m_curr_tactic;
bool m_use_inc_solver_results;
model_ref m_model;
proof * m_proof;
std::string m_reason_unknown;
statistics m_stats;
#ifdef Z3DEBUG
unsigned m_num_scopes;
#endif
bool m_produce_proofs;
bool m_produce_models;
bool m_produce_unsat_cores;
progress_callback * m_callback;
void reset_results();
void init_inc_solver();
tactic_factory * get_tactic_factory() const;
lbool check_sat_with_assumptions(unsigned num_assumptions, expr * const * assumptions);
struct mk_tactic;
bool has_quantifiers() const;
bool use_tactic_when_undef() const;
public:
strategic_solver();
~strategic_solver();
ast_manager & m() const { SASSERT(m_manager); return *m_manager; }
void set_inc_solver(solver * s);
void set_inc_solver_timeout(unsigned timeout);
void set_default_tactic(tactic_factory * fct);
void set_tactic_for(symbol const & logic, tactic_factory * fct);
void set_inc_unknown_behavior(inc_unknown_behavior b) { m_inc_unknown_behavior = b; }
void force_tactic(bool f) { m_force_tactic = f; }
virtual void set_front_end_params(front_end_params & p) { m_fparams = &p; }
virtual void updt_params(params_ref const & p);
virtual void collect_param_descrs(param_descrs & r);
virtual void set_produce_proofs(bool f) { m_produce_proofs = f; }
virtual void set_produce_models(bool f) { m_produce_models = f; }
virtual void set_produce_unsat_cores(bool f) { m_produce_unsat_cores = f; }
virtual unsigned get_num_assertions() const = 0;
virtual expr * get_assertion(unsigned idx) const = 0;
virtual void display(std::ostream & out) const;
virtual void init(ast_manager & m, symbol const & logic);
virtual void collect_statistics(statistics & st) const;
virtual void reset();
virtual void assert_expr(expr * t);
virtual void push();
virtual void pop(unsigned n);
virtual unsigned get_scope_level() const;
virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions);
virtual void get_unsat_core(ptr_vector<expr> & r);
virtual void get_model(model_ref & m);
virtual proof * get_proof();
virtual std::string reason_unknown() const;
virtual void get_labels(svector<symbol> & r);
virtual void set_cancel(bool f);
virtual void set_progress_callback(progress_callback * callback);
};
// Specialization for the SMT 2.0 command language frontend
class strategic_solver_cmd : public strategic_solver {
cmd_context & m_ctx;
public:
strategic_solver_cmd(cmd_context & ctx);
virtual unsigned get_num_assertions() const;
virtual expr * get_assertion(unsigned idx) const;
};
// Specialization for Z3 API
class strategic_solver_api : public strategic_solver {
struct ctx {
expr_ref_vector m_assertions;
unsigned_vector m_scopes;
ctx(ast_manager & m);
};
scoped_ptr<ctx> m_ctx;
public:
strategic_solver_api() {}
virtual void init(ast_manager & m, symbol const & logic);
virtual void assert_expr(expr * t);
virtual void push();
virtual void pop(unsigned n);
virtual void reset();
virtual unsigned get_num_assertions() const;
virtual expr * get_assertion(unsigned idx) const;
};
#endif

961
src/grobner/grobner.cpp Normal file
View file

@ -0,0 +1,961 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
grobner.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-12-04.
Revision History:
--*/
#include"grobner.h"
#include"ast_pp.h"
#include"ref_util.h"
// #define PROFILE_GB
grobner::grobner(ast_manager & m, v_dependency_manager & d):
m_manager(m),
m_dep_manager(d),
m_util(m),
m_var_lt(m_var2weight),
m_monomial_lt(m_var_lt),
m_changed_leading_term(false),
m_unsat(0) {
}
grobner::~grobner() {
flush();
}
void grobner::flush() {
dec_ref_map_keys(m_manager, m_var2weight);
del_equations(0);
}
void grobner::del_equations(unsigned old_size) {
SASSERT(m_equations_to_delete.size() >= old_size);
equation_vector::iterator it = m_equations_to_delete.begin();
equation_vector::iterator end = m_equations_to_delete.end();
it += old_size;
for (; it != end; ++it) {
equation * eq = *it;
if (eq)
del_equation(eq);
}
m_equations_to_delete.shrink(old_size);
}
void grobner::del_equation(equation * eq) {
m_processed.erase(eq);
m_to_process.erase(eq);
SASSERT(m_equations_to_delete[eq->m_bidx] == eq);
m_equations_to_delete[eq->m_bidx] = 0;
ptr_vector<monomial>::iterator it1 = eq->m_monomials.begin();
ptr_vector<monomial>::iterator end1 = eq->m_monomials.end();
for (; it1 != end1; ++it1) {
monomial * m = *it1;
del_monomial(m);
}
dealloc(eq);
}
void grobner::del_monomial(monomial * m) {
ptr_vector<expr>::iterator it2 = m->m_vars.begin();
ptr_vector<expr>::iterator end2 = m->m_vars.end();
for (; it2 != end2; ++it2) {
expr * v = *it2;
m_manager.dec_ref(v);
}
dealloc(m);
}
void grobner::unfreeze_equations(unsigned old_size) {
SASSERT(m_equations_to_unfreeze.size() >= old_size);
equation_vector::iterator it = m_equations_to_unfreeze.begin();
equation_vector::iterator end = m_equations_to_unfreeze.end();
it += old_size;
for (; it != end; ++it) {
equation * eq = *it;
m_to_process.insert(eq);
}
m_equations_to_unfreeze.shrink(old_size);
}
void grobner::push_scope() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_equations_to_unfreeze_lim = m_equations_to_unfreeze.size();
s.m_equations_to_delete_lim = m_equations_to_delete.size();
}
void grobner::pop_scope(unsigned num_scopes) {
SASSERT(num_scopes >= get_scope_level());
unsigned new_lvl = get_scope_level() - num_scopes;
scope & s = m_scopes[new_lvl];
unfreeze_equations(s.m_equations_to_unfreeze_lim);
del_equations(s.m_equations_to_delete_lim);
m_scopes.shrink(new_lvl);
}
void grobner::reset() {
flush();
m_processed.reset();
m_to_process.reset();
m_equations_to_unfreeze.reset();
m_equations_to_delete.reset();
m_unsat = 0;
}
void grobner::display_var(std::ostream & out, expr * var) const {
if (is_app(var) && to_app(var)->get_num_args() > 0)
out << "#" << var->get_id();
else
out << mk_pp(var, m_manager);
}
void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const {
for (unsigned i = 0; i < num_vars; i++) {
display_var(out, vars[i]);
out << " ";
}
}
void grobner::display_monomial(std::ostream & out, monomial const & m) const {
if (!m.m_coeff.is_one() || m.m_vars.empty()) {
out << m.m_coeff;
if (!m.m_vars.empty())
out << "*";
}
if (!m.m_vars.empty()) {
ptr_vector<expr>::const_iterator it = m.m_vars.begin();
ptr_vector<expr>::const_iterator end = m.m_vars.end();
unsigned power = 1;
expr * prev = *it;
it++;
for (; it != end; ++it) {
expr * curr = *it;
if (curr == prev) {
power++;
}
else {
display_var(out, prev);
if (power > 1)
out << "^" << power;
power = 1;
prev = curr;
out << "*";
}
}
display_var(out, prev);
if (power > 1)
out << "^" << power;
}
}
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
bool first = true;
for (unsigned i = 0; i < num_monomials; i++) {
monomial const * m = monomials[i];
if (first)
first = false;
else
out << " + ";
display_monomial(out, *m);
}
}
void grobner::display_equation(std::ostream & out, equation const & eq) const {
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.c_ptr());
out << " = 0\n";
}
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const {
if (!v.empty()) {
out << header << "\n";
equation_set::iterator it = v.begin();
equation_set::iterator end = v.end();
for (; it != end; ++it)
display_equation(out, *(*it));
}
}
void grobner::display(std::ostream & out) const {
display_equations(out, m_processed, "processed:");
display_equations(out, m_to_process, "to process:");
}
void grobner::set_weight(expr * n, int weight) {
if (weight == 0)
return;
if (!m_var2weight.contains(n))
m_manager.inc_ref(n);
m_var2weight.insert(n, weight);
}
/**
\brief Update equation using the new variable order.
Return true if the leading term was modified.
*/
bool grobner::update_order(equation * eq) {
if (eq->get_num_monomials() == 0)
return false;
monomial * first = eq->m_monomials[0];
ptr_vector<monomial>::iterator it = eq->m_monomials.begin();
ptr_vector<monomial>::iterator end = eq->m_monomials.end();
for (; it != end; ++it) {
monomial * m = *it;
std::stable_sort(m->m_vars.begin(), m->m_vars.end(), m_var_lt);
}
std::stable_sort(eq->m_monomials.begin(), eq->m_monomials.end(), m_monomial_lt);
return eq->m_monomials[0] != first;
}
void grobner::update_order(equation_set & s, bool processed) {
ptr_buffer<equation> to_remove;
equation_set::iterator it = s.begin();
equation_set::iterator end = s.end();
for (;it != end; ++it) {
equation * eq = *it;
if (update_order(eq)) {
if (processed) {
to_remove.push_back(eq);
m_to_process.insert(eq);
}
}
}
ptr_buffer<equation>::iterator it2 = to_remove.begin();
ptr_buffer<equation>::iterator end2 = to_remove.end();
for (; it2 != end2; ++it2)
s.erase(*it2);
}
void grobner::update_order() {
update_order(m_to_process, false);
update_order(m_processed, true);
}
bool grobner::var_lt::operator()(expr * v1, expr * v2) const {
int w1 = 0;
int w2 = 0;
m_var2weight.find(v1, w1);
m_var2weight.find(v2, w2);
return (w1 > w2) || (w1 == w2 && v1->get_id() < v2->get_id());
}
bool grobner::monomial_lt::operator()(monomial * m1, monomial * m2) const {
// Using graded lex order.
if (m1->get_degree() > m2->get_degree())
return true;
if (m1->get_degree() < m2->get_degree())
return false;
ptr_vector<expr>::iterator it1 = m1->m_vars.begin();
ptr_vector<expr>::iterator it2 = m2->m_vars.begin();
ptr_vector<expr>::iterator end1 = m1->m_vars.end();
for (; it1 != end1; ++it1, ++it2) {
expr * v1 = *it1;
expr * v2 = *it2;
if (m_var_lt(v1, v2))
return true;
if (v1 != v2)
return false;
}
return false;
}
inline void grobner::add_var(monomial * m, expr * v) {
SASSERT(!m_util.is_numeral(v));
m_manager.inc_ref(v);
m->m_vars.push_back(v);
}
grobner::monomial * grobner::mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars) {
monomial * r = alloc(monomial);
r->m_coeff = coeff;
for (unsigned i = 0; i < num_vars; i++)
add_var(r, vars[i]);
std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt);
return r;
}
grobner::monomial * grobner::mk_monomial(rational const & coeff, expr * m) {
monomial * r = alloc(monomial);
if (m_util.is_numeral(m, r->m_coeff)) {
r->m_coeff *= coeff;
return r;
}
if (m_util.is_mul(m)) {
expr * body = m;
SASSERT(!m_util.is_numeral(to_app(m)->get_arg(1))); // monomial is in normal form
if (m_util.is_numeral(to_app(m)->get_arg(0), r->m_coeff)) {
r->m_coeff *= coeff;
body = to_app(m)->get_arg(1);
}
else {
r->m_coeff = coeff;
}
while (m_util.is_mul(body)) {
add_var(r, to_app(body)->get_arg(0));
body = to_app(body)->get_arg(1);
}
add_var(r, body);
std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt);
}
else {
r->m_coeff = coeff;
r->m_vars.push_back(m);
m_manager.inc_ref(m);
}
return r;
}
void grobner::init_equation(equation * eq, v_dependency * d) {
eq->m_scope_lvl = get_scope_level();
unsigned bidx = m_equations_to_delete.size();
eq->m_bidx = bidx;
eq->m_dep = d;
eq->m_lc = true;
m_equations_to_delete.push_back(eq);
SASSERT(m_equations_to_delete[eq->m_bidx] == eq);
}
void grobner::assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex) {
ptr_vector<monomial> ms;
ms.append(num_monomials, monomials);
std::stable_sort(ms.begin(), ms.end(), m_monomial_lt);
merge_monomials(ms);
if (!ms.empty()) {
normalize_coeff(ms);
equation * eq = alloc(equation);
eq->m_monomials.swap(ms);
init_equation(eq, ex);
m_to_process.insert(eq);
}
}
void grobner::assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex) {
#define MK_EQ(COEFF) \
ptr_vector<monomial> ms; \
for (unsigned i = 0; i < num_monomials; i++) \
ms.push_back(mk_monomial(COEFF, monomials[i])); \
std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); \
merge_monomials(ms); \
if (!ms.empty()) { \
equation * eq = alloc(equation); \
normalize_coeff(ms); \
eq->m_monomials.swap(ms); \
init_equation(eq, ex); \
m_to_process.insert(eq); \
}
MK_EQ(coeffs[i]);
}
void grobner::assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex) {
rational one(1);
MK_EQ(one);
}
void grobner::extract_monomials(expr * lhs, ptr_buffer<expr> & monomials) {
while (m_util.is_add(lhs)) {
SASSERT(!m_util.is_add(to_app(lhs)->get_arg(0)));
monomials.push_back(to_app(lhs)->get_arg(0));
lhs = to_app(lhs)->get_arg(1);
}
monomials.push_back(lhs);
}
void grobner::assert_eq(expr * eq, v_dependency * ex) {
SASSERT(m_manager.is_eq(eq));
expr * lhs = to_app(eq)->get_arg(0);
expr * rhs = to_app(eq)->get_arg(1);
SASSERT(m_util.is_numeral(rhs));
ptr_buffer<expr> monomials;
extract_monomials(lhs, monomials);
rational c;
bool is_int = false;
m_util.is_numeral(rhs, c, is_int);
expr_ref new_c(m_manager);
if (!c.is_zero()) {
c.neg();
new_c = m_util.mk_numeral(c, is_int);
monomials.push_back(new_c);
}
assert_eq_0(monomials.size(), monomials.c_ptr(), ex);
}
void grobner::assert_monomial_tautology(expr * m) {
equation * eq = alloc(equation);
eq->m_monomials.push_back(mk_monomial(rational(1), m));
// create (quote m)
monomial * m1 = alloc(monomial);
m1->m_coeff = rational(-1);
m_manager.inc_ref(m);
m1->m_vars.push_back(m);
eq->m_monomials.push_back(m1);
normalize_coeff(eq->m_monomials);
init_equation(eq, static_cast<v_dependency*>(0)); \
m_to_process.insert(eq);
}
/**
\brief Return true if the body of m1 and m2 are equal
*/
bool grobner::is_eq_monomial_body(monomial const * m1, monomial const * m2) {
if (m1->get_degree() != m2->get_degree())
return false;
ptr_vector<expr>::const_iterator it1 = m1->m_vars.begin();
ptr_vector<expr>::const_iterator it2 = m2->m_vars.begin();
ptr_vector<expr>::const_iterator end1 = m1->m_vars.end();
for (; it1 != end1; ++it1, ++it2) {
expr * v1 = *it1;
expr * v2 = *it2;
if (v1 != v2)
return false;
}
return true;
}
/**
\brief Merge monomials (* c1 m) (* c2 m).
\remark This method assumes the monomials are sorted.
*/
void grobner::merge_monomials(ptr_vector<monomial> & monomials) {
TRACE("grobner", tout << "before merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";);
unsigned j = 0;
unsigned sz = monomials.size();
if (sz == 0)
return;
for (unsigned i = 1; i < sz; ++i) {
monomial * m1 = monomials[j];
monomial * m2 = monomials[i];
if (is_eq_monomial_body(m1, m2)) {
m1->m_coeff += m2->m_coeff;
del_monomial(m2);
}
else {
if (m1->m_coeff.is_zero())
del_monomial(m1); // cancelled
else
j++;
monomials[j] = m2;
}
}
SASSERT(j < sz);
monomial * m1 = monomials[j];
if (m1->m_coeff.is_zero())
del_monomial(m1); // cancelled
else
j++;
monomials.shrink(j);
TRACE("grobner", tout << "after merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";);
}
/**
\brief Divide the coefficients by the coefficient of the leading term.
*/
void grobner::normalize_coeff(ptr_vector<monomial> & monomials) {
if (monomials.empty())
return;
rational c = monomials[0]->m_coeff;
if (c.is_one())
return;
unsigned sz = monomials.size();
for (unsigned i = 0; i < sz; i++)
monomials[i]->m_coeff /= c;
}
/**
\brief Simplify the given monomials
*/
void grobner::simplify(ptr_vector<monomial> & monomials) {
std::stable_sort(monomials.begin(), monomials.end(), m_monomial_lt);
merge_monomials(monomials);
normalize_coeff(monomials);
}
/**
\brief Return true if the equation is of the form k = 0, where k is a numeral different from zero.
\remark This method assumes the equation is simplified.
*/
inline bool grobner::is_inconsistent(equation * eq) const {
SASSERT(!(eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0) || !eq->m_monomials[0]->m_coeff.is_zero());
return eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0;
}
/**
\brief Return true if the equation is of the form 0 = 0.
*/
inline bool grobner::is_trivial(equation * eq) const {
return eq->m_monomials.empty();
}
/**
\brief Sort monomials, and merge monomials with the same body.
*/
void grobner::simplify(equation * eq) {
simplify(eq->m_monomials);
if (is_inconsistent(eq) && !m_unsat)
m_unsat = eq;
}
/**
\brief Return true if monomial m1 is (* c1 M) and m2 is (* c2 M M').
Store M' in rest.
\remark This method assumes the variables of m1 and m2 are sorted.
*/
bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest) const {
unsigned i1 = 0;
unsigned i2 = 0;
unsigned sz1 = m1->m_vars.size();
unsigned sz2 = m2->m_vars.size();
if (sz1 <= sz2) {
while (true) {
if (i1 >= sz1) {
for (; i2 < sz2; i2++)
rest.push_back(m2->m_vars[i2]);
TRACE("grobner",
tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of ";
display_monomial(tout, *m2); tout << "\n";
tout << "rest: "; display_vars(tout, rest.size(), rest.c_ptr()); tout << "\n";);
return true;
}
if (i2 >= sz2)
break;
expr * var1 = m1->m_vars[i1];
expr * var2 = m2->m_vars[i2];
if (var1 == var2) {
i1++;
i2++;
continue;
}
if (m_var_lt(var2, var1)) {
i2++;
rest.push_back(var2);
continue;
}
SASSERT(m_var_lt(var1, var2));
break;
}
}
// is not subset
TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of ";
display_monomial(tout, *m2); tout << "\n";);
return false;
}
/**
\brief Multiply the monomials of source starting at position start_idx by (coeff * vars), and store the resultant monomials
at result.
*/
void grobner::mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector<expr> const & vars, ptr_vector<monomial> & result) {
unsigned sz = source->get_num_monomials();
for (unsigned i = start_idx; i < sz; i++) {
monomial const * m = source->get_monomial(i);
monomial * new_m = alloc(monomial);
new_m->m_coeff = m->m_coeff;
new_m->m_coeff *= coeff;
new_m->m_vars.append(m->m_vars.size(), m->m_vars.c_ptr());
new_m->m_vars.append(vars.size(), vars.c_ptr());
ptr_vector<expr>::iterator it = new_m->m_vars.begin();
ptr_vector<expr>::iterator end = new_m->m_vars.end();
for (; it != end; ++it)
m_manager.inc_ref(*it);
std::stable_sort(new_m->m_vars.begin(), new_m->m_vars.end(), m_var_lt);
result.push_back(new_m);
}
}
/**
\brief Copy the given monomial.
*/
grobner::monomial * grobner::copy_monomial(monomial const * m) {
monomial * r = alloc(monomial);
r->m_coeff = m->m_coeff;
ptr_vector<expr>::const_iterator it = m->m_vars.begin();
ptr_vector<expr>::const_iterator end = m->m_vars.end();
for (; it != end; ++it)
add_var(r, *it);
return r;
}
/**
\brief Copy the given equation.
*/
grobner::equation * grobner::copy_equation(equation const * eq) {
equation * r = alloc(equation);
unsigned sz = eq->get_num_monomials();
for (unsigned i = 0; i < sz; i++)
r->m_monomials.push_back(copy_monomial(eq->get_monomial(i)));
init_equation(r, eq->m_dep);
r->m_lc = eq->m_lc;
return r;
}
/**
\brief Simplify the target equation using the source as a rewrite rule.
Return 0 if target was not simplified.
Return target if target was simplified but source->m_scope_lvl <= target->m_scope_lvl.
Return new_equation if source->m_scope_lvl > target->m_scope_lvl, moreover target is freezed, and new_equation contains the result.
*/
grobner::equation * grobner::simplify(equation const * source, equation * target) {
TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source););
if (source->get_num_monomials() == 0)
return 0;
m_stats.m_simplify++;
bool result = false;
bool simplified;
do {
simplified = false;
unsigned i = 0;
unsigned j = 0;
unsigned sz = target->m_monomials.size();
monomial const * LT = source->get_monomial(0);
ptr_vector<monomial> & new_monomials = m_tmp_monomials;
new_monomials.reset();
ptr_vector<expr> & rest = m_tmp_vars1;
for (; i < sz; i++) {
monomial * curr = target->m_monomials[i];
rest.reset();
if (is_subset(LT, curr, rest)) {
if (i == 0)
m_changed_leading_term = true;
if (source->m_scope_lvl > target->m_scope_lvl) {
target = copy_equation(target);
SASSERT(target->m_scope_lvl >= source->m_scope_lvl);
}
if (!result) {
// first time that source is being applied.
target->m_dep = m_dep_manager.mk_join(target->m_dep, source->m_dep);
}
simplified = true;
result = true;
rational coeff = curr->m_coeff;
coeff /= LT->m_coeff;
coeff.neg();
if (!rest.empty())
target->m_lc = false;
mul_append(1, source, coeff, rest, new_monomials);
del_monomial(curr);
}
else {
target->m_monomials[j] = curr;
j++;
}
}
if (simplified) {
target->m_monomials.shrink(j);
target->m_monomials.append(new_monomials.size(), new_monomials.c_ptr());
simplify(target);
}
}
while (simplified);
TRACE("grobner", tout << "result: "; display_equation(tout, *target););
return result ? target : 0;
}
/**
\brief Simplify given equation using processed equalities.
Return 0, if the equation was not simplified.
Return eq, if the equation was simplified using destructive updates.
Return new_eq otherwise.
*/
grobner::equation * grobner::simplify_using_processed(equation * eq) {
bool result = false;
bool simplified;
TRACE("grobner", tout << "simplifying: "; display_equation(tout, *eq); tout << "using already processed equalities\n";);
do {
simplified = false;
equation_set::iterator it = m_processed.begin();
equation_set::iterator end = m_processed.end();
for (; it != end; ++it) {
equation const * p = *it;
equation * new_eq = simplify(p, eq);
if (new_eq) {
result = true;
simplified = true;
eq = new_eq;
}
}
}
while (simplified);
TRACE("grobner", tout << "simplification result: "; display_equation(tout, *eq););
return result ? eq : 0;
}
/**
\brief Return true if eq1 is a better than e2, for being the next equation to be processed.
*/
bool grobner::is_better_choice(equation * eq1, equation * eq2) {
if (!eq2)
return true;
if (eq1->m_monomials.empty())
return true;
if (eq2->m_monomials.empty())
return false;
if (eq1->m_monomials[0]->get_degree() < eq2->m_monomials[0]->get_degree())
return true;
if (eq1->m_monomials[0]->get_degree() > eq2->m_monomials[0]->get_degree())
return false;
return eq1->m_monomials.size() < eq2->m_monomials.size();
}
/**
\brief Pick next unprocessed equation
*/
grobner::equation * grobner::pick_next() {
equation * r = 0;
ptr_buffer<equation> to_delete;
equation_set::iterator it = m_to_process.begin();
equation_set::iterator end = m_to_process.end();
for (; it != end; ++it) {
equation * curr = *it;
if (is_trivial(curr))
to_delete.push_back(curr);
else if (is_better_choice(curr, r))
r = curr;
}
ptr_buffer<equation>::const_iterator it1 = to_delete.begin();
ptr_buffer<equation>::const_iterator end1 = to_delete.end();
for (; it1 != end1; ++it1)
del_equation(*it1);
if (r)
m_to_process.erase(r);
TRACE("grobner", tout << "selected equation: "; if (!r) tout << "<null>\n"; else display_equation(tout, *r););
return r;
}
/**
\brief Use the given equation to simplify processed terms.
*/
void grobner::simplify_processed(equation * eq) {
ptr_buffer<equation> to_insert;
ptr_buffer<equation> to_remove;
ptr_buffer<equation> to_delete;
equation_set::iterator it = m_processed.begin();
equation_set::iterator end = m_processed.end();
for (; it != end; ++it) {
equation * curr = *it;
m_changed_leading_term = false;
// if the leading term is simplified, then the equation has to be moved to m_to_process
equation * new_curr = simplify(eq, curr);
if (new_curr != 0) {
if (new_curr != curr) {
m_equations_to_unfreeze.push_back(curr);
to_remove.push_back(curr);
if (m_changed_leading_term) {
m_to_process.insert(new_curr);
to_remove.push_back(curr);
}
else {
to_insert.push_back(new_curr);
}
curr = new_curr;
}
else {
if (m_changed_leading_term) {
m_to_process.insert(curr);
to_remove.push_back(curr);
}
}
}
if (is_trivial(curr))
to_delete.push_back(curr);
}
ptr_buffer<equation>::const_iterator it1 = to_insert.begin();
ptr_buffer<equation>::const_iterator end1 = to_insert.end();
for (; it1 != end1; ++it1)
m_processed.insert(*it1);
it1 = to_remove.begin();
end1 = to_remove.end();
for (; it1 != end1; ++it1)
m_processed.erase(*it1);
it1 = to_delete.begin();
end1 = to_delete.end();
for (; it1 != end1; ++it1)
del_equation(*it1);
}
/**
\brief Use the given equation to simplify to-process terms.
*/
void grobner::simplify_to_process(equation * eq) {
equation_set::iterator it = m_to_process.begin();
equation_set::iterator end = m_to_process.end();
ptr_buffer<equation> to_insert;
ptr_buffer<equation> to_remove;
ptr_buffer<equation> to_delete;
for (; it != end; ++it) {
equation * curr = *it;
equation * new_curr = simplify(eq, curr);
if (new_curr != 0 && new_curr != curr) {
m_equations_to_unfreeze.push_back(curr);
to_insert.push_back(new_curr);
to_remove.push_back(curr);
curr = new_curr;
}
if (is_trivial(curr))
to_delete.push_back(curr);
}
ptr_buffer<equation>::const_iterator it1 = to_insert.begin();
ptr_buffer<equation>::const_iterator end1 = to_insert.end();
for (; it1 != end1; ++it1)
m_to_process.insert(*it1);
it1 = to_remove.begin();
end1 = to_remove.end();
for (; it1 != end1; ++it1)
m_to_process.erase(*it1);
it1 = to_delete.begin();
end1 = to_delete.end();
for (; it1 != end1; ++it1)
del_equation(*it1);
}
/**
\brief If m1 = (* c M M1) and m2 = (* d M M2) and M is non empty, then return true and store M1 in rest1 and M2 in rest2.
*/
bool grobner::unify(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest1, ptr_vector<expr> & rest2) {
TRACE("grobner", tout << "unifying: "; display_monomial(tout, *m1); tout << " "; display_monomial(tout, *m2); tout << "\n";);
bool found_M = false;
unsigned i1 = 0;
unsigned i2 = 0;
unsigned sz1 = m1->m_vars.size();
unsigned sz2 = m2->m_vars.size();
while (true) {
if (i1 >= sz1) {
if (found_M) {
for (; i2 < sz2; i2++)
rest2.push_back(m2->m_vars[i2]);
return true;
}
return false;
}
if (i2 >= sz2) {
if (found_M) {
for (; i1 < sz1; i1++)
rest1.push_back(m1->m_vars[i1]);
return true;
}
return false;
}
expr * var1 = m1->m_vars[i1];
expr * var2 = m2->m_vars[i2];
if (var1 == var2) {
found_M = true;
i1++;
i2++;
}
else if (m_var_lt(var2, var1)) {
i2++;
rest2.push_back(var2);
}
else {
i1++;
rest1.push_back(var1);
}
}
}
/**
\brief Superpose the given two equations.
*/
void grobner::superpose(equation * eq1, equation * eq2) {
if (eq1->m_monomials.empty() || eq2->m_monomials.empty())
return;
m_stats.m_superpose++;
ptr_vector<expr> & rest1 = m_tmp_vars1;
rest1.reset();
ptr_vector<expr> & rest2 = m_tmp_vars2;
rest2.reset();
if (unify(eq1->m_monomials[0], eq2->m_monomials[0], rest1, rest2)) {
TRACE("grobner", tout << "superposing:\n"; display_equation(tout, *eq1); display_equation(tout, *eq2);
tout << "rest1: "; display_vars(tout, rest1.size(), rest1.c_ptr()); tout << "\n";
tout << "rest2: "; display_vars(tout, rest2.size(), rest2.c_ptr()); tout << "\n";);
ptr_vector<monomial> & new_monomials = m_tmp_monomials;
new_monomials.reset();
mul_append(1, eq1, eq2->m_monomials[0]->m_coeff, rest2, new_monomials);
rational c = eq1->m_monomials[0]->m_coeff;
c.neg();
mul_append(1, eq2, c, rest1, new_monomials);
simplify(new_monomials);
TRACE("grobner", tout << "resulting monomials: "; display_monomials(tout, new_monomials.size(), new_monomials.c_ptr()); tout << "\n";);
if (new_monomials.empty())
return;
m_num_new_equations++;
equation * new_eq = alloc(equation);
new_eq->m_monomials.swap(new_monomials);
init_equation(new_eq, m_dep_manager.mk_join(eq1->m_dep, eq2->m_dep));
new_eq->m_lc = false;
m_to_process.insert(new_eq);
}
}
/**
\brief Superpose the given equations with the equations in m_processed.
*/
void grobner::superpose(equation * eq) {
equation_set::iterator it = m_processed.begin();
equation_set::iterator end = m_processed.end();
for (; it != end; ++it) {
equation * curr = *it;
superpose(eq, curr);
}
}
bool grobner::compute_basis(unsigned threshold) {
m_stats.m_compute_basis++;
m_num_new_equations = 0;
while (m_num_new_equations < threshold) {
equation * eq = pick_next();
if (!eq)
return true;
m_stats.m_num_processed++;
#ifdef PROFILE_GB
if (m_stats.m_num_processed % 100 == 0) {
verbose_stream() << "[grobner] " << m_processed.size() << " " << m_to_process.size() << "\n";
}
#endif
equation * new_eq = simplify_using_processed(eq);
if (new_eq != 0 && eq != new_eq) {
// equation was updated using non destructive updates
m_equations_to_unfreeze.push_back(eq);
eq = new_eq;
}
simplify_processed(eq);
superpose(eq);
m_processed.insert(eq);
simplify_to_process(eq);
TRACE("grobner", tout << "end of iteration:\n"; display(tout););
}
return false;
}
void grobner::copy_to(equation_set const & s, ptr_vector<equation> & result) const {
equation_set::iterator it = s.begin();
equation_set::iterator end = s.end();
for (; it != end; ++it)
result.push_back(*it);
}
/**
\brief Copy the equations in m_processed and m_to_process to result.
\warning This equations can be deleted when compute_basis is invoked.
*/
void grobner::get_equations(ptr_vector<equation> & result) const {
copy_to(m_processed, result);
copy_to(m_to_process, result);
}

281
src/grobner/grobner.h Normal file
View file

@ -0,0 +1,281 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
grobner.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-12-04.
Revision History:
--*/
#ifndef _GROBNER_H_
#define _GROBNER_H_
#include"ast.h"
#include"arith_decl_plugin.h"
#include"heap.h"
#include"obj_hashtable.h"
#include"region.h"
#include"dependency.h"
struct grobner_stats {
long m_simplify; long m_superpose; long m_compute_basis; long m_num_processed;
void reset() { memset(this, 0, sizeof(grobner_stats)); }
grobner_stats() { reset(); }
};
/**
\brief Simple Grobner basis implementation with no indexing.
*/
class grobner {
protected:
struct monomial_lt;
public:
grobner_stats m_stats;
class monomial {
rational m_coeff;
ptr_vector<expr> m_vars; //!< sorted variables
friend class grobner;
friend struct monomial_lt;
monomial() {}
public:
rational const & get_coeff() const { return m_coeff; }
unsigned get_degree() const { return m_vars.size(); }
unsigned get_size() const { return get_degree(); }
expr * get_var(unsigned idx) const { return m_vars[idx]; }
};
class equation {
unsigned m_scope_lvl; //!< scope level when this equation was created.
unsigned m_bidx:31; //!< position at m_equations_to_delete
unsigned m_lc:1; //!< true if equation if a linear combination of the input equations.
ptr_vector<monomial> m_monomials; //!< sorted monomials
v_dependency * m_dep; //!< justification for the equality
friend class grobner;
equation() {}
public:
unsigned get_num_monomials() const { return m_monomials.size(); }
monomial const * get_monomial(unsigned idx) const { return m_monomials[idx]; }
monomial * const * get_monomials() const { return m_monomials.c_ptr(); }
v_dependency * get_dependency() const { return m_dep; }
unsigned hash() const { return m_bidx; }
bool is_linear_combination() const { return m_lc; }
};
protected:
static bool is_eq_monomial_body(monomial const * m1, monomial const * m2);
struct var_lt {
obj_map<expr, int> & m_var2weight;
var_lt(obj_map<expr, int> & m):m_var2weight(m) {}
bool operator()(expr * v1, expr * v2) const;
};
struct monomial_lt {
var_lt & m_var_lt;
monomial_lt(var_lt & lt):m_var_lt(lt) {}
bool operator()(monomial * m1, monomial * m2) const;
};
typedef obj_hashtable<equation> equation_set;
typedef ptr_vector<equation> equation_vector;
ast_manager & m_manager;
v_dependency_manager & m_dep_manager;
arith_util m_util;
obj_map<expr, int> m_var2weight;
var_lt m_var_lt;
monomial_lt m_monomial_lt;
equation_set m_processed;
equation_set m_to_process;
equation_vector m_equations_to_unfreeze;
equation_vector m_equations_to_delete;
bool m_changed_leading_term; // set to true, if the leading term was simplified.
equation * m_unsat;
struct scope {
unsigned m_equations_to_unfreeze_lim;
unsigned m_equations_to_delete_lim;
};
svector<scope> m_scopes;
ptr_vector<monomial> m_tmp_monomials;
ptr_vector<expr> m_tmp_vars1;
ptr_vector<expr> m_tmp_vars2;
unsigned m_num_new_equations; // temporary variable
bool is_monomial_lt(monomial const & m1, monomial const & m2) const;
void display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const;
void display_var(std::ostream & out, expr * var) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const;
void display_equations(std::ostream & out, equation_set const & v, char const * header) const;
void del_equations(unsigned old_size);
void unfreeze_equations(unsigned old_size);
void del_equation(equation * eq);
void flush();
bool update_order(equation * eq);
void update_order(equation_set & s, bool processed);
void add_var(monomial * m, expr * v);
monomial * mk_monomial(rational const & coeff, expr * m);
void init_equation(equation * eq, v_dependency * d);
void extract_monomials(expr * lhs, ptr_buffer<expr> & monomials);
void merge_monomials(ptr_vector<monomial> & monomials);
bool is_inconsistent(equation * eq) const;
bool is_trivial(equation * eq) const;
void normalize_coeff(ptr_vector<monomial> & monomials);
void simplify(ptr_vector<monomial> & monomials);
void simplify(equation * eq);
bool is_subset(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest) const;
void mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector<expr> const & vars, ptr_vector<monomial> & result);
monomial * copy_monomial(monomial const * m);
equation * copy_equation(equation const * eq);
equation * simplify(equation const * source, equation * target);
equation * simplify_using_processed(equation * eq);
bool is_better_choice(equation * eq1, equation * eq2);
equation * pick_next();
void simplify_processed(equation * eq);
void simplify_to_process(equation * eq);
bool unify(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest1, ptr_vector<expr> & rest2);
void superpose(equation * eq1, equation * eq2);
void superpose(equation * eq);
void copy_to(equation_set const & s, ptr_vector<equation> & result) const;
public:
grobner(ast_manager & m, v_dependency_manager & dep_m);
~grobner();
unsigned get_scope_level() const { return m_scopes.size(); }
/**
\brief Set the weight of a term that is viewed as a variable by this module.
The weight is used to order monomials. If the weight is not set for a term t, then the
weight of t is assumed to be 0.
*/
void set_weight(expr * n, int weight);
int get_weight(expr * n) const { int w = 0; m_var2weight.find(n, w); return w; }
/**
\brief Update equations after set_weight was invoked once or more.
*/
void update_order();
/**
\brief Create a new monomial. The caller owns the monomial until it invokes assert_eq_0.
A monomial cannot be use to create several equations.
*/
monomial * mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars);
void del_monomial(monomial * m);
/**
\brief Assert the given equality.
This method assumes eq is simplified.
*/
void assert_eq(expr * eq, v_dependency * ex = 0);
/**
\brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0.
This method assumes the monomials were simplified.
*/
void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = 0);
/**
\brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0.
This method assumes the monomials were simplified.
*/
void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = 0);
/**
\brief Assert the equality coeffs[0] * monomials[0] + ... + coeffs[num_monomials-1] * monomials[num_monomials - 1] = 0.
This method assumes the monomials were simplified.
*/
void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = 0);
/**
\brief Assert the monomial tautology (quote (x_1 * ... * x_n)) - x_1 * ... * x_n = 0
*/
void assert_monomial_tautology(expr * m);
/**
\brief Compute Grobner basis.
Return true if the threshold was not reached.
*/
bool compute_basis(unsigned threshold);
/**
\brief Return true if an inconsistency was detected.
*/
bool inconsistent() const { return m_unsat != 0; }
/**
\brief Simplify the given expression using the equalities asserted
using assert_eq. Store the result in 'result'.
*/
void simplify(expr * n, expr_ref & result);
/**
\brief Reset state. Remove all equalities asserted with assert_eq.
*/
void reset();
void get_equations(ptr_vector<equation> & result) const;
void push_scope();
void pop_scope(unsigned num_scopes);
void display_equation(std::ostream & out, equation const & eq) const;
void display_monomial(std::ostream & out, monomial const & m) const;
void display(std::ostream & out) const;
};
#endif /* _GROBNER_H_ */

223
src/macros/macro_finder.cpp Normal file
View file

@ -0,0 +1,223 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_finder.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#include"macro_finder.h"
#include"occurs.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
bool macro_finder::is_macro(expr * n, app * & head, expr * & def) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
TRACE("macro_finder", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
return m_util.is_simple_macro(body, num_decls, head, def);
}
/**
\brief Detect macros of the form
1- (forall (X) (= (+ (f X) (R X)) c))
2- (forall (X) (<= (+ (f X) (R X)) c))
3- (forall (X) (>= (+ (f X) (R X)) c))
The second and third cases are first converted into
(forall (X) (= (f X) (+ c (* -1 (R x)) (k X))))
and
(forall (X) (<= (k X) 0)) when case 2
(forall (X) (>= (k X) 0)) when case 3
For case 2 & 3, the new quantifiers are stored in new_exprs and new_prs.
*/
bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
arith_simplifier_plugin * as = get_arith_simp();
arith_util & autil = as->get_arith_util();
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
if (!autil.is_le(body) && !autil.is_ge(body) && !m_manager.is_eq(body))
return false;
if (!as->is_add(to_app(body)->get_arg(0)))
return false;
app_ref head(m_manager);
expr_ref def(m_manager);
bool inv = false;
if (!m_util.is_arith_macro(body, num_decls, head, def, inv))
return false;
app_ref new_body(m_manager);
if (!inv || m_manager.is_eq(body))
new_body = m_manager.mk_app(to_app(body)->get_decl(), head, def);
else if (as->is_le(body))
new_body = autil.mk_ge(head, def);
else
new_body = autil.mk_le(head, def);
quantifier_ref new_q(m_manager);
new_q = m_manager.update_quantifier(to_quantifier(n), new_body);
proof * new_pr = 0;
if (m_manager.proofs_enabled()) {
proof * rw = m_manager.mk_rewrite(n, new_q);
new_pr = m_manager.mk_modus_ponens(pr, rw);
}
if (m_manager.is_eq(body)) {
return m_macro_manager.insert(head->get_decl(), new_q, new_pr);
}
// is ge or le
//
TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";);
func_decl * f = head->get_decl();
func_decl * k = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
app * k_app = m_manager.mk_app(k, head->get_num_args(), head->get_args());
expr_ref_buffer new_rhs_args(m_manager);
expr_ref new_rhs2(m_manager);
as->mk_add(def, k_app, new_rhs2);
expr * body1 = m_manager.mk_eq(head, new_rhs2);
expr * body2 = m_manager.mk_app(new_body->get_decl(), k_app, as->mk_numeral(rational(0)));
quantifier * q1 = m_manager.update_quantifier(new_q, body1);
expr * patterns[1] = { m_manager.mk_pattern(k_app) };
quantifier * q2 = m_manager.update_quantifier(new_q, 1, patterns, body2);
new_exprs.push_back(q1);
new_exprs.push_back(q2);
if (m_manager.proofs_enabled()) {
// new_pr : new_q
// rw : [rewrite] new_q ~ q1 & q2
// mp : [modus_pones new_pr rw] q1 & q2
// pr1 : [and-elim mp] q1
// pr2 : [and-elim mp] q2
app * q1q2 = m_manager.mk_and(q1,q2);
proof * rw = m_manager.mk_oeq_rewrite(new_q, q1q2);
proof * mp = m_manager.mk_modus_ponens(new_pr, rw);
proof * pr1 = m_manager.mk_and_elim(mp, 0);
proof * pr2 = m_manager.mk_and_elim(mp, 1);
new_prs.push_back(pr1);
new_prs.push_back(pr2);
}
return true;
}
/**
n is of the form: (forall (X) (iff (= (f X) t) def[X]))
Convert it into:
(forall (X) (= (f X) (ite def[X] t (k X))))
(forall (X) (not (= (k X) t)))
where k is a fresh symbol.
The new quantifiers and proofs are stored in new_exprs and new_prs
*/
static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr,
expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
func_decl * f = head->get_decl();
func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
app * k_app = m.mk_app(k, head->get_num_args(), head->get_args());
app * ite = m.mk_ite(def, t, k_app);
app * body_1 = m.mk_eq(head, ite);
app * body_2 = m.mk_not(m.mk_eq(k_app, t));
quantifier * q1 = m.update_quantifier(q, body_1);
expr * pats[1] = { m.mk_pattern(k_app) };
quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns
new_exprs.push_back(q1);
new_exprs.push_back(q2);
if (m.proofs_enabled()) {
// r : [rewrite] q ~ q1 & q2
// pr : q
// mp : [modus_pones pr pr1] q1 & q2
// pr1 : [and-elim mp] q1
// pr2 : [and-elim mp] q2
app * q1q2 = m.mk_and(q1,q2);
proof * r = m.mk_oeq_rewrite(q, q1q2);
proof * mp = m.mk_modus_ponens(pr, r);
proof * pr1 = m.mk_and_elim(mp, 0);
proof * pr2 = m.mk_and_elim(mp, 1);
new_prs.push_back(pr1);
new_prs.push_back(pr2);
}
}
macro_finder::macro_finder(ast_manager & m, macro_manager & mm):
m_manager(m),
m_macro_manager(mm),
m_util(mm.get_util()) {
}
macro_finder::~macro_finder() {
}
bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
TRACE("macro_finder", tout << "starting expand_macros:\n";
m_macro_manager.display(tout););
bool found_new_macro = false;
for (unsigned i = 0; i < num; i++) {
expr * n = exprs[i];
proof * pr = m_manager.proofs_enabled() ? prs[i] : 0;
expr_ref new_n(m_manager);
proof_ref new_pr(m_manager);
m_macro_manager.expand_macros(n, pr, new_n, new_pr);
app * head = 0;
expr * def = 0;
app * t = 0;
if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) {
TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << mk_pp(new_n, m_manager) << "\n";);
found_new_macro = true;
}
else if (is_arith_macro(new_n, new_pr, new_exprs, new_prs)) {
TRACE("macro_finder_found", tout << "found new arith macro:\n" << mk_pp(new_n, m_manager) << "\n";);
found_new_macro = true;
}
else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) {
TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << mk_pp(head, m_manager) << "\n" << mk_pp(t, m_manager) << "\n" <<
mk_pp(def, m_manager) << "\n";);
pseudo_predicate_macro2macro(m_manager, head, t, def, to_quantifier(new_n), new_pr, new_exprs, new_prs);
found_new_macro = true;
}
else {
new_exprs.push_back(new_n);
if (m_manager.proofs_enabled())
new_prs.push_back(new_pr);
}
}
return found_new_macro;
}
void macro_finder::operator()(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
TRACE("macro_finder", tout << "processing macros...\n";);
expr_ref_vector _new_exprs(m_manager);
proof_ref_vector _new_prs(m_manager);
if (expand_macros(num, exprs, prs, _new_exprs, _new_prs)) {
while (true) {
expr_ref_vector old_exprs(m_manager);
proof_ref_vector old_prs(m_manager);
_new_exprs.swap(old_exprs);
_new_prs.swap(old_prs);
SASSERT(_new_exprs.empty());
SASSERT(_new_prs.empty());
if (!expand_macros(old_exprs.size(), old_exprs.c_ptr(), old_prs.c_ptr(), _new_exprs, _new_prs))
break;
}
}
new_exprs.append(_new_exprs);
new_prs.append(_new_prs);
}

55
src/macros/macro_finder.h Normal file
View file

@ -0,0 +1,55 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_finder.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#ifndef _MACRO_FINDER_H_
#define _MACRO_FINDER_H_
#include"macro_manager.h"
#include"arith_simplifier_plugin.h"
bool is_macro_head(expr * n, unsigned num_decls);
bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, obj_hashtable<func_decl> const * forbidden_set, app * & head, expr * & def);
inline bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, app * & head, expr * & def) {
return is_simple_macro(m, n, num_decls, 0, head, def);
}
/**
\brief Macro finder is responsible for finding universally quantified sub-formulas that can be used
as macros.
*/
class macro_finder {
ast_manager & m_manager;
macro_manager & m_macro_manager;
macro_util & m_util;
arith_simplifier_plugin * get_arith_simp() { return m_util.get_arith_simp(); }
bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
bool is_macro(expr * n, app * & head, expr * & def);
bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t);
bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def);
public:
macro_finder(ast_manager & m, macro_manager & mm);
~macro_finder();
void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
};
#endif /* _MACRO_FINDER_H_ */

View file

@ -0,0 +1,319 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_manager.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
Christoph Wintersteiger (t-cwinte), 2010-04-13: Added cycle detection for macro definitions
Leonardo de Moura (leonardo) 2010-12-15: Moved dependency management to func_decl_dependencies.h
--*/
#include"macro_manager.h"
#include"for_each_expr.h"
#include"var_subst.h"
#include"ast_pp.h"
#include"recurse_expr_def.h"
macro_manager::macro_manager(ast_manager & m, simplifier & s):
m_manager(m),
m_simplifier(s),
m_util(m, s),
m_decls(m),
m_macros(m),
m_macro_prs(m),
m_forbidden(m),
m_deps(m) {
m_util.set_forbidden_set(&m_forbidden_set);
}
macro_manager::~macro_manager() {
}
void macro_manager::push_scope() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_decls_lim = m_decls.size();
s.m_forbidden_lim = m_forbidden.size();
}
void macro_manager::pop_scope(unsigned num_scopes) {
unsigned new_lvl = m_scopes.size() - num_scopes;
scope & s = m_scopes[new_lvl];
restore_decls(s.m_decls_lim);
restore_forbidden(s.m_forbidden_lim);
m_scopes.shrink(new_lvl);
}
void macro_manager::restore_decls(unsigned old_sz) {
unsigned sz = m_decls.size();
for (unsigned i = old_sz; i < sz; i++) {
m_decl2macro.erase(m_decls.get(i));
m_deps.erase(m_decls.get(i));
if (m_manager.proofs_enabled())
m_decl2macro_pr.erase(m_decls.get(i));
}
m_decls.shrink(old_sz);
m_macros.shrink(old_sz);
if (m_manager.proofs_enabled())
m_macro_prs.shrink(old_sz);
}
void macro_manager::restore_forbidden(unsigned old_sz) {
unsigned sz = m_forbidden.size();
for (unsigned i = old_sz; i < sz; i++)
m_forbidden_set.erase(m_forbidden.get(i));
m_forbidden.shrink(old_sz);
}
void macro_manager::reset() {
m_decl2macro.reset();
m_decl2macro_pr.reset();
m_decls.reset();
m_macros.reset();
m_macro_prs.reset();
m_scopes.reset();
m_forbidden_set.reset();
m_forbidden.reset();
m_deps.reset();
}
bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) {
TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(m, m_manager) << "\n";);
// if we already have a macro for f then return false;
if (m_decls.contains(f)) {
TRACE("macro_insert", tout << "we already have a macro for: " << f->get_name() << "\n";);
return false;
}
app * head;
expr * definition;
get_head_def(m, f, head, definition);
func_decl_set * s = m_deps.mk_func_decl_set();
m_deps.collect_func_decls(definition, s);
if (!m_deps.insert(f, s)) {
return false;
}
// add macro
m_decl2macro.insert(f, m);
m_decls.push_back(f);
m_macros.push_back(m);
if (m_manager.proofs_enabled()) {
m_macro_prs.push_back(pr);
m_decl2macro_pr.insert(f, pr);
}
TRACE("macro_insert", tout << "A macro was successfully created for: " << f->get_name() << "\n";);
// Nothing's forbidden anymore; if something's bad, we detected it earlier.
// mark_forbidden(m->get_expr());
return true;
}
namespace macro_manager_ns {
struct proc {
obj_hashtable<func_decl> & m_forbidden_set;
func_decl_ref_vector & m_forbidden;
proc(obj_hashtable<func_decl> & s, func_decl_ref_vector & v):m_forbidden_set(s), m_forbidden(v) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app * n) {
func_decl * d = n->get_decl();
if (n->get_num_args() > 0 && n->get_family_id() == null_family_id && !m_forbidden_set.contains(d)) {
m_forbidden_set.insert(d);
m_forbidden.push_back(d);
}
}
};
};
/**
\brief Mark all func_decls used in exprs as forbidden.
*/
void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
expr_mark visited;
macro_manager_ns::proc p(m_forbidden_set, m_forbidden);
for (unsigned i = 0; i < n; i++)
for_each_expr(p, visited, exprs[i]);
}
void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const {
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d));
SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d));
if (is_app_of(lhs, d)) {
head = to_app(lhs);
def = rhs;
}
else {
head = to_app(rhs);
def = lhs;
}
}
void macro_manager::display(std::ostream & out) {
unsigned sz = m_decls.size();
for (unsigned i = 0; i < sz; i++) {
func_decl * f = m_decls.get(i);
quantifier * q = 0;
m_decl2macro.find(f, q);
app * head;
expr * def;
get_head_def(q, f, head, def);
SASSERT(q);
out << mk_pp(head, m_manager) << " ->\n" << mk_pp(def, m_manager) << "\n";
}
}
func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & interp) const {
func_decl * f = m_decls.get(i);
quantifier * q = m_macros.get(i);
app * head;
expr * def;
get_head_def(q, f, head, def);
TRACE("macro_bug",
tout << f->get_name() << "\n" << mk_pp(head, m_manager) << "\n" << mk_pp(q, m_manager) << "\n";);
m_util.mk_macro_interpretation(head, def, interp);
return f;
}
macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm, simplifier & s):
simplifier(m),
m_macro_manager(mm) {
// REMARK: theory simplifier should not be used by macro_expander...
// is_arith_macro rewrites a quantifer such as:
// forall (x Int) (= (+ x (+ (f x) 1)) 2)
// into
// forall (x Int) (= (f x) (+ 1 (* -1 x)))
// The goal is to make simple macro detection detect the arith macro.
// The arith simplifier will undo this transformation.
// borrow_plugins(s);
enable_ac_support(false);
}
macro_manager::macro_expander::~macro_expander() {
// release_plugins();
}
void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) {
simplifier::reduce1_quantifier(q);
// If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore.
// The MAM assumes valid patterns, and it crashes if invalid patterns are provided.
// For example, it will crash if the pattern does not contain all variables.
//
// Alternative solution: use pattern_validation to check if the pattern is still valid.
// I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion.
// So, I'm just erasing them.
expr * new_q_expr = 0;
proof * new_q_pr = 0;
get_cached(q, new_q_expr, new_q_pr);
if (!is_quantifier(new_q_expr))
return;
quantifier * new_q = to_quantifier(new_q_expr);
bool erase_patterns = false;
if (q->get_num_patterns() != new_q->get_num_patterns() ||
q->get_num_no_patterns() != new_q->get_num_no_patterns()) {
erase_patterns = true;
}
else {
for (unsigned i = 0; !erase_patterns && i < q->get_num_patterns(); i++) {
if (q->get_pattern(i) != new_q->get_pattern(i))
erase_patterns = true;
}
for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) {
if (q->get_no_pattern(i) != new_q->get_no_pattern(i))
erase_patterns = true;
}
}
if (erase_patterns) {
ast_manager & m = get_manager();
expr * new_new_q = m.update_quantifier(new_q, 0, 0, 0, 0, new_q->get_expr());
// we can use the same proof since new_new_q and new_q are identical modulo patterns/annotations
cache_result(q, new_new_q, new_q_pr);
}
}
bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref & p) {
if (!is_app(_n))
return false;
app * n = to_app(_n);
quantifier * q = 0;
func_decl * d = n->get_decl();
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m_manager) << "\nd:\n" << d->get_name() << "\n";);
if (m_macro_manager.m_decl2macro.find(d, q)) {
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m_manager) << "\n";);
app * head = 0;
expr * def = 0;
m_macro_manager.get_head_def(q, d, head, def);
unsigned num = n->get_num_args();
SASSERT(head && def);
ptr_buffer<expr> subst_args;
subst_args.resize(num, 0);
for (unsigned i = 0; i < num; i++) {
var * v = to_var(head->get_arg(i));
SASSERT(v->get_idx() < num);
unsigned nidx = num - v->get_idx() - 1;
SASSERT(subst_args[nidx] == 0);
subst_args[nidx] = n->get_arg(i);
}
var_subst s(m_manager);
s(def, num, subst_args.c_ptr(), r);
if (m_manager.proofs_enabled()) {
expr_ref instance(m_manager);
s(q->get_expr(), num, subst_args.c_ptr(), instance);
proof * qi_pr = m_manager.mk_quant_inst(m_manager.mk_or(m_manager.mk_not(q), instance), num, subst_args.c_ptr());
proof * q_pr = 0;
m_macro_manager.m_decl2macro_pr.find(d, q_pr);
SASSERT(q_pr != 0);
proof * prs[2] = { qi_pr, q_pr };
p = m_manager.mk_unit_resolution(2, prs);
}
else {
p = 0;
}
return true;
}
return false;
}
void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) {
if (has_macros()) {
// Expand macros with "real" proof production support (NO rewrite*)
expr_ref old_n(m_manager);
proof_ref old_pr(m_manager);
old_n = n;
old_pr = pr;
for (;;) {
macro_expander proc(m_manager, *this, m_simplifier);
proof_ref n_eq_r_pr(m_manager);
TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m_manager) << "\n";);
proc(old_n, r, n_eq_r_pr);
new_pr = m_manager.mk_modus_ponens(old_pr, n_eq_r_pr);
if (r.get() == old_n.get())
return;
old_n = r;
old_pr = new_pr;
}
}
else {
r = n;
new_pr = pr;
}
}

View file

@ -0,0 +1,99 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_manager.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-04-05.
Revision History:
--*/
#ifndef _MACRO_MANAGER_H_
#define _MACRO_MANAGER_H_
#include"ast_util.h"
#include"obj_hashtable.h"
#include"simplifier.h"
#include"recurse_expr.h"
#include"func_decl_dependencies.h"
#include"macro_util.h"
/**
\brief Macros are universally quantified formulas of the form:
(forall X (= (f X) T[X]))
(forall X (iff (f X) T[X]))
where T[X] does not contain X.
This class is responsible for storing macros and expanding them.
It has support for backtracking and tagging declarations in an expression as forbidded for being macros.
*/
class macro_manager {
ast_manager & m_manager;
simplifier & m_simplifier;
macro_util m_util;
obj_map<func_decl, quantifier *> m_decl2macro; // func-decl -> quantifier
obj_map<func_decl, proof *> m_decl2macro_pr; // func-decl -> quantifier_proof
func_decl_ref_vector m_decls;
quantifier_ref_vector m_macros;
proof_ref_vector m_macro_prs;
obj_hashtable<func_decl> m_forbidden_set;
func_decl_ref_vector m_forbidden;
struct scope {
unsigned m_decls_lim;
unsigned m_forbidden_lim;
};
svector<scope> m_scopes;
func_decl_dependencies m_deps;
void restore_decls(unsigned old_sz);
void restore_forbidden(unsigned old_sz);
class macro_expander : public simplifier {
protected:
macro_manager & m_macro_manager;
virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
virtual void reduce1_quantifier(quantifier * q);
public:
macro_expander(ast_manager & m, macro_manager & mm, simplifier & s);
~macro_expander();
};
friend class macro_expander;
public:
macro_manager(ast_manager & m, simplifier & s);
~macro_manager();
ast_manager & get_manager() const { return m_manager; }
macro_util & get_util() { return m_util; }
bool insert(func_decl * f, quantifier * m, proof * pr);
bool has_macros() const { return !m_macros.empty(); }
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void mark_forbidden(unsigned n, expr * const * exprs);
void mark_forbidden(expr * e) { mark_forbidden(1, &e); }
bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); }
obj_hashtable<func_decl> const & get_forbidden_set() const { return m_forbidden_set; }
void display(std::ostream & out);
unsigned get_num_macros() const { return m_decls.size(); }
unsigned get_first_macro_last_level() const { return m_scopes.empty() ? 0 : m_scopes.back().m_decls_lim; }
func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); }
func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const;
quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = 0; m_decl2macro.find(f, q); return q; }
void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const;
void expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr);
};
#endif /* _MACRO_MANAGER_H_ */

View file

@ -0,0 +1,184 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
macro_substitution.cpp
Abstract:
Mapping from func_decl to quantifiers of the form
Forall X. f(X) = T[X]
Forall X. f(X) iff C[X]
Author:
Leonardo (leonardo) 2012-02-17
Notes:
--*/
#include"macro_substitution.h"
#include"ref_util.h"
typedef obj_map<func_decl, proof*> func_decl2proof;
typedef obj_map<func_decl, expr_dependency*> func_decl2expr_dependency;
void macro_substitution::init() {
if (proofs_enabled())
m_decl2macro_pr = alloc(func_decl2proof);
if (unsat_core_enabled())
m_decl2macro_dep = alloc(func_decl2expr_dependency);
}
macro_substitution::macro_substitution(ast_manager & m):
m_manager(m),
m_cores_enabled(false),
m_proofs_enabled(m.proofs_enabled()) {
init();
}
macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled):
m_manager(m),
m_cores_enabled(cores_enabled),
m_proofs_enabled(m.proofs_enabled()) {
init();
}
macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled):
m_manager(m),
m_cores_enabled(cores_enabled),
m_proofs_enabled(proofs_enabled) {
SASSERT(!proofs_enabled || m.proofs_enabled());
init();
}
macro_substitution::~macro_substitution() {
reset();
}
void macro_substitution::reset() {
dec_ref_map_key_values(m_manager, m_decl2macro);
if (proofs_enabled())
dec_ref_map_values(m_manager, *m_decl2macro_pr);
if (unsat_core_enabled())
dec_ref_map_values(m_manager, *m_decl2macro_dep);
}
void macro_substitution::cleanup() {
reset();
m_decl2macro.finalize();
if (proofs_enabled())
m_decl2macro_pr->finalize();
if (unsat_core_enabled())
m_decl2macro_dep->finalize();
}
void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) {
DEBUG_CODE({
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = body->get_arg(0);
expr * rhs = body->get_arg(1);
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
});
obj_map<func_decl, quantifier *>::obj_map_entry * entry = m_decl2macro.insert_if_not_there2(f, 0);
if (entry->get_data().m_value == 0) {
// new entry
m_manager.inc_ref(f);
m_manager.inc_ref(q);
entry->get_data().m_value = q;
if (proofs_enabled()) {
SASSERT(!m_decl2macro_pr->contains(f));
m_decl2macro_pr->insert(f, pr);
m_manager.inc_ref(pr);
}
if (unsat_core_enabled()) {
SASSERT(!m_decl2macro_dep->contains(f));
m_decl2macro_dep->insert(f, dep);
m_manager.inc_ref(dep);
}
}
else {
// replacing entry
m_manager.inc_ref(q);
m_manager.dec_ref(entry->get_data().m_value);
entry->get_data().m_value = q;
if (proofs_enabled()) {
obj_map<func_decl, proof *>::obj_map_entry * entry_pr = m_decl2macro_pr->find_core(f);
SASSERT(entry_pr != 0);
m_manager.inc_ref(pr);
m_manager.dec_ref(entry_pr->get_data().m_value);
entry_pr->get_data().m_value = pr;
}
if (unsat_core_enabled()) {
obj_map<func_decl, expr_dependency*>::obj_map_entry * entry_dep = m_decl2macro_dep->find_core(f);
SASSERT(entry_dep != 0);
m_manager.inc_ref(dep);
m_manager.dec_ref(entry_dep->get_data().m_value);
entry_dep->get_data().m_value = dep;
}
}
}
void macro_substitution::erase(func_decl * f) {
if (proofs_enabled()) {
proof * pr = 0;
if (m_decl2macro_pr->find(f, pr)) {
m_manager.dec_ref(pr);
m_decl2macro_pr->erase(f);
}
}
if (unsat_core_enabled()) {
expr_dependency * dep = 0;
if (m_decl2macro_dep->find(f, dep)) {
m_manager.dec_ref(dep);
m_decl2macro_dep->erase(f);
}
}
quantifier * q = 0;
if (m_decl2macro.find(f, q)) {
m_manager.dec_ref(f);
m_manager.dec_ref(q);
m_decl2macro.erase(f);
}
}
void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) {
app * body = to_app(q->get_expr());
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
SASSERT(!is_app_of(lhs, f) || !is_app_of(rhs, f));
if (is_app_of(lhs, f)) {
head = to_app(lhs);
def = rhs;
}
else {
head = to_app(rhs);
def = lhs;
}
}
bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr) {
if (m_decl2macro.find(f, q)) {
if (proofs_enabled())
m_decl2macro_pr->find(f, pr);
return true;
}
return false;
}
bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep) {
if (m_decl2macro.find(f, q)) {
if (proofs_enabled())
m_decl2macro_pr->find(f, pr);
if (unsat_core_enabled())
m_decl2macro_dep->find(f, dep);
return true;
}
return false;
}

View file

@ -0,0 +1,59 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
macro_substitution.h
Abstract:
Mapping from func_decl to quantifiers of the form
Forall X. f(X) = T[X]
Forall X. f(X) iff C[X]
Author:
Leonardo (leonardo) 2012-02-17
Notes:
--*/
#ifndef _MACRO_SUBSTITUTION_H_
#define _MACRO_SUBSTITUTION_H_
#include"ast.h"
class macro_substitution {
ast_manager & m_manager;
obj_map<func_decl, quantifier *> m_decl2macro;
scoped_ptr<obj_map<func_decl, proof *> > m_decl2macro_pr;
scoped_ptr<obj_map<func_decl, expr_dependency *> > m_decl2macro_dep;
unsigned m_cores_enabled:1;
unsigned m_proofs_enabled:1;
void init();
public:
macro_substitution(ast_manager & m);
macro_substitution(ast_manager & m, bool cores_enabled);
macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled);
~macro_substitution();
ast_manager & m() const { return m_manager; }
bool proofs_enabled() const { return m_proofs_enabled; }
bool unsat_core_enabled() const { return m_cores_enabled; }
bool empty() const { return m_decl2macro.empty(); }
void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = 0);
void erase(func_decl * f);
bool contains(func_decl * f) { return m_decl2macro.contains(f); }
bool find(func_decl * f, quantifier * & q, proof * & pr);
bool find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep);
void get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def);
void reset();
void cleanup();
};
#endif

928
src/macros/macro_util.cpp Normal file
View file

@ -0,0 +1,928 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_util.cpp
Abstract:
Macro finding goodies.
They are used during preprocessing (MACRO_FINDER=true), and model building.
Author:
Leonardo de Moura (leonardo) 2010-12-15.
Revision History:
--*/
#include"macro_util.h"
#include"occurs.h"
#include"arith_simplifier_plugin.h"
#include"basic_simplifier_plugin.h"
#include"bv_simplifier_plugin.h"
#include"var_subst.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"ast_util.h"
#include"for_each_expr.h"
#include"well_sorted.h"
macro_util::macro_util(ast_manager & m, simplifier & s):
m_manager(m),
m_simplifier(s),
m_arith_simp(0),
m_bv_simp(0),
m_basic_simp(0),
m_forbidden_set(0),
m_curr_clause(0) {
}
arith_simplifier_plugin * macro_util::get_arith_simp() const {
if (m_arith_simp == 0) {
const_cast<macro_util*>(this)->m_arith_simp = static_cast<arith_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_family_id("arith")));
}
SASSERT(m_arith_simp != 0);
return m_arith_simp;
}
bv_simplifier_plugin * macro_util::get_bv_simp() const {
if (m_bv_simp == 0) {
const_cast<macro_util*>(this)->m_bv_simp = static_cast<bv_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_family_id("bv")));
}
SASSERT(m_bv_simp != 0);
return m_bv_simp;
}
basic_simplifier_plugin * macro_util::get_basic_simp() const {
if (m_basic_simp == 0) {
const_cast<macro_util*>(this)->m_basic_simp = static_cast<basic_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.get_basic_family_id()));
}
SASSERT(m_basic_simp != 0);
return m_basic_simp;
}
bool macro_util::is_bv(expr * n) const {
return get_bv_simp()->is_bv(n);
}
bool macro_util::is_bv_sort(sort * s) const {
return get_bv_simp()->is_bv_sort(s);
}
bool macro_util::is_add(expr * n) const {
return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n);
}
bool macro_util::is_times_minus_one(expr * n, expr * & arg) const {
return get_arith_simp()->is_times_minus_one(n, arg) || get_bv_simp()->is_times_minus_one(n, arg);
}
bool macro_util::is_le(expr * n) const {
return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n);
}
bool macro_util::is_le_ge(expr * n) const {
return get_arith_simp()->is_le_ge(n) || get_bv_simp()->is_le_ge(n);
}
poly_simplifier_plugin * macro_util::get_poly_simp_for(sort * s) const {
if (is_bv_sort(s))
return get_bv_simp();
else
return get_arith_simp();
}
app * macro_util::mk_zero(sort * s) const {
poly_simplifier_plugin * ps = get_poly_simp_for(s);
ps->set_curr_sort(s);
return ps->mk_zero();
}
void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const {
if (is_bv(t1)) {
get_bv_simp()->mk_sub(t1, t2, r);
}
else {
get_arith_simp()->mk_sub(t1, t2, r);
}
}
void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const {
if (is_bv(t1)) {
get_bv_simp()->mk_add(t1, t2, r);
}
else {
get_arith_simp()->mk_add(t1, t2, r);
}
}
void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const {
if (num_args == 0) {
r = mk_zero(s);
return;
}
poly_simplifier_plugin * ps = get_poly_simp_for(s);
ps->set_curr_sort(s);
ps->mk_add(num_args, args, r);
}
/**
\brief Return true if \c n is an application of the form
(f x_{k_1}, ..., x_{k_n})
where f is uninterpreted
n == num_decls
x_{k_i}'s are variables
and {k_1, ..., k_n } is equals to the set {0, ..., num_decls-1}
*/
bool macro_util::is_macro_head(expr * n, unsigned num_decls) const {
if (is_app(n) &&
!to_app(n)->get_decl()->is_associative() &&
to_app(n)->get_family_id() == null_family_id &&
to_app(n)->get_num_args() == num_decls) {
sbuffer<int> var2pos;
var2pos.resize(num_decls, -1);
for (unsigned i = 0; i < num_decls; i++) {
expr * c = to_app(n)->get_arg(i);
if (!is_var(c))
return false;
unsigned idx = to_var(c)->get_idx();
if (idx >= num_decls || var2pos[idx] != -1)
return false;
var2pos[idx] = i;
}
return true;
}
return false;
}
/**
\brief Return true if n is of the form
(= (f x_{k_1}, ..., x_{k_n}) t) OR
(iff (f x_{k_1}, ..., x_{k_n}) t)
where
is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND
t does not contain f AND
f is not in forbidden_set
In case of success
head will contain (f x_{k_1}, ..., x_{k_n}) AND
def will contain t
*/
bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs)) {
head = to_app(lhs);
def = rhs;
return true;
}
}
return false;
}
/**
\brief Return true if n is of the form
(= t (f x_{k_1}, ..., x_{k_n})) OR
(iff t (f x_{k_1}, ..., x_{k_n}))
where
is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND
t does not contain f AND
f is not in forbidden_set
In case of success
head will contain (f x_{k_1}, ..., x_{k_n}) AND
def will contain t
*/
bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs)) {
head = to_app(rhs);
def = lhs;
return true;
}
}
return false;
}
/**
\brief Return true if n contains f. The method ignores the sub-expression \c exception.
\remark n is a "polynomial".
*/
bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) const {
expr * curr = n;
unsigned num_args;
expr * const * args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (arg != exception && occurs(f, arg))
return true;
}
return false;
}
bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const {
// TODO: obsolete... we should move to collect_arith_macro_candidates
arith_simplifier_plugin * as = get_arith_simp();
if (!m_manager.is_eq(n) && !as->is_le(n) && !as->is_ge(n))
return false;
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (!as->is_numeral(rhs))
return false;
inv = false;
ptr_buffer<expr> args;
expr * h = 0;
unsigned lhs_num_args;
expr * const * lhs_args;
if (is_add(lhs)) {
lhs_num_args = to_app(lhs)->get_num_args();
lhs_args = to_app(lhs)->get_args();
}
else {
lhs_num_args = 1;
lhs_args = &lhs;
}
for (unsigned i = 0; i < lhs_num_args; i++) {
expr * arg = lhs_args[i];
expr * neg_arg;
if (h == 0 &&
is_macro_head(arg, num_decls) &&
!is_forbidden(to_app(arg)->get_decl()) &&
!poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) {
h = arg;
}
else if (h == 0 && as->is_times_minus_one(arg, neg_arg) &&
is_macro_head(neg_arg, num_decls) &&
!is_forbidden(to_app(neg_arg)->get_decl()) &&
!poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) {
h = neg_arg;
inv = true;
}
else {
args.push_back(arg);
}
}
if (h == 0)
return false;
head = to_app(h);
expr_ref tmp(m_manager);
as->mk_add(args.size(), args.c_ptr(), tmp);
if (inv)
as->mk_sub(tmp, rhs, def);
else
as->mk_sub(rhs, tmp, def);
return true;
}
/**
\brief Auxiliary function for is_pseudo_predicate_macro. It detects the pattern (= (f X) t)
*/
bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t) {
if (!m_manager.is_eq(n))
return false;
expr * lhs = to_app(n)->get_arg(0);
expr * rhs = to_app(n)->get_arg(1);
if (!is_ground(lhs) && !is_ground(rhs))
return false;
sort * s = m_manager.get_sort(lhs);
if (m_manager.is_uninterp(s))
return false;
sort_size sz = s->get_num_elements();
if (sz.is_finite() && sz.size() == 1)
return false;
if (is_macro_head(lhs, num_decls)) {
head = to_app(lhs);
t = to_app(rhs);
return true;
}
if (is_macro_head(rhs, num_decls)) {
head = to_app(rhs);
t = to_app(lhs);
return true;
}
return false;
}
/**
\brief Returns true if n if of the form (forall (X) (iff (= (f X) t) def[X]))
where t is a ground term, (f X) is the head.
*/
bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def) {
if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
return false;
TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
expr * body = to_quantifier(n)->get_expr();
unsigned num_decls = to_quantifier(n)->get_num_decls();
if (!m_manager.is_iff(body))
return false;
expr * lhs = to_app(body)->get_arg(0);
expr * rhs = to_app(body)->get_arg(1);
if (is_pseudo_head(lhs, num_decls, head, t) &&
!is_forbidden(head->get_decl()) &&
!occurs(head->get_decl(), rhs)) {
def = rhs;
return true;
}
if (is_pseudo_head(rhs, num_decls, head, t) &&
!is_forbidden(head->get_decl()) &&
!occurs(head->get_decl(), lhs)) {
def = lhs;
return true;
}
return false;
}
/**
\brief A quasi-macro head is of the form f[X_1, ..., X_n],
where n == num_decls, f[X_1, ..., X_n] is a term starting with symbol f, f is uninterpreted,
contains all universally quantified variables as arguments.
Note that, some arguments of f[X_1, ..., X_n] may not be variables.
Examples of quasi-macros:
f(x_1, x_1 + x_2, x_2) for num_decls == 2
g(x_1, x_1) for num_decls == 1
Return true if \c n is a quasi-macro. Store the macro head in \c head, and the conditions to apply the macro in \c cond.
*/
bool macro_util::is_quasi_macro_head(expr * n, unsigned num_decls) const {
if (is_app(n) &&
to_app(n)->get_family_id() == null_family_id &&
to_app(n)->get_num_args() >= num_decls) {
unsigned num_args = to_app(n)->get_num_args();
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned num_found_vars = 0;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
if (idx >= num_decls)
return false;
if (found_vars[idx] == false) {
found_vars[idx] = true;
num_found_vars++;
}
}
else {
if (occurs(to_app(n)->get_decl(), arg))
return false;
}
}
return num_found_vars == num_decls;
}
return false;
}
/**
\brief Convert a quasi-macro head into a macro head, and store the conditions under
which it is valid in cond.
*/
void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const {
unsigned num_args = qhead->get_num_args();
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
ptr_buffer<expr> new_args;
ptr_buffer<expr> new_conds;
unsigned next_var_idx = num_decls;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = qhead->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
SASSERT(idx < num_decls);
if (found_vars[idx] == false) {
found_vars[idx] = true;
new_args.push_back(arg);
continue;
}
}
var * new_var = m_manager.mk_var(next_var_idx, m_manager.get_sort(arg));
next_var_idx++;
expr * new_cond = m_manager.mk_eq(new_var, arg);
new_args.push_back(new_var);
new_conds.push_back(new_cond);
}
get_basic_simp()->mk_and(new_conds.size(), new_conds.c_ptr(), cond);
head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.c_ptr());
}
/**
\brief Given a macro defined by head and def, stores an interpretation for head->get_decl() in interp.
This method assumes is_macro_head(head, head->get_num_args()) returns true,
and def does not contain head->get_decl().
See normalize_expr
*/
void macro_util::mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const {
SASSERT(is_macro_head(head, head->get_num_args()));
SASSERT(!occurs(head->get_decl(), def));
normalize_expr(head, def, interp);
}
/**
\brief The variables in head may be in the wrong order.
Example: f(x_1, x_0) instead of f(x_0, x_1)
This method is essentially renaming the variables in t.
Suppose t is g(x_1, x_0 + x_1)
This method will store g(x_0, x_1 + x_0) in norm_t.
f(x_1, x_2) --> f(x_0, x_1)
f(x_3, x_2) --> f(x_0, x_1)
*/
void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const {
expr_ref_buffer var_mapping(m_manager);
bool changed = false;
unsigned num_args = head->get_num_args();
unsigned max = num_args;
for (unsigned i = 0; i < num_args; i++) {
var * v = to_var(head->get_arg(i));
if (v->get_idx() >= max)
max = v->get_idx() + 1;
}
TRACE("normalize_expr_bug",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";);
for (unsigned i = 0; i < num_args; i++) {
var * v = to_var(head->get_arg(i));
if (v->get_idx() != i) {
changed = true;
var * new_var = m_manager.mk_var(i, v->get_sort());
CTRACE("normalize_expr_bug", v->get_idx() >= num_args, tout << mk_pp(v, m_manager) << ", num_args: " << num_args << "\n";);
SASSERT(v->get_idx() < max);
var_mapping.setx(max - v->get_idx() - 1, new_var);
}
else {
var_mapping.setx(max - i - 1, v);
}
}
if (changed) {
// REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution.
var_subst subst(m_manager);
TRACE("macro_util_bug",
tout << "head: " << mk_pp(head, m_manager) << "\n";
tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitituion:\n";
for (unsigned i = 0; i < var_mapping.size(); i++) {
tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n";
});
subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t);
SASSERT(is_well_sorted(m_manager, norm_t));
}
else {
norm_t = t;
}
}
// -----------------------------
//
// "Hint" support
// See comment at is_hint_atom
// for a definition of what a hint is.
//
// -----------------------------
bool is_hint_head(expr * n, ptr_buffer<var> & vars) {
if (!is_app(n))
return false;
if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id)
return false;
unsigned num_args = to_app(n)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(n)->get_arg(i);
if (is_var(arg))
vars.push_back(to_var(arg));
}
return !vars.empty();
}
/**
\brief Returns true if the variables in n is a subset of \c vars.
*/
bool vars_of_is_subset(expr * n, ptr_buffer<var> const & vars) {
if (is_ground(n))
return true;
obj_hashtable<expr> visited;
ptr_buffer<expr> todo;
todo.push_back(n);
while (!todo.empty()) {
expr * curr = todo.back();
todo.pop_back();
if (is_var(curr)) {
if (std::find(vars.begin(), vars.end(), to_var(curr)) == vars.end())
return false;
}
else if (is_app(curr)) {
unsigned num_args = to_app(curr)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(curr)->get_arg(i);
if (is_ground(arg))
continue;
if (visited.contains(arg))
continue;
visited.insert(arg);
todo.push_back(arg);
}
}
else {
SASSERT(is_quantifier(curr));
return false; // do no support nested quantifier... being conservative.
}
}
return true;
}
/**
\brief (= lhs rhs) is a hint atom if
lhs is of the form (f t_1 ... t_n)
and all variables occurring in rhs are direct arguments of lhs.
*/
bool is_hint_atom(expr * lhs, expr * rhs) {
ptr_buffer<var> vars;
if (!is_hint_head(lhs, vars))
return false;
return !occurs(to_app(lhs)->get_decl(), rhs) && vars_of_is_subset(rhs, vars);
}
void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref & new_head) {
unsigned num_args = head->get_num_args();
ptr_buffer<expr> new_args;
sbuffer<bool> found_vars;
found_vars.resize(num_decls, false);
unsigned next_var_idx = num_decls;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = head->get_arg(i);
if (is_var(arg)) {
unsigned idx = to_var(arg)->get_idx();
SASSERT(idx < num_decls);
if (found_vars[idx] == false) {
found_vars[idx] = true;
new_args.push_back(arg);
continue;
}
}
var * new_var = m.mk_var(next_var_idx, m.get_sort(arg));
next_var_idx++;
new_args.push_back(new_var);
}
new_head = m.mk_app(head->get_decl(), new_args.size(), new_args.c_ptr());
}
/**
\brief Return true if n can be viewed as a polynomial "hint" based on head.
That is, n (but the monomial exception) only uses the variables in head, and does not use
head->get_decl().
is_hint_head(head, vars) must also return true
*/
bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"
<< mk_pp(exception, m_manager) << "\n";);
ptr_buffer<var> vars;
if (!is_hint_head(head, vars)) {
TRACE("macro_util_hint", tout << "failed because head is not hint head\n";);
return false;
}
func_decl * f = head->get_decl();
unsigned num_args;
expr * const * args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) {
TRACE("macro_util_hint", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";);
return false;
}
}
TRACE("macro_util_hint", tout << "succeeded\n";);
return true;
}
// -----------------------------
//
// Macro candidates
//
// -----------------------------
macro_util::macro_candidates::macro_candidates(ast_manager & m):
m_defs(m),
m_conds(m) {
}
void macro_util::macro_candidates::reset() {
m_fs.reset();
m_defs.reset();
m_conds.reset();
m_ineq.reset();
m_satisfy.reset();
m_hint.reset();
}
void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint) {
m_fs.push_back(f);
m_defs.push_back(def);
m_conds.push_back(cond);
m_ineq.push_back(ineq);
m_satisfy.push_back(satisfy_atom);
m_hint.push_back(hint);
}
// -----------------------------
//
// Macro util
//
// -----------------------------
void macro_util::insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) {
expr_ref norm_def(m_manager);
expr_ref norm_cond(m_manager);
normalize_expr(head, def, norm_def);
if (cond != 0)
normalize_expr(head, cond, norm_cond);
else if (!hint)
norm_cond = m_manager.mk_true();
SASSERT(!hint || norm_cond.get() == 0);
r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint);
}
void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom,
bool hint, macro_candidates & r) {
if (!is_macro_head(head, head->get_num_args())) {
app_ref new_head(m_manager);
expr_ref extra_cond(m_manager);
expr_ref new_cond(m_manager);
if (!hint) {
quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond);
if (cond == 0)
new_cond = extra_cond;
else
get_basic_simp()->mk_and(cond, extra_cond, new_cond);
}
else {
hint_to_macro_head(m_manager, head, num_decls, new_head);
}
insert_macro(new_head, def, new_cond, ineq, satisfy_atom, hint, r);
}
else {
insert_macro(head, def, cond, ineq, satisfy_atom, hint, r);
}
}
bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
if (m_curr_clause == 0)
return false;
SASSERT(is_clause(m_manager, m_curr_clause));
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
if (l != except_lit && occurs(f, l))
return true;
}
return false;
}
void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) {
if (m_curr_clause == 0)
return;
SASSERT(is_clause(m_manager, m_curr_clause));
basic_simplifier_plugin * bs = get_basic_simp();
expr_ref_buffer neg_other_lits(m_manager);
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
for (unsigned i = 0; i < num_lits; i++) {
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
if (l != except_lit) {
expr_ref neg_l(m_manager);
bs->mk_not(l, neg_l);
neg_other_lits.push_back(neg_l);
}
}
if (neg_other_lits.empty())
return;
get_basic_simp()->mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond);
}
void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args) {
args.reset();
bool stop = false;
unsigned num_args;
expr * const * _args;
if (is_add(n)) {
num_args = to_app(n)->get_num_args();
_args = to_app(n)->get_args();
}
else {
num_args = 1;
_args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = _args[i];
if (arg != exception)
args.push_back(arg);
}
}
void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) {
expr_ref cond(m_manager);
if (!hint)
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r);
}
void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) {
if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro.
return;
bool stop = false;
ptr_buffer<expr> args;
unsigned lhs_num_args;
expr * const * lhs_args;
if (is_add(lhs)) {
lhs_num_args = to_app(lhs)->get_num_args();
lhs_args = to_app(lhs)->get_args();
}
else {
lhs_num_args = 1;
lhs_args = &lhs;
}
for (unsigned i = 0; i < lhs_num_args; i++) {
expr * arg = lhs_args[i];
expr * neg_arg;
if (!is_app(arg))
continue;
func_decl * f = to_app(arg)->get_decl();
bool _is_arith_macro =
is_quasi_macro_head(arg, num_decls) &&
!is_forbidden(f) &&
!poly_contains_head(lhs, f, arg) &&
!occurs(f, rhs) &&
!rest_contains_decl(f, atom);
bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(arg), arg);
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest);
expr_ref def(m_manager);
mk_sub(rhs, rest, def);
add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r);
}
else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) {
f = to_app(neg_arg)->get_decl();
bool _is_arith_macro =
is_quasi_macro_head(neg_arg, num_decls) &&
!is_forbidden(f) &&
!poly_contains_head(lhs, f, arg) &&
!occurs(f, rhs) &&
!rest_contains_decl(f, atom);
bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(neg_arg), arg);
if (_is_arith_macro || _is_poly_hint) {
collect_poly_args(lhs, arg, args);
expr_ref rest(m_manager);
mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest);
expr_ref def(m_manager);
mk_sub(rest, rhs, def);
add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r);
}
}
}
}
void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
TRACE("macro_util_hint", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";);
if (!m_manager.is_eq(atom) && !is_le_ge(atom))
return;
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
bool is_ineq = !m_manager.is_eq(atom);
collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r);
collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r);
}
/**
\brief Collect macro candidates for atom \c atom.
The candidates are stored in \c r.
The following post-condition holds:
for each i in [0, r.size() - 1]
we have a conditional macro of the form
r.get_cond(i) IMPLIES f(x_1, ..., x_n) = r.get_def(i)
where
f == r.get_fs(i) .., x_n), f is uninterpreted and x_1, ..., x_n are variables.
r.get_cond(i) and r.get_defs(i) do not contain f or variables not in {x_1, ..., x_n}
The idea is to use r.get_defs(i) as the interpretation for f in a model M whenever r.get_cond(i)
Given a model M and values { v_1, ..., v_n }
Let M' be M{x_1 -> v_1, ..., v_n -> v_n}
Note that M'(f(x_1, ..., x_n)) = M(f)(v_1, ..., v_n)
Then, IF we have that M(f)(v_1, ..., v_n) = M'(r.get_def(i)) AND
M'(r.get_cond(i)) = true
THEN M'(atom) = true
That is, if the conditional macro is used then the atom is satisfied when M'(r.get_cond(i)) = true
IF r.is_ineq(i) = false, then
M(f)(v_1, ..., v_n) ***MUST BE*** M'(r.get_def(i)) whenever M'(r.get_cond(i)) = true
IF r.satisfy_atom(i) = true, then we have the stronger property:
Then, IF we have that (M'(r.get_cond(i)) = true IMPLIES M(f)(v_1, ..., v_n) = M'(r.get_def(i)))
THEN M'(atom) = true
*/
void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) {
if (m_manager.is_eq(atom) || m_manager.is_iff(atom)) {
expr * lhs = to_app(atom)->get_arg(0);
expr * rhs = to_app(atom)->get_arg(1);
if (is_quasi_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs) &&
!rest_contains_decl(to_app(lhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r);
}
else if (is_hint_atom(lhs, rhs)) {
insert_quasi_macro(to_app(lhs), num_decls, rhs, 0, false, true, true, r);
}
if (is_quasi_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs) &&
!rest_contains_decl(to_app(rhs)->get_decl(), atom)) {
expr_ref cond(m_manager);
get_rest_clause_as_cond(atom, cond);
insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r);
}
else if (is_hint_atom(rhs, lhs)) {
insert_quasi_macro(to_app(rhs), num_decls, lhs, 0, false, true, true, r);
}
}
collect_arith_macro_candidates(atom, num_decls, r);
}
void macro_util::collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
m_curr_clause = 0;
r.reset();
collect_macro_candidates_core(atom, num_decls, r);
}
void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r) {
r.reset();
expr * n = q->get_expr();
if (has_quantifiers(n))
return;
unsigned num_decls = q->get_num_decls();
SASSERT(m_curr_clause == 0);
if (is_clause(m_manager, n)) {
m_curr_clause = n;
unsigned num_lits = get_clause_num_literals(m_manager, n);
for (unsigned i = 0; i < num_lits; i++)
collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r);
m_curr_clause = 0;
}
else {
collect_macro_candidates_core(n, num_decls, r);
}
}

144
src/macros/macro_util.h Normal file
View file

@ -0,0 +1,144 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
macro_util.h
Abstract:
Macro finding goodies.
They are used during preprocessing (MACRO_FINDER=true), and model building.
Author:
Leonardo de Moura (leonardo) 2010-12-15.
Revision History:
--*/
#ifndef _MACRO_UTIL_H_
#define _MACRO_UTIL_H_
#include"ast.h"
#include"obj_hashtable.h"
#include"simplifier.h"
class poly_simplifier_plugin;
class arith_simplifier_plugin;
class bv_simplifier_plugin;
class basic_simplifier_plugin;
class macro_util {
public:
/**
\brief See collect_macro_candidates.
*/
class macro_candidates {
ptr_vector<func_decl> m_fs;
expr_ref_vector m_defs;
expr_ref_vector m_conds;
svector<bool> m_ineq; // true if the macro is based on an inequality instead of equality.
svector<bool> m_satisfy;
svector<bool> m_hint; // macro did not contain all universal variables in the quantifier.
friend class macro_util;
ast_manager & get_manager() { return m_conds.get_manager(); }
public:
macro_candidates(ast_manager & m);
~macro_candidates() { reset(); }
void reset();
void insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint);
bool empty() const { return m_fs.empty(); }
unsigned size() const { return m_fs.size(); }
func_decl * get_f(unsigned i) const { return m_fs[i]; }
expr * get_def(unsigned i) const { return m_defs.get(i); }
expr * get_cond(unsigned i) const { return m_conds.get(i); }
bool ineq(unsigned i) const { return m_ineq[i]; }
bool satisfy_atom(unsigned i) const { return m_satisfy[i]; }
bool hint(unsigned i) const { return m_hint[i]; }
};
private:
ast_manager & m_manager;
simplifier & m_simplifier;
arith_simplifier_plugin * m_arith_simp;
bv_simplifier_plugin * m_bv_simp;
basic_simplifier_plugin * m_basic_simp;
obj_hashtable<func_decl> * m_forbidden_set;
bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); }
bool poly_contains_head(expr * n, func_decl * f, expr * exception) const;
void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros,
macro_candidates & r);
void normalize_expr(app * head, expr * t, expr_ref & norm_t) const;
void insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r);
void insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint,
macro_candidates & r);
expr * m_curr_clause; // auxiliary var used in collect_macro_candidates.
// Return true if m_curr_clause contains f in a literal different from except_lit
bool rest_contains_decl(func_decl * f, expr * except_lit);
// Store in extra_cond (and (not l_1) ... (not l_n)) where l_i's are the literals of m_curr_clause that are different from except_lit.
void get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond);
void collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args);
void add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r);
void collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool ineq, macro_candidates & r);
void collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r);
void collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r);
bool is_poly_hint(expr * n, app * head, expr * exception);
public:
macro_util(ast_manager & m, simplifier & s);
void set_forbidden_set(obj_hashtable<func_decl> * s) { m_forbidden_set = s; }
arith_simplifier_plugin * get_arith_simp() const;
bv_simplifier_plugin * get_bv_simp() const;
basic_simplifier_plugin * get_basic_simp() const;
bool is_macro_head(expr * n, unsigned num_decls) const;
bool is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const;
bool is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const;
bool is_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const {
return is_left_simple_macro(n, num_decls, head, def) || is_right_simple_macro(n, num_decls, head, def);
}
bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const;
bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
bool inv;
return is_arith_macro(n, num_decls, head, def, inv);
}
bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t);
bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def);
bool is_quasi_macro_head(expr * n, unsigned num_decls) const;
void quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const;
void mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const;
void collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r);
void collect_macro_candidates(quantifier * q, macro_candidates & r);
//
// Auxiliary goodness that allows us to manipulate BV and Arith polynomials.
//
bool is_bv(expr * n) const;
bool is_bv_sort(sort * s) const;
app * mk_zero(sort * s) const;
bool is_add(expr * n) const;
bool is_times_minus_one(expr * n, expr * & arg) const;
bool is_le(expr * n) const;
bool is_le_ge(expr * n) const;
void mk_sub(expr * t1, expr * t2, expr_ref & r) const;
void mk_add(expr * t1, expr * t2, expr_ref & r) const;
void mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const;
poly_simplifier_plugin * get_poly_simp_for(sort * s) const;
};
#endif

317
src/macros/quasi_macros.cpp Normal file
View file

@ -0,0 +1,317 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quasi_macros.cpp
Abstract:
<abstract>
Author:
Christoph Wintersteiger (t-cwinte) 2010-04-23
Revision History:
--*/
#include"quasi_macros.h"
#include"for_each_expr.h"
#include"ast_pp.h"
#include"uint_set.h"
#include"var_subst.h"
quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s) :
m_manager(m),
m_macro_manager(mm),
m_bsimp(p),
m_simplifier(s),
m_new_vars(m),
m_new_eqs(m),
m_new_qsorts(m) {
}
quasi_macros::~quasi_macros() {
}
void quasi_macros::find_occurrences(expr * e) {
unsigned j;
m_todo.reset();
m_todo.push_back(e);
// we remember whether we have seen an expr once, or more than once;
// when we see it the second time, we don't have to visit it another time,
// as we are only intersted in finding unique function applications.
m_visited_once.reset();
m_visited_more.reset();
while (!m_todo.empty()) {
expr * cur = m_todo.back();
m_todo.pop_back();
if (m_visited_more.is_marked(cur))
continue;
if (m_visited_once.is_marked(cur))
m_visited_more.mark(cur, true);
m_visited_once.mark(cur, true);
switch (cur->get_kind()) {
case AST_VAR: break;
case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break;
case AST_APP:
if (is_uninterp(cur) && !is_ground(cur)) {
func_decl * f = to_app(cur)->get_decl();
m_occurrences.insert_if_not_there(f, 0);
occurrences_map::iterator it = m_occurrences.find_iterator(f);
it->m_value++;
}
j = to_app(cur)->get_num_args();
while (j)
m_todo.push_back(to_app(cur)->get_arg(--j));
break;
default: UNREACHABLE();
}
}
};
bool quasi_macros::is_unique(func_decl * f) const {
return m_occurrences.find(f) == 1;
}
struct var_dep_proc {
bit_vector m_bitset;
public:
var_dep_proc(quantifier * q) { m_bitset.resize(q->get_num_decls(), false); }
void operator()(var * n) { m_bitset.set(n->get_idx(), true); }
void operator()(quantifier * n) {}
void operator()(app * n) {}
bool all_used(void) {
for (unsigned i = 0; i < m_bitset.size() ; i++)
if (!m_bitset.get(i))
return false;
return true;
}
};
bool quasi_macros::fully_depends_on(app * a, quantifier * q) const {
// CMW: This checks whether all variables in q are used _somewhere_ deep down in the children of a
/* var_dep_proc proc(q);
for_each_expr(proc, a);
return proc.all_used(); */
// CMW: This code instead checks that all variables appear at least once as a
// direct argument of a, i.e., a->get_arg(i) == v for some i
bit_vector bitset;
bitset.resize(q->get_num_decls(), false);
for (unsigned i = 0 ; i < a->get_num_args() ; i++) {
if (is_var(a->get_arg(i)))
bitset.set(to_var(a->get_arg(i))->get_idx(), true);
}
for (unsigned i = 0; i < bitset.size() ; i++) {
if (!bitset.get(i))
return false;
}
return true;
}
bool quasi_macros::depends_on(expr * e, func_decl * f) const {
ptr_vector<expr> todo;
expr_mark visited;
todo.push_back(e);
while(!todo.empty()) {
expr * cur = todo.back();
todo.pop_back();
if (visited.is_marked(cur))
continue;
if (is_app(cur)) {
app * a = to_app(cur);
if (a->get_decl() == f)
return true;
unsigned j = a->get_num_args();
while (j>0)
todo.push_back(a->get_arg(--j));
}
visited.mark(cur, true);
}
return false;
}
bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
// Our definition of a quasi-macro:
// Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted,
// f[X] contains all universally quantified variables, and f does not occur in T[X].
if (is_quantifier(e) && to_quantifier(e)->is_forall()) {
quantifier * q = to_quantifier(e);
expr * qe = q->get_expr();
if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) {
expr * lhs = to_app(qe)->get_arg(0);
expr * rhs = to_app(qe)->get_arg(1);
if (is_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) &&
!depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) {
a = to_app(lhs);
t = rhs;
return true;
} else if (is_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) &&
!depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) {
a = to_app(rhs);
t = lhs;
return true;
}
} else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0)) &&
is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false
a = to_app(to_app(qe)->get_arg(0));
t = m_manager.mk_false();
return true;
} else if (is_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true
a = to_app(qe);
t = m_manager.mk_true();
return true;
}
}
return false;
}
void quasi_macros::quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro) {
m_new_var_names.reset();
m_new_vars.reset();
m_new_qsorts.reset();
m_new_eqs.reset();
func_decl * f = a->get_decl();
// CMW: we rely on the fact that all variables in q appear at least once as
// a direct argument of `a'.
bit_vector v_seen;
v_seen.resize(q->get_num_decls(), false);
for (unsigned i = 0 ; i < a->get_num_args() ; i++) {
if (!is_var(a->get_arg(i)) ||
v_seen.get(to_var(a->get_arg(i))->get_idx())) {
unsigned inx = m_new_var_names.size();
m_new_name.str("");
m_new_name << "X" << inx;
m_new_var_names.push_back(symbol(m_new_name.str().c_str()));
m_new_qsorts.push_back(f->get_domain()[i]);
m_new_vars.push_back(m_manager.mk_var(inx + q->get_num_decls(), f->get_domain()[i]));
m_new_eqs.push_back(m_manager.mk_eq(m_new_vars.back(), a->get_arg(i)));
} else {
var * v = to_var(a->get_arg(i));
m_new_vars.push_back(v);
v_seen.set(v->get_idx(), true);
}
}
// Reverse the new variable names and sorts. [CMW: There is a smarter way to do this.]
vector<symbol> new_var_names_rev;
sort_ref_vector new_qsorts_rev(m_manager);
unsigned i = m_new_var_names.size();
while (i > 0) {
i--;
new_var_names_rev.push_back(m_new_var_names.get(i));
new_qsorts_rev.push_back(m_new_qsorts.get(i));
}
// We want to keep all the old variables [already reversed]
for (unsigned i = 0 ; i < q->get_num_decls() ; i++) {
new_var_names_rev.push_back(q->get_decl_name(i));
new_qsorts_rev.push_back(q->get_decl_sort(i));
}
// Macro := Forall m_new_vars . appl = ITE( m_new_eqs, t, f_else)
app_ref appl(m_manager);
expr_ref eq(m_manager);
appl = m_manager.mk_app(f, m_new_vars.size(), m_new_vars.c_ptr());
func_decl * fd = m_manager.mk_fresh_func_decl(f->get_name(), symbol("else"),
f->get_arity(), f->get_domain(),
f->get_range());
expr * f_else = m_manager.mk_app(fd, m_new_vars.size(), m_new_vars.c_ptr());
expr_ref ite(m_manager);
ite = m_manager.mk_ite(m_manager.mk_and(m_new_eqs.size(), m_new_eqs.c_ptr()), t, f_else);
eq = m_manager.mk_eq(appl, ite);
macro = m_manager.mk_quantifier(true, new_var_names_rev.size(),
new_qsorts_rev.c_ptr(), new_var_names_rev.c_ptr(), eq);
}
bool quasi_macros::find_macros(unsigned n, expr * const * exprs) {
TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl;
for (unsigned i = 0 ; i < n ; i++)
tout << i << ": " << mk_pp(exprs[i], m_manager) << std::endl; );
bool res = false;
m_occurrences.reset();
// Find out how many non-ground appearences for each uninterpreted function there are
for ( unsigned i = 0 ; i < n ; i++ )
find_occurrences(exprs[i]);
TRACE("quasi_macros", tout << "Occurrences: " << std::endl;
for (occurrences_map::iterator it = m_occurrences.begin();
it != m_occurrences.end();
it++)
tout << it->m_key->get_name() << ": " << it->m_value << std::endl; );
// Find all macros
for ( unsigned i = 0 ; i < n ; i++ ) {
app_ref a(m_manager);
expr_ref t(m_manager);
if (is_quasi_macro(exprs[i], a, t)) {
quantifier_ref macro(m_manager);
quasi_macro_to_macro(to_quantifier(exprs[i]), a, t, macro);
TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i], m_manager) << std::endl;
tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; );
proof * pr = 0;
if (m_manager.proofs_enabled())
pr = m_manager.mk_def_axiom(macro);
if (m_macro_manager.insert(a->get_decl(), macro, pr))
res = true;
}
}
return res;
}
void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
for ( unsigned i = 0 ; i < n ; i++ ) {
expr_ref r(m_manager), rs(m_manager);
proof_ref pr(m_manager), ps(m_manager);
proof * p = m_manager.proofs_enabled() ? prs[i] : 0;
m_macro_manager.expand_macros(exprs[i], p, r, pr);
m_simplifier(r, rs, ps);
new_exprs.push_back(rs);
new_prs.push_back(ps);
}
}
bool quasi_macros::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
if (find_macros(n, exprs)) {
apply_macros(n, exprs, prs, new_exprs, new_prs);
return true;
} else {
// just copy them over
for ( unsigned i = 0 ; i < n ; i++ ) {
new_exprs.push_back(exprs[i]);
if (m_manager.proofs_enabled())
new_prs.push_back(prs[i]);
}
return false;
}
}

69
src/macros/quasi_macros.h Normal file
View file

@ -0,0 +1,69 @@
/*++
Copyright (c) 2010 Microsoft Corporation
Module Name:
quasi_macros.cpp
Abstract:
<abstract>
Author:
Christoph Wintersteiger (t-cwinte) 2010-04-23
Revision History:
--*/
#ifndef _QUASI_MACROS_H_
#define _QUASI_MACROS_H_
#include<sstream>
#include"macro_manager.h"
#include"basic_simplifier_plugin.h"
#include"simplifier.h"
/**
\brief Finds quasi macros and applies them.
*/
class quasi_macros {
typedef obj_map<func_decl, unsigned> occurrences_map;
ast_manager & m_manager;
macro_manager & m_macro_manager;
basic_simplifier_plugin & m_bsimp;
simplifier & m_simplifier;
occurrences_map m_occurrences;
ptr_vector<expr> m_todo;
vector<symbol> m_new_var_names;
expr_ref_vector m_new_vars;
expr_ref_vector m_new_eqs;
sort_ref_vector m_new_qsorts;
std::stringstream m_new_name;
expr_mark m_visited_once;
expr_mark m_visited_more;
bool is_unique(func_decl * f) const;
bool fully_depends_on(app * a, quantifier * q) const;
bool depends_on(expr * e, func_decl * f) const;
bool is_quasi_macro(expr * e, app_ref & a, expr_ref &v) const;
void quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro);
void find_occurrences(expr * e);
bool find_macros(unsigned n, expr * const * exprs);
void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
public:
quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s);
~quasi_macros();
/**
\brief Find pure function macros and apply them.
*/
bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
};
#endif

1
src/muz_qe/README Normal file
View file

@ -0,0 +1 @@
muZ and Quantifier Elimination modules

View file

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

View file

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

490
src/muz_qe/dl_base.cpp Normal file
View file

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

1250
src/muz_qe/dl_base.h Normal file

File diff suppressed because it is too large Load diff

View file

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

128
src/muz_qe/dl_bmc_engine.h Normal file
View file

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

View file

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

View file

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

View file

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

134
src/muz_qe/dl_check_table.h Normal file
View file

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

437
src/muz_qe/dl_cmds.cpp Normal file
View file

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

47
src/muz_qe/dl_cmds.h Normal file
View file

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

1190
src/muz_qe/dl_compiler.cpp Normal file

File diff suppressed because it is too large Load diff

272
src/muz_qe/dl_compiler.h Normal file
View file

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

1705
src/muz_qe/dl_context.cpp Normal file

File diff suppressed because it is too large Load diff

468
src/muz_qe/dl_context.h Normal file
View file

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

160
src/muz_qe/dl_costs.cpp Normal file
View file

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

115
src/muz_qe/dl_costs.h Normal file
View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

342
src/muz_qe/dl_instruction.h Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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