mirror of
https://github.com/Z3Prover/z3
synced 2025-04-30 12:25:51 +00:00
delay internalize (#4714)
* adding array solver Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use default in model construction Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * debug delay internalization Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bv Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * get rid of implied values and bounds Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * redo egraph * remove out Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove files Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
25724401cf
commit
367e5fdd52
60 changed files with 1343 additions and 924 deletions
|
@ -31,57 +31,27 @@ namespace array {
|
|||
ctx.push(push_back_vector<euf::solver, svector<axiom_record>>(m_axiom_trail));
|
||||
}
|
||||
|
||||
bool solver::assert_axiom(unsigned idx) {
|
||||
axiom_record const& r = m_axiom_trail[idx];
|
||||
bool solver::propagate_axiom(unsigned idx) {
|
||||
if (m_axioms.contains(idx))
|
||||
return false;
|
||||
m_axioms.insert(idx);
|
||||
ctx.push(insert_map<euf::solver, axiom_table_t, unsigned>(m_axioms, idx));
|
||||
expr* child = r.n->get_expr();
|
||||
app* select;
|
||||
return assert_axiom(idx);
|
||||
}
|
||||
|
||||
bool solver::assert_axiom(unsigned idx) {
|
||||
axiom_record& r = m_axiom_trail[idx];
|
||||
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));
|
||||
return assert_store_axiom(to_app(r.n->get_expr()));
|
||||
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))
|
||||
return assert_select_as_array_axiom(select, to_app(child));
|
||||
else if (a.is_store(child))
|
||||
return assert_select_store_axiom(select, to_app(child));
|
||||
else if (a.is_map(child))
|
||||
return assert_select_map_axiom(select, to_app(child));
|
||||
else if (is_lambda(child))
|
||||
return assert_select_lambda_axiom(select, child);
|
||||
else
|
||||
UNREACHABLE();
|
||||
break;
|
||||
return assert_select(idx, r);
|
||||
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
|
||||
return true;
|
||||
break;
|
||||
return assert_default(r);
|
||||
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());
|
||||
return assert_congruent_axiom(r.n->get_expr(), r.select->get_expr());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
@ -89,6 +59,68 @@ namespace array {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool solver::assert_default(axiom_record& r) {
|
||||
expr* child = r.n->get_expr();
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
if (!ctx.is_relevant(child))
|
||||
return false;
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
struct solver::set_delay_bit : trail<euf::solver> {
|
||||
solver& s;
|
||||
unsigned m_idx;
|
||||
set_delay_bit(solver& s, unsigned idx) : s(s), m_idx(idx) {}
|
||||
void undo(euf::solver& euf) override {
|
||||
s.m_axiom_trail[m_idx].m_delayed = false;
|
||||
}
|
||||
};
|
||||
|
||||
bool solver::assert_select(unsigned idx, axiom_record& r) {
|
||||
expr* child = r.n->get_expr();
|
||||
app* select = r.select->get_app();
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
//std::cout << mk_bounded_pp(child, m) << " " << ctx.is_relevant(child) << " " << mk_bounded_pp(select, m) << "\n";
|
||||
if (!ctx.is_relevant(child))
|
||||
return false;
|
||||
for (unsigned i = 1; i < select->get_num_args(); ++i)
|
||||
if (!ctx.is_relevant(select->get_arg(i)))
|
||||
return false;
|
||||
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())
|
||||
// std::cout << "delayed: " << r.m_delayed << "\n";
|
||||
if (get_config().m_array_delay_exp_axiom && r.select->get_arg(0)->get_root() != r.n->get_root() && !r.m_delayed) {
|
||||
IF_VERBOSE(11, verbose_stream() << "delay: " << mk_bounded_pp(child, m) << " " << mk_bounded_pp(select, m) << "\n");
|
||||
ctx.push(set_delay_bit(*this, idx));
|
||||
r.m_delayed = true;
|
||||
return false;
|
||||
}
|
||||
if (r.select->get_arg(0)->get_root() != r.n->get_root() && r.m_delayed)
|
||||
return false;
|
||||
if (a.is_const(child))
|
||||
return assert_select_const_axiom(select, to_app(child));
|
||||
else if (a.is_as_array(child))
|
||||
return assert_select_as_array_axiom(select, to_app(child));
|
||||
else if (a.is_store(child))
|
||||
return assert_select_store_axiom(select, to_app(child));
|
||||
else if (a.is_map(child))
|
||||
return assert_select_map_axiom(select, to_app(child));
|
||||
else if (is_lambda(child))
|
||||
return assert_select_lambda_axiom(select, child);
|
||||
else
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert
|
||||
* select(n, i) = v
|
||||
|
@ -96,6 +128,7 @@ namespace array {
|
|||
* n := store(a, i, v)
|
||||
*/
|
||||
bool solver::assert_store_axiom(app* e) {
|
||||
TRACE("array", tout << "store-axiom: " << mk_bounded_pp(e, m) << "\n";);
|
||||
++m_stats.m_num_store_axiom;
|
||||
SASSERT(a.is_store(e));
|
||||
unsigned num_args = e->get_num_args();
|
||||
|
@ -182,6 +215,7 @@ namespace array {
|
|||
* e1 = e2 or select(e1, diff(e1,e2)) != select(e2, diff(e1, e2))
|
||||
*/
|
||||
bool solver::assert_extensionality(expr* e1, expr* e2) {
|
||||
TRACE("array", tout << "extensionality-axiom: " << mk_bounded_pp(e1, m) << " == " << mk_bounded_pp(e2, m) << "\n";);
|
||||
++m_stats.m_num_extensionality_axiom;
|
||||
func_decl_ref_vector* funcs = nullptr;
|
||||
VERIFY(m_sort2diff.find(m.get_sort(e1), funcs));
|
||||
|
@ -289,7 +323,6 @@ namespace array {
|
|||
return ctx.propagate(expr2enode(val), e_internalize(def), array_axiom());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* let n := store(a, i, v)
|
||||
* Assert:
|
||||
|
@ -368,6 +401,7 @@ namespace array {
|
|||
\brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars)
|
||||
*/
|
||||
bool solver::assert_congruent_axiom(expr* e1, expr* e2) {
|
||||
TRACE("array", tout << "congruence-axiom: " << mk_bounded_pp(e1, m) << " " << mk_bounded_pp(e2, m) << "\n";);
|
||||
++m_stats.m_num_congruence_axiom;
|
||||
sort* srt = m.get_sort(e1);
|
||||
unsigned dimension = get_array_arity(srt);
|
||||
|
@ -446,10 +480,24 @@ namespace array {
|
|||
for (unsigned v = 0; v < num_vars; v++) {
|
||||
propagate_parent_select_axioms(v);
|
||||
auto& d = get_var_data(v);
|
||||
if (d.m_prop_upward)
|
||||
if (!d.m_prop_upward)
|
||||
continue;
|
||||
euf::enode* n = var2enode(v);
|
||||
bool has_default = false;
|
||||
for (euf::enode* p : euf::enode_parents(n))
|
||||
has_default |= a.is_default(p->get_expr());
|
||||
if (has_default)
|
||||
propagate_parent_default(v);
|
||||
}
|
||||
return unit_propagate();
|
||||
bool change = false;
|
||||
unsigned sz = m_axiom_trail.size();
|
||||
m_delay_qhead = 0;
|
||||
for (; m_delay_qhead < sz; ++m_delay_qhead)
|
||||
if (m_axiom_trail[m_delay_qhead].m_delayed && assert_axiom(m_delay_qhead))
|
||||
change = true;
|
||||
if (unit_propagate())
|
||||
change = true;
|
||||
return change;
|
||||
}
|
||||
|
||||
bool solver::add_interface_equalities() {
|
||||
|
@ -466,9 +514,11 @@ namespace array {
|
|||
continue;
|
||||
if (have_different_model_values(v1, v2))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(e1, e2), m);
|
||||
if (ctx.get_egraph().are_diseq(var2enode(v1), var2enode(v2)))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(e1, e2), m);
|
||||
sat::literal lit = b_internalize(eq);
|
||||
if (s().value(lit) == l_undef)
|
||||
if (s().value(lit) == l_undef)
|
||||
prop = true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue