3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-28 11:25:51 +00:00
z3/src/sat/smt/array_model.cpp
Nikolaj Bjorner 1f150ecd52 #6319
#6319 - fix incompleteness in propagation of default to all array terms in the equivalence class.

Fix bug with q_mbi where domain restrictions are not using values because the current model does not evaluate certain bound variables to values. Set model completion when adding these bound variables to the model to ensure their values are not missed.

Add better propagation of diagnostics when tactics and the new solver return unknown. The reason for unknown can now be traced to what theory was culprit (currently no additional information)
2022-09-23 22:22:34 -05:00

359 lines
12 KiB
C++

/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
array_model.cpp
Abstract:
Theory plugin for arrays
Author:
Nikolaj Bjorner (nbjorner) 2020-09-08
--*/
#include "model/array_factory.h"
#include "sat/smt/array_solver.h"
#include "sat/smt/euf_solver.h"
namespace array {
void solver::init_model() {
collect_defaults();
collect_selects();
}
void solver::finalize_model(model& mdl) {
std::for_each(m_selects_range.begin(), m_selects_range.end(), delete_proc<select_set>());
}
bool solver::add_dep(euf::enode* n, top_sort<euf::enode>& dep) {
if (!a.is_array(n->get_expr())) {
dep.insert(n, nullptr);
return true;
}
if (a.is_array(n->get_expr())) {
for (euf::enode* p : euf::enode_parents(n->get_root()))
if (a.is_default(p->get_expr()))
dep.add(n, p);
for (euf::enode* p : *get_select_set(n)) {
dep.add(n, p);
for (unsigned i = 1; i < p->num_args(); ++i)
dep.add(n, p->get_arg(i));
}
}
for (euf::enode* k : euf::enode_class(n))
if (a.is_const(k->get_expr()))
dep.add(n, k->get_arg(0));
theory_var v = get_th_var(n);
euf::enode* d = get_default(v);
if (d)
dep.add(n, d);
if (!dep.contains_dep(n))
dep.insert(n, nullptr);
return true;
}
void solver::add_value(euf::enode* n, model& mdl, expr_ref_vector& values) {
SASSERT(a.is_array(n->get_expr()));
ptr_vector<expr> args;
sort* srt = n->get_sort();
n = n->get_root();
if (a.is_as_array(n->get_expr())) {
values.set(n->get_expr_id(), n->get_expr());
return;
}
theory_var v = get_th_var(n);
euf::enode* d = get_default(v);
if (a.is_const(n->get_expr())) {
expr* val = values.get(d->get_root_id());
SASSERT(val);
values.set(n->get_expr_id(), a.mk_const_array(n->get_sort(), val));
return;
}
unsigned arity = get_array_arity(srt);
func_decl * f = mk_aux_decl_for_array_sort(m, srt);
func_interp * fi = alloc(func_interp, m, arity);
mdl.register_decl(f, fi);
if (d && !fi->get_else())
fi->set_else(values.get(d->get_root_id()));
if (!fi->get_else() && get_else(v))
fi->set_else(get_else(v));
if (!fi->get_else()) {
expr* else_value = nullptr;
unsigned max_occ_num = 0;
obj_map<expr, unsigned> num_occ;
for (euf::enode* p : euf::enode_parents(n->get_root())) {
if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n->get_root()) {
expr* v = values.get(p->get_root_id(), nullptr);
if (!v)
continue;
unsigned no = 0;
num_occ.find(v, no);
++no;
num_occ.insert(v, no);
if (no > max_occ_num) {
else_value = v;
max_occ_num = no;
}
}
}
if (else_value)
fi->set_else(else_value);
}
if (!get_else(v) && fi->get_else())
set_else(v, fi->get_else());
if (!get_else(v)) {
expr* else_value = mdl.get_some_value(get_array_range(srt));
fi->set_else(else_value);
set_else(v, else_value);
}
for (euf::enode* p : *get_select_set(n)) {
expr* value = values.get(p->get_root_id(), nullptr);
if (!value || value == fi->get_else())
continue;
args.reset();
for (unsigned i = 1; i < p->num_args(); ++i) {
if (!values.get(p->get_arg(i)->get_root_id())) {
TRACE("array", tout << ctx.bpp(p->get_arg(i)) << "\n");
}
SASSERT(values.get(p->get_arg(i)->get_root_id()));
}
for (unsigned i = 1; i < p->num_args(); ++i)
args.push_back(values.get(p->get_arg(i)->get_root_id()));
fi->insert_entry(args.data(), value);
}
TRACE("array", tout << "array-as-function " << ctx.bpp(n) << " := " << mk_pp(f, m) << "\n" << "default " << mk_pp(fi->get_else(), m) << "\n";);
parameter p(f);
values.set(n->get_expr_id(), m.mk_app(get_id(), OP_AS_ARRAY, 1, &p));
}
bool solver::must_have_different_model_values(theory_var v1, theory_var v2) {
euf::enode* else1 = nullptr, * else2 = nullptr;
euf::enode* n1 = var2enode(v1);
expr* e1 = n1->get_expr();
if (!a.is_array(e1))
return true;
else1 = get_default(v1);
else2 = get_default(v2);
if (else1 && else2 && else1->get_root() != else2->get_root() && has_large_domain(e1))
return true;
return false;
}
unsigned solver::sel_hash::operator()(euf::enode * n) const {
return get_composite_hash<euf::enode *, sel_khasher, sel_chasher>(n, n->num_args() - 1, sel_khasher(), sel_chasher());
}
bool solver::sel_eq::operator()(euf::enode * n1, euf::enode * n2) const {
SASSERT(n1->num_args() == n2->num_args());
unsigned num_args = n1->num_args();
for (unsigned i = 1; i < num_args; i++)
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
return false;
return true;
}
void solver::collect_selects() {
int num_vars = get_num_vars();
m_selects.reset();
m_selects_domain.reset();
m_selects_range.reset();
for (theory_var v = 0; v < num_vars; ++v) {
euf::enode * r = var2enode(v)->get_root();
if (is_representative(v) && ctx.is_relevant(r)) {
for (euf::enode * parent : euf::enode_parents(r)) {
if (parent->get_cg() == parent &&
ctx.is_relevant(parent) &&
a.is_select(parent->get_expr()) &&
parent->get_arg(0)->get_root() == r) {
select_set * s = get_select_set(r);
SASSERT(!s->contains(parent) || (*(s->find(parent)))->get_root() == parent->get_root());
s->insert(parent);
}
}
}
}
euf::enode_pair_vector todo;
for (euf::enode * r : m_selects_domain)
for (euf::enode* sel : *get_select_set(r))
propagate_select_to_store_parents(r, sel, todo);
for (unsigned qhead = 0; qhead < todo.size(); qhead++) {
euf::enode_pair & pair = todo[qhead];
euf::enode * r = pair.first;
euf::enode * sel = pair.second;
propagate_select_to_store_parents(r, sel, todo);
}
}
void solver::propagate_select_to_store_parents(euf::enode* r, euf::enode* sel, euf::enode_pair_vector& todo) {
SASSERT(r->get_root() == r);
SASSERT(a.is_select(sel->get_expr()));
if (!ctx.is_relevant(r))
return;
for (euf::enode * parent : euf::enode_parents(r)) {
if (ctx.is_relevant(parent) &&
a.is_store(parent->get_expr()) &&
parent->get_arg(0)->get_root() == r) {
// propagate upward
select_set * parent_sel_set = get_select_set(parent);
euf::enode * parent_root = parent->get_root();
if (parent_sel_set->contains(sel))
continue;
SASSERT(sel->num_args() + 1 == parent->num_args());
// check whether the sel idx was overwritten by the store
unsigned num_args = sel->num_args();
unsigned i = 1;
for (; i < num_args; i++) {
if (sel->get_arg(i)->get_root() != parent->get_arg(i)->get_root())
break;
}
if (i < num_args) {
SASSERT(!parent_sel_set->contains(sel) || (*(parent_sel_set->find(sel)))->get_root() == sel->get_root());
parent_sel_set->insert(sel);
todo.push_back(std::make_pair(parent_root, sel));
}
}
}
}
solver::select_set* solver::get_select_set(euf::enode* n) {
euf::enode * r = n->get_root();
select_set * set = nullptr;
m_selects.find(r, set);
if (set == nullptr) {
set = alloc(select_set);
m_selects.insert(r, set);
m_selects_domain.push_back(r);
m_selects_range.push_back(set);
}
return set;
}
void solver::collect_defaults() {
unsigned num_vars = get_num_vars();
m_defaults.reset();
m_else_values.reset();
m_parents.reset();
m_parents.resize(num_vars, -1);
m_defaults.resize(num_vars);
m_else_values.resize(num_vars);
//
// Create equivalence classes for defaults.
//
for (unsigned v = 0; v < num_vars; ++v) {
euf::enode * n = var2enode(v);
expr* e = n->get_expr();
theory_var r = get_representative(v);
mg_merge(v, r);
if (a.is_const(e))
set_default(v, n->get_arg(0));
else if (a.is_store(e)) {
theory_var w = get_th_var(n->get_arg(0));
SASSERT(w != euf::null_theory_var);
mg_merge(v, get_representative(w));
TRACE("array", tout << "merge: " << ctx.bpp(n) << " " << v << " " << w << "\n";);
}
else if (a.is_default(e)) {
theory_var w = get_th_var(n->get_arg(0));
SASSERT(w != euf::null_theory_var);
set_default(w, n);
}
}
}
void solver::set_default(theory_var v, euf::enode* n) {
v = mg_find(v);
CTRACE("array", !m_defaults[v], tout << "set default: " << v << " " << ctx.bpp(n) << "\n";);
if (!m_defaults[v])
m_defaults[v] = n;
}
euf::enode* solver::get_default(theory_var v) {
return m_defaults[mg_find(v)];
}
void solver::set_else(theory_var v, expr* e) {
m_else_values[mg_find(v)] = e;
}
expr* solver::get_else(theory_var v) {
return m_else_values[mg_find(v)];
}
euf::theory_var solver::mg_find(theory_var n) {
if (m_parents[n] < 0)
return n;
theory_var n0 = n;
n = m_parents[n0];
if (m_parents[n] < -1)
return n;
while (m_parents[n] >= 0)
n = m_parents[n];
// compress path.
while (m_parents[n0] >= 0) {
theory_var n1 = m_parents[n0];
m_parents[n0] = n;
n0 = n1;
}
return n;
}
void solver::mg_merge(theory_var u, theory_var v) {
u = mg_find(u);
v = mg_find(v);
if (u != v) {
SASSERT(m_parents[u] < 0);
SASSERT(m_parents[v] < 0);
if (m_parents[u] > m_parents[v])
std::swap(u, v);
m_parents[u] += m_parents[v];
m_parents[v] = u;
if (!m_defaults[u])
m_defaults[u] = m_defaults[v];
CTRACE("array", m_defaults[v],
tout << ctx.bpp(m_defaults[v]->get_root()) << "\n";
tout << ctx.bpp(m_defaults[u]->get_root()) << "\n";
);
// NB. it may be the case that m_defaults[u] != m_defaults[v]
// when m and n are finite arrays.
}
}
}