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:
parent
4722fdfca5
commit
add684d8e9
377 changed files with 204 additions and 62 deletions
40
src/api_headers/z3.h
Normal file
40
src/api_headers/z3.h
Normal 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
7563
src/api_headers/z3_api.h
Normal file
File diff suppressed because it is too large
Load diff
34
src/api_headers/z3_internal_types.h
Normal file
34
src/api_headers/z3_internal_types.h
Normal 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
|
54
src/api_headers/z3_macros.h
Normal file
54
src/api_headers/z3_macros.h
Normal 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
64
src/api_headers/z3_v1.h
Normal 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
77
src/ast/expr2var.cpp
Normal 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
75
src/ast/expr2var.h
Normal 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
97
src/ast/expr_abstract.cpp
Normal 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
28
src/ast/expr_abstract.h
Normal 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
142
src/ast/expr_functors.cpp
Normal 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
121
src/ast/expr_functors.h
Normal 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
604
src/ast/static_features.cpp
Normal 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
169
src/ast/static_features.h
Normal 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
404
src/ast/trail.h
Normal 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_ */
|
||||
|
128
src/bit_blaster/bit_blaster.cpp
Normal file
128
src/bit_blaster/bit_blaster.cpp
Normal 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) {
|
||||
}
|
65
src/bit_blaster/bit_blaster.h
Normal file
65
src/bit_blaster/bit_blaster.h
Normal 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_ */
|
||||
|
187
src/bit_blaster/bit_blaster_model_converter.cpp
Normal file
187
src/bit_blaster/bit_blaster_model_converter.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
27
src/bit_blaster/bit_blaster_model_converter.h
Normal file
27
src/bit_blaster/bit_blaster_model_converter.h
Normal 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
|
649
src/bit_blaster/bit_blaster_rewriter.cpp
Normal file
649
src/bit_blaster/bit_blaster_rewriter.cpp
Normal 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);
|
||||
}
|
||||
|
42
src/bit_blaster/bit_blaster_rewriter.h
Normal file
42
src/bit_blaster/bit_blaster_rewriter.h
Normal 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
|
||||
|
165
src/bit_blaster/bit_blaster_tactic.cpp
Normal file
165
src/bit_blaster/bit_blaster_tactic.cpp
Normal 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));
|
||||
}
|
28
src/bit_blaster/bit_blaster_tactic.h
Normal file
28
src/bit_blaster/bit_blaster_tactic.h
Normal 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
|
135
src/bit_blaster/bit_blaster_tpl.h
Normal file
135
src/bit_blaster/bit_blaster_tpl.h
Normal 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
|
1201
src/bit_blaster/bit_blaster_tpl_def.h
Normal file
1201
src/bit_blaster/bit_blaster_tpl_def.h
Normal file
File diff suppressed because it is too large
Load diff
508
src/bit_blaster/bv1_blaster_tactic.cpp
Normal file
508
src/bit_blaster/bv1_blaster_tactic.cpp
Normal 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);
|
||||
}
|
35
src/bit_blaster/bv1_blaster_tactic.h
Normal file
35
src/bit_blaster/bv1_blaster_tactic.h
Normal 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
|
436
src/bit_blaster/eager_bit_blaster.cpp
Normal file
436
src/bit_blaster/eager_bit_blaster.cpp
Normal 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);
|
||||
}
|
||||
|
107
src/bit_blaster/eager_bit_blaster.h
Normal file
107
src/bit_blaster/eager_bit_blaster.h
Normal 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
32
src/dead/expr_weight.cpp
Normal 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
35
src/dead/expr_weight.h
Normal 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
187
src/dead/smt_euf.cpp
Normal 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
55
src/dead/smt_euf.h
Normal 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
|
855
src/euclid/euclidean_solver.cpp
Normal file
855
src/euclid/euclidean_solver.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
106
src/euclid/euclidean_solver.h
Normal file
106
src/euclid/euclidean_solver.h
Normal 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
|
29
src/framework/goal_shared_occs.cpp
Normal file
29
src/framework/goal_shared_occs.cpp
Normal 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);
|
||||
}
|
||||
}
|
45
src/framework/goal_shared_occs.h
Normal file
45
src/framework/goal_shared_occs.h
Normal 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
|
485
src/framework/strategic_solver.cpp
Normal file
485
src/framework/strategic_solver.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
155
src/framework/strategic_solver.h
Normal file
155
src/framework/strategic_solver.h
Normal 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
961
src/grobner/grobner.cpp
Normal 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
281
src/grobner/grobner.h
Normal 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
223
src/macros/macro_finder.cpp
Normal 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
55
src/macros/macro_finder.h
Normal 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_ */
|
||||
|
319
src/macros/macro_manager.cpp
Normal file
319
src/macros/macro_manager.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
99
src/macros/macro_manager.h
Normal file
99
src/macros/macro_manager.h
Normal 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_ */
|
||||
|
184
src/macros/macro_substitution.cpp
Normal file
184
src/macros/macro_substitution.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
59
src/macros/macro_substitution.h
Normal file
59
src/macros/macro_substitution.h
Normal 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
928
src/macros/macro_util.cpp
Normal 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
144
src/macros/macro_util.h
Normal 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
317
src/macros/quasi_macros.cpp
Normal 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
69
src/macros/quasi_macros.h
Normal 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
1
src/muz_qe/README
Normal file
|
@ -0,0 +1 @@
|
|||
muZ and Quantifier Elimination modules
|
161
src/muz_qe/arith_bounds_tactic.cpp
Normal file
161
src/muz_qe/arith_bounds_tactic.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
37
src/muz_qe/arith_bounds_tactic.h
Normal file
37
src/muz_qe/arith_bounds_tactic.h
Normal 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
490
src/muz_qe/dl_base.cpp
Normal 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
1250
src/muz_qe/dl_base.h
Normal file
File diff suppressed because it is too large
Load diff
778
src/muz_qe/dl_bmc_engine.cpp
Normal file
778
src/muz_qe/dl_bmc_engine.cpp
Normal 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
128
src/muz_qe/dl_bmc_engine.h
Normal 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
|
706
src/muz_qe/dl_bound_relation.cpp
Normal file
706
src/muz_qe/dl_bound_relation.cpp
Normal 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());
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
174
src/muz_qe/dl_bound_relation.h
Normal file
174
src/muz_qe/dl_bound_relation.h
Normal 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
|
||||
|
363
src/muz_qe/dl_check_table.cpp
Normal file
363
src/muz_qe/dl_check_table.cpp
Normal 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
134
src/muz_qe/dl_check_table.h
Normal 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
437
src/muz_qe/dl_cmds.cpp
Normal 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
47
src/muz_qe/dl_cmds.h
Normal 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
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
272
src/muz_qe/dl_compiler.h
Normal 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
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
468
src/muz_qe/dl_context.h
Normal 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
160
src/muz_qe/dl_costs.cpp
Normal 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
115
src/muz_qe/dl_costs.h
Normal 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_ */
|
||||
|
456
src/muz_qe/dl_external_relation.cpp
Normal file
456
src/muz_qe/dl_external_relation.cpp
Normal 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, ¶m, 0, (sort*const*)0), m);
|
||||
reduce_assign(empty_decl, 0, 0, 1, args);
|
||||
return alloc(external_relation, *this, s, e);
|
||||
}
|
||||
|
||||
sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) {
|
||||
vector<parameter> sorts;
|
||||
ast_manager& m = get_ast_manager();
|
||||
family_id fid = get_family_id();
|
||||
for (unsigned i = 0; i < sig.size(); ++i) {
|
||||
sorts.push_back(parameter(sig[i]));
|
||||
}
|
||||
return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr());
|
||||
}
|
||||
|
||||
sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) {
|
||||
SASSERT(s->get_num_parameters() > col);
|
||||
SASSERT(s->get_parameter(col).is_ast());
|
||||
SASSERT(is_sort(s->get_parameter(col).get_ast()));
|
||||
return to_sort(s->get_parameter(col).get_ast());
|
||||
}
|
||||
|
||||
family_id external_relation_plugin::get_family_id() {
|
||||
return m_ext.get_family_id();
|
||||
}
|
||||
|
||||
|
||||
void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) {
|
||||
ast_manager& m = get_ast_manager();
|
||||
family_id fid = get_family_id();
|
||||
parameter param(condition);
|
||||
f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s);
|
||||
}
|
||||
|
||||
class external_relation_plugin::join_fn : public convenient_relation_join_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_join_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2)
|
||||
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2),
|
||||
m_plugin(p),
|
||||
m_join_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < col_cnt; ++i) {
|
||||
params.push_back(parameter(cols1[i]));
|
||||
params.push_back(parameter(cols2[i]));
|
||||
}
|
||||
sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) };
|
||||
m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) {
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
m_args[0] = get(r1).get_relation();
|
||||
m_args[1] = get(r2).get_relation();
|
||||
m_plugin.reduce(m_join_fn, 2, m_args, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2,
|
||||
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
|
||||
if (!check_kind(r1) || !check_kind(r2)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::project_fn : public convenient_relation_project_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_project_fn;
|
||||
public:
|
||||
project_fn(external_relation_plugin& p, sort* relation_sort,
|
||||
const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
|
||||
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols),
|
||||
m_plugin(p),
|
||||
m_project_fn(p.get_ast_manager()) {
|
||||
vector<parameter> params;
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
for (unsigned i = 0; i < removed_col_cnt; ++i) {
|
||||
params.push_back(parameter(removed_cols[i]));
|
||||
}
|
||||
m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r) {
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
expr* rel = get(r).get_relation();
|
||||
m_plugin.reduce(m_project_fn, 1, &rel, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), to_app(res));
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * removed_cols) {
|
||||
return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::rename_fn : public convenient_relation_rename_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_rename_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
|
||||
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle),
|
||||
m_plugin(p),
|
||||
m_rename_fn(p.get_ast_manager()) {
|
||||
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < cycle_len; ++i) {
|
||||
SASSERT(cycle[i] < orig_sig.size());
|
||||
params.push_back(parameter(cycle[i]));
|
||||
}
|
||||
m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort);
|
||||
}
|
||||
|
||||
virtual relation_base * operator()(const relation_base & r) {
|
||||
expr* rel = get(r).get_relation();
|
||||
expr_ref res(m_plugin.get_ast_manager());
|
||||
m_args[0] = rel;
|
||||
m_plugin.reduce(m_rename_fn, 1, &rel, res);
|
||||
return alloc(external_relation, m_plugin, get_result_signature(), res);
|
||||
}
|
||||
};
|
||||
|
||||
relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r,
|
||||
unsigned cycle_len, const unsigned * permutation_cycle) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::union_fn : public relation_union_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_union_fn;
|
||||
expr* m_args[2];
|
||||
expr* m_outs[2];
|
||||
|
||||
public:
|
||||
union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort):
|
||||
m_plugin(p),
|
||||
m_union_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
sort* domain[2] = { relation_sort, relation_sort };
|
||||
m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain);
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) {
|
||||
ast_manager& m = m_plugin.get_ast_manager();
|
||||
expr_ref_vector res(m);
|
||||
m_args[0] = get(r).get_relation();
|
||||
m_args[1] = get(src).get_relation();
|
||||
m_outs[0] = m_args[0];
|
||||
unsigned num_out = 1;
|
||||
if (delta) {
|
||||
m_outs[1] = get(*delta).get_relation();
|
||||
++num_out;
|
||||
}
|
||||
m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs);
|
||||
}
|
||||
};
|
||||
|
||||
relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort());
|
||||
}
|
||||
|
||||
relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src,
|
||||
const relation_base * delta) {
|
||||
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort());
|
||||
}
|
||||
|
||||
class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
app_ref m_condition;
|
||||
func_decl_ref m_filter_fn;
|
||||
public:
|
||||
filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition)
|
||||
: m_plugin(p),
|
||||
m_condition(condition, p.get_ast_manager()),
|
||||
m_filter_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
p.mk_filter_fn(relation_sort, condition, m_filter_fn);
|
||||
SASSERT(m.is_bool(condition));
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
SASSERT(m_plugin.check_kind(r));
|
||||
expr* arg = get(r).get_relation();
|
||||
m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg);
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition);
|
||||
}
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r,
|
||||
const relation_element & value, unsigned col) {
|
||||
if(!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
ast_manager& m = get_ast_manager();
|
||||
app_ref condition(m);
|
||||
expr_ref var(m);
|
||||
sort* relation_sort = get(r).get_sort();
|
||||
sort* column_sort = get_column_sort(col, relation_sort);
|
||||
var = m.mk_var(col, column_sort);
|
||||
condition = m.mk_eq(var, value);
|
||||
return mk_filter_interpreted_fn(r, condition);
|
||||
}
|
||||
|
||||
class external_relation_plugin::filter_identical_fn : public relation_mutator_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref_vector m_filter_fn;
|
||||
public:
|
||||
filter_identical_fn(external_relation_plugin& p, sort* relation_sort,
|
||||
unsigned col_cnt, const unsigned * identical_cols)
|
||||
: m_plugin(p), m_filter_fn(p.get_ast_manager()) {
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
func_decl_ref fn(m);
|
||||
app_ref eq(m);
|
||||
if (col_cnt <= 1) {
|
||||
return;
|
||||
}
|
||||
unsigned col = identical_cols[0];
|
||||
sort* s = p.get_column_sort(col, relation_sort);
|
||||
var* v0 = m.mk_var(col, s);
|
||||
for (unsigned i = 1; i < col_cnt; ++i) {
|
||||
col = identical_cols[i];
|
||||
s = p.get_column_sort(col, relation_sort);
|
||||
eq = m.mk_eq(v0, m.mk_var(col, s));
|
||||
p.mk_filter_fn(relation_sort, eq.get(), fn);
|
||||
m_filter_fn.push_back(fn);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void operator()(relation_base & r) {
|
||||
expr* r0 = get(r).get_relation();
|
||||
for (unsigned i = 0; i < m_filter_fn.size(); ++i) {
|
||||
m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r,
|
||||
unsigned col_cnt, const unsigned * identical_cols) {
|
||||
if (!check_kind(r)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols);
|
||||
}
|
||||
|
||||
|
||||
class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn {
|
||||
external_relation_plugin& m_plugin;
|
||||
func_decl_ref m_negated_filter_fn;
|
||||
expr* m_args[2];
|
||||
public:
|
||||
negation_filter_fn(external_relation_plugin& p,
|
||||
const relation_base & tgt, const relation_base & neg_t,
|
||||
unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) :
|
||||
convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols),
|
||||
m_plugin(p),
|
||||
m_negated_filter_fn(p.get_ast_manager())
|
||||
{
|
||||
ast_manager& m = p.get_ast_manager();
|
||||
family_id fid = p.get_family_id();
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < joined_col_cnt; ++i) {
|
||||
params.push_back(parameter(t_cols[i]));
|
||||
params.push_back(parameter(negated_cols[i]));
|
||||
}
|
||||
sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() };
|
||||
m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain);
|
||||
}
|
||||
|
||||
void operator()(relation_base & t, const relation_base & negated_obj) {
|
||||
m_args[0] = get(t).get_relation();
|
||||
m_args[1] = get(negated_obj).get_relation();
|
||||
m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args);
|
||||
}
|
||||
};
|
||||
|
||||
relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t,
|
||||
const relation_base & negated_obj, unsigned joined_col_cnt,
|
||||
const unsigned * t_cols, const unsigned * negated_cols) {
|
||||
if (!check_kind(t) || !check_kind(negated_obj)) {
|
||||
return 0;
|
||||
}
|
||||
return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols);
|
||||
}
|
||||
|
||||
};
|
154
src/muz_qe/dl_external_relation.h
Normal file
154
src/muz_qe/dl_external_relation.h
Normal 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
|
2372
src/muz_qe/dl_finite_product_relation.cpp
Normal file
2372
src/muz_qe/dl_finite_product_relation.cpp
Normal file
File diff suppressed because it is too large
Load diff
366
src/muz_qe/dl_finite_product_relation.h
Normal file
366
src/muz_qe/dl_finite_product_relation.h
Normal 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_ */
|
||||
|
1056
src/muz_qe/dl_instruction.cpp
Normal file
1056
src/muz_qe/dl_instruction.cpp
Normal file
File diff suppressed because it is too large
Load diff
342
src/muz_qe/dl_instruction.h
Normal file
342
src/muz_qe/dl_instruction.h
Normal 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_ */
|
||||
|
655
src/muz_qe/dl_interval_relation.cpp
Normal file
655
src/muz_qe/dl_interval_relation.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
140
src/muz_qe/dl_interval_relation.h
Normal file
140
src/muz_qe/dl_interval_relation.h
Normal 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
|
||||
|
217
src/muz_qe/dl_mk_bit_blast.cpp
Normal file
217
src/muz_qe/dl_mk_bit_blast.cpp
Normal 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);
|
||||
}
|
||||
|
||||
};
|
53
src/muz_qe/dl_mk_bit_blast.h
Normal file
53
src/muz_qe/dl_mk_bit_blast.h
Normal 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_ */
|
||||
|
209
src/muz_qe/dl_mk_coalesce.cpp
Normal file
209
src/muz_qe/dl_mk_coalesce.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
62
src/muz_qe/dl_mk_coalesce.h
Normal file
62
src/muz_qe/dl_mk_coalesce.h
Normal 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_ */
|
||||
|
107
src/muz_qe/dl_mk_coi_filter.cpp
Normal file
107
src/muz_qe/dl_mk_coi_filter.cpp
Normal 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();
|
||||
}
|
||||
|
||||
};
|
||||
|
50
src/muz_qe/dl_mk_coi_filter.h
Normal file
50
src/muz_qe/dl_mk_coi_filter.h
Normal 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_ */
|
||||
|
893
src/muz_qe/dl_mk_explanations.cpp
Normal file
893
src/muz_qe/dl_mk_explanations.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
92
src/muz_qe/dl_mk_explanations.h
Normal file
92
src/muz_qe/dl_mk_explanations.h
Normal 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_ */
|
||||
|
168
src/muz_qe/dl_mk_filter_rules.cpp
Normal file
168
src/muz_qe/dl_mk_filter_rules.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
82
src/muz_qe/dl_mk_filter_rules.h
Normal file
82
src/muz_qe/dl_mk_filter_rules.h
Normal 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_ */
|
||||
|
569
src/muz_qe/dl_mk_interp_tail_simplifier.cpp
Normal file
569
src/muz_qe/dl_mk_interp_tail_simplifier.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
101
src/muz_qe/dl_mk_interp_tail_simplifier.h
Normal file
101
src/muz_qe/dl_mk_interp_tail_simplifier.h
Normal 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_ */
|
||||
|
395
src/muz_qe/dl_mk_magic_sets.cpp
Normal file
395
src/muz_qe/dl_mk_magic_sets.cpp
Normal 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;
|
||||
}
|
||||
};
|
||||
|
130
src/muz_qe/dl_mk_magic_sets.h
Normal file
130
src/muz_qe/dl_mk_magic_sets.h
Normal 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_ */
|
||||
|
153
src/muz_qe/dl_mk_partial_equiv.cpp
Normal file
153
src/muz_qe/dl_mk_partial_equiv.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
50
src/muz_qe/dl_mk_partial_equiv.h
Normal file
50
src/muz_qe/dl_mk_partial_equiv.h
Normal 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_ */
|
||||
|
||||
|
884
src/muz_qe/dl_mk_rule_inliner.cpp
Normal file
884
src/muz_qe/dl_mk_rule_inliner.cpp
Normal 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();
|
||||
}
|
||||
|
||||
};
|
||||
|
199
src/muz_qe/dl_mk_rule_inliner.h
Normal file
199
src/muz_qe/dl_mk_rule_inliner.h
Normal 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_ */
|
||||
|
537
src/muz_qe/dl_mk_similarity_compressor.cpp
Normal file
537
src/muz_qe/dl_mk_similarity_compressor.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue