3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00
z3/lib/gl_tactic.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

1048 lines
32 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
gl_tactic.cpp
Abstract:
A T-function
and perhaps Goldreich Levin-based heuristic.
Author:
Nikolaj (nbjorner) 2011-12-18
Notes:
This module experiments with T-function bit-vector operations.
TBD:
1. convert to tactic.
2. implement using fewer y bits based on satisfying assignment.
3. try out code-generation.
4. better lemma learning?
5. Incremental refinement into SAT.
--*/
#include"gl_tactic.h"
#include"assertion_set_strategy.h"
#include"bv_decl_plugin.h"
#include"shallow_context_simplifier.h"
#include"elim_uncnstr_vars.h"
#include"max_bv_sharing.h"
#include"max_bool_sharing.h"
#include"bv_size_reduction.h"
#include"context_simplifier.h"
#include"nnf.h"
#include"cooperate.h"
#include"ast_smt2_pp.h"
#include"elim_var_model_converter.h"
#include"smt_context.h"
#include"ast_pp.h"
using namespace gl;
typedef svector<uint64> assignment;
static void gl_unsupported(ast_manager& m, expr* e) {
TRACE("gl_st", if (e) tout << "unsupported: " << mk_ismt2_pp(e, m) << "\n";);
throw gl_exception("");
}
class expr_evaluator {
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned uint32;
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
enum code {
ADD,
SUB,
MUL,
ITE,
BAND,
BOR,
BXOR,
BNOT,
ULT,
SLT,
SHL,
ASHR,
LSHR,
LOADC,
LOADV,
EQ,
UREM,
SREM,
SMOD,
UDIV,
SDIV
};
struct oper {
code m_code;
unsigned dst;
unsigned src1;
unsigned src2;
unsigned src3;
uint64 data;
unsigned bw;
static oper mk_binary(code c, unsigned bw, unsigned dst, unsigned src1, unsigned src2) {
return oper(c, bw, dst, src1, src2, 0, 0);
}
static oper mk_ite(unsigned bw, unsigned dst, unsigned src1, unsigned src2, unsigned src3) {
return oper(ITE, bw, dst, src1, src2, src3, 0);
}
static oper mk_loadc(unsigned bw, unsigned dst, uint64 data) {
return oper(LOADC, bw, dst, 0, 0, 0, data);
}
private:
oper(code c, unsigned bw, unsigned dst, unsigned src1, unsigned src2, unsigned src3, uint64 data):
m_code(c), bw(bw), dst(dst), src1(src1), src2(src2), src3(src3), data(data) { }
};
ast_manager& m;
bv_util m_bv;
svector<uint64> m_regs;
svector<oper> m_ops;
unsigned m_dst;
obj_map<expr, unsigned> m_cache;
assignment const* m_vals_tmp;
obj_map<app, unsigned>* m_vars_tmp;
unsigned mk_oper(code c, expr* e, unsigned src1, unsigned src2) {
return mk_oper(c, get_bitwidth(e), src1, src2);
}
unsigned get_bitwidth(expr* e) {
unsigned bw = 0;
if (m.is_bool(e)) {
bw = 1;
}
else if (m_bv.is_bv(e)) {
bw= m_bv.get_bv_size(e);
if (bw > 64) {
unsupported(e);
}
}
else {
unsupported(e);
}
return bw;
}
unsigned mk_oper(code c, unsigned bw, unsigned src1, unsigned src2) {
unsigned dst = m_regs.size();
m_regs.push_back(0);
m_ops.push_back(oper::mk_binary(c, bw, dst, src1, src2));
return dst;
}
unsigned mk_const(expr* e, uint64 c) {
return mk_const(get_bitwidth(e), c);
}
unsigned mk_const(unsigned bw, uint64 c) {
unsigned dst = m_regs.size();
m_regs.push_back(0);
m_ops.push_back(oper::mk_loadc(bw, dst, c));
return dst;
}
unsigned mk_ite(expr* e, unsigned src1, unsigned src2, unsigned src3) {
unsigned dst = m_regs.size();
unsigned bw = get_bitwidth(e);
m_regs.push_back(0);
m_ops.push_back(oper::mk_ite(bw, dst, src1, src2, src3));
return dst;
}
unsigned mk_oper(code c, unsigned num_args, expr* const* args, uint64 def) {
if (num_args == 0) {
unsupported(0);
return mk_const(static_cast<unsigned>(0), def);
}
unsigned dst = compile(args[0]);
for (unsigned i = 1; i < num_args; ++i) {
dst = mk_oper(c, args[i], dst, compile(args[i]));
}
return dst;
}
unsigned compile(app* a) {
unsigned src1 = 0, src2 = 0, src3 = 0, dst = 0;
uint64 src0 = 0;
unsigned num_args = a->get_num_args();
expr* const* args = a->get_args();
unsigned var_id = 0;
if (num_args >= 1) {
src1 = compile(a->get_arg(0));
}
if (num_args >= 2) {
src2 = compile(a->get_arg(1));
}
if (num_args >= 3) {
src3 = compile(a->get_arg(2));
}
if (m_bv.is_bv(a) && m_bv.get_bv_size(a) > 64) {
unsupported(a);
}
if (m_vars_tmp->find(a, var_id)) {
return mk_oper(LOADV, a, var_id, 0);
}
if (a->get_family_id() == m.get_basic_family_id()) {
switch(a->get_decl_kind()) {
case OP_AND:
return mk_oper(BAND, num_args, args, 1);
case OP_OR:
return mk_oper(BOR, num_args, args, 0);
case OP_NOT:
return mk_oper(BNOT, a, src1, 0);
case OP_EQ:
return mk_oper(EQ, a, src1, src2);
case OP_ITE:
return mk_ite(a, src1, src2, src3);
case OP_DISTINCT:
unsupported(a);
case OP_TRUE:
return mk_const(a, 1);
case OP_FALSE:
return mk_const(a, 0);
case OP_XOR:
return mk_oper(BNOT, a, mk_oper(EQ, a, src1, src2), 0);
case OP_IFF:
return mk_oper(EQ, a, src1, src2); // mask?
default:
UNREACHABLE();
}
}
if (a->get_family_id() == m_bv.get_family_id()) {
unsigned bv_size;
rational val;
switch(a->get_decl_kind()) {
case OP_BV_NUM:
VERIFY(m_bv.is_numeral(a, val, bv_size));
if (bv_size > 64) {
unsupported(a);
}
src0 = val.get_uint64();
return mk_const(a, src0);
case OP_BIT1:
return mk_const(a, 1);
case OP_BIT0:
return mk_const(a, 0);
case OP_BNEG:
SASSERT(num_args == 1);
return mk_oper(SUB, a, 0, src1);
case OP_BADD:
return mk_oper(ADD, num_args, args, 0);
case OP_BSUB:
SASSERT(num_args == 2);
return mk_oper(SUB, a, src1, src2);
case OP_BMUL:
return mk_oper(MUL, num_args, args, 1);
case OP_BSDIV:
case OP_BSDIV0:
case OP_BSDIV_I:
return mk_oper(SDIV, a, src1, src2);
case OP_BUDIV:
case OP_BUDIV0:
case OP_BUDIV_I:
return mk_oper(UDIV, a, src1, src2);
case OP_BSREM:
case OP_BSREM0:
case OP_BSREM_I:
return mk_oper(SREM, a, src1, src2);
case OP_BUREM:
case OP_BUREM0:
case OP_BUREM_I:
return mk_oper(UREM, a, src1, src2);
case OP_BSMOD:
case OP_BSMOD0:
case OP_BSMOD_I:
return mk_oper(SMOD, a, src1, src2);
case OP_ULEQ:
return mk_oper(BOR, a, mk_oper(ULT, a, src1, src2), mk_oper(EQ, a, src1, src2));
case OP_SLEQ:
return mk_oper(BOR, a, mk_oper(SLT, a, src1, src2), mk_oper(EQ, a, src1, src2));
case OP_UGEQ:
return mk_oper(BOR, a, mk_oper(ULT, a, src2, src1), mk_oper(EQ, a, src1, src2));
case OP_SGEQ:
return mk_oper(BOR, a, mk_oper(SLT, a, src2, src1), mk_oper(EQ, a, src1, src2));
case OP_ULT:
return mk_oper(ULT, a, src1, src2);
case OP_SLT:
return mk_oper(SLT, a, src1, src2);
case OP_UGT:
return mk_oper(ULT, a, src2, src1);
case OP_SGT:
return mk_oper(SLT, a, src2, src1);
case OP_BAND:
return mk_oper(BAND, num_args, args, ~((uint64)0));
case OP_BOR:
return mk_oper(BOR, num_args, args, 0);
case OP_BNOT:
return mk_oper(BNOT, a, src1, 0);
case OP_BXOR:
SASSERT(num_args == 2);
return mk_oper(BXOR, a, src1, src2);
case OP_BNAND:
return mk_oper(BNOT, a, mk_oper(BAND, num_args, args, ~((uint64)0)), 0);
case OP_BNOR:
return mk_oper(BNOT, a, mk_oper(BOR, num_args, args, 0), 0);
case OP_BXNOR:
SASSERT(num_args == 2);
return mk_oper(BNOT, a, mk_oper(BXOR, a, src1, src2), 0);
case OP_CONCAT: {
SASSERT(num_args > 0);
expr* last = args[num_args-1];
src1 = compile(last);
unsigned offset = m_bv.get_bv_size(last);
for (unsigned i = num_args-1; i > 0; ) {
--i;
unsigned bv_size = m_bv.get_bv_size(args[i]);
uint64 mask = (1ull << offset)-1;
offset += bv_size;
src2 = compile(args[i]);
src2 = mk_oper(SHL, args[i], src2, offset);
src1 = mk_oper(BAND, offset, src1, mk_const(offset, mask));
src1 = mk_oper(BOR, offset, src1, src2);
}
return src1;
}
case OP_SIGN_EXT:
unsupported(a);
case OP_ZERO_EXT:
unsupported(a);
case OP_EXTRACT: {
unsigned lo = m_bv.get_extract_low(a);
unsigned hi = m_bv.get_extract_high(a);
if (lo > 0) {
dst = mk_oper(SHL, a, src1, lo);
}
else {
dst = src1;
}
return dst;
}
case OP_REPEAT:
case OP_BREDOR:
case OP_BREDAND:
case OP_BCOMP:
unsupported(a);
case OP_BSHL:
SASSERT(num_args == 2);
return mk_oper(SHL, a, src1, src2);
case OP_BLSHR:
SASSERT(num_args == 2);
return mk_oper(LSHR, a, src1, src2);
case OP_BASHR:
SASSERT(num_args == 2);
return mk_oper(ASHR, a, src1, src2);
case OP_ROTATE_LEFT:
case OP_ROTATE_RIGHT:
case OP_EXT_ROTATE_LEFT:
case OP_EXT_ROTATE_RIGHT:
case OP_BUMUL_NO_OVFL: // no unsigned multiplication overflow predicate
case OP_BSMUL_NO_OVFL: // no signed multiplication overflow predicate
case OP_BSMUL_NO_UDFL: // no signed multiplication underflow predicate
case OP_BIT2BOOL: // predicate
unsupported(a);
case OP_MKBV: // bools to bv
return src1;
case OP_INT2BV:
case OP_BV2INT:
case OP_CARRY:
case OP_XOR3:
unsupported(a);
}
}
unsupported(a);
UNREACHABLE();
return 0;
}
uint64 to_bool(bool b) { return b?0x1:0x0; }
void interpret(oper oper) {
unsigned mask = 0;
switch(oper.m_code) {
case LOADC:
m_regs[oper.dst] = oper.data;
break;
case LOADV:
m_regs[oper.dst] = (*m_vals_tmp)[oper.src1];
break;
case ADD:
m_regs[oper.dst] = m_regs[oper.src1] + m_regs[oper.src2];
break;
case SUB:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = (uint8)m_regs[oper.src1] - (uint8)m_regs[oper.src2]; break;
case 16: m_regs[oper.dst] = (uint16)m_regs[oper.src1] - (uint16)m_regs[oper.src2]; break;
case 32: m_regs[oper.dst] = (uint32)m_regs[oper.src1] - (uint32)m_regs[oper.src2]; break;
case 64: m_regs[oper.dst] = m_regs[oper.src1] - m_regs[oper.src2]; break;
default: unsupported(0);
}
break;
case MUL:
m_regs[oper.dst] = m_regs[oper.src1] * m_regs[oper.src2];
break;
case SDIV:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = (int8)m_regs[oper.src1] / (int8)m_regs[oper.src2]; break;
case 16: m_regs[oper.dst] = (int16)m_regs[oper.src1] / (int16)m_regs[oper.src2]; break;
case 32: m_regs[oper.dst] = (int32)m_regs[oper.src1] / (int32)m_regs[oper.src2]; break;
case 64: m_regs[oper.dst] = (int64)m_regs[oper.src1] / (int64)m_regs[oper.src2]; break;
default: unsupported(0);
}
break;
case UDIV:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = (uint8)m_regs[oper.src1] / (uint8)m_regs[oper.src2]; break;
case 16: m_regs[oper.dst] = (uint16)m_regs[oper.src1] / (uint16)m_regs[oper.src2]; break;
case 32: m_regs[oper.dst] = (uint32)m_regs[oper.src1] / (uint32)m_regs[oper.src2]; break;
case 64: m_regs[oper.dst] = m_regs[oper.src1] / m_regs[oper.src2]; break;
default: unsupported(0);
}
break;
case SMOD:
case SREM:
case UREM:
unsupported(0);
case BAND:
m_regs[oper.dst] = m_regs[oper.src1] & m_regs[oper.src2];
break;
case BOR:
m_regs[oper.dst] = m_regs[oper.src1] | m_regs[oper.src2];
break;
case BXOR:
m_regs[oper.dst] = m_regs[oper.src1] ^ m_regs[oper.src2];
break;
case BNOT:
m_regs[oper.dst] = ~m_regs[oper.src1];
break;
case SHL:
mask = (1ull << oper.bw) - 1;
m_regs[oper.dst] = m_regs[oper.src1] << (mask & m_regs[oper.src2]);
break;
case LSHR:
m_regs[oper.dst] = m_regs[oper.src1] >> (mask & m_regs[oper.src2]);
break;
case ASHR:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = (int8)m_regs[oper.src1] >> (int8)m_regs[oper.src2]; break;
case 16: m_regs[oper.dst] = (int16)m_regs[oper.src1] >> (int16)m_regs[oper.src2]; break;
case 32: m_regs[oper.dst] = (int32)m_regs[oper.src1] >> (int32)m_regs[oper.src2]; break;
case 64: m_regs[oper.dst] = (int64)m_regs[oper.src1] >> (int64)m_regs[oper.src2]; break;
default: unsupported(0);
}
break;
case ULT:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = to_bool((uint8)m_regs[oper.src1] < (uint8)m_regs[oper.src2]); break;
case 16: m_regs[oper.dst] = to_bool((uint16)m_regs[oper.src1] < (uint16)m_regs[oper.src2]); break;
case 32: m_regs[oper.dst] = to_bool((uint32)m_regs[oper.src1] < (uint32)m_regs[oper.src2]); break;
case 64: m_regs[oper.dst] = to_bool(m_regs[oper.src1] < m_regs[oper.src2]); break;
default: unsupported(0);
}
break;
case SLT:
switch(oper.bw)
{
case 8: m_regs[oper.dst] = to_bool((int8)m_regs[oper.src1] < (int8)m_regs[oper.src2]); break;
case 16: m_regs[oper.dst] = to_bool((int16)m_regs[oper.src1] < (int16)m_regs[oper.src2]); break;
case 32: m_regs[oper.dst] = to_bool((int32)m_regs[oper.src1] < (int32)m_regs[oper.src2]); break;
case 64: m_regs[oper.dst] = to_bool((int64)m_regs[oper.src1] < (int64)m_regs[oper.src2]); break;
default: unsupported(0);
}
break;
case ITE:
m_regs[oper.dst] = (0x1&m_regs[oper.src1])?m_regs[oper.src2]:m_regs[oper.src3];
break;
case EQ:
mask = (1ull << oper.bw) - 1;
m_regs[oper.dst] = to_bool((mask & m_regs[oper.src1]) == (mask & m_regs[oper.src2]));
break;
default:
UNREACHABLE();
break;
}
}
unsigned compile(expr* e) {
unsigned val;
TRACE("gl_st", tout << mk_ismt2_pp(e, m) << "\n";);
if (!is_app(e)) {
unsupported(e);
}
app* a = to_app(e);
if (!m_cache.find(a, val)) {
val = compile(a);
m_cache.insert(a, val);
}
return val;
}
void interpret() {
for (unsigned i = 0; i < m_ops.size(); ++i) {
interpret(m_ops[i]);
}
}
void unsupported(expr* e) {
gl_unsupported(m, e);
}
public:
expr_evaluator(ast_manager& m): m(m), m_bv(m), m_vals_tmp(0)
{}
void compile(obj_map<app,unsigned>& vars, expr* e) {
m_vars_tmp = &vars;
m_dst = compile(e);
}
uint64 evaluate(assignment const& assign) {
m_vals_tmp = &assign;
interpret();
return m_regs[m_dst];
}
};
// compile a formula by abstracting T-functions (of certain size)
// to propositional abstrations.
class t_function_abstractor {
ast_manager& m;
bv_util m_bv;
family_id m_bv_fid;
app_ref_vector* m_t_funs, *m_t_proxies;
obj_map<app,unsigned>* m_t_vars;
obj_map<app,app*> m_cache;
void unsupported(expr* e) { gl_unsupported(m, e); }
void unsupported() { gl_unsupported(m, 0); }
bool is_bv(app* t) const {
return m_bv_fid == t->get_family_id();
}
// t is a T function. Extract constraints on inputs.
void absT(expr* _t, app_ref& result) {
app* c;
if (!is_app(_t)) {
unsupported(_t);
}
app* t = to_app(_t);
if (m_cache.find(t, c)) {
result = c;
}
else {
absT(t, result);
m_cache.insert(t, result);
}
}
void absT(app* t, app_ref& result) {
if (is_bv(t)) {
absTBv(t, result);
}
else {
absTVar(t, result);
}
}
void absT(unsigned num_args, expr* const* args, expr_ref_vector& rs) {
app_ref result(m);
for (unsigned i = 0; i < num_args; ++i) {
absT(args[i], result);
rs.push_back(result);
}
}
void absTVar(app* t, app_ref& result) {
absF(t, result);
m_t_vars->insert(result, m_t_vars->size());
}
void absTBv(app* t, app_ref& result) {
SASSERT(is_bv(t));
expr_ref_vector results(m);
switch(t->get_decl_kind()) {
case OP_BV_NUM:
case OP_BIT1:
case OP_BIT0:
case OP_BADD:
case OP_BSUB:
case OP_BNEG:
case OP_BMUL:
case OP_BAND:
case OP_BOR:
case OP_BNOT:
case OP_BXOR:
case OP_BNAND:
case OP_BNOR:
case OP_BXNOR:
case OP_CONCAT:
case OP_SIGN_EXT:
case OP_ZERO_EXT:
case OP_EXTRACT:
case OP_BSHL:
absT(t->get_num_args(), t->get_args(), results);
result = m.mk_app(t->get_decl(), results.size(), results.c_ptr());
break;
default:
absTVar(t, result);
break;
}
}
// f is a formula/non-T function. Extract T functions.
void absF(expr* _f, app_ref& result) {
app* c;
if (!is_app(_f)) {
unsupported(_f);
}
app* f = to_app(_f);
if (m_cache.find(f, c)) {
result = c;
}
else {
absF(f, result);
m_cache.insert(f, result);
}
}
void absF(app* a, app_ref& result) {
if (a->get_family_id() == m.get_basic_family_id()) {
absFBasic(a, result);
}
else if (is_bv(a)) {
absFBv(a, result);
}
else {
expr_ref_vector rs(m);
absF(a->get_num_args(), a->get_args(), rs);
result = m.mk_app(a->get_decl(), rs.size(), rs.c_ptr());
}
}
void absF(unsigned num_args, expr*const* args, expr_ref_vector& rs) {
app_ref result(m);
for (unsigned i = 0; i < num_args; ++i) {
absF(args[i], result);
rs.push_back(result);
}
}
void absFBasic(app* f, app_ref& result) {
expr_ref_vector results(m);
expr* e1, *e2;
if (m.is_eq(f, e1, e2) && m_bv.is_bv(e1)) {
app_ref r1(m), r2(m);
r1 = to_app(m_bv.mk_bv_xor(2, f->get_args()));
absFBv(r1, r2);
e1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(e1));
result = m.mk_eq(e1, r2);
}
else {
absF(f->get_num_args(), f->get_args(), results);
result = m.mk_app(f->get_decl(), results.size(), results.c_ptr());
}
}
void absFBv(app* a, app_ref& result) {
expr_ref_vector results(m);
switch(a->get_decl_kind()) {
case OP_BADD:
case OP_BSUB:
case OP_BNEG:
case OP_BMUL:
case OP_BAND:
case OP_BOR:
case OP_BNOT:
case OP_BXOR:
case OP_BNAND:
case OP_BNOR:
case OP_BXNOR:
case OP_CONCAT:
case OP_SIGN_EXT:
case OP_ZERO_EXT:
case OP_EXTRACT:
case OP_BSHL: {
app_ref tmp(m);
absT(a, tmp);
result = m.mk_fresh_const("T",m.get_sort(a));
m_t_funs->push_back(tmp);
m_t_proxies->push_back(result);
return;
}
default:
absF(a->get_num_args(), a->get_args(), results);
result = m.mk_app(a->get_decl(), results.size(), results.c_ptr());
return;
}
}
public:
t_function_abstractor(ast_manager& m): m(m), m_bv(m), m_bv_fid(m.get_family_id("bv")) {}
void operator()(expr* e, app_ref& result, app_ref_vector& t_funs, app_ref_vector& t_proxies, obj_map<app, unsigned>& t_vars) {
m_cache.reset();
m_t_funs = &t_funs;
m_t_proxies = &t_proxies;
m_t_vars = &t_vars;
absF(e, result);
}
};
class gl_eval {
ast_manager& m;
expr_evaluator m_eval;
app_ref m_proxy;
expr_ref m_proxy_val;
uint64 m_expected;
bool m_expected_valid;
public:
gl_eval(ast_manager& m): m(m), m_eval(m), m_proxy(m), m_proxy_val(m), m_expected(0) {}
void set_model(model_ref& mdl) {
m_proxy_val = 0;
if (mdl->eval(m_proxy, m_proxy_val, false) && m_proxy_val.get()) {
rational r;
bv_util bv(m);
unsigned bv_size;
m_expected_valid = true;
VERIFY(bv.is_numeral(m_proxy_val, r, bv_size));
m_expected = r.get_uint64();
TRACE("gl_st", tout << mk_pp(m_proxy, m) << " |-> " << r << "\n";);
}
else {
m_expected_valid = false;
}
}
void set_proxy(app* e) { m_proxy = e; }
expr* get_proxy() { return m_proxy; }
expr* get_proxy_val() { return m_proxy_val; }
bool operator()(assignment const& assign, unsigned num_bits) {
if (!m_expected_valid) {
TRACE("gl_st", tout << mk_pp(m_proxy, m) << " not valid\n";);
return true;
}
else {
uint64 val = m_eval.evaluate(assign);
uint64 mask = (1ULL << num_bits) - 1ULL;
TRACE("gl_st", tout << mk_pp(m_proxy, m) << " " << val << " expected: " << m_expected << " num_bits: " << num_bits << "\n";);
return (val & mask) == (m_expected & mask);
}
}
void compile(obj_map<app, unsigned>& var2index, expr* e) {
m_eval.compile(var2index, e);
}
};
class gl_st : public assertion_set_strategy {
ast_manager& m;
front_end_params m_params;
// variables and their assignments. obj_map<app, unsigned> m_var2index;
ptr_vector<app> m_index2var;
svector<uint64> m_vals;
// t-functions
vector<gl_eval*> m_evals;
// external search state.
smt::context m_ctx;
// internal search state.
unsigned_vector m_search_stack;
unsigned m_max_bitwidth;
unsigned m_max_search_depth;
bv_util m_bv;
volatile bool m_cancel;
public:
gl_st(ast_manager& m, params_ref const& p):
m(m), m_ctx(m, m_params),
m_bv(m), m_cancel(false),
m_max_search_depth(0) {
m_params.m_model = true;
}
virtual ~gl_st() {
for (unsigned i = 0; i < m_evals.size(); ++i) dealloc(m_evals[i]);
}
struct nonsat_by_gl { };
struct unsupported_by_gl { };
virtual void operator()(assertion_set & s, model_converter_ref & mc) {
ptr_vector<expr> conjs;
for (unsigned i = 0; i < s.size(); ++i) {
conjs.push_back(s.form(i));
}
app_ref fml1(m.mk_and(conjs.size(), conjs.c_ptr()), m);
app_ref fml2(m);
t_function_abstractor TAbs(m);
app_ref_vector t_funs(m), t_proxies(m);
TAbs(fml1, fml2, t_funs, t_proxies, m_var2index);
TRACE("gl_st",
tout << mk_pp(fml1, m) << "\n";
tout << mk_pp(fml2, m) << "\n";);
m_index2var.resize(m_var2index.size());
m_vals.resize(m_var2index.size());
obj_map<app, unsigned>::iterator it = m_var2index.begin(), end = m_var2index.end();
for (; it != end; ++it) {
m_index2var[it->m_value] = it->m_key;
}
m_max_bitwidth = 0;
for (unsigned i = 0; i < t_funs.size(); ++i) {
gl_eval* eval = alloc(gl_eval, m);
eval->compile(m_var2index, t_funs[i].get());
eval->set_proxy(t_proxies[i].get());
m_evals.push_back(eval);
m_max_bitwidth = std::max(m_max_bitwidth, m_bv.get_bv_size(t_proxies[i].get()));
}
m_ctx.assert_expr(fml2);
if (search()) {
elim_var_model_converter* emc = alloc(elim_var_model_converter, m);
mc = emc;
for (unsigned i = 0; i < m_index2var.size(); ++i) {
expr_ref vl(m);
if (m.is_bool(m_index2var[i])) {
vl = m_vals[i]? m.mk_true():m.mk_false();
}
else if (m_bv.is_bv(m_index2var[i])) {
vl = m_bv.mk_numeral(m_vals[i], m_bv.get_bv_size(m_index2var[i]));
}
emc->insert(m_index2var[i]->get_decl(), vl);
}
// TBD: other variables.
s.reset();
}
else {
throw nonsat_by_gl();
}
}
virtual void cleanup() {
}
virtual void collect_statistics(statistics & st) const {
}
virtual void reset_statistics() {
}
protected:
virtual void set_cancel(bool f) {
}
private:
void checkpoint() {
if (m_cancel)
throw gl_exception(STE_CANCELED_MSG);
cooperate("gl");
}
bool search() {
while (true) {
lbool res = m_ctx.check();
if (res == l_false) {
return false;
}
model_ref mdl;
m_ctx.get_model(mdl);
for (unsigned i = 0; i < m_evals.size(); ++i) {
m_evals[i]->set_model(mdl);
}
m_max_search_depth = 0;
if (t_search()) {
return true;
}
expr_ref_vector eqs(m);
for (unsigned i = 0; i < m_evals.size(); ++i) {
expr* v = m_evals[i]->get_proxy_val();
expr* p = m_evals[i]->get_proxy();
if (v && p) {
if (m_bv.is_bv(v) && m_bv.get_bv_size(v) > m_max_search_depth) {
v = m_bv.mk_extract(m_max_search_depth-1,0,v);
p = m_bv.mk_extract(m_max_search_depth-1,0,p);
}
eqs.push_back(m.mk_eq(v, p));
}
}
expr_ref fml(m);
fml = m.mk_and(eqs.size(), eqs.c_ptr());
TRACE("gl_st", tout << "block " << mk_pp(fml, m) << "\n";);
m_ctx.assert_expr(m.mk_not(fml));
}
return false;
}
bool t_search() {
if (!decide()) {
return false;
}
while (true) {
while (!propagate()) {
if (!backtrack()) {
return false;
}
}
if (!decide()) {
return true;
}
}
}
bool propagate() {
// update the assignment.
for (unsigned i = 0; i < m_index2var.size(); ++i) {
m_vals[i] = 0;
for (unsigned j = 0; j < m_search_stack.size(); ++j) {
if (m_search_stack[j] & (1 << i)) {
m_vals[i] |= (1ULL << j);
}
}
}
// evaluate under assignment
for (unsigned i = 0; i < m_evals.size(); ++i) {
if (!(*m_evals[i])(m_vals, m_search_stack.size())) {
return false;
}
}
return true;
}
// backtrack to previous level.
bool backtrack() {
while (!next()) {
TRACE("gl_st", tout << "level: " << m_search_stack.size() << "\n";);
m_search_stack.pop_back();
if (m_search_stack.empty()) {
return false;
}
}
return true;
}
bool next() {
SASSERT(!m_search_stack.empty());
m_search_stack.back()++;
TRACE("gl_st", tout << "next " << m_search_stack.back() << "\n";);
return !(m_search_stack.back() & (1 << m_index2var.size()));
}
// choose the next assignment.
bool decide() {
if (m_search_stack.size() == m_max_bitwidth) {
return false;
}
else {
m_search_stack.push_back(0);
m_max_search_depth = std::max(m_max_search_depth, m_search_stack.size());
return true;
}
}
std::ostream& display(std::ostream& out) {
out << "gl";
return out;
}
};
MK_FAIL_IF(fail_if_not_bv, !is_qfbv(s), "failed: GL supports only pure QF_BV for now.");
static as_st * mk_gl_preamble(ast_manager & m, params_ref const & p) {
params_ref main_p;
main_p.set_bool(":elim-and", true);
main_p.set_bool(":push-ite-bv", true);
main_p.set_bool(":blast-distinct", true);
main_p.set_bool(":hi-div0", true);
params_ref simp2_p = p;
simp2_p.set_bool(":som", true);
simp2_p.set_bool(":pull-cheap-ite", true);
simp2_p.set_bool(":push-ite-bv", false);
simp2_p.set_bool(":local-ctx", true);
simp2_p.set_uint(":local-ctx-limit", 10000000);
params_ref hoist_p;
hoist_p.set_bool(":hoist-mul", true);
hoist_p.set_bool(":som", false);
params_ref gaussian_p;
// conservative guassian elimination.
gaussian_p.set_uint(":gaussian-max-occs", 2);
return and_then(and_then(mk_simplifier(m),
mk_shallow_simplifier(m),
using_params(mk_gaussian(m), gaussian_p),
mk_elim_uncnstr_vars(m),
mk_bv_size_reduction(m),
using_params(mk_simplifier(m), simp2_p)),
using_params(mk_simplifier(m), hoist_p),
mk_max_bv_sharing(m),
mk_nnf(p));
}
as_st * mk_gl(ast_manager & m, params_ref const& p) {
return alloc(gl_st, m, p);
}
as_st * mk_qfbv_gl_strategy(ast_manager & m, params_ref const & p) {
params_ref gl_p(p);
as_st * st = and_then(mk_gl_preamble(m, p),
fail_if_not_bv(),
mk_gl(m, gl_p));
st->updt_params(p);
return st;
}