mirror of
https://github.com/Z3Prover/z3
synced 2025-04-30 04:15:51 +00:00
arrays (#4684)
* arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fill Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * update drat and fix euf bugs Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * const qualifiers Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg ba Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * build warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
d56dd1db7b
commit
796e2fd9eb
79 changed files with 2571 additions and 1850 deletions
|
@ -27,6 +27,8 @@ namespace array {
|
|||
m_axiom_trail.push_back(r);
|
||||
if (m_axioms.contains(idx))
|
||||
m_axiom_trail.pop_back();
|
||||
else
|
||||
ctx.push(push_back_vector<euf::solver, svector<axiom_record>>(m_axiom_trail));
|
||||
}
|
||||
|
||||
bool solver::assert_axiom(unsigned idx) {
|
||||
|
@ -39,10 +41,16 @@ namespace array {
|
|||
app* select;
|
||||
switch (r.m_kind) {
|
||||
case axiom_record::kind_t::is_store:
|
||||
TRACE("array", tout << "store-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
return assert_store_axiom(to_app(child));
|
||||
case axiom_record::kind_t::is_select:
|
||||
select = r.select->get_app();
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
TRACE("array", tout << "select-axiom: " << mk_bounded_pp(select, m, 2) << " " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
if (r.select->get_arg(0)->get_root() != r.n->get_root()) {
|
||||
IF_VERBOSE(0, verbose_stream() << "could delay " << mk_pp(select, m) << " " << mk_pp(child, m) << "\n");
|
||||
}
|
||||
if (a.is_const(child))
|
||||
return assert_select_const_axiom(select, to_app(child));
|
||||
else if (a.is_as_array(child))
|
||||
|
@ -57,20 +65,22 @@ namespace array {
|
|||
UNREACHABLE();
|
||||
break;
|
||||
case axiom_record::kind_t::is_default:
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
TRACE("array", tout << "default-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
if (a.is_const(child))
|
||||
return assert_default_const_axiom(to_app(child));
|
||||
else if (a.is_store(child))
|
||||
return assert_default_store_axiom(to_app(child));
|
||||
else if (a.is_map(child))
|
||||
return assert_default_map_axiom(to_app(child));
|
||||
else if (a.is_as_array(child))
|
||||
return assert_default_as_array_axiom(to_app(child));
|
||||
else
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
break;
|
||||
case axiom_record::kind_t::is_extensionality:
|
||||
TRACE("array", tout << "extensionality-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
return assert_extensionality(r.n->get_arg(0)->get_expr(), r.n->get_arg(1)->get_expr());
|
||||
case axiom_record::kind_t::is_congruence:
|
||||
TRACE("array", tout << "congruence-axiom: " << mk_bounded_pp(child, m, 2) << " " << mk_bounded_pp(r.select->get_expr(), m, 2) << "\n";);
|
||||
return assert_congruent_axiom(child, r.select->get_expr());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -86,7 +96,7 @@ namespace array {
|
|||
* n := store(a, i, v)
|
||||
*/
|
||||
bool solver::assert_store_axiom(app* e) {
|
||||
m_stats.m_num_store_axiom++;
|
||||
++m_stats.m_num_store_axiom;
|
||||
SASSERT(a.is_store(e));
|
||||
unsigned num_args = e->get_num_args();
|
||||
ptr_vector<expr> sel_args(num_args - 1, e->get_args());
|
||||
|
@ -104,7 +114,7 @@ namespace array {
|
|||
* where i = (i_1, ..., i_n), j = (j_1, .., j_n), k in 1..n
|
||||
*/
|
||||
bool solver::assert_select_store_axiom(app* select, app* store) {
|
||||
m_stats.m_num_select_store_axiom++;
|
||||
++m_stats.m_num_select_store_axiom;
|
||||
SASSERT(a.is_store(store));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(store->get_num_args() == 1 + select->get_num_args());
|
||||
|
@ -126,7 +136,10 @@ namespace array {
|
|||
if (s1->get_root() == s2->get_root())
|
||||
return false;
|
||||
sat::literal sel_eq = b_internalize(sel_eq_e);
|
||||
if (s().value(sel_eq) == l_true)
|
||||
return false;
|
||||
|
||||
bool new_prop = false;
|
||||
for (unsigned i = 1; i < num_args; i++) {
|
||||
expr* idx1 = store->get_arg(i);
|
||||
expr* idx2 = select->get_arg(i);
|
||||
|
@ -135,13 +148,15 @@ namespace array {
|
|||
if (r1 == r2)
|
||||
continue;
|
||||
if (m.are_distinct(r1->get_expr(), r2->get_expr())) {
|
||||
new_prop = true;
|
||||
add_clause(sel_eq);
|
||||
break;
|
||||
}
|
||||
sat::literal idx_eq = b_internalize(m.mk_eq(idx1, idx2));
|
||||
add_clause(idx_eq, sel_eq);
|
||||
if (add_clause(idx_eq, sel_eq))
|
||||
new_prop = true;
|
||||
}
|
||||
return true;
|
||||
return new_prop;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +164,7 @@ namespace array {
|
|||
* select(const(v), i) = v
|
||||
*/
|
||||
bool solver::assert_select_const_axiom(app* select, app* cnst) {
|
||||
m_stats.m_num_select_const_axiom++;
|
||||
++m_stats.m_num_select_const_axiom;
|
||||
expr* val = nullptr;
|
||||
VERIFY(a.is_const(cnst, val));
|
||||
SASSERT(a.is_select(select));
|
||||
|
@ -167,7 +182,7 @@ namespace array {
|
|||
* e1 = e2 or select(e1, diff(e1,e2)) != select(e2, diff(e1, e2))
|
||||
*/
|
||||
bool solver::assert_extensionality(expr* e1, expr* e2) {
|
||||
m_stats.m_num_extensionality_axiom++;
|
||||
++m_stats.m_num_extensionality_axiom;
|
||||
func_decl_ref_vector* funcs = nullptr;
|
||||
VERIFY(m_sort2diff.find(m.get_sort(e1), funcs));
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
|
@ -184,10 +199,7 @@ namespace array {
|
|||
expr_ref sel1_eq_sel2(m.mk_eq(sel1, sel2), m);
|
||||
literal lit1 = b_internalize(n1_eq_n2);
|
||||
literal lit2 = b_internalize(sel1_eq_sel2);
|
||||
if (s().value(lit1) == l_true || s().value(lit2) == l_false)
|
||||
return false;
|
||||
add_clause(lit1, ~lit2);
|
||||
return true;
|
||||
return add_clause(lit1, ~lit2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,17 +207,12 @@ namespace array {
|
|||
* select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
|
||||
*/
|
||||
bool solver::assert_select_map_axiom(app* select, app* map) {
|
||||
m_stats.m_num_select_map_axiom++;
|
||||
++m_stats.m_num_select_map_axiom;
|
||||
SASSERT(a.is_map(map));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(map->get_num_args() > 0);
|
||||
func_decl* f = a.get_map_func_decl(map);
|
||||
|
||||
TRACE("array",
|
||||
tout << mk_bounded_pp(map, m) << "\n";
|
||||
tout << mk_bounded_pp(select, 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);
|
||||
|
@ -238,7 +245,7 @@ namespace array {
|
|||
* select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n)
|
||||
*/
|
||||
bool solver::assert_select_as_array_axiom(app* select, app* arr) {
|
||||
m_stats.m_num_select_as_array_axiom++;
|
||||
++m_stats.m_num_select_as_array_axiom;
|
||||
SASSERT(a.is_as_array(arr));
|
||||
SASSERT(a.is_select(select));
|
||||
unsigned num_args = select->get_num_args();
|
||||
|
@ -257,39 +264,31 @@ namespace array {
|
|||
* default(map[f](a,..,d)) = f(default(a),..,default(d))
|
||||
*/
|
||||
bool solver::assert_default_map_axiom(app* map) {
|
||||
m_stats.m_num_default_map_axiom++;
|
||||
++m_stats.m_num_default_map_axiom;
|
||||
SASSERT(a.is_map(map));
|
||||
func_decl* f = a.get_map_func_decl(map);
|
||||
SASSERT(map->get_num_args() == f->get_arity());
|
||||
ptr_buffer<expr> args2;
|
||||
expr_ref_vector args2(m);
|
||||
for (expr* arg : *map)
|
||||
args2.push_back(a.mk_default(arg));
|
||||
|
||||
expr_ref def1(a.mk_default(map), m);
|
||||
expr_ref def2(m.mk_app(f, args2), m);
|
||||
rewrite(def2);
|
||||
return ctx.propagate(e_internalize(def1), e_internalize(def2), array_axiom());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assert:
|
||||
* default(const(e)) = e
|
||||
*/
|
||||
bool solver::assert_default_const_axiom(app* cnst) {
|
||||
m_stats.m_num_default_const_axiom++;
|
||||
++m_stats.m_num_default_const_axiom;
|
||||
expr* val = nullptr;
|
||||
VERIFY(a.is_const(cnst, val));
|
||||
TRACE("array", tout << mk_bounded_pp(cnst, m) << "\n";);
|
||||
expr_ref def(a.mk_default(cnst), m);
|
||||
return ctx.propagate(expr2enode(val), e_internalize(def), array_axiom());
|
||||
}
|
||||
|
||||
bool solver::assert_default_as_array_axiom(app* as_array) {
|
||||
// no-op
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* let n := store(a, i, v)
|
||||
|
@ -303,19 +302,15 @@ namespace array {
|
|||
* default(n) = default(a)
|
||||
*/
|
||||
bool solver::assert_default_store_axiom(app* store) {
|
||||
m_stats.m_num_default_store_axiom++;
|
||||
++m_stats.m_num_default_store_axiom;
|
||||
SASSERT(a.is_store(store));
|
||||
SASSERT(store->get_num_args() >= 3);
|
||||
expr_ref def1(m), def2(m);
|
||||
bool prop = false;
|
||||
|
||||
unsigned num_args = store->get_num_args();
|
||||
|
||||
def1 = a.mk_default(store);
|
||||
def2 = a.mk_default(store->get_arg(0));
|
||||
|
||||
bool is_new = false;
|
||||
|
||||
if (has_unitary_domain(store)) {
|
||||
def2 = store->get_arg(num_args - 1);
|
||||
}
|
||||
|
@ -357,6 +352,7 @@ namespace array {
|
|||
* Assert select(lambda xs . M, N1,.., Nk) -> M[N1/x1, ..., Nk/xk]
|
||||
*/
|
||||
bool solver::assert_select_lambda_axiom(app* select, expr* lambda) {
|
||||
++m_stats.m_num_select_lambda_axiom;
|
||||
SASSERT(is_lambda(lambda));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(m.get_sort(lambda) == m.get_sort(select->get_arg(0)));
|
||||
|
@ -373,8 +369,8 @@ namespace array {
|
|||
*/
|
||||
bool solver::assert_congruent_axiom(expr* e1, expr* e2) {
|
||||
++m_stats.m_num_congruence_axiom;
|
||||
sort* s = m.get_sort(e1);
|
||||
unsigned dimension = get_array_arity(s);
|
||||
sort* srt = m.get_sort(e1);
|
||||
unsigned dimension = get_array_arity(srt);
|
||||
expr_ref n1_eq_n2(m.mk_eq(e1, e2), m);
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
args1.push_back(e1);
|
||||
|
@ -382,10 +378,10 @@ namespace array {
|
|||
svector<symbol> names;
|
||||
sort_ref_vector sorts(m);
|
||||
for (unsigned i = 0; i < dimension; i++) {
|
||||
sort * srt = get_array_domain(s, i);
|
||||
sorts.push_back(srt);
|
||||
sort * asrt = get_array_domain(srt, i);
|
||||
sorts.push_back(asrt);
|
||||
names.push_back(symbol(i));
|
||||
expr * k = m.mk_var(dimension - i - 1, srt);
|
||||
expr * k = m.mk_var(dimension - i - 1, asrt);
|
||||
args1.push_back(k);
|
||||
args2.push_back(k);
|
||||
}
|
||||
|
@ -395,8 +391,8 @@ namespace array {
|
|||
expr_ref q(m.mk_forall(dimension, sorts.c_ptr(), names.c_ptr(), eq), m);
|
||||
rewrite(q);
|
||||
sat::literal fa_eq = b_internalize(q);
|
||||
add_clause(~b_internalize(n1_eq_n2), fa_eq);
|
||||
return true;
|
||||
sat::literal neq = b_internalize(n1_eq_n2);
|
||||
return add_clause(~neq, fa_eq);
|
||||
}
|
||||
|
||||
bool solver::has_unitary_domain(app* array_term) {
|
||||
|
@ -411,7 +407,7 @@ namespace array {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool solver::has_large_domain(app* array_term) {
|
||||
bool solver::has_large_domain(expr* array_term) {
|
||||
SASSERT(a.is_array(array_term));
|
||||
sort* s = m.get_sort(array_term);
|
||||
unsigned dim = get_array_arity(s);
|
||||
|
@ -429,7 +425,6 @@ namespace array {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::pair<app*, func_decl*> solver::mk_epsilon(sort* s) {
|
||||
app* eps = nullptr;
|
||||
func_decl* diag = nullptr;
|
||||
|
@ -444,24 +439,16 @@ namespace array {
|
|||
return std::make_pair(eps, diag);
|
||||
}
|
||||
|
||||
void solver::push_parent_select_store_axioms(theory_var v) {
|
||||
expr* e = var2expr(v);
|
||||
if (!a.is_array(e))
|
||||
return;
|
||||
auto& d = get_var_data(v);
|
||||
for (euf::enode* store : d.m_parents)
|
||||
if (a.is_store(store->get_expr()))
|
||||
for (euf::enode* sel : d.m_parents)
|
||||
if (a.is_select(sel->get_expr()))
|
||||
push_axiom(select_axiom(sel, store));
|
||||
}
|
||||
|
||||
bool solver::add_delayed_axioms() {
|
||||
if (!get_config().m_array_delay_exp_axiom)
|
||||
return false;
|
||||
unsigned num_vars = get_num_vars();
|
||||
for (unsigned v = 0; v < num_vars; v++)
|
||||
push_parent_select_store_axioms(v);
|
||||
for (unsigned v = 0; v < num_vars; v++) {
|
||||
propagate_parent_select_axioms(v);
|
||||
auto& d = get_var_data(v);
|
||||
if (d.m_prop_upward)
|
||||
propagate_parent_default(v);
|
||||
}
|
||||
return unit_propagate();
|
||||
}
|
||||
|
||||
|
@ -471,13 +458,15 @@ namespace array {
|
|||
bool prop = false;
|
||||
for (unsigned i = roots.size(); i-- > 0; ) {
|
||||
theory_var v1 = roots[i];
|
||||
euf::enode* n1 = var2enode(v1);
|
||||
expr* e1 = var2expr(v1);
|
||||
for (unsigned j = i; j-- > 0; ) {
|
||||
theory_var v2 = roots[j];
|
||||
euf::enode* n2 = var2enode(v2);
|
||||
if (m.get_sort(n1->get_expr()) != m.get_sort(n2->get_expr()))
|
||||
expr* e2 = var2expr(v2);
|
||||
if (m.get_sort(e1) != m.get_sort(e2))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(n1->get_expr(), n2->get_expr()), m);
|
||||
if (have_different_model_values(v1, v2))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(e1, e2), m);
|
||||
sat::literal lit = b_internalize(eq);
|
||||
if (s().value(lit) == l_undef)
|
||||
prop = true;
|
||||
|
@ -498,15 +487,10 @@ namespace array {
|
|||
if (r->is_marked1()) {
|
||||
continue;
|
||||
}
|
||||
// arrays used as indices in other arrays have to be treated as shared.
|
||||
// issue #3532, #3529
|
||||
//
|
||||
if (ctx.is_shared(r) || is_select_arg(r)) {
|
||||
TRACE("array", tout << "new shared var: #" << r->get_expr_id() << "\n";);
|
||||
theory_var r_th_var = r->get_th_var(get_id());
|
||||
SASSERT(r_th_var != euf::null_theory_var);
|
||||
roots.push_back(r_th_var);
|
||||
}
|
||||
// arrays used as indices in other arrays have to be treated as shared issue #3532, #3529
|
||||
if (ctx.is_shared(r) || is_select_arg(r))
|
||||
roots.push_back(r->get_th_var(get_id()));
|
||||
|
||||
r->mark1();
|
||||
to_unmark.push_back(r);
|
||||
}
|
||||
|
@ -516,6 +500,7 @@ namespace array {
|
|||
}
|
||||
|
||||
bool solver::is_select_arg(euf::enode* r) {
|
||||
SASSERT(r->is_root());
|
||||
for (euf::enode* n : euf::enode_parents(r))
|
||||
if (a.is_select(n->get_expr()))
|
||||
for (unsigned i = 1; i < n->num_args(); ++i)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue