mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 20:05: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
|
@ -13,6 +13,54 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2020-09-08
|
||||
|
||||
Notes:
|
||||
|
||||
A node n has attribtes:
|
||||
|
||||
parent_selects: { A[i] | A ~ n }
|
||||
parent_lambdas: { store(A,i,v) | A ~ n } u { map(f, .., A, ..) | A ~ n }
|
||||
lambdas: { const(v) | const(v) ~ n }
|
||||
u { map(f,..) | map(f,..) ~ n }
|
||||
u { store(A,i,v) | store(A,i,v) ~ n }
|
||||
u { as-array(f) | as-array(f) ~ n }
|
||||
|
||||
The attributes are used for propagation.
|
||||
When n1 is merged with n2, and n1 is the new root, the attributes from n2 are added to n1.
|
||||
The merge also looks for new redexes.
|
||||
|
||||
Let A[j] in parent_selects(n2) :
|
||||
|
||||
lambda in parent_lambdas(n1)
|
||||
-------------------------------
|
||||
lambda[j] = beta-reduce(lambda[j])
|
||||
|
||||
lambda in lambdas(n1)
|
||||
-------------------------------
|
||||
lambda[j] = beta-reduce(lambda[j])
|
||||
|
||||
Beta reduction rules are:
|
||||
beta-reduce(store(A,j,v)[i]) = if(i = j, v, A[j])
|
||||
beta-reduce(map(f,A,B)[i]) = f(A[i],B[i])
|
||||
beta-reduce(as-array(f)[i]) = f(i)
|
||||
beta-reduce(const(v)[i]) = v
|
||||
beta-reduce((lambda x M[x])[i]) = M[i]
|
||||
|
||||
For enforcing
|
||||
store(A,j,v)[i] = beta-reduce(store(A,j,v)[i])
|
||||
|
||||
only the following axiom is instantiated:
|
||||
- i = j or store(A,j,v)[i] = A[i]
|
||||
|
||||
The other required axiom, store(A,j,v)[j] = v
|
||||
is added eagerly whenever store(A,j,v) is created.
|
||||
|
||||
Current setup: to enforce extensionality on lambdas,
|
||||
also currently, as a base-line it is eager:
|
||||
|
||||
A ~ B, A = lambda x. M[x]
|
||||
-------------------------------
|
||||
A = B => forall i . M[i] = B[i]
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_ll_pp.h"
|
||||
|
@ -21,7 +69,7 @@ Author:
|
|||
|
||||
namespace array {
|
||||
|
||||
solver::solver(euf::solver& ctx, theory_id id):
|
||||
solver::solver(euf::solver& ctx, theory_id id) :
|
||||
th_euf_solver(ctx, id),
|
||||
a(m),
|
||||
m_sort2epsilon(m),
|
||||
|
@ -36,20 +84,16 @@ namespace array {
|
|||
}
|
||||
|
||||
sat::check_result solver::check() {
|
||||
flet<bool> _is_redundant(m_is_redundant, true);
|
||||
// flet<bool> _is_redundant(m_is_redundant, true);
|
||||
bool turn[2] = { false, false };
|
||||
turn[s().rand()(2)] = true;
|
||||
for (unsigned idx = 0; idx < 2; ++idx) {
|
||||
if (turn[idx]) {
|
||||
if (add_delayed_axioms())
|
||||
return sat::CR_CONTINUE;
|
||||
}
|
||||
else {
|
||||
if (add_interface_equalities())
|
||||
return sat::CR_CONTINUE;
|
||||
}
|
||||
if (turn[idx] && add_delayed_axioms())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
else if (!turn[idx] && add_interface_equalities())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
}
|
||||
return sat::CR_DONE;
|
||||
return sat::check_result::CR_DONE;
|
||||
}
|
||||
|
||||
void solver::push() {
|
||||
|
@ -57,38 +101,52 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::pop(unsigned n) {
|
||||
n = lazy_pop(n);
|
||||
n = lazy_pop(n);
|
||||
if (n == 0)
|
||||
return;
|
||||
m_var_data.resize(get_num_vars());
|
||||
}
|
||||
|
||||
std::ostream& solver::display(std::ostream& out) const {
|
||||
std::ostream& solver::display(std::ostream& out) const {
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
auto& d = get_var_data(i);
|
||||
out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
|
||||
display_info(out, "parent beta", d.m_parent_lambdas);
|
||||
display_info(out, "parent select", d.m_parent_selects);
|
||||
display_info(out, "beta ", d.m_lambdas);
|
||||
}
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
std::ostream& solver::display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const {
|
||||
if (v.empty())
|
||||
return out;
|
||||
out << id << ": ";
|
||||
for (euf::enode* p : v)
|
||||
out << mk_bounded_pp(p->get_expr(), m, 2) << " ";
|
||||
out << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; }
|
||||
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; }
|
||||
|
||||
void solver::collect_statistics(statistics& st) const {
|
||||
st.update("array store", m_stats.m_num_store_axiom);
|
||||
st.update("array sel/store", m_stats.m_num_select_store_axiom);
|
||||
st.update("array sel/const", m_stats.m_num_select_const_axiom);
|
||||
st.update("array sel/map", m_stats.m_num_select_map_axiom);
|
||||
st.update("array store", m_stats.m_num_store_axiom);
|
||||
st.update("array sel/store", m_stats.m_num_select_store_axiom);
|
||||
st.update("array sel/const", m_stats.m_num_select_const_axiom);
|
||||
st.update("array sel/map", m_stats.m_num_select_map_axiom);
|
||||
st.update("array sel/as array", m_stats.m_num_select_as_array_axiom);
|
||||
st.update("array def/map", m_stats.m_num_default_map_axiom);
|
||||
st.update("array def/const", m_stats.m_num_default_const_axiom);
|
||||
st.update("array def/store", m_stats.m_num_default_store_axiom);
|
||||
st.update("array ext ax", m_stats.m_num_extensionality_axiom);
|
||||
st.update("array cong ax", m_stats.m_num_congruence_axiom);
|
||||
st.update("array exp ax2", m_stats.m_num_select_store_axiom_delayed);
|
||||
st.update("array splits", m_stats.m_num_eq_splits);
|
||||
st.update("array sel/lambda", m_stats.m_num_select_lambda_axiom);
|
||||
st.update("array def/map", m_stats.m_num_default_map_axiom);
|
||||
st.update("array def/const", m_stats.m_num_default_const_axiom);
|
||||
st.update("array def/store", m_stats.m_num_default_store_axiom);
|
||||
st.update("array ext ax", m_stats.m_num_extensionality_axiom);
|
||||
st.update("array cong ax", m_stats.m_num_congruence_axiom);
|
||||
st.update("array exp ax2", m_stats.m_num_select_store_axiom_delayed);
|
||||
st.update("array splits", m_stats.m_num_eq_splits);
|
||||
}
|
||||
|
||||
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
|
||||
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
|
||||
auto* result = alloc(solver, ctx, get_id());
|
||||
ast_translation tr(m, ctx.get_manager());
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
|
@ -97,21 +155,21 @@ namespace array {
|
|||
euf::enode* n = ctx.get_enode(e2);
|
||||
result->mk_var(n);
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void solver::new_eq_eh(euf::th_eq const& eq) {
|
||||
m_find.merge(eq.m_v1, eq.m_v2);
|
||||
}
|
||||
|
||||
bool solver::unit_propagate() {
|
||||
bool solver::unit_propagate() {
|
||||
if (m_qhead == m_axiom_trail.size())
|
||||
return false;
|
||||
bool prop = false;
|
||||
ctx.push(value_trail<euf::solver, unsigned>(m_qhead));
|
||||
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)
|
||||
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)
|
||||
if (assert_axiom(m_qhead))
|
||||
prop = true;
|
||||
prop = true;
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
@ -121,76 +179,97 @@ namespace array {
|
|||
SASSERT(n1->get_root() == n2->get_root());
|
||||
SASSERT(n1->is_root() || n2->is_root());
|
||||
SASSERT(v1 == find(v1));
|
||||
|
||||
expr* e1 = n1->get_expr();
|
||||
expr* e2 = n2->get_expr();
|
||||
auto& d1 = get_var_data(v1);
|
||||
auto& d2 = get_var_data(v2);
|
||||
if (d2.m_prop_upward && !d1.m_prop_upward)
|
||||
if (d2.m_prop_upward && !d1.m_prop_upward)
|
||||
set_prop_upward(v1);
|
||||
if (a.is_array(e1))
|
||||
for (euf::enode* parent : d2.m_parents) {
|
||||
add_parent(v1, parent);
|
||||
if (a.is_store(parent->get_expr()))
|
||||
add_store(v1, parent);
|
||||
}
|
||||
for (euf::enode* lambda : d2.m_lambdas)
|
||||
add_lambda(v1, lambda);
|
||||
for (euf::enode* lambda : d2.m_parent_lambdas)
|
||||
add_parent_lambda(v1, lambda);
|
||||
for (euf::enode* select : d2.m_parent_selects)
|
||||
add_parent_select(v1, select);
|
||||
if (is_lambda(e1) || is_lambda(e2))
|
||||
push_axiom(congruence_axiom(n1, n2));
|
||||
}
|
||||
|
||||
void solver::unmerge_eh(theory_var v1, theory_var v2) {
|
||||
auto& p1 = get_var_data(v1).m_parents;
|
||||
auto& p2 = get_var_data(v2).m_parents;
|
||||
p1.shrink(p1.size() - p2.size());
|
||||
void solver::tracked_push(euf::enode_vector& v, euf::enode* n) {
|
||||
v.push_back(n);
|
||||
ctx.push(push_back_trail<euf::solver, euf::enode*, false>(v));
|
||||
}
|
||||
|
||||
void solver::add_store(theory_var v, euf::enode* store) {
|
||||
SASSERT(a.is_store(store->get_expr()));
|
||||
auto& d = get_var_data(v);
|
||||
unsigned lambda_equiv_class_size = get_lambda_equiv_size(d);
|
||||
if (get_config().m_array_always_prop_upward || lambda_equiv_class_size >= 1)
|
||||
set_prop_upward(d);
|
||||
for (euf::enode* n : d.m_parents)
|
||||
if (a.is_select(n->get_expr()))
|
||||
push_axiom(select_axiom(n, store));
|
||||
if (get_config().m_array_always_prop_upward || lambda_equiv_class_size >= 1)
|
||||
set_prop_upward(store);
|
||||
}
|
||||
void solver::add_parent_select(theory_var v_child, euf::enode* select) {
|
||||
SASSERT(a.is_select(select->get_expr()));
|
||||
SASSERT(m.get_sort(select->get_arg(0)->get_expr()) == m.get_sort(var2expr(v_child)));
|
||||
|
||||
void solver::add_parent(theory_var v_child, euf::enode* parent) {
|
||||
SASSERT(parent->is_root());
|
||||
get_var_data(v_child).m_parents.push_back(parent);
|
||||
v_child = find(v_child);
|
||||
tracked_push(get_var_data(v_child).m_parent_selects, select);
|
||||
euf::enode* child = var2enode(v_child);
|
||||
euf::enode* r = child->get_root();
|
||||
expr* p = parent->get_expr();
|
||||
expr* c = child->get_expr();
|
||||
if (a.is_select(p) && parent->get_arg(0)->get_root() == r) {
|
||||
if (a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c))
|
||||
push_axiom(select_axiom(parent, child));
|
||||
#if 0
|
||||
if (!get_config().m_array_delay_exp_axiom && d.m_prop_upward) {
|
||||
auto& d = get_var_data(v_child);
|
||||
for (euf::enode* p2 : d.m_parents)
|
||||
if (a.is_store(p2->get_expr()))
|
||||
push_axiom(select_axiom(parent, p2));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (a.mk_default(p)) {
|
||||
if (a.is_const(c) || a.is_store(c) || a.is_map(c) || a.is_as_array(c))
|
||||
push_axiom(default_axiom(child));
|
||||
if (can_beta_reduce(child))
|
||||
push_axiom(select_axiom(select, child));
|
||||
}
|
||||
|
||||
void solver::add_lambda(theory_var v, euf::enode* lambda) {
|
||||
SASSERT(can_beta_reduce(lambda));
|
||||
auto& d = get_var_data(find(v));
|
||||
if (should_set_prop_upward(d))
|
||||
set_prop_upward(d);
|
||||
tracked_push(d.m_lambdas, lambda);
|
||||
if (should_set_prop_upward(d)) {
|
||||
set_prop_upward(lambda);
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
}
|
||||
|
||||
void solver::add_parent_lambda(theory_var v_child, euf::enode* lambda) {
|
||||
SASSERT(can_beta_reduce(lambda));
|
||||
auto& d = get_var_data(find(v_child));
|
||||
tracked_push(d.m_parent_lambdas, lambda);
|
||||
if (should_set_prop_upward(d))
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
|
||||
void solver::add_parent_default(theory_var v, euf::enode* def) {
|
||||
SASSERT(a.is_default(def->get_expr()));
|
||||
auto& d = get_var_data(find(v));
|
||||
for (euf::enode* lambda : d.m_lambdas)
|
||||
push_axiom(default_axiom(lambda));
|
||||
if (should_prop_upward(d))
|
||||
propagate_parent_default(v);
|
||||
}
|
||||
|
||||
void solver::propagate_select_axioms(var_data const& d, euf::enode* lambda) {
|
||||
for (euf::enode* select : d.m_parent_selects)
|
||||
push_axiom(select_axiom(select, lambda));
|
||||
}
|
||||
|
||||
void solver::propagate_parent_default(theory_var v) {
|
||||
auto& d = get_var_data(find(v));
|
||||
for (euf::enode* lambda : d.m_parent_lambdas)
|
||||
push_axiom(default_axiom(lambda));
|
||||
}
|
||||
|
||||
void solver::propagate_parent_select_axioms(theory_var v) {
|
||||
v = find(v);
|
||||
expr* e = var2expr(v);
|
||||
if (!a.is_array(e))
|
||||
return;
|
||||
auto& d = get_var_data(v);
|
||||
for (euf::enode* lambda : d.m_parent_lambdas)
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
|
||||
void solver::set_prop_upward(theory_var v) {
|
||||
auto& d = get_var_data(find(v));
|
||||
if (!d.m_prop_upward) {
|
||||
ctx.push(reset_flag_trail<euf::solver>(d.m_prop_upward));
|
||||
d.m_prop_upward = true;
|
||||
if (!get_config().m_array_delay_exp_axiom)
|
||||
push_parent_select_store_axioms(v);
|
||||
set_prop_upward(d);
|
||||
}
|
||||
if (d.m_prop_upward)
|
||||
return;
|
||||
ctx.push(reset_flag_trail<euf::solver>(d.m_prop_upward));
|
||||
d.m_prop_upward = true;
|
||||
if (should_prop_upward(d))
|
||||
propagate_parent_select_axioms(v);
|
||||
set_prop_upward(d);
|
||||
}
|
||||
|
||||
void solver::set_prop_upward(euf::enode* n) {
|
||||
|
@ -199,22 +278,28 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::set_prop_upward(var_data& d) {
|
||||
for (auto* p : d.m_parents)
|
||||
for (auto* p : d.m_lambdas)
|
||||
set_prop_upward(p);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the size of the equivalence class for array terms
|
||||
\brief Return the size of the equivalence class for array terms
|
||||
that can be expressed as \lambda i : Index . [.. (select a i) ..]
|
||||
*/
|
||||
unsigned solver::get_lambda_equiv_size(var_data const& d) {
|
||||
unsigned sz = 0;
|
||||
for (auto* p : d.m_parents)
|
||||
if (a.is_store(p->get_expr()))
|
||||
++sz;
|
||||
return sz;
|
||||
unsigned solver::get_lambda_equiv_size(var_data const& d) const {
|
||||
return d.m_parent_selects.size() + 2 * d.m_lambdas.size();
|
||||
}
|
||||
|
||||
bool solver::should_set_prop_upward(var_data const& d) const {
|
||||
return get_config().m_array_always_prop_upward || get_lambda_equiv_size(d) >= 1;
|
||||
}
|
||||
|
||||
bool solver::should_prop_upward(var_data const& d) const {
|
||||
return !get_config().m_array_delay_exp_axiom && d.m_prop_upward;
|
||||
}
|
||||
|
||||
bool solver::can_beta_reduce(euf::enode* n) const {
|
||||
expr* c = n->get_expr();
|
||||
return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue