mirror of
https://github.com/Z3Prover/z3
synced 2026-01-01 08:49:52 +00:00
Issue #7502 shows that running nlsat eagerly during final check can block quantifier instantiation. To give space for quantifier instances we introduce two levels for final check such that nlsat is only applied in the second and final level.
885 lines
32 KiB
C++
885 lines
32 KiB
C++
/*++
|
|
Copyright (c) 2006 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
theory_array_full.cpp
|
|
|
|
Abstract:
|
|
|
|
<abstract>
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner 2008-22-10
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "util/stats.h"
|
|
#include "ast/ast_ll_pp.h"
|
|
#include "ast/ast_pp.h"
|
|
#include "ast/ast_util.h"
|
|
#include "ast/ast_smt2_pp.h"
|
|
#include "smt/smt_context.h"
|
|
#include "smt/theory_array_full.h"
|
|
|
|
namespace smt {
|
|
|
|
theory_array_full::theory_array_full(context& ctx) :
|
|
theory_array(ctx),
|
|
m_sort2epsilon(ctx.get_manager()),
|
|
m_sort2diag(ctx.get_manager()) {}
|
|
|
|
theory_array_full::~theory_array_full() {
|
|
std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc<var_data_full>());
|
|
}
|
|
|
|
theory* theory_array_full::mk_fresh(context* new_ctx) {
|
|
return alloc(theory_array_full, *new_ctx);
|
|
}
|
|
|
|
void theory_array_full::add_map(theory_var v, enode* s) {
|
|
if (m_params.m_array_cg && !s->is_cgr()) {
|
|
return;
|
|
}
|
|
SASSERT(is_map(s));
|
|
v = find(v);
|
|
var_data_full * d_full = m_var_data_full[v];
|
|
var_data * d = m_var_data[v];
|
|
//
|
|
// TODO: defaulting to exhaustive up-propagation.
|
|
// instead apply stratified filter.
|
|
set_prop_upward(v,d);
|
|
d_full->m_maps.push_back(s);
|
|
m_trail_stack.push(push_back_trail<enode *, false>(d_full->m_maps));
|
|
for (unsigned i = 0; i < d->m_parent_selects.size(); ++i) {
|
|
enode* n = d->m_parent_selects[i];
|
|
SASSERT(is_select(n));
|
|
instantiate_select_map_axiom(n, s);
|
|
}
|
|
set_prop_upward(s);
|
|
}
|
|
|
|
bool theory_array_full::instantiate_axiom_map_for(theory_var v) {
|
|
bool result = false;
|
|
var_data * d = m_var_data[v];
|
|
var_data_full * d_full = m_var_data_full[v];
|
|
for (unsigned i = 0; i < d_full->m_parent_maps.size(); ++i) {
|
|
enode* pm = d_full->m_parent_maps[i];
|
|
for (unsigned j = 0; j < d->m_parent_selects.size(); ++j) {
|
|
enode* ps = d->m_parent_selects[j];
|
|
if (instantiate_select_map_axiom(ps, pm))
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void theory_array_full::add_parent_map(theory_var v, enode* s) {
|
|
if (m_params.m_array_cg && !s->is_cgr()) {
|
|
return;
|
|
}
|
|
SASSERT(v != null_theory_var);
|
|
SASSERT(is_map(s));
|
|
v = find(v);
|
|
var_data * d = m_var_data[v];
|
|
var_data_full * d_full = m_var_data_full[v];
|
|
d_full->m_parent_maps.push_back(s);
|
|
m_trail_stack.push(push_back_trail<enode *, false>(d_full->m_parent_maps));
|
|
if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
|
|
for (unsigned i = 0; i < d->m_parent_selects.size(); ++i) {
|
|
enode * n = d->m_parent_selects[i];
|
|
if (!m_params.m_array_cg || n->is_cgr()) {
|
|
instantiate_select_map_axiom(n, s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// set set_prop_upward on root and recursively on children if necessary.
|
|
//
|
|
void theory_array_full::set_prop_upward(theory_var v) {
|
|
v = find(v);
|
|
var_data * d = m_var_data[v];
|
|
if (!d->m_prop_upward) {
|
|
if (m_params.m_array_weak) {
|
|
add_weak_var(v);
|
|
return;
|
|
}
|
|
m_trail_stack.push(reset_flag_trail(d->m_prop_upward));
|
|
d->m_prop_upward = true;
|
|
TRACE(array, tout << "#" << v << "\n";);
|
|
if (!m_params.m_array_delay_exp_axiom) {
|
|
instantiate_axiom2b_for(v);
|
|
instantiate_axiom_map_for(v);
|
|
}
|
|
var_data_full * d2 = m_var_data_full[v];
|
|
for (enode * n : d->m_stores) {
|
|
set_prop_upward(n);
|
|
}
|
|
for (enode * n : d2->m_maps) {
|
|
set_prop_upward(n);
|
|
}
|
|
for (enode * n : d2->m_consts) {
|
|
set_prop_upward(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// call set_prop_upward on array arguments.
|
|
//
|
|
void theory_array_full::set_prop_upward(enode * n) {
|
|
TRACE(array, tout << pp(n, m) << "\n";);
|
|
if (is_store(n)) {
|
|
set_prop_upward(n->get_arg(0)->get_th_var(get_id()));
|
|
}
|
|
else if (is_map(n)) {
|
|
for (enode* arg : enode::args(n)) {
|
|
set_prop_upward(arg->get_th_var(get_id()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_array_full::set_prop_upward(theory_var v, var_data* d) {
|
|
if (m_params.m_array_always_prop_upward || !d->m_stores.empty()) {
|
|
theory_array::set_prop_upward(v, d);
|
|
}
|
|
else {
|
|
var_data_full * d2 = m_var_data_full[v];
|
|
unsigned sz = d2->m_maps.size();
|
|
for(unsigned i = 0; i < sz; ++i) {
|
|
set_prop_upward(d2->m_maps[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
unsigned theory_array_full::get_lambda_equiv_size(theory_var v, var_data* d) {
|
|
var_data_full * d2 = m_var_data_full[v];
|
|
return d->m_stores.size() + 2*d2->m_consts.size() + 2*d2->m_maps.size();
|
|
}
|
|
|
|
void theory_array_full::add_const(theory_var v, enode* cnst) {
|
|
var_data * d = m_var_data[v];
|
|
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
|
|
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) {
|
|
set_prop_upward(v, d);
|
|
}
|
|
ptr_vector<enode> & consts = m_var_data_full[v]->m_consts;
|
|
m_trail_stack.push(push_back_trail<enode *, false>(consts));
|
|
consts.push_back(cnst);
|
|
instantiate_default_const_axiom(cnst);
|
|
for (unsigned i = 0; i < d->m_parent_selects.size(); ++i) {
|
|
enode * n = d->m_parent_selects[i];
|
|
SASSERT(is_select(n));
|
|
instantiate_select_const_axiom(n, cnst);
|
|
}
|
|
}
|
|
|
|
void theory_array_full::add_lambda(theory_var v, enode* lam) {
|
|
var_data * d = m_var_data[v];
|
|
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
|
|
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1)
|
|
set_prop_upward(v, d);
|
|
ptr_vector<enode> & lambdas = m_var_data_full[v]->m_lambdas;
|
|
m_trail_stack.push(push_back_trail<enode *, false>(lambdas));
|
|
lambdas.push_back(lam);
|
|
instantiate_default_lambda_def_axiom(lam);
|
|
}
|
|
|
|
void theory_array_full::add_as_array(theory_var v, enode* arr) {
|
|
var_data * d = m_var_data[v];
|
|
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
|
|
if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) {
|
|
set_prop_upward(v, d);
|
|
}
|
|
ptr_vector<enode> & as_arrays = m_var_data_full[v]->m_as_arrays;
|
|
m_trail_stack.push(push_back_trail<enode *, false>(as_arrays));
|
|
as_arrays.push_back(arr);
|
|
instantiate_default_as_array_axiom(arr);
|
|
for (unsigned i = 0; i < d->m_parent_selects.size(); ++i) {
|
|
enode* n = d->m_parent_selects[i];
|
|
SASSERT(is_select(n));
|
|
instantiate_select_as_array_axiom(n, arr);
|
|
}
|
|
}
|
|
|
|
|
|
void theory_array_full::reset_eh() {
|
|
theory_array::reset_eh();
|
|
std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc<var_data_full>());
|
|
m_var_data_full.reset();
|
|
m_eqs.reset();
|
|
}
|
|
|
|
void theory_array_full::display_var(std::ostream & out, theory_var v) const {
|
|
theory_array::display_var(out, v);
|
|
var_data_full const * d = m_var_data_full[v];
|
|
out << " maps: {";
|
|
display_ids(out, d->m_maps.size(), d->m_maps.data());
|
|
out << "} p_parent_maps: {";
|
|
display_ids(out, d->m_parent_maps.size(), d->m_parent_maps.data());
|
|
out << "} p_const: {";
|
|
display_ids(out, d->m_consts.size(), d->m_consts.data());
|
|
out << "}\n";
|
|
}
|
|
|
|
theory_var theory_array_full::mk_var(enode * n) {
|
|
theory_var r = theory_array::mk_var(n);
|
|
SASSERT(r == static_cast<int>(m_var_data_full.size()));
|
|
m_var_data_full.push_back(alloc(var_data_full));
|
|
var_data_full * d = m_var_data_full.back();
|
|
if (is_map(n)) {
|
|
instantiate_default_map_axiom(n);
|
|
d->m_maps.push_back(n);
|
|
}
|
|
else if (is_const(n)) {
|
|
instantiate_default_const_axiom(n);
|
|
d->m_consts.push_back(n);
|
|
}
|
|
else if (is_default(n)) {
|
|
// no-op
|
|
}
|
|
else if (is_as_array(n)) {
|
|
instantiate_default_as_array_axiom(n);
|
|
d->m_as_arrays.push_back(n);
|
|
}
|
|
else if (m.is_lambda_def(n->get_decl())) {
|
|
instantiate_default_lambda_def_axiom(n);
|
|
d->m_lambdas.push_back(n);
|
|
m_lambdas.push_back(n);
|
|
ctx.push_trail(push_back_vector(m_lambdas));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
bool theory_array_full::internalize_atom(app * atom, bool) {
|
|
return internalize_term(atom);
|
|
}
|
|
|
|
bool theory_array_full::internalize_term(app * n) {
|
|
if (ctx.e_internalized(n)) {
|
|
return true;
|
|
}
|
|
TRACE(array, tout << mk_pp(n, m) << "\n";);
|
|
|
|
if (is_store(n) || is_select(n)) {
|
|
return theory_array::internalize_term(n);
|
|
}
|
|
|
|
if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n)) {
|
|
if (!is_array_ext(n))
|
|
found_unsupported_op(n);
|
|
return false;
|
|
}
|
|
|
|
if (!internalize_term_core(n)) {
|
|
return true;
|
|
}
|
|
|
|
if (is_map(n) || is_array_ext(n)) {
|
|
for (expr* e : *n) {
|
|
enode* arg = ctx.get_enode(e);
|
|
if (!is_attached_to_var(arg)) {
|
|
mk_var(arg);
|
|
}
|
|
}
|
|
}
|
|
else if (is_default(n)) {
|
|
enode* arg0 = ctx.get_enode(n->get_arg(0));
|
|
if (!is_attached_to_var(arg0)) {
|
|
mk_var(arg0);
|
|
}
|
|
}
|
|
|
|
enode* node = ctx.get_enode(n);
|
|
if (!is_attached_to_var(node)) {
|
|
mk_var(node);
|
|
}
|
|
|
|
if (is_default(n)) {
|
|
enode* arg0 = ctx.get_enode(n->get_arg(0));
|
|
theory_var v_arg = arg0->get_th_var(get_id());
|
|
add_parent_default(v_arg);
|
|
}
|
|
else if (is_map(n)) {
|
|
for (expr* e : *n) {
|
|
enode* arg = ctx.get_enode(e);
|
|
theory_var v_arg = arg->get_th_var(get_id());
|
|
add_parent_map(v_arg, node);
|
|
}
|
|
instantiate_default_map_axiom(node);
|
|
}
|
|
else if (is_const(n)) {
|
|
instantiate_default_const_axiom(node);
|
|
}
|
|
else if (is_as_array(n)) {
|
|
// The array theory is not a decision procedure
|
|
// for as-array.
|
|
// Ex: (as-array f) = (as-array g) & f(0) = 0 & g(0) = 1
|
|
// There is nothing to propagate the disequality.
|
|
// Even if there was, as-array on interpreted
|
|
// functions will be incomplete.
|
|
// The instantiation operations are still sound to include.
|
|
m_as_array.push_back(node);
|
|
ctx.push_trail(push_back_vector(m_as_array));
|
|
instantiate_default_as_array_axiom(node);
|
|
}
|
|
else if (is_array_ext(n)) {
|
|
SASSERT(n->get_num_args() == 2);
|
|
instantiate_extensionality(ctx.get_enode(n->get_arg(0)), ctx.get_enode(n->get_arg(1)));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void theory_array_full::merge_eh(theory_var v1, theory_var v2, theory_var u, theory_var w) {
|
|
theory_array::merge_eh(v1, v2, u, w);
|
|
// v1 is the new root
|
|
SASSERT(v1 == find(v1));
|
|
var_data_full * d2 = m_var_data_full[v2];
|
|
for (enode * n : d2->m_maps)
|
|
add_map(v1, n);
|
|
for (enode * n : d2->m_parent_maps)
|
|
add_parent_map(v1, n);
|
|
for (enode * n : d2->m_consts)
|
|
add_const(v1, n);
|
|
for (enode * n : d2->m_as_arrays)
|
|
add_as_array(v1, n);
|
|
for (enode* n : d2->m_lambdas)
|
|
add_lambda(v1, n);
|
|
TRACE(array,
|
|
tout << pp(get_enode(v1), m) << "\n";
|
|
tout << pp(get_enode(v2), m) << "\n";
|
|
tout << "merge in\n"; display_var(tout, v2);
|
|
tout << "after merge\n"; display_var(tout, v1););
|
|
}
|
|
|
|
void theory_array_full::add_parent_default(theory_var v) {
|
|
SASSERT(v != null_theory_var);
|
|
v = find(v);
|
|
var_data* d = m_var_data[v];
|
|
TRACE(array, tout << "v" << v << " " << pp(get_enode(v), m) << " "
|
|
<< d->m_prop_upward << " " << m_params.m_array_delay_exp_axiom << "\n";);
|
|
for (enode * store : d->m_stores) {
|
|
SASSERT(is_store(store));
|
|
instantiate_default_store_axiom(store);
|
|
}
|
|
|
|
if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
|
|
instantiate_parent_stores_default(v);
|
|
}
|
|
}
|
|
|
|
void theory_array_full::add_parent_select(theory_var v, enode * s) {
|
|
TRACE(array,
|
|
tout << v << " select parent: " << pp(s, m) << "\n";
|
|
display_var(tout, v);
|
|
);
|
|
theory_array::add_parent_select(v,s);
|
|
v = find(v);
|
|
var_data_full* d_full = m_var_data_full[v];
|
|
var_data* d = m_var_data[v];
|
|
for (enode * n : d_full->m_consts) {
|
|
instantiate_select_const_axiom(s, n);
|
|
}
|
|
for (enode * map : d_full->m_maps) {
|
|
SASSERT(is_map(map));
|
|
instantiate_select_map_axiom(s, map);
|
|
}
|
|
if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
|
|
for (enode * map : d_full->m_parent_maps) {
|
|
SASSERT(is_map(map));
|
|
if (!m_params.m_array_cg || map->is_cgr()) {
|
|
instantiate_select_map_axiom(s, map);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_array_full::relevant_eh(app* n) {
|
|
TRACE(array, tout << mk_pp(n, m) << "\n";);
|
|
theory_array::relevant_eh(n);
|
|
if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)){
|
|
return;
|
|
}
|
|
ctx.ensure_internalized(n);
|
|
enode* node = ctx.get_enode(n);
|
|
if (is_select(n)) {
|
|
enode * arg = ctx.get_enode(n->get_arg(0));
|
|
theory_var v = arg->get_th_var(get_id());
|
|
SASSERT(v != null_theory_var);
|
|
add_parent_select(find(v), node);
|
|
}
|
|
else if (is_default(n)) {
|
|
enode * arg = ctx.get_enode(n->get_arg(0));
|
|
theory_var v = arg->get_th_var(get_id());
|
|
SASSERT(v != null_theory_var);
|
|
set_prop_upward(v);
|
|
add_parent_default(find(v));
|
|
}
|
|
else if (is_const(n)) {
|
|
instantiate_default_const_axiom(node);
|
|
theory_var v = node->get_th_var(get_id());
|
|
set_prop_upward(v);
|
|
add_parent_default(find(v));
|
|
}
|
|
else if (is_map(n)) {
|
|
for (expr * e : *n) {
|
|
enode* arg = ctx.get_enode(e);
|
|
theory_var v_arg = find(arg->get_th_var(get_id()));
|
|
add_parent_map(v_arg, node);
|
|
set_prop_upward(v_arg);
|
|
}
|
|
instantiate_default_map_axiom(node);
|
|
}
|
|
else if (is_as_array(n)) {
|
|
instantiate_default_as_array_axiom(node);
|
|
}
|
|
}
|
|
|
|
bool theory_array_full::should_research(expr_ref_vector & unsat_core) {
|
|
return false;
|
|
}
|
|
|
|
void theory_array_full::add_theory_assumptions(expr_ref_vector & assumptions) {
|
|
}
|
|
|
|
//
|
|
// Assert axiom:
|
|
// select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
|
|
//
|
|
bool theory_array_full::instantiate_select_map_axiom(enode* sl, enode* mp) {
|
|
app* map = mp->get_expr();
|
|
app* select = sl->get_expr();
|
|
SASSERT(is_map(map));
|
|
SASSERT(is_select(select));
|
|
SASSERT(map->get_num_args() > 0);
|
|
func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast());
|
|
|
|
|
|
TRACE(array_map_bug, tout << "invoked instantiate_select_map_axiom\n";
|
|
tout << sl->get_owner_id() << " " << mp->get_owner_id() << "\n";
|
|
tout << mk_ismt2_pp(sl->get_expr(), m) << "\n" << mk_ismt2_pp(mp->get_expr(), m) << "\n";);
|
|
|
|
if (!ctx.add_fingerprint(mp, mp->get_owner_id(), sl->get_num_args() - 1, sl->get_args() + 1)) {
|
|
return false;
|
|
}
|
|
|
|
TRACE(array_map_bug, tout << "new axiom\n";);
|
|
|
|
m_stats.m_num_map_axiom++;
|
|
TRACE(array,
|
|
tout << mk_bounded_pp(mp->get_expr(), m) << "\n";
|
|
tout << mk_bounded_pp(sl->get_expr(), m) << "\n";);
|
|
unsigned num_args = select->get_num_args();
|
|
unsigned num_arrays = map->get_num_args();
|
|
ptr_buffer<expr> args1, args2;
|
|
vector<ptr_vector<expr> > args2l;
|
|
args1.push_back(map);
|
|
for (expr* ar : *map) {
|
|
ptr_vector<expr> arg;
|
|
arg.push_back(ar);
|
|
args2l.push_back(arg);
|
|
}
|
|
for (unsigned i = 1; i < num_args; ++i) {
|
|
expr* arg = select->get_arg(i);
|
|
for (unsigned j = 0; j < num_arrays; ++j) {
|
|
args2l[j].push_back(arg);
|
|
}
|
|
args1.push_back(arg);
|
|
}
|
|
for (unsigned j = 0; j < num_arrays; ++j) {
|
|
expr* sel = mk_select(args2l[j].size(), args2l[j].data());
|
|
args2.push_back(sel);
|
|
}
|
|
|
|
expr_ref sel1(m), sel2(m);
|
|
sel1 = mk_select(args1.size(), args1.data());
|
|
sel2 = m.mk_app(f, args2.size(), args2.data());
|
|
ctx.get_rewriter()(sel2);
|
|
ctx.internalize(sel1, false);
|
|
ctx.internalize(sel2, false);
|
|
|
|
TRACE(array_map_bug,
|
|
tout << "select-map axiom\n" << mk_ismt2_pp(sel1, m) << "\n=\n" << mk_ismt2_pp(sel2,m) << "\n";);
|
|
|
|
return try_assign_eq(sel1, sel2);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Assert axiom:
|
|
// default(map[f](a,..,d)) = f(default(a),..,default(d))
|
|
//
|
|
|
|
bool theory_array_full::instantiate_default_map_axiom(enode* mp) {
|
|
SASSERT(is_map(mp));
|
|
|
|
app* map = mp->get_expr();
|
|
if (!ctx.add_fingerprint(this, m_default_map_fingerprint, 1, &mp)) {
|
|
return false;
|
|
}
|
|
TRACE(array, tout << mk_bounded_pp(map, m) << "\n";);
|
|
|
|
m_stats.m_num_default_map_axiom++;
|
|
|
|
func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast());
|
|
SASSERT(map->get_num_args() == f->get_arity());
|
|
ptr_buffer<expr> args2;
|
|
for (expr* arg : *map) {
|
|
args2.push_back(mk_default(arg));
|
|
}
|
|
|
|
expr_ref def2(m.mk_app(f, args2.size(), args2.data()), m);
|
|
ctx.get_rewriter()(def2);
|
|
expr_ref def1(mk_default(map), m);
|
|
ctx.internalize(def1, false);
|
|
ctx.internalize(def2, false);
|
|
return try_assign_eq(def1, def2);
|
|
}
|
|
|
|
|
|
bool theory_array_full::instantiate_default_const_axiom(enode* cnst) {
|
|
if (!ctx.add_fingerprint(this, m_default_const_fingerprint, 1, &cnst)) {
|
|
return false;
|
|
}
|
|
m_stats.m_num_default_const_axiom++;
|
|
SASSERT(is_const(cnst));
|
|
TRACE(array, tout << mk_bounded_pp(cnst->get_expr(), m) << "\n";);
|
|
expr* val = cnst->get_arg(0)->get_expr();
|
|
expr_ref def(mk_default(cnst->get_expr()), m);
|
|
ctx.internalize(def, false);
|
|
return try_assign_eq(val, def);
|
|
}
|
|
|
|
/**
|
|
* instantiate f(ep1,ep2,..,ep_n) = default(as-array f)
|
|
* it is disabled to avoid as-array terms during search.
|
|
*/
|
|
|
|
bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) {
|
|
return false;
|
|
#if 0
|
|
if (!ctx.add_fingerprint(this, m_default_as_array_fingerprint, 1, &arr)) {
|
|
return false;
|
|
}
|
|
m_stats.m_num_default_as_array_axiom++;
|
|
SASSERT(is_as_array(arr));
|
|
TRACE(array, tout << mk_bounded_pp(arr->get_owner(), m) << "\n";);
|
|
expr* def = mk_default(arr->get_owner());
|
|
func_decl * f = array_util(m).get_as_array_func_decl(arr->get_owner());
|
|
ptr_vector<expr> args;
|
|
for (unsigned i = 0; i < f->get_arity(); ++i) {
|
|
args.push_back(mk_epsilon(f->get_domain(i)));
|
|
}
|
|
expr_ref val(m.mk_app(f, args.size(), args.c_ptr()), m);
|
|
ctx.internalize(def, false);
|
|
ctx.internalize(val.get(), false);
|
|
return try_assign_eq(val.get(), def);
|
|
#endif
|
|
}
|
|
|
|
bool theory_array_full::instantiate_default_lambda_def_axiom(enode* arr) {
|
|
if (!ctx.add_fingerprint(this, m_default_lambda_fingerprint, 1, &arr))
|
|
return false;
|
|
m_stats.m_num_default_lambda_axiom++;
|
|
expr* e = arr->get_expr();
|
|
expr_ref def(mk_default(e), m);
|
|
quantifier* lam = m.is_lambda_def(arr->get_decl());
|
|
TRACE(array, tout << mk_pp(lam, m) << "\n" << mk_pp(e, m) << "\n");
|
|
expr_ref_vector args(m);
|
|
var_subst subst(m, false);
|
|
args.push_back(subst(lam, to_app(e)->get_num_args(), to_app(e)->get_args()));
|
|
for (unsigned i = 0; i < lam->get_num_decls(); ++i)
|
|
args.push_back(mk_epsilon(lam->get_decl_sort(i)).first);
|
|
expr_ref val(mk_select(args), m);
|
|
ctx.get_rewriter()(val);
|
|
if (has_quantifiers(val)) {
|
|
expr_ref fn(m.mk_fresh_const("lambda-body", val->get_sort()), m);
|
|
expr_ref eq(m.mk_eq(fn, val), m);
|
|
ctx.assert_expr(eq);
|
|
ctx.internalize_assertions();
|
|
val = fn;
|
|
}
|
|
ctx.internalize(def, false);
|
|
ctx.internalize(val.get(), false);
|
|
return try_assign_eq(val.get(), def);
|
|
}
|
|
|
|
|
|
bool theory_array_full::has_unitary_domain(app* array_term) {
|
|
SASSERT(is_array_sort(array_term));
|
|
sort* s = array_term->get_sort();
|
|
unsigned dim = get_dimension(s);
|
|
parameter const * params = s->get_info()->get_parameters();
|
|
for (unsigned i = 0; i < dim; ++i) {
|
|
SASSERT(params[i].is_ast());
|
|
sort* d = to_sort(params[i].get_ast());
|
|
if (d->is_infinite() || d->is_very_big() || 1 != d->get_num_elements().size())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool theory_array_full::has_large_domain(app* array_term) {
|
|
SASSERT(is_array_sort(array_term));
|
|
sort* s = array_term->get_sort();
|
|
unsigned dim = get_dimension(s);
|
|
parameter const * params = s->get_info()->get_parameters();
|
|
rational sz(1);
|
|
for (unsigned i = 0; i < dim; ++i) {
|
|
SASSERT(params[i].is_ast());
|
|
sort* d = to_sort(params[i].get_ast());
|
|
if (d->is_infinite() || d->is_very_big()) {
|
|
return true;
|
|
}
|
|
sz *= rational(d->get_num_elements().size(),rational::ui64());
|
|
if (sz >= rational(1 << 14)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Assert axiom:
|
|
// select(const v, i_1, ..., i_n) = v
|
|
//
|
|
bool theory_array_full::instantiate_select_const_axiom(enode* select, enode* cnst) {
|
|
SASSERT(is_const(cnst));
|
|
SASSERT(is_select(select));
|
|
SASSERT(cnst->get_num_args() == 1);
|
|
unsigned num_args = select->get_num_args();
|
|
if (!ctx.add_fingerprint(cnst, cnst->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
|
|
return false;
|
|
}
|
|
|
|
m_stats.m_num_select_const_axiom++;
|
|
ptr_buffer<expr> sel_args;
|
|
sel_args.push_back(cnst->get_expr());
|
|
for (unsigned short i = 1; i < num_args; ++i) {
|
|
sel_args.push_back(select->get_expr()->get_arg(i));
|
|
}
|
|
expr * sel = mk_select(sel_args.size(), sel_args.data());
|
|
expr * val = cnst->get_expr()->get_arg(0);
|
|
TRACE(array, tout << "new select-const axiom...\n";
|
|
tout << "const: " << mk_bounded_pp(cnst->get_expr(), m) << "\n";
|
|
tout << "select: " << mk_bounded_pp(select->get_expr(), m) << "\n";
|
|
tout << " sel/const: " << mk_bounded_pp(sel, m) << "\n";
|
|
tout << "value: " << mk_bounded_pp(val, m) << "\n";
|
|
tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n";
|
|
);
|
|
|
|
ctx.internalize(sel, false);
|
|
return try_assign_eq(sel,val);
|
|
}
|
|
|
|
|
|
//
|
|
// Assert axiom:
|
|
// select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n)
|
|
//
|
|
bool theory_array_full::instantiate_select_as_array_axiom(enode* select, enode* arr) {
|
|
SASSERT(is_as_array(arr->get_expr()));
|
|
SASSERT(is_select(select));
|
|
SASSERT(arr->get_num_args() == 0);
|
|
unsigned num_args = select->get_num_args();
|
|
if (!ctx.add_fingerprint(arr, arr->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
|
|
return false;
|
|
}
|
|
|
|
m_stats.m_num_select_as_array_axiom++;
|
|
ptr_buffer<expr> sel_args;
|
|
sel_args.push_back(arr->get_expr());
|
|
for (unsigned short i = 1; i < num_args; ++i) {
|
|
sel_args.push_back(select->get_expr()->get_arg(i));
|
|
}
|
|
expr * sel = mk_select(sel_args.size(), sel_args.data());
|
|
func_decl * f = array_util(m).get_as_array_func_decl(arr->get_expr());
|
|
expr_ref val(m.mk_app(f, sel_args.size()-1, sel_args.data()+1), m);
|
|
TRACE(array, tout << "new select-as-array axiom...\n";
|
|
tout << "as-array: " << mk_bounded_pp(arr->get_expr(), m) << "\n";
|
|
tout << "select: " << mk_bounded_pp(select->get_expr(), m) << "\n";
|
|
tout << " sel/as-array: " << mk_bounded_pp(sel, m) << "\n";
|
|
tout << "value: " << mk_bounded_pp(val.get(), m) << "\n";
|
|
tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n";
|
|
);
|
|
|
|
ctx.internalize(sel, false);
|
|
ctx.internalize(val.get(), false);
|
|
return try_assign_eq(sel,val);
|
|
}
|
|
|
|
|
|
bool theory_array_full::instantiate_default_store_axiom(enode* store) {
|
|
SASSERT(is_store(store));
|
|
SASSERT(store->get_num_args() >= 3);
|
|
app* store_app = store->get_expr();
|
|
if (!ctx.add_fingerprint(this, m_default_store_fingerprint, store->get_num_args(), store->get_args())) {
|
|
return false;
|
|
}
|
|
|
|
m_stats.m_num_default_store_axiom++;
|
|
|
|
expr_ref def1(m), def2(m);
|
|
|
|
TRACE(array, tout << mk_bounded_pp(store_app, m) << "\n";);
|
|
|
|
unsigned num_args = store_app->get_num_args();
|
|
|
|
def1 = mk_default(store_app);
|
|
def2 = mk_default(store_app->get_arg(0));
|
|
|
|
bool is_new = false;
|
|
|
|
if (has_unitary_domain(store_app)) {
|
|
def2 = store_app->get_arg(num_args - 1);
|
|
}
|
|
else if (!has_large_domain(store_app)) {
|
|
//
|
|
// let A = store(B, i, v)
|
|
//
|
|
// Add:
|
|
// default(A) = A[epsilon]
|
|
// default(B) = B[epsilon]
|
|
//
|
|
//
|
|
expr_ref_vector args1(m), args2(m);
|
|
args1.push_back(store_app);
|
|
args2.push_back(store_app->get_arg(0));
|
|
|
|
for (unsigned i = 1; i + 1 < num_args; ++i) {
|
|
expr* arg = store_app->get_arg(i);
|
|
sort* srt = arg->get_sort();
|
|
auto ep = mk_epsilon(srt);
|
|
args1.push_back(ep.first);
|
|
args2.push_back(ep.first);
|
|
}
|
|
app_ref sel1(m), sel2(m);
|
|
sel1 = mk_select(args1);
|
|
sel2 = mk_select(args2);
|
|
ctx.internalize(def1, false);
|
|
ctx.internalize(def2, false);
|
|
is_new = try_assign_eq(def1, sel1) || try_assign_eq(def2, sel2);
|
|
return is_new;
|
|
}
|
|
|
|
ctx.internalize(def1, false);
|
|
ctx.internalize(def2, false);
|
|
return try_assign_eq(def1, def2) || is_new;
|
|
}
|
|
|
|
std::pair<app*,func_decl*> theory_array_full::mk_epsilon(sort* s) {
|
|
app* eps = nullptr;
|
|
func_decl* diag = nullptr;
|
|
if (!m_sort2epsilon.find(s, eps)) {
|
|
eps = m.mk_fresh_const("epsilon", s);
|
|
m_trail_stack.push(ast2ast_trail<sort, app>(m_sort2epsilon, s, eps));
|
|
}
|
|
if (!m_sort2diag.find(s, diag)) {
|
|
diag = m.mk_fresh_func_decl("diag", 1, &s, s);
|
|
m_trail_stack.push(ast2ast_trail<sort, func_decl>(m_sort2diag, s, diag));
|
|
}
|
|
return std::make_pair(eps, diag);
|
|
}
|
|
|
|
final_check_status theory_array_full::assert_delayed_axioms() {
|
|
final_check_status r = FC_DONE;
|
|
if (!m_params.m_array_delay_exp_axiom) {
|
|
r = FC_DONE;
|
|
}
|
|
else {
|
|
r = theory_array::assert_delayed_axioms();
|
|
unsigned num_vars = get_num_vars();
|
|
for (unsigned v = 0; v < num_vars; v++) {
|
|
var_data * d = m_var_data[v];
|
|
if (d->m_prop_upward && instantiate_axiom_map_for(v))
|
|
r = FC_CONTINUE;
|
|
if (d->m_prop_upward) {
|
|
if (instantiate_parent_stores_default(v))
|
|
r = FC_CONTINUE;
|
|
}
|
|
}
|
|
}
|
|
bool should_giveup = m_found_unsupported_op || has_propagate_up_trail() || has_non_beta_as_array();
|
|
if (r == FC_DONE && should_giveup)
|
|
r = FC_GIVEUP;
|
|
return r;
|
|
}
|
|
|
|
bool theory_array_full::has_non_beta_as_array() {
|
|
for (enode* n : m_as_array) {
|
|
for (enode* p : n->get_parents())
|
|
if (ctx.is_relevant(p) && !ctx.is_beta_redex(p, n)) {
|
|
TRACE(array, tout << "not a beta redex " << enode_pp(p, ctx) << "\n");
|
|
return true;
|
|
}
|
|
}
|
|
for (enode* n : m_lambdas)
|
|
for (enode* p : n->get_parents())
|
|
if (ctx.is_relevant(p) && !is_default(p) && !ctx.is_beta_redex(p, n)) {
|
|
TRACE(array, tout << "lambda is not a beta redex " << enode_pp(p, ctx) << "\n");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool theory_array_full::instantiate_parent_stores_default(theory_var v) {
|
|
SASSERT(v != null_theory_var);
|
|
TRACE(array, tout << "v" << v << "\n";);
|
|
v = find(v);
|
|
var_data* d = m_var_data[v];
|
|
bool result = false;
|
|
for (unsigned i = 0; i < d->m_parent_stores.size(); ++i) {
|
|
enode* store = d->m_parent_stores[i];
|
|
SASSERT(is_store(store));
|
|
if ((!m_params.m_array_cg || store->is_cgr()) &&
|
|
instantiate_default_store_axiom(store)) {
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool theory_array_full::try_assign_eq(expr* v1, expr* v2) {
|
|
TRACE(array, tout << mk_bounded_pp(v1, m) << "\n==\n" << mk_bounded_pp(v2, m) << "\n";);
|
|
|
|
if (m_eqs.contains(v1, v2)) {
|
|
return false;
|
|
}
|
|
else {
|
|
m_eqs.insert(v1, v2, true);
|
|
literal eq(mk_eq(v1, v2, true));
|
|
scoped_trace_stream _sc(*this, eq);
|
|
ctx.mark_as_relevant(eq);
|
|
assert_axiom(eq);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void theory_array_full::pop_scope_eh(unsigned num_scopes) {
|
|
unsigned num_old_vars = get_old_num_vars(num_scopes);
|
|
theory_array::pop_scope_eh(num_scopes);
|
|
std::for_each(m_var_data_full.begin() + num_old_vars, m_var_data_full.end(), delete_proc<var_data_full>());
|
|
m_var_data_full.shrink(num_old_vars);
|
|
m_eqs.reset();
|
|
}
|
|
|
|
void theory_array_full::collect_statistics(::statistics & st) const {
|
|
theory_array::collect_statistics(st);
|
|
st.update("array map ax", m_stats.m_num_map_axiom);
|
|
st.update("array def const", m_stats.m_num_default_const_axiom);
|
|
st.update("array sel const", m_stats.m_num_select_const_axiom);
|
|
st.update("array def store", m_stats.m_num_default_store_axiom);
|
|
st.update("array def as-array", m_stats.m_num_default_as_array_axiom);
|
|
st.update("array sel as-array", m_stats.m_num_select_as_array_axiom);
|
|
st.update("array def lambda", m_stats.m_num_default_lambda_axiom);
|
|
}
|
|
}
|