mirror of
https://github.com/Z3Prover/z3
synced 2025-04-28 11:25:51 +00:00
Further refactoring ackermannization.
This commit is contained in:
parent
2679b74543
commit
f3240024e7
20 changed files with 1621 additions and 0 deletions
378
src/ackermannization/lackr_model_constructor.cpp
Normal file
378
src/ackermannization/lackr_model_constructor.cpp
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model_constructor.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Mikolas Janota
|
||||
|
||||
Revision History:
|
||||
--*/
|
||||
#include"lackr_model_constructor.h"
|
||||
#include"model_evaluator.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"ackr_info.h"
|
||||
#include"for_each_expr.h"
|
||||
#include"bv_rewriter.h"
|
||||
#include"bool_rewriter.h"
|
||||
|
||||
struct lackr_model_constructor::imp {
|
||||
public:
|
||||
imp(ast_manager& m,
|
||||
ackr_info_ref info,
|
||||
model_ref& abstr_model,
|
||||
conflict_list& conflicts)
|
||||
: m_m(m)
|
||||
, m_info(info)
|
||||
, m_abstr_model(abstr_model)
|
||||
, m_conflicts(conflicts)
|
||||
, m_b_rw(m)
|
||||
, m_bv_rw(m)
|
||||
, m_empty_model(m)
|
||||
, m_ackr_helper(m)
|
||||
{}
|
||||
|
||||
~imp() {
|
||||
{
|
||||
values2val_t::iterator i = m_values2val.begin();
|
||||
const values2val_t::iterator e = m_values2val.end();
|
||||
for (; i != e; ++i) {
|
||||
m_m.dec_ref(i->m_key);
|
||||
m_m.dec_ref(i->m_value.value);
|
||||
m_m.dec_ref(i->m_value.source_term);
|
||||
}
|
||||
}
|
||||
{
|
||||
app2val_t::iterator i = m_app2val.begin();
|
||||
const app2val_t::iterator e = m_app2val.end();
|
||||
for (; i != e; ++i) {
|
||||
m_m.dec_ref(i->m_value);
|
||||
m_m.dec_ref(i->m_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Returns true iff model was successfully constructed.
|
||||
//
|
||||
bool check() {
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_constants(); i++) {
|
||||
func_decl * const c = m_abstr_model->get_constant(i);
|
||||
app * const term = m_info->find_term(c);
|
||||
if (term) m_stack.push_back(term);
|
||||
else m_stack.push_back(m_m.mk_const(c));
|
||||
}
|
||||
return run();
|
||||
}
|
||||
|
||||
|
||||
void make_model(model_ref& destination) {
|
||||
{
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); i++) {
|
||||
sort * const s = m_abstr_model->get_uninterpreted_sort(i);
|
||||
ptr_vector<expr> u = m_abstr_model->get_universe(s);
|
||||
destination->register_usort(s, u.size(), u.c_ptr());
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_functions(); i++) {
|
||||
func_decl * const fd = m_abstr_model->get_function(i);
|
||||
func_interp * const fi = m_abstr_model->get_func_interp(fd);
|
||||
destination->register_decl(fd, fi);
|
||||
}
|
||||
|
||||
{
|
||||
const app2val_t::iterator e = m_app2val.end();
|
||||
app2val_t::iterator i = m_app2val.end();
|
||||
for (; i != e; ++i) {
|
||||
app * a = i->m_key;
|
||||
if (a->get_num_args()) continue;
|
||||
destination->register_decl(a->get_decl(), i->m_value);
|
||||
}
|
||||
}
|
||||
|
||||
obj_map<func_decl, func_interp*> interpretations;
|
||||
{
|
||||
const values2val_t::iterator e = m_values2val.end();
|
||||
values2val_t::iterator i = m_values2val.end();
|
||||
for (; i != e; ++i) add_entry(i->m_key, i->m_value.value, interpretations);
|
||||
}
|
||||
|
||||
{
|
||||
obj_map<func_decl, func_interp*>::iterator ie = interpretations.end();
|
||||
obj_map<func_decl, func_interp*>::iterator ii = interpretations.begin();
|
||||
for (; ii != ie; ++ii) {
|
||||
func_decl* const fd = ii->m_key;
|
||||
func_interp* const fi = ii->get_value();
|
||||
fi->set_else(m_m.get_some_value(fd->get_range()));
|
||||
destination->register_decl(fd, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_entry(app* term, expr* value,
|
||||
obj_map<func_decl, func_interp*>& interpretations) {
|
||||
func_interp* fi = 0;
|
||||
func_decl * const declaration = term->get_decl();
|
||||
const unsigned sz = declaration->get_arity();
|
||||
SASSERT(sz == term->get_num_args());
|
||||
if (!interpretations.find(declaration, fi)) {
|
||||
fi = alloc(func_interp, m_m, sz);
|
||||
interpretations.insert(declaration, fi);
|
||||
}
|
||||
fi->insert_new_entry(term->get_args(), value);
|
||||
}
|
||||
private:
|
||||
ast_manager& m_m;
|
||||
ackr_info_ref m_info;
|
||||
model_ref& m_abstr_model;
|
||||
conflict_list& m_conflicts;
|
||||
bool_rewriter m_b_rw;
|
||||
bv_rewriter m_bv_rw;
|
||||
scoped_ptr<model_evaluator> m_evaluator;
|
||||
model m_empty_model;
|
||||
private:
|
||||
struct val_info { expr * value; app * source_term; };
|
||||
typedef obj_map<app, expr *> app2val_t;
|
||||
typedef obj_map<app, val_info> values2val_t;
|
||||
values2val_t m_values2val;
|
||||
app2val_t m_app2val;
|
||||
ptr_vector<expr> m_stack;
|
||||
ackr_helper m_ackr_helper;
|
||||
|
||||
static inline val_info mk_val_info(expr* value, app* source_term) {
|
||||
val_info rv;
|
||||
rv.value = value;
|
||||
rv.source_term = source_term;
|
||||
return rv;
|
||||
}
|
||||
|
||||
//
|
||||
// Performs congruence check on terms on the stack.
|
||||
// (Currently stops upon the first failure).
|
||||
// Returns true if and only if congruence check succeeded.
|
||||
bool run() {
|
||||
m_evaluator = alloc(model_evaluator, m_empty_model);
|
||||
expr_mark visited;
|
||||
expr * curr;
|
||||
while (!m_stack.empty()) {
|
||||
curr = m_stack.back();
|
||||
if (visited.is_marked(curr)) {
|
||||
m_stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (curr->get_kind()) {
|
||||
case AST_VAR:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
case AST_APP: {
|
||||
app * a = to_app(curr);
|
||||
if (for_each_expr_args(m_stack, visited, a->get_num_args(), a->get_args())) {
|
||||
visited.mark(a, true);
|
||||
m_stack.pop_back();
|
||||
if (!mk_value(a)) return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_QUANTIFIER:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool is_val(expr * e) { return m_m.is_value(e); }
|
||||
|
||||
inline bool eval_cached(app * a, expr *& val) {
|
||||
if (is_val(a)) { val = a; return true; }
|
||||
return m_app2val.find(a, val);
|
||||
}
|
||||
|
||||
bool evaluate(app * const a, expr_ref& result) {
|
||||
SASSERT(!is_val(a));
|
||||
const unsigned num = a->get_num_args();
|
||||
if (num == 0) { // handle constants
|
||||
make_value_constant(a, result);
|
||||
return true;
|
||||
}
|
||||
// evaluate arguments
|
||||
expr_ref_vector values(m_m);
|
||||
values.reserve(num);
|
||||
expr * const * args = a->get_args();
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
expr * val;
|
||||
const bool b = eval_cached(to_app(args[i]), val); // TODO: OK conversion to_app?
|
||||
CTRACE("model_constructor", !b, tout << "fail arg val(\n" << mk_ismt2_pp(args[i], m_m, 2); );
|
||||
TRACE("model_constructor", tout <<
|
||||
"arg val " << i << "(\n" << mk_ismt2_pp(args[i], m_m, 2)
|
||||
<< " : " << mk_ismt2_pp(val, m_m, 2) << '\n'; );
|
||||
SASSERT(b);
|
||||
values[i] = val;
|
||||
}
|
||||
// handle functions
|
||||
if (m_ackr_helper.should_ackermannize(a)) { // handle uninterpreted
|
||||
app_ref key(m_m.mk_app(a->get_decl(), values.c_ptr()), m_m);
|
||||
if (!make_value_uninterpreted_function(a, values, key.get(), result)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else { // handle interpreted
|
||||
make_value_interpreted_function(a, values, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Check and record the value for a given term, given that all arguments are already checked.
|
||||
//
|
||||
bool mk_value(app * a) {
|
||||
if (is_val(a)) return true; // skip numerals
|
||||
TRACE("model_constructor", tout << "mk_value(\n" << mk_ismt2_pp(a, m_m, 2) << ")\n";);
|
||||
SASSERT(!m_app2val.contains(a));
|
||||
const unsigned num = a->get_num_args();
|
||||
expr_ref result(m_m);
|
||||
if (!evaluate(a, result)) return false;
|
||||
SASSERT(is_val(result));
|
||||
TRACE("model_constructor",
|
||||
tout << "map term(\n" << mk_ismt2_pp(a, m_m, 2) << "\n->"
|
||||
<< mk_ismt2_pp(result.get(), m_m, 2)<< ")\n"; );
|
||||
CTRACE("model_constructor",
|
||||
!is_val(result.get()),
|
||||
tout << "eval fail\n" << mk_ismt2_pp(a, m_m, 2) << mk_ismt2_pp(result, m_m, 2) << "\n";
|
||||
);
|
||||
SASSERT(is_val(result.get()));
|
||||
m_app2val.insert(a, result.get()); // memoize
|
||||
m_m.inc_ref(a);
|
||||
m_m.inc_ref(result.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Constants from the abstract model are directly mapped to the concrete one.
|
||||
void make_value_constant(app * const a, expr_ref& result) {
|
||||
SASSERT(a->get_num_args() == 0);
|
||||
func_decl * const fd = a->get_decl();
|
||||
expr * val = m_abstr_model->get_const_interp(fd);
|
||||
if (val == 0) { // TODO: avoid model completetion?
|
||||
sort * s = fd->get_range();
|
||||
val = m_abstr_model->get_some_value(s);
|
||||
}
|
||||
result = val;
|
||||
}
|
||||
|
||||
bool make_value_uninterpreted_function(app* a,
|
||||
expr_ref_vector& values,
|
||||
app* key,
|
||||
expr_ref& result) {
|
||||
// get ackermann constant
|
||||
app * const ac = m_info->get_abstr(a);
|
||||
func_decl * const a_fd = a->get_decl();
|
||||
SASSERT(ac->get_num_args() == 0);
|
||||
SASSERT(a_fd->get_range() == ac->get_decl()->get_range());
|
||||
expr_ref value(m_m);
|
||||
value = m_abstr_model->get_const_interp(ac->get_decl());
|
||||
// get ackermann constant's interpretation
|
||||
if (value.get() == 0) { // TODO: avoid model completion?
|
||||
sort * s = a_fd->get_range();
|
||||
value = m_abstr_model->get_some_value(s);
|
||||
}
|
||||
// check congruence
|
||||
val_info vi;
|
||||
if(m_values2val.find(key,vi)) { // already is mapped to a value
|
||||
SASSERT(vi.source_term);
|
||||
const bool ok = vi.value == value;
|
||||
if (!ok) {
|
||||
TRACE("model_constructor",
|
||||
tout << "already mapped by(\n" << mk_ismt2_pp(vi.source_term, m_m, 2) << "\n->"
|
||||
<< mk_ismt2_pp(vi.value, m_m, 2) << ")\n"; );
|
||||
m_conflicts.push_back(std::make_pair(a, vi.source_term));
|
||||
}
|
||||
result = vi.value;
|
||||
return ok;
|
||||
} else { // new value
|
||||
result = value;
|
||||
vi.value = value;
|
||||
vi.source_term = a;
|
||||
m_values2val.insert(key,vi);
|
||||
m_m.inc_ref(vi.source_term);
|
||||
m_m.inc_ref(vi.value);
|
||||
m_m.inc_ref(key);
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void make_value_interpreted_function(app* a,
|
||||
expr_ref_vector& values,
|
||||
expr_ref& result) {
|
||||
const unsigned num = values.size();
|
||||
func_decl * const fd = a->get_decl();
|
||||
const family_id fid = fd->get_family_id();
|
||||
expr_ref term(m_m);
|
||||
term = m_m.mk_app(a->get_decl(), num, values.c_ptr());
|
||||
m_evaluator->operator() (term, result);
|
||||
TRACE("model_constructor",
|
||||
tout << "eval(\n" << mk_ismt2_pp(term.get(), m_m, 2) << "\n->"
|
||||
<< mk_ismt2_pp(result.get(), m_m, 2) << ")\n"; );
|
||||
return;
|
||||
if (fid == m_b_rw.get_fid()) {
|
||||
decl_kind k = fd->get_decl_kind();
|
||||
if (k == OP_EQ) {
|
||||
// theory dispatch for =
|
||||
SASSERT(num == 2);
|
||||
family_id s_fid = m_m.get_sort(values.get(0))->get_family_id();
|
||||
br_status st = BR_FAILED;
|
||||
if (s_fid == m_bv_rw.get_fid())
|
||||
st = m_bv_rw.mk_eq_core(values.get(0), values.get(1), result);
|
||||
} else {
|
||||
br_status st = m_b_rw.mk_app_core(fd, num, values.c_ptr(), result);
|
||||
}
|
||||
} else {
|
||||
br_status st = BR_FAILED;
|
||||
if (fid == m_bv_rw.get_fid()) {
|
||||
st = m_bv_rw.mk_app_core(fd, num, values.c_ptr(), result);
|
||||
}
|
||||
else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
lackr_model_constructor::lackr_model_constructor(ast_manager& m, ackr_info_ref info)
|
||||
: m_imp(0)
|
||||
, m_m(m)
|
||||
, m_state(UNKNOWN)
|
||||
, m_info(info)
|
||||
, m_ref_count(0)
|
||||
{}
|
||||
|
||||
lackr_model_constructor::~lackr_model_constructor() {
|
||||
if (m_imp) dealloc(m_imp);
|
||||
}
|
||||
|
||||
bool lackr_model_constructor::check(model_ref& abstr_model) {
|
||||
m_conflicts.reset();
|
||||
if (m_imp) {
|
||||
dealloc(m_imp);
|
||||
m_imp = 0;
|
||||
}
|
||||
m_imp = alloc(lackr_model_constructor::imp, m_m, m_info, abstr_model, m_conflicts);
|
||||
const bool rv = m_imp->check();
|
||||
m_state = rv ? CHECKED : CONFLICT;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void lackr_model_constructor::make_model(model_ref& model) {
|
||||
SASSERT(m_state == CHECKED);
|
||||
m_imp->make_model(model);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue