mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +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
|
@ -940,11 +940,6 @@ namespace sat {
|
|||
m_phase[v] = !l.sign();
|
||||
m_assigned_since_gc[v] = true;
|
||||
m_trail.push_back(l);
|
||||
|
||||
if (m_ext && m_external[v] && (!is_probing() || at_base_lvl()))
|
||||
m_ext->asserted(l);
|
||||
// else
|
||||
// std::cout << "assert " << l << "\n";
|
||||
|
||||
switch (m_config.m_branching_heuristic) {
|
||||
case BH_VSIDS:
|
||||
|
@ -1042,7 +1037,7 @@ namespace sat {
|
|||
lbool val1, val2;
|
||||
bool keep;
|
||||
unsigned curr_level = lvl(l);
|
||||
TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n"; );
|
||||
TRACE("sat_propagate", tout << "propagating: " << l << "@" << curr_level << " " << m_justification[l.var()] << "\n"; );
|
||||
|
||||
literal not_l = ~l;
|
||||
SASSERT(value(l) == l_true);
|
||||
|
@ -1204,6 +1199,9 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
wlist.set_end(it2);
|
||||
if (m_ext && m_external[l.var()] && (!is_probing() || at_base_lvl()))
|
||||
m_ext->asserted(l);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3575,6 +3573,7 @@ namespace sat {
|
|||
m_trail.shrink(old_sz);
|
||||
m_qhead = m_trail.size();
|
||||
if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n");
|
||||
CTRACE("sat", !m_replay_assign.empty(), tout << "replay-assign: " << m_replay_assign << "\n";);
|
||||
for (unsigned i = m_replay_assign.size(); i-- > 0; ) {
|
||||
literal lit = m_replay_assign[i];
|
||||
m_trail.push_back(lit);
|
||||
|
|
|
@ -371,9 +371,13 @@ namespace sat {
|
|||
switch (value(l)) {
|
||||
case l_false: set_conflict(j, ~l); break;
|
||||
case l_undef: assign_core(l, j); break;
|
||||
case l_true: return;
|
||||
case l_true: update_assign(l, j); break;
|
||||
}
|
||||
}
|
||||
void update_assign(literal l, justification j) {
|
||||
if (lvl(l) > j.level())
|
||||
m_justification[l.var()] = j;
|
||||
}
|
||||
void assign_unit(literal l) { assign(l, justification(0)); }
|
||||
void assign_scoped(literal l) { assign(l, justification(scope_lvl())); }
|
||||
void assign_core(literal l, justification jst);
|
||||
|
|
|
@ -123,7 +123,7 @@ public:
|
|||
ast_translation tr(m, dst_m);
|
||||
m_solver.pop_to_base_level();
|
||||
inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental());
|
||||
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
auto* ext = get_euf();
|
||||
if (ext) {
|
||||
auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep2asm, is_incremental());
|
||||
euf::solver::scoped_set_translate st(*ext, dst_m, si);
|
||||
|
@ -258,6 +258,8 @@ public:
|
|||
|
||||
void push_internal() {
|
||||
m_solver.user_push();
|
||||
if (get_euf())
|
||||
get_euf()->user_push();
|
||||
++m_num_scopes;
|
||||
m_mcs.push_back(m_mcs.back());
|
||||
m_fmls_lim.push_back(m_fmls.size());
|
||||
|
@ -280,6 +282,8 @@ public:
|
|||
m_num_scopes -= n;
|
||||
// ? m_internalized_converted = false;
|
||||
m_has_uninterpreted.pop(n);
|
||||
if (get_euf())
|
||||
get_euf()->user_pop(n);
|
||||
while (n > 0) {
|
||||
m_mcs.pop_back();
|
||||
m_fmls_head = m_fmls_head_lim.back();
|
||||
|
@ -337,6 +341,11 @@ public:
|
|||
m_params.set_sym("pb.solver", p1.pb_solver());
|
||||
m_solver.updt_params(m_params);
|
||||
m_solver.set_incremental(is_incremental() && !override_incremental());
|
||||
if (p1.euf() && !get_euf()) {
|
||||
ensure_euf();
|
||||
for (unsigned i = 0; i < m_num_scopes; ++i)
|
||||
get_euf()->user_push();
|
||||
}
|
||||
|
||||
}
|
||||
void collect_statistics(statistics & st) const override {
|
||||
|
@ -374,19 +383,6 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO
|
||||
expr_ref get_implied_value(expr* e) override {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
|
||||
expr_ref get_implied_lower_bound(expr* e) override {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
|
||||
expr_ref get_implied_upper_bound(expr* e) override {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
|
||||
expr_ref_vector last_cube(bool is_sat) {
|
||||
expr_ref_vector result(m);
|
||||
result.push_back(is_sat ? m.mk_true() : m.mk_false());
|
||||
|
@ -624,6 +620,10 @@ public:
|
|||
m_preprocess->reset();
|
||||
}
|
||||
|
||||
euf::solver* get_euf() {
|
||||
return dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
}
|
||||
|
||||
euf::solver* ensure_euf() {
|
||||
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
return ext;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,8 @@ namespace array {
|
|||
|
||||
void solver::internalize_lambda(euf::enode* n) {
|
||||
set_prop_upward(n);
|
||||
push_axiom(default_axiom(n));
|
||||
if (!a.is_store(n->get_expr()))
|
||||
push_axiom(default_axiom(n));
|
||||
add_lambda(n->get_th_var(get_id()), n);
|
||||
}
|
||||
|
||||
|
@ -150,5 +151,46 @@ namespace array {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if v is shared between two different "instances" of the array theory.
|
||||
It is shared if it is used in more than one role. The possible roles are: array, index, and value.
|
||||
Example:
|
||||
(store v i j) <--- v is used as an array
|
||||
(select A v) <--- v is used as an index
|
||||
(store A i v) <--- v is used as an value
|
||||
*/
|
||||
bool solver::is_shared(theory_var v) const {
|
||||
euf::enode* n = var2enode(v);
|
||||
euf::enode* r = n->get_root();
|
||||
bool is_array = false;
|
||||
bool is_index = false;
|
||||
bool is_value = false;
|
||||
auto set_array = [&](euf::enode* arg) { if (arg->get_root() == r) is_array = true; };
|
||||
auto set_index = [&](euf::enode* arg) { if (arg->get_root() == r) is_index = true; };
|
||||
auto set_value = [&](euf::enode* arg) { if (arg->get_root() == r) is_value = true; };
|
||||
|
||||
for (euf::enode* parent : euf::enode_parents(r)) {
|
||||
app* p = parent->get_app();
|
||||
unsigned num_args = parent->num_args();
|
||||
if (a.is_store(p)) {
|
||||
set_array(parent->get_arg(0));
|
||||
for (unsigned i = 1; i < num_args - 1; i++)
|
||||
set_index(parent->get_arg(i));
|
||||
set_value(parent->get_arg(num_args - 1));
|
||||
}
|
||||
else if (a.is_select(p)) {
|
||||
set_array(parent->get_arg(0));
|
||||
for (unsigned i = 1; i < num_args - 1; i++)
|
||||
set_index(parent->get_arg(i));
|
||||
}
|
||||
else if (a.is_const(p)) {
|
||||
set_value(parent->get_arg(0));
|
||||
}
|
||||
if (is_array + is_index + is_value > 1)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ Author:
|
|||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "model/array_factory.h"
|
||||
#include "sat/smt/array_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
|
@ -29,6 +28,10 @@ namespace array {
|
|||
return;
|
||||
}
|
||||
for (euf::enode* p : euf::enode_parents(n)) {
|
||||
if (a.is_default(p->get_expr())) {
|
||||
dep.add(n, p);
|
||||
continue;
|
||||
}
|
||||
if (!a.is_select(p->get_expr()))
|
||||
continue;
|
||||
dep.add(n, p);
|
||||
|
@ -37,9 +40,7 @@ namespace array {
|
|||
}
|
||||
for (euf::enode* k : euf::enode_class(n))
|
||||
if (a.is_const(k->get_expr()))
|
||||
dep.add(n, k);
|
||||
else if (a.is_default(k->get_expr()))
|
||||
dep.add(n, k);
|
||||
dep.add(n, k->get_arg(0));
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,23 +53,58 @@ namespace array {
|
|||
func_interp * fi = alloc(func_interp, m, arity);
|
||||
mdl.register_decl(f, fi);
|
||||
|
||||
for (euf::enode* p : euf::enode_parents(n)) {
|
||||
if (!a.is_select(p->get_expr()) || p->get_arg(0)->get_root() != n->get_root())
|
||||
continue;
|
||||
args.reset();
|
||||
for (unsigned i = 1; i < p->num_args(); ++i)
|
||||
args.push_back(values.get(p->get_arg(i)->get_root_id()));
|
||||
expr* value = values.get(p->get_root_id());
|
||||
fi->insert_entry(args.c_ptr(), value);
|
||||
}
|
||||
if (!fi->get_else())
|
||||
for (euf::enode* k : euf::enode_class(n))
|
||||
if (a.is_const(k->get_expr()))
|
||||
fi->set_else(k->get_arg(0)->get_root()->get_expr());
|
||||
if (!fi->get_else())
|
||||
for (euf::enode* k : euf::enode_parents(n))
|
||||
if (a.is_default(k->get_expr()))
|
||||
fi->set_else(k->get_root()->get_expr());
|
||||
for (euf::enode* k : euf::enode_class(n))
|
||||
if (a.is_const(k->get_expr()))
|
||||
fi->set_else(values.get(k->get_arg(0)->get_root_id()));
|
||||
|
||||
if (!fi->get_else())
|
||||
for (euf::enode* p : euf::enode_parents(n))
|
||||
if (a.is_default(p->get_expr()))
|
||||
fi->set_else(values.get(p->get_root_id()));
|
||||
|
||||
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)) {
|
||||
if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n->get_root()) {
|
||||
expr* v = values.get(p->get_root_id());
|
||||
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);
|
||||
}
|
||||
|
||||
for (euf::enode* p : euf::enode_parents(n)) {
|
||||
if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n->get_root()) {
|
||||
// std::cout << "parent " << mk_bounded_pp(p->get_expr(), m) << "\n";
|
||||
expr* value = values.get(p->get_root_id());
|
||||
if (!value || value == fi->get_else())
|
||||
continue;
|
||||
args.reset();
|
||||
bool relevant = true;
|
||||
for (unsigned i = 1; relevant && i < p->num_args(); ++i)
|
||||
relevant = ctx.is_relevant(p->get_arg(i)->get_root());
|
||||
if (!relevant)
|
||||
continue;
|
||||
for (unsigned i = 1; i < p->num_args(); ++i)
|
||||
args.push_back(values.get(p->get_arg(i)->get_root_id()));
|
||||
// for (expr* arg : args)
|
||||
// std::cout << "arg " << mk_bounded_pp(arg, m) << "\n";
|
||||
fi->insert_entry(args.c_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
parameter p(f);
|
||||
values.set(n->get_root_id(), m.mk_app(get_id(), OP_AS_ARRAY, 1, &p));
|
||||
|
|
|
@ -107,8 +107,8 @@ namespace array {
|
|||
auto& d = get_var_data(i);
|
||||
out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
|
||||
display_info(out, "parent lambdas", d.m_parent_lambdas);
|
||||
display_info(out, "parent select", d.m_parent_selects);
|
||||
display_info(out, "b ", d.m_lambdas);
|
||||
display_info(out, "parent select", d.m_parent_selects);
|
||||
display_info(out, "lambdas", d.m_lambdas);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ namespace array {
|
|||
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::clone(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) {
|
||||
|
@ -165,7 +165,7 @@ namespace array {
|
|||
bool prop = false;
|
||||
ctx.push(value_trail<euf::solver, unsigned>(m_qhead));
|
||||
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)
|
||||
if (assert_axiom(m_qhead))
|
||||
if (propagate_axiom(m_qhead))
|
||||
prop = true;
|
||||
return prop;
|
||||
}
|
||||
|
@ -174,7 +174,6 @@ namespace array {
|
|||
euf::enode* n1 = var2enode(v1);
|
||||
euf::enode* n2 = var2enode(v2);
|
||||
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();
|
||||
|
@ -204,7 +203,7 @@ namespace array {
|
|||
v_child = find(v_child);
|
||||
tracked_push(get_var_data(v_child).m_parent_selects, select);
|
||||
euf::enode* child = var2enode(v_child);
|
||||
if (can_beta_reduce(child))
|
||||
if (can_beta_reduce(child) && child != select->get_arg(0))
|
||||
push_axiom(select_axiom(select, child));
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ namespace array {
|
|||
kind_t m_kind;
|
||||
euf::enode* n;
|
||||
euf::enode* select;
|
||||
bool m_delayed { false };
|
||||
axiom_record(kind_t k, euf::enode* n, euf::enode* select = nullptr) : m_kind(k), n(n), select(select) {}
|
||||
|
||||
struct hash {
|
||||
|
@ -119,8 +120,13 @@ namespace array {
|
|||
axiom_table_t m_axioms;
|
||||
svector<axiom_record> m_axiom_trail;
|
||||
unsigned m_qhead { 0 };
|
||||
unsigned m_delay_qhead { 0 };
|
||||
struct set_delay_bit;
|
||||
void push_axiom(axiom_record const& r);
|
||||
bool propagate_axiom(unsigned idx);
|
||||
bool assert_axiom(unsigned idx);
|
||||
bool assert_select(unsigned idx, axiom_record & r);
|
||||
bool assert_default(axiom_record & r);
|
||||
|
||||
axiom_record select_axiom(euf::enode* s, euf::enode* n) { return axiom_record(axiom_record::kind_t::is_select, n, s); }
|
||||
axiom_record default_axiom(euf::enode* n) { return axiom_record(axiom_record::kind_t::is_default, n); }
|
||||
|
@ -180,7 +186,6 @@ namespace array {
|
|||
bool have_different_model_values(theory_var v1, theory_var v2);
|
||||
|
||||
// diagnostics
|
||||
|
||||
std::ostream& display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const;
|
||||
public:
|
||||
solver(euf::solver& ctx, theory_id id);
|
||||
|
@ -195,7 +200,7 @@ namespace array {
|
|||
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
|
||||
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
|
||||
void collect_statistics(statistics& st) const override;
|
||||
euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override;
|
||||
euf::th_solver* clone(sat::solver* s, euf::solver& ctx) override;
|
||||
void new_eq_eh(euf::th_eq const& eq) override;
|
||||
bool unit_propagate() override;
|
||||
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
|
||||
|
@ -204,6 +209,7 @@ namespace array {
|
|||
void internalize(expr* e, bool redundant) override;
|
||||
euf::theory_var mk_var(euf::enode* n) override;
|
||||
void apply_sort_cnstr(euf::enode* n, sort* s) override;
|
||||
bool is_shared(theory_var v) const override;
|
||||
|
||||
void tracked_push(euf::enode_vector& v, euf::enode* n);
|
||||
|
||||
|
|
|
@ -3137,14 +3137,14 @@ namespace sat {
|
|||
}
|
||||
|
||||
extension* ba_solver::copy(solver* s) {
|
||||
return fresh(s, m, si, m_id);
|
||||
return clone_aux(s, m, si, m_id);
|
||||
}
|
||||
|
||||
euf::th_solver* ba_solver::fresh(solver* new_s, euf::solver& new_ctx) {
|
||||
return fresh(new_s, new_ctx.get_manager(), new_ctx.get_si(), get_id());
|
||||
euf::th_solver* ba_solver::clone(solver* new_s, euf::solver& new_ctx) {
|
||||
return clone_aux(new_s, new_ctx.get_manager(), new_ctx.get_si(), get_id());
|
||||
}
|
||||
|
||||
euf::th_solver* ba_solver::fresh(solver* new_s, ast_manager& m, sat::sat_internalizer& si, euf::theory_id id) {
|
||||
euf::th_solver* ba_solver::clone_aux(solver* new_s, ast_manager& m, sat::sat_internalizer& si, euf::theory_id id) {
|
||||
ba_solver* result = alloc(ba_solver, m, si, id);
|
||||
result->set_solver(new_s);
|
||||
copy_constraints(result, m_constraints);
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace sat {
|
|||
unsigned_vector m_weights;
|
||||
svector<wliteral> m_wlits;
|
||||
|
||||
euf::th_solver* fresh(sat::solver* new_s, ast_manager& m, sat::sat_internalizer& si, euf::theory_id id);
|
||||
euf::th_solver* clone_aux(sat::solver* new_s, ast_manager& m, sat::sat_internalizer& si, euf::theory_id id);
|
||||
|
||||
bool subsumes(card& c1, card& c2, literal_vector& comp);
|
||||
bool subsumes(card& c1, clause& c2, bool& self);
|
||||
|
@ -433,7 +433,7 @@ namespace sat {
|
|||
literal internalize(expr* e, bool sign, bool root, bool redundant) override;
|
||||
void internalize(expr* e, bool redundant) override;
|
||||
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override;
|
||||
euf::th_solver* fresh(solver* s, euf::solver& ctx) override;
|
||||
euf::th_solver* clone(solver* s, euf::solver& ctx) override;
|
||||
|
||||
ptr_vector<constraint> const & constraints() const { return m_constraints; }
|
||||
std::ostream& display(std::ostream& out, constraint const& c, bool values) const;
|
||||
|
|
|
@ -20,53 +20,77 @@ Author:
|
|||
|
||||
namespace bv {
|
||||
|
||||
bool solver::check_delay_internalized(euf::enode* n) {
|
||||
expr* e = n->get_expr();
|
||||
bool solver::check_delay_internalized(expr* e) {
|
||||
if (!ctx.is_relevant(e))
|
||||
return true;
|
||||
if (get_internalize_mode(e) != internalize_mode::delay_i)
|
||||
return true;
|
||||
SASSERT(bv.is_bv(e));
|
||||
SASSERT(get_internalize_mode(e) != internalize_mode::no_delay_i);
|
||||
switch (to_app(e)->get_decl_kind()) {
|
||||
case OP_BMUL:
|
||||
return check_mul(n);
|
||||
return check_mul(to_app(e));
|
||||
case OP_BSMUL_NO_OVFL:
|
||||
case OP_BSMUL_NO_UDFL:
|
||||
case OP_BUMUL_NO_OVFL:
|
||||
return check_bool_eval(expr2enode(e));
|
||||
default:
|
||||
return check_eval(n);
|
||||
return check_bv_eval(expr2enode(e));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solver::should_bit_blast(expr* e) {
|
||||
return bv.get_bv_size(e) <= 10;
|
||||
return bv.get_bv_size(e) <= 12;
|
||||
}
|
||||
|
||||
void solver::eval_args(euf::enode* n, vector<rational>& args) {
|
||||
rational val;
|
||||
for (euf::enode* arg : euf::enode_args(n)) {
|
||||
theory_var v = arg->get_th_var(get_id());
|
||||
VERIFY(get_fixed_value(v, val));
|
||||
args.push_back(val);
|
||||
}
|
||||
expr_ref solver::eval_args(euf::enode* n, expr_ref_vector& args) {
|
||||
for (euf::enode* arg : euf::enode_args(n))
|
||||
args.push_back(eval_bv(arg));
|
||||
expr_ref r(m.mk_app(n->get_decl(), args), m);
|
||||
ctx.get_rewriter()(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool solver::check_mul(euf::enode* n) {
|
||||
SASSERT(n->num_args() >= 2);
|
||||
app* e = to_app(n->get_expr());
|
||||
rational val, val_mul(1);
|
||||
vector<rational> args;
|
||||
eval_args(n, args);
|
||||
for (rational const& val_arg : args)
|
||||
val_mul *= val_arg;
|
||||
expr_ref solver::eval_bv(euf::enode* n) {
|
||||
rational val;
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
SASSERT(get_fixed_value(v, val));
|
||||
VERIFY(get_fixed_value(v, val));
|
||||
val_mul = mod(val_mul, power2(get_bv_size(v)));
|
||||
IF_VERBOSE(12, verbose_stream() << "check_mul " << mk_bounded_pp(n->get_expr(), m) << " " << args << " = " << val_mul << " =? " << val << "\n");
|
||||
if (val_mul == val)
|
||||
return expr_ref(bv.mk_numeral(val, get_bv_size(v)), m);
|
||||
}
|
||||
|
||||
bool solver::check_mul(app* e) {
|
||||
SASSERT(e->get_num_args() >= 2);
|
||||
expr_ref_vector args(m);
|
||||
euf::enode* n = expr2enode(e);
|
||||
auto r1 = eval_bv(n);
|
||||
auto r2 = eval_args(n, args);
|
||||
if (r1 == r2)
|
||||
return true;
|
||||
|
||||
// Some possible approaches:
|
||||
TRACE("bv", tout << mk_bounded_pp(e, m) << " evaluates to " << r1 << " arguments: " << args << "\n";);
|
||||
// check x*0 = 0
|
||||
if (!check_mul_zero(e, args, r1, r2))
|
||||
return false;
|
||||
|
||||
// check base cases: val_mul = 0 or val = 0, some values in product are 1,
|
||||
// check x*1 = x
|
||||
if (!check_mul_one(e, args, r1, r2))
|
||||
return false;
|
||||
|
||||
// Add propagation axiom for arguments
|
||||
if (!check_mul_invertibility(e, args, r1))
|
||||
return false;
|
||||
|
||||
// check discrepancies in low-order bits
|
||||
// Add axioms for multiplication when fixing high-order bits to 0
|
||||
// Add axioms for multiplication when fixing high-order bits
|
||||
if (!check_mul_low_bits(e, args, r1, r2))
|
||||
return false;
|
||||
|
||||
|
||||
// Some other possible approaches:
|
||||
// algebraic rules:
|
||||
// x*(y+z), and there are nodes for x*y or x*z -> x*(y+z) = x*y + x*z
|
||||
// compute S-polys for a set of constraints.
|
||||
|
||||
// Hensel lifting:
|
||||
// The idea is dual to fixing high-order bits. Fix the low order bits where multiplication
|
||||
|
@ -78,40 +102,316 @@ namespace bv {
|
|||
|
||||
// check tangets hi >= y >= y0 and hi' >= x => x*y >= x*y0
|
||||
|
||||
// compute S-polys for a set of constraints.
|
||||
|
||||
if (m_cheap_axioms)
|
||||
return true;
|
||||
|
||||
set_delay_internalize(e, internalize_mode::no_delay_i);
|
||||
internalize_circuit(e, v);
|
||||
internalize_circuit(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solver::check_eval(euf::enode* n) {
|
||||
expr_ref_vector args(m);
|
||||
expr_ref r1(m), r2(m);
|
||||
rational val;
|
||||
app* a = to_app(n->get_expr());
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
VERIFY(get_fixed_value(v, val));
|
||||
r1 = bv.mk_numeral(val, get_bv_size(v));
|
||||
SASSERT(bv.is_bv(a));
|
||||
for (euf::enode* arg : euf::enode_args(n)) {
|
||||
SASSERT(bv.is_bv(arg->get_expr()));
|
||||
theory_var v_arg = arg->get_th_var(get_id());
|
||||
VERIFY(get_fixed_value(v_arg, val));
|
||||
args.push_back(bv.mk_numeral(val, get_bv_size(v_arg)));
|
||||
/**
|
||||
* Add invertibility condition for multiplication
|
||||
*
|
||||
* x * y = z => (y | -y) & z = z
|
||||
*
|
||||
* This propagator relates to Niemetz and Preiner's consistency and invertibility conditions.
|
||||
* The idea is that the side-conditions for ensuring invertibility are valid
|
||||
* and in some cases are cheap to bit-blast. For multiplication, we include only
|
||||
* the _consistency_ condition because the side-constraints for invertibility
|
||||
* appear expensive (to paraphrase FMCAD 2020 paper):
|
||||
* x * s = t => (s = 0 or mcb(x << c, y << c))
|
||||
*
|
||||
* for c = ctz(s) and y = (t >> c) / (s >> c)
|
||||
*
|
||||
* mcb(x,t/s) just mean that the bit-vectors are compatible as ternary bit-vectors,
|
||||
* which for propagation means that they are the same.
|
||||
*/
|
||||
|
||||
bool solver::check_mul_invertibility(app* n, expr_ref_vector const& arg_values, expr* value) {
|
||||
|
||||
expr_ref inv(m), eq(m);
|
||||
|
||||
auto invert = [&](expr* s, expr* t) {
|
||||
return bv.mk_bv_and(bv.mk_bv_or(s, bv.mk_bv_neg(s)), t);
|
||||
};
|
||||
auto check_invert = [&](expr* s) {
|
||||
inv = invert(s, value);
|
||||
ctx.get_rewriter()(inv);
|
||||
return inv == value;
|
||||
};
|
||||
auto add_inv = [&](expr* s) {
|
||||
inv = invert(s, n);
|
||||
expr_ref eq(m.mk_eq(inv, n), m);
|
||||
TRACE("bv", tout << "enforce " << eq << "\n";);
|
||||
add_unit(b_internalize(eq));
|
||||
};
|
||||
bool ok = true;
|
||||
for (unsigned i = 0; i < arg_values.size(); ++i) {
|
||||
if (!check_invert(arg_values[i])) {
|
||||
add_inv(n->get_arg(i));
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
r2 = m.mk_app(a->get_decl(), args);
|
||||
ctx.get_rewriter()(r2);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that multiplication with 0 is correctly propagated.
|
||||
* If not, create algebraic axioms enforcing 0*x = 0 and x*0 = 0
|
||||
*
|
||||
* z = 0, then lsb(x) + 1 + lsb(y) + 1 >= sz
|
||||
*/
|
||||
bool solver::check_mul_zero(app* n, expr_ref_vector const& arg_values, expr* mul_value, expr* arg_value) {
|
||||
SASSERT(mul_value != arg_value);
|
||||
SASSERT(!(bv.is_zero(mul_value) && bv.is_zero(arg_value)));
|
||||
if (bv.is_zero(arg_value)) {
|
||||
unsigned sz = n->get_num_args();
|
||||
expr_ref_vector args(m, sz, n->get_args());
|
||||
for (unsigned i = 0; i < sz && !s().inconsistent(); ++i) {
|
||||
args[i] = arg_value;
|
||||
expr_ref r(m.mk_app(n->get_decl(), args), m);
|
||||
set_delay_internalize(r, internalize_mode::init_bits_only_i); // do not bit-blast this multiplier.
|
||||
expr_ref eq(m.mk_eq(r, arg_value), m);
|
||||
args[i] = n->get_arg(i);
|
||||
std::cout << eq << "@" << s().scope_lvl() << "\n";
|
||||
add_unit(b_internalize(eq));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (bv.is_zero(mul_value)) {
|
||||
return true;
|
||||
#if 0
|
||||
vector<expr_ref_vector> lsb_bits;
|
||||
for (expr* arg : *n) {
|
||||
expr_ref_vector bits(m);
|
||||
encode_lsb_tail(arg, bits);
|
||||
lsb_bits.push_back(bits);
|
||||
}
|
||||
expr_ref_vector zs(m);
|
||||
literal_vector lits;
|
||||
expr_ref eq(m.mk_eq(n, mul_value), m);
|
||||
lits.push_back(~b_internalize(eq));
|
||||
|
||||
for (unsigned i = 0; i < lsb_bits.size(); ++i) {
|
||||
}
|
||||
expr_ref z(m.mk_or(zs), m);
|
||||
add_clause(lits);
|
||||
// sum of lsb should be at least sz
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/***
|
||||
* check that 1*y = y, x*1 = x
|
||||
*/
|
||||
bool solver::check_mul_one(app* n, expr_ref_vector const& arg_values, expr* mul_value, expr* arg_value) {
|
||||
if (arg_values.size() != 2)
|
||||
return true;
|
||||
if (bv.is_one(arg_values[0])) {
|
||||
expr_ref mul1(m.mk_app(n->get_decl(), arg_values[0], n->get_arg(1)), m);
|
||||
set_delay_internalize(mul1, internalize_mode::init_bits_only_i);
|
||||
expr_ref eq(m.mk_eq(mul1, n->get_arg(1)), m);
|
||||
add_unit(b_internalize(eq));
|
||||
TRACE("bv", tout << eq << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (bv.is_one(arg_values[1])) {
|
||||
expr_ref mul1(m.mk_app(n->get_decl(), n->get_arg(0), arg_values[1]), m);
|
||||
set_delay_internalize(mul1, internalize_mode::init_bits_only_i);
|
||||
expr_ref eq(m.mk_eq(mul1, n->get_arg(0)), m);
|
||||
add_unit(b_internalize(eq));
|
||||
TRACE("bv", tout << eq << "\n";);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for discrepancies in low-order bits.
|
||||
* Add bit-blasting axioms if there are discrepancies within low order bits.
|
||||
*/
|
||||
bool solver::check_mul_low_bits(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2) {
|
||||
rational v0, v1, two(2);
|
||||
unsigned sz;
|
||||
VERIFY(bv.is_numeral(value1, v0, sz));
|
||||
VERIFY(bv.is_numeral(value2, v1));
|
||||
unsigned num_bits = 10;
|
||||
if (sz <= num_bits)
|
||||
return true;
|
||||
bool diff = false;
|
||||
for (unsigned i = 0; !diff && i < num_bits; ++i) {
|
||||
rational b0 = mod(v0, two);
|
||||
rational b1 = mod(v1, two);
|
||||
diff = b0 != b1;
|
||||
div(v0, two, v0);
|
||||
div(v1, two, v1);
|
||||
}
|
||||
if (!diff)
|
||||
return true;
|
||||
|
||||
auto safe_for_fixing_bits = [&](expr* e) {
|
||||
euf::enode* n = expr2enode(e);
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
for (unsigned i = num_bits; i < sz; ++i) {
|
||||
sat::literal lit = m_bits[v][i];
|
||||
if (s().value(lit) == l_true && s().lvl(lit) > s().search_lvl())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
for (expr* arg : *n)
|
||||
if (!safe_for_fixing_bits(arg))
|
||||
return true;
|
||||
if (!safe_for_fixing_bits(n))
|
||||
return true;
|
||||
|
||||
auto value_for_bv = [&](expr* e) {
|
||||
euf::enode* n = expr2enode(e);
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
rational val(0);
|
||||
for (unsigned i = num_bits; i < sz; ++i) {
|
||||
sat::literal lit = m_bits[v][i];
|
||||
if (s().value(lit) == l_true && s().lvl(lit) <= s().search_lvl())
|
||||
val += power2(i - num_bits);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
auto extract_low_bits = [&](expr* e) {
|
||||
rational val = value_for_bv(e);
|
||||
expr_ref lo(bv.mk_extract(num_bits - 1, 0, e), m);
|
||||
expr_ref hi(bv.mk_numeral(val, sz - num_bits), m);
|
||||
return expr_ref(bv.mk_concat(lo, hi), m);
|
||||
};
|
||||
expr_ref_vector args(m);
|
||||
for (expr* arg : *n)
|
||||
args.push_back(extract_low_bits(arg));
|
||||
expr_ref lhs(extract_low_bits(n), m);
|
||||
expr_ref rhs(m.mk_app(n->get_decl(), args), m);
|
||||
set_delay_internalize(rhs, internalize_mode::no_delay_i);
|
||||
expr_ref eq(m.mk_eq(lhs, rhs), m);
|
||||
add_unit(b_internalize(eq));
|
||||
TRACE("bv", tout << "low-bits: " << eq << "\n";);
|
||||
std::cout << "low bits\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The i'th bit in xs is 1 if the most significant bit of x is i or higher.
|
||||
*/
|
||||
void solver::encode_msb_tail(expr* x, expr_ref_vector& xs) {
|
||||
theory_var v = expr2enode(x)->get_th_var(get_id());
|
||||
sat::literal_vector const& bits = m_bits[v];
|
||||
if (bits.empty())
|
||||
return;
|
||||
expr_ref tmp = literal2expr(bits.back());
|
||||
for (unsigned i = bits.size() - 1; i-- > 0; ) {
|
||||
auto b = bits[i];
|
||||
tmp = m.mk_or(literal2expr(b), tmp);
|
||||
xs.push_back(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The i'th bit in xs is 1 if the least significant bit of x is i or lower.
|
||||
*/
|
||||
void solver::encode_lsb_tail(expr* x, expr_ref_vector& xs) {
|
||||
theory_var v = expr2enode(x)->get_th_var(get_id());
|
||||
sat::literal_vector const& bits = m_bits[v];
|
||||
if (bits.empty())
|
||||
return;
|
||||
expr_ref tmp = literal2expr(bits[0]);
|
||||
for (unsigned i = 1; i < bits.size(); ++i) {
|
||||
auto b = bits[i];
|
||||
tmp = m.mk_or(literal2expr(b), tmp);
|
||||
xs.push_back(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check non-overflow of unsigned multiplication.
|
||||
*
|
||||
* no_overflow(x, y) = > msb(x) + msb(y) <= sz;
|
||||
* msb(x) + msb(y) < sz => no_overflow(x,y)
|
||||
*/
|
||||
bool solver::check_umul_no_overflow(app* n, expr_ref_vector const& arg_values, expr* value) {
|
||||
SASSERT(arg_values.size() == 2);
|
||||
SASSERT(m.is_true(value) || m.is_false(value));
|
||||
rational v0, v1;
|
||||
unsigned sz;
|
||||
VERIFY(bv.is_numeral(arg_values[0], v0, sz));
|
||||
VERIFY(bv.is_numeral(arg_values[1], v1));
|
||||
unsigned msb0 = v0.get_num_bits();
|
||||
unsigned msb1 = v1.get_num_bits();
|
||||
expr_ref_vector xs(m), ys(m), zs(m);
|
||||
|
||||
if (m.is_true(value) && msb0 + msb1 > sz && !v0.is_zero() && !v1.is_zero()) {
|
||||
sat::literal no_overflow = expr2literal(n);
|
||||
encode_msb_tail(n->get_arg(0), xs);
|
||||
encode_msb_tail(n->get_arg(1), ys);
|
||||
for (unsigned i = 1; i <= sz; ++i) {
|
||||
sat::literal bit0 = b_internalize(xs.get(i - 1));
|
||||
sat::literal bit1 = b_internalize(ys.get(sz - i));
|
||||
add_clause(~no_overflow, ~bit0, ~bit1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (m.is_false(value) && msb0 + msb1 < sz) {
|
||||
encode_msb_tail(n->get_arg(0), xs);
|
||||
encode_msb_tail(n->get_arg(1), ys);
|
||||
sat::literal_vector lits;
|
||||
lits.push_back(expr2literal(n));
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
expr_ref msb_ge_sz(m.mk_and(xs.get(i - 1), ys.get(sz - i - 1)), m);
|
||||
lits.push_back(b_internalize(msb_ge_sz));
|
||||
}
|
||||
add_clause(lits);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solver::check_bv_eval(euf::enode* n) {
|
||||
expr_ref_vector args(m);
|
||||
app* a = n->get_app();
|
||||
SASSERT(bv.is_bv(a));
|
||||
auto r1 = eval_bv(n);
|
||||
auto r2 = eval_args(n, args);
|
||||
if (r1 == r2)
|
||||
return true;
|
||||
if (m_cheap_axioms)
|
||||
return true;
|
||||
set_delay_internalize(a, internalize_mode::no_delay_i);
|
||||
internalize_circuit(a, v);
|
||||
internalize_circuit(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solver::check_bool_eval(euf::enode* n) {
|
||||
expr_ref_vector args(m);
|
||||
SASSERT(m.is_bool(n->get_expr()));
|
||||
sat::literal lit = expr2literal(n->get_expr());
|
||||
expr* r1 = m.mk_bool_val(s().value(lit) == l_true);
|
||||
auto r2 = eval_args(n, args);
|
||||
if (r1 == r2)
|
||||
return true;
|
||||
app* a = n->get_app();
|
||||
if (bv.is_bv_umul_no_ovfl(a) && !check_umul_no_overflow(a, args, r1))
|
||||
return false;
|
||||
if (m_cheap_axioms)
|
||||
return true;
|
||||
set_delay_internalize(a, internalize_mode::no_delay_i);
|
||||
internalize_circuit(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
void solver::set_delay_internalize(expr* e, internalize_mode mode) {
|
||||
if (!m_delay_internalize.contains(e))
|
||||
ctx.push(insert_obj_map<euf::solver, expr, internalize_mode>(m_delay_internalize, e));
|
||||
else
|
||||
ctx.push(remove_obj_map<euf::solver, expr, internalize_mode>(m_delay_internalize, e, m_delay_internalize[e]));
|
||||
m_delay_internalize.insert(e, mode);
|
||||
}
|
||||
|
||||
|
@ -120,6 +420,7 @@ namespace bv {
|
|||
return internalize_mode::no_delay_i;
|
||||
if (!get_config().m_bv_delay)
|
||||
return internalize_mode::no_delay_i;
|
||||
internalize_mode mode;
|
||||
switch (to_app(e)->get_decl_kind()) {
|
||||
case OP_BMUL:
|
||||
case OP_BSMUL_NO_OVFL:
|
||||
|
@ -129,18 +430,15 @@ namespace bv {
|
|||
case OP_BUREM_I:
|
||||
case OP_BSREM_I:
|
||||
case OP_BUDIV_I:
|
||||
case OP_BSDIV_I: {
|
||||
case OP_BSDIV_I:
|
||||
if (should_bit_blast(e))
|
||||
return internalize_mode::no_delay_i;
|
||||
internalize_mode mode = internalize_mode::init_bits_i;
|
||||
mode = internalize_mode::delay_i;
|
||||
if (!m_delay_internalize.find(e, mode))
|
||||
set_delay_internalize(e, mode);
|
||||
return mode;
|
||||
}
|
||||
m_delay_internalize.insert(e, mode);
|
||||
return mode;
|
||||
default:
|
||||
return internalize_mode::no_delay_i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ Author:
|
|||
|
||||
--*/
|
||||
|
||||
#include "params/bv_rewriter_params.hpp"
|
||||
#include "sat/smt/bv_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
|
@ -22,26 +23,10 @@ Author:
|
|||
|
||||
namespace bv {
|
||||
|
||||
solver::bit_atom& solver::atom::to_bit() {
|
||||
SASSERT(is_bit());
|
||||
return dynamic_cast<bit_atom&>(*this);
|
||||
}
|
||||
|
||||
solver::def_atom& solver::atom::to_def() {
|
||||
SASSERT(!is_bit());
|
||||
SASSERT(!is_eq());
|
||||
return dynamic_cast<def_atom&>(*this);
|
||||
}
|
||||
|
||||
solver::eq_atom& solver::atom::to_eq() {
|
||||
SASSERT(is_eq());
|
||||
return dynamic_cast<eq_atom&>(*this);
|
||||
}
|
||||
|
||||
class solver::add_var_pos_trail : public trail<euf::solver> {
|
||||
solver::bit_atom* m_atom;
|
||||
solver::atom* m_atom;
|
||||
public:
|
||||
add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {}
|
||||
add_var_pos_trail(solver::atom* a) :m_atom(a) {}
|
||||
void undo(euf::solver& euf) override {
|
||||
SASSERT(m_atom->m_occs);
|
||||
m_atom->m_occs = m_atom->m_occs->m_next;
|
||||
|
@ -49,9 +34,9 @@ namespace bv {
|
|||
};
|
||||
|
||||
class solver::add_eq_occurs_trail : public trail<euf::solver> {
|
||||
bit_atom* m_atom;
|
||||
atom* m_atom;
|
||||
public:
|
||||
add_eq_occurs_trail(bit_atom* a) :m_atom(a) {}
|
||||
add_eq_occurs_trail(atom* a) :m_atom(a) {}
|
||||
void undo(euf::solver& euf) override {
|
||||
SASSERT(m_atom->m_eqs);
|
||||
m_atom->m_eqs = m_atom->m_eqs->m_next;
|
||||
|
@ -61,10 +46,10 @@ namespace bv {
|
|||
};
|
||||
|
||||
class solver::del_eq_occurs_trail : public trail<euf::solver> {
|
||||
bit_atom* m_atom;
|
||||
atom* m_atom;
|
||||
eq_occurs* m_node;
|
||||
public:
|
||||
del_eq_occurs_trail(bit_atom* a, eq_occurs* n) : m_atom(a), m_node(n) {}
|
||||
del_eq_occurs_trail(atom* a, eq_occurs* n) : m_atom(a), m_node(n) {}
|
||||
void undo(euf::solver& euf) override {
|
||||
if (m_node->m_next)
|
||||
m_node->m_next->m_prev = m_node;
|
||||
|
@ -75,7 +60,7 @@ namespace bv {
|
|||
}
|
||||
};
|
||||
|
||||
void solver::del_eq_occurs(bit_atom* a, eq_occurs* occ) {
|
||||
void solver::del_eq_occurs(atom* a, eq_occurs* occ) {
|
||||
eq_occurs* prev = occ->m_prev;
|
||||
if (prev)
|
||||
prev->m_next = occ->m_next;
|
||||
|
@ -104,8 +89,7 @@ namespace bv {
|
|||
m_bits.push_back(sat::literal_vector());
|
||||
m_wpos.push_back(0);
|
||||
m_zero_one_bits.push_back(zero_one_bits());
|
||||
ctx.attach_th_var(n, this, r);
|
||||
|
||||
ctx.attach_th_var(n, this, r);
|
||||
TRACE("bv", tout << "mk-var: " << r << " " << n->get_expr_id() << " " << mk_bounded_pp(n->get_expr(), m) << "\n";);
|
||||
return r;
|
||||
}
|
||||
|
@ -157,14 +141,14 @@ namespace bv {
|
|||
SASSERT(!n->is_attached_to(get_id()));
|
||||
theory_var v = mk_var(n);
|
||||
SASSERT(n->is_attached_to(get_id()));
|
||||
if (internalize_mode::init_bits_i == get_internalize_mode(a))
|
||||
if (internalize_mode::no_delay_i != get_internalize_mode(a))
|
||||
mk_bits(n->get_th_var(get_id()));
|
||||
else
|
||||
internalize_circuit(a, v);
|
||||
internalize_circuit(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solver::internalize_circuit(app* a, theory_var v) {
|
||||
bool solver::internalize_circuit(app* a) {
|
||||
|
||||
std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits)> bin;
|
||||
std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref& bit)> ebin;
|
||||
|
@ -177,8 +161,9 @@ namespace bv {
|
|||
#define internalize_nfl(F) ebin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref& out) { m_bb.F(sz, xs, ys, out);}; internalize_novfl(a, ebin);
|
||||
|
||||
switch (a->get_decl_kind()) {
|
||||
case OP_BV_NUM: internalize_num(a, v); break;
|
||||
case OP_BV_NUM: internalize_num(a); break;
|
||||
case OP_BNOT: internalize_un(mk_not); break;
|
||||
case OP_BNEG: internalize_un(mk_neg); break;
|
||||
case OP_BREDAND: internalize_un(mk_redand); break;
|
||||
case OP_BREDOR: internalize_un(mk_redor); break;
|
||||
case OP_BSDIV_I: internalize_bin(mk_sdiv); break;
|
||||
|
@ -199,7 +184,7 @@ namespace bv {
|
|||
case OP_BNAND: internalize_bin(mk_nand); break;
|
||||
case OP_BNOR: internalize_bin(mk_nor); break;
|
||||
case OP_BXNOR: internalize_bin(mk_xnor); break;
|
||||
case OP_BCOMP: internalize_bin(mk_comp); break;
|
||||
case OP_BCOMP: internalize_bin(mk_comp); break;
|
||||
case OP_SIGN_EXT: internalize_pun(mk_sign_extend); break;
|
||||
case OP_ZERO_EXT: internalize_pun(mk_zero_extend); break;
|
||||
case OP_ROTATE_LEFT: internalize_pun(mk_rotate_left); break;
|
||||
|
@ -208,8 +193,14 @@ namespace bv {
|
|||
case OP_BSMUL_NO_OVFL: internalize_nfl(mk_smul_no_overflow); break;
|
||||
case OP_BSMUL_NO_UDFL: internalize_nfl(mk_smul_no_underflow); break;
|
||||
case OP_BIT2BOOL: internalize_bit2bool(a); break;
|
||||
case OP_ULEQ: internalize_le<false>(a); break;
|
||||
case OP_SLEQ: internalize_le<true>(a); break;
|
||||
case OP_ULEQ: internalize_le<false, false, false>(a); break;
|
||||
case OP_SLEQ: internalize_le<true, false, false>(a); break;
|
||||
case OP_UGEQ: internalize_le<false, true, false>(a); break;
|
||||
case OP_SGEQ: internalize_le<true, true, false>(a); break;
|
||||
case OP_ULT: internalize_le<false, true, true>(a); break;
|
||||
case OP_SLT: internalize_le<true, true, true>(a); break;
|
||||
case OP_UGT: internalize_le<false, false, true>(a); break;
|
||||
case OP_SGT: internalize_le<true, false, true>(a); break;
|
||||
case OP_XOR3: internalize_xor3(a); break;
|
||||
case OP_CARRY: internalize_carry(a); break;
|
||||
case OP_BSUB: internalize_sub(a); break;
|
||||
|
@ -218,12 +209,14 @@ namespace bv {
|
|||
case OP_MKBV: internalize_mkbv(a); break;
|
||||
case OP_INT2BV: internalize_int2bv(a); break;
|
||||
case OP_BV2INT: internalize_bv2int(a); break;
|
||||
case OP_BUDIV: internalize_udiv(a); break;
|
||||
case OP_BSDIV0: break;
|
||||
case OP_BUDIV0: break;
|
||||
case OP_BSREM0: break;
|
||||
case OP_BUREM0: break;
|
||||
case OP_BSMOD0: break;
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << mk_bounded_pp(a, m) << "\n");
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
@ -231,7 +224,7 @@ namespace bv {
|
|||
}
|
||||
|
||||
void solver::mk_bits(theory_var v) {
|
||||
TRACE("bv", tout << "v" << v << "\n";);
|
||||
TRACE("bv", tout << "v" << v << "@" << s().scope_lvl() << "\n";);
|
||||
expr* e = var2expr(v);
|
||||
unsigned bv_size = get_bv_size(v);
|
||||
m_bits[v].reset();
|
||||
|
@ -239,6 +232,7 @@ namespace bv {
|
|||
expr_ref b2b(bv.mk_bit2bool(e, i), m);
|
||||
m_bits[v].push_back(sat::null_literal);
|
||||
sat::literal lit = ctx.internalize(b2b, false, false, m_is_redundant);
|
||||
TRACE("bv", tout << "add-bit: " << lit << " " << literal2expr(lit) << "\n";);
|
||||
SASSERT(m_bits[v].back() == lit);
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +257,12 @@ namespace bv {
|
|||
}
|
||||
|
||||
void solver::get_bits(theory_var v, expr_ref_vector& r) {
|
||||
for (literal lit : m_bits[v]) {
|
||||
if (!literal2expr(lit))
|
||||
ctx.display(std::cout << "Missing expression: " << lit << "\n");
|
||||
SASSERT(literal2expr(lit));
|
||||
}
|
||||
|
||||
for (literal lit : m_bits[v])
|
||||
r.push_back(literal2expr(lit));
|
||||
}
|
||||
|
@ -289,22 +289,23 @@ namespace bv {
|
|||
void solver::add_bit(theory_var v, literal l) {
|
||||
unsigned idx = m_bits[v].size();
|
||||
m_bits[v].push_back(l);
|
||||
TRACE("bv", tout << "add-bit: v" << v << "[" << idx << "] " << l << " " << literal2expr(l) << "@" << s().scope_lvl() << "\n";);
|
||||
SASSERT(m_num_scopes == 0);
|
||||
s().set_external(l.var());
|
||||
set_bit_eh(v, l, idx);
|
||||
}
|
||||
|
||||
solver::bit_atom* solver::mk_bit_atom(sat::bool_var bv) {
|
||||
solver::atom* solver::mk_atom(sat::bool_var bv) {
|
||||
atom* a = get_bv2a(bv);
|
||||
if (a)
|
||||
return a->is_bit() ? &a->to_bit() : nullptr;
|
||||
else {
|
||||
bit_atom* b = new (get_region()) bit_atom();
|
||||
insert_bv2a(bv, b);
|
||||
ctx.push(mk_atom_trail(bv, *this));
|
||||
return b;
|
||||
}
|
||||
if (a)
|
||||
return a;
|
||||
a = new (get_region()) atom();
|
||||
insert_bv2a(bv, a);
|
||||
ctx.push(mk_atom_trail(bv, *this));
|
||||
return a;
|
||||
}
|
||||
|
||||
#if 0
|
||||
solver::eq_atom* solver::mk_eq_atom(sat::bool_var bv) {
|
||||
atom* a = get_bv2a(bv);
|
||||
if (a)
|
||||
|
@ -316,6 +317,7 @@ namespace bv {
|
|||
return b;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void solver::set_bit_eh(theory_var v, literal l, unsigned idx) {
|
||||
|
@ -323,14 +325,12 @@ namespace bv {
|
|||
if (s().value(l) != l_undef && s().lvl(l) == 0)
|
||||
register_true_false_bit(v, idx);
|
||||
else if (m_bits[v].size() > 1) {
|
||||
bit_atom* b = mk_bit_atom(l.var());
|
||||
if (b) {
|
||||
if (b->m_occs)
|
||||
find_new_diseq_axioms(*b, v, idx);
|
||||
if (!b->is_fresh())
|
||||
ctx.push(add_var_pos_trail(b));
|
||||
b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs);
|
||||
}
|
||||
atom* b = mk_atom(l.var());
|
||||
if (b->m_occs)
|
||||
find_new_diseq_axioms(*b, v, idx);
|
||||
if (!b->is_fresh())
|
||||
ctx.push(add_var_pos_trail(b));
|
||||
b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,19 @@ namespace bv {
|
|||
SASSERT(get_bv_size(n) == bits.size());
|
||||
SASSERT(euf::null_theory_var != n->get_th_var(get_id()));
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
m_bits[v].reset();
|
||||
|
||||
if (!m_bits[v].empty()) {
|
||||
SASSERT(bits.size() == m_bits[v].size());
|
||||
unsigned i = 0;
|
||||
for (expr* bit : bits) {
|
||||
sat::literal lit = ctx.internalize(bit, false, false, m_is_redundant);
|
||||
TRACE("bv", tout << "set " << m_bits[v][i] << " == " << lit << "\n";);
|
||||
add_clause(~lit, m_bits[v][i]);
|
||||
add_clause(lit, ~m_bits[v][i]);
|
||||
++i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (expr* bit : bits)
|
||||
add_bit(v, ctx.internalize(bit, false, false, m_is_redundant));
|
||||
for (expr* bit : bits)
|
||||
|
@ -356,11 +368,13 @@ namespace bv {
|
|||
return get_bv_size(var2enode(v));
|
||||
}
|
||||
|
||||
void solver::internalize_num(app* n, theory_var v) {
|
||||
void solver::internalize_num(app* a) {
|
||||
numeral val;
|
||||
unsigned sz = 0;
|
||||
SASSERT(expr2enode(n)->interpreted());
|
||||
VERIFY(bv.is_numeral(n, val, sz));
|
||||
euf::enode* n = expr2enode(a);
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
SASSERT(n->interpreted());
|
||||
VERIFY(bv.is_numeral(a, val, sz));
|
||||
expr_ref_vector bits(m);
|
||||
m_bb.num2bits(val, sz, bits);
|
||||
SASSERT(bits.size() == sz);
|
||||
|
@ -453,18 +467,20 @@ namespace bv {
|
|||
}
|
||||
}
|
||||
|
||||
template<bool Signed>
|
||||
template<bool Signed, bool Rev, bool Negated>
|
||||
void solver::internalize_le(app* n) {
|
||||
SASSERT(n->get_num_args() == 2);
|
||||
expr_ref_vector arg1_bits(m), arg2_bits(m);
|
||||
get_arg_bits(n, 0, arg1_bits);
|
||||
get_arg_bits(n, 1, arg2_bits);
|
||||
get_arg_bits(n, Rev ? 1 : 0, arg1_bits);
|
||||
get_arg_bits(n, Rev ? 0 : 1, arg2_bits);
|
||||
expr_ref le(m);
|
||||
if (Signed)
|
||||
m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
|
||||
else
|
||||
m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
|
||||
literal def = ctx.internalize(le, false, false, m_is_redundant);
|
||||
if (Negated)
|
||||
def.neg();
|
||||
add_def(def, expr2literal(n));
|
||||
}
|
||||
|
||||
|
@ -498,6 +514,29 @@ namespace bv {
|
|||
add_clause(r, ~l1, ~l2, ~l3);
|
||||
}
|
||||
|
||||
void solver::internalize_udiv_i(app* a) {
|
||||
std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits)> bin;
|
||||
bin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits) { m_bb.mk_udiv(sz, xs, ys, bits); };
|
||||
internalize_binary(a, bin);
|
||||
}
|
||||
|
||||
void solver::internalize_udiv(app* n) {
|
||||
bv_rewriter_params p(s().params());
|
||||
expr* arg1 = n->get_arg(0);
|
||||
expr* arg2 = n->get_arg(1);
|
||||
if (p.hi_div0()) {
|
||||
expr_ref eq(m.mk_eq(n, bv.mk_bv_udiv_i(arg1, arg2)), m);
|
||||
add_unit(b_internalize(eq));
|
||||
return;
|
||||
}
|
||||
unsigned sz = bv.get_bv_size(n);
|
||||
expr_ref zero(bv.mk_numeral(0, sz), m);
|
||||
expr_ref eq(m.mk_eq(arg2, zero), m);
|
||||
expr_ref udiv(m.mk_ite(eq, bv.mk_bv_udiv0(arg1), bv.mk_bv_udiv_i(arg1, arg2)), m);
|
||||
expr_ref eq2(m.mk_eq(n, udiv), m);
|
||||
add_unit(b_internalize(eq2));
|
||||
}
|
||||
|
||||
void solver::internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn) {
|
||||
SASSERT(n->get_num_args() == 1);
|
||||
expr_ref_vector arg1_bits(m), bits(m);
|
||||
|
@ -554,7 +593,9 @@ namespace bv {
|
|||
}
|
||||
|
||||
void solver::add_def(sat::literal def, sat::literal l) {
|
||||
def_atom* a = new (get_region()) def_atom(l, def);
|
||||
atom* a = new (get_region()) atom();
|
||||
a->m_var = l;
|
||||
a->m_def = def;
|
||||
insert_bv2a(l.var(), a);
|
||||
ctx.push(mk_atom_trail(l.var(), *this));
|
||||
add_clause(l, ~def);
|
||||
|
@ -612,8 +653,9 @@ namespace bv {
|
|||
sat::literal lit0 = m_bits[v_arg][idx];
|
||||
if (lit0 == sat::null_literal) {
|
||||
m_bits[v_arg][idx] = lit;
|
||||
TRACE("bv", tout << "add-bit: " << lit << " " << literal2expr(lit) << "\n";);
|
||||
if (arg_sz > 1) {
|
||||
bit_atom* a = new (get_region()) bit_atom();
|
||||
atom* a = new (get_region()) atom();
|
||||
a->m_occs = new (get_region()) var_pos_occ(v_arg, idx);
|
||||
insert_bv2a(lit.var(), a);
|
||||
ctx.push(mk_atom_trail(lit.var(), *this));
|
||||
|
@ -677,7 +719,7 @@ namespace bv {
|
|||
}
|
||||
|
||||
void solver::eq_internalized(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, literal lit, euf::enode* n) {
|
||||
bit_atom* a = mk_bit_atom(b1);
|
||||
atom* a = mk_atom(b1);
|
||||
// eq_atom* b = mk_eq_atom(lit.var());
|
||||
if (a) {
|
||||
if (!a->is_fresh())
|
||||
|
|
|
@ -23,8 +23,8 @@ namespace bv {
|
|||
void solver::validate_atoms() const {
|
||||
sat::bool_var v = 0;
|
||||
for (auto* a : m_bool_var2atom) {
|
||||
if (a && a->is_bit()) {
|
||||
for (auto vp : a->to_bit()) {
|
||||
if (a) {
|
||||
for (auto vp : *a) {
|
||||
SASSERT(m_bits[vp.first][vp.second].var() == v);
|
||||
VERIFY(m_bits[vp.first][vp.second].var() == v);
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@ namespace bv {
|
|||
};
|
||||
|
||||
class solver::bit_occs_trail : public trail<euf::solver> {
|
||||
bit_atom& a;
|
||||
atom& a;
|
||||
var_pos_occ* m_occs;
|
||||
|
||||
public:
|
||||
bit_occs_trail(solver& s, bit_atom& a): a(a), m_occs(a.m_occs) {}
|
||||
bit_occs_trail(solver& s, atom& a): a(a), m_occs(a.m_occs) {}
|
||||
|
||||
virtual void undo(euf::solver& euf) {
|
||||
IF_VERBOSE(1, verbose_stream() << "add back occurrences " << & a << "\n");
|
||||
|
@ -134,7 +134,7 @@ namespace bv {
|
|||
/**
|
||||
*\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom.
|
||||
*/
|
||||
void solver::find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx) {
|
||||
void solver::find_new_diseq_axioms(atom& a, theory_var v, unsigned idx) {
|
||||
if (!get_config().m_bv_eq_axioms)
|
||||
return;
|
||||
literal l = m_bits[v][idx];
|
||||
|
@ -180,12 +180,8 @@ namespace bv {
|
|||
}
|
||||
}
|
||||
else if (m.is_bool(e) && (a = m_bool_var2atom.get(expr2literal(e).var(), nullptr))) {
|
||||
if (a->is_bit()) {
|
||||
for (var_pos vp : a->to_bit())
|
||||
out << " " << var2enode(vp.first)->get_expr_id() << "[" << vp.second << "]";
|
||||
}
|
||||
else
|
||||
out << "def-atom";
|
||||
for (var_pos vp : *a)
|
||||
out << " " << var2enode(vp.first)->get_expr_id() << "[" << vp.second << "]";
|
||||
}
|
||||
else
|
||||
out << " " << mk_bounded_pp(e, m, 1);
|
||||
|
@ -269,7 +265,7 @@ namespace bv {
|
|||
|
||||
void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) {
|
||||
auto& c = bv_justification::from_index(idx);
|
||||
TRACE("bv", display_constraint(tout, idx););
|
||||
TRACE("bv", display_constraint(tout, idx) << "\n";);
|
||||
switch (c.m_kind) {
|
||||
case bv_justification::kind_t::eq2bit:
|
||||
SASSERT(s().value(c.m_antecedent) == l_true);
|
||||
|
@ -389,12 +385,10 @@ namespace bv {
|
|||
void solver::asserted(literal l) {
|
||||
atom* a = get_bv2a(l.var());
|
||||
TRACE("bv", tout << "asserted: " << l << "\n";);
|
||||
if (a && a->is_bit()) {
|
||||
if (a) {
|
||||
force_push();
|
||||
m_prop_queue.push_back(propagation_item(&a->to_bit()));
|
||||
}
|
||||
else if (a && a->is_eq()) {
|
||||
for (auto p : a->to_eq().m_eqs) {
|
||||
m_prop_queue.push_back(propagation_item(a));
|
||||
for (auto p : a->m_bit2occ) {
|
||||
del_eq_occurs(p.first, p.second);
|
||||
}
|
||||
}
|
||||
|
@ -422,7 +416,6 @@ namespace bv {
|
|||
++num_eq_assigned;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(20, verbose_stream() << "atoms: " << num_atoms << " eqs: " << num_eqs << " atoms-assigned:" << num_assigned << " eqs-assigned: " << num_eq_assigned << " lits: " << num_lit_assigned << "\n");
|
||||
}
|
||||
else
|
||||
propagate_bits(p.m_vp);
|
||||
|
@ -495,20 +488,39 @@ namespace bv {
|
|||
return num_assigned > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check each delay internalized bit-vector operation for compliance.
|
||||
*
|
||||
* TBD: add model-repair attempt after cheap propagation axioms have been added
|
||||
*/
|
||||
sat::check_result solver::check() {
|
||||
force_push();
|
||||
SASSERT(m_prop_queue.size() == m_prop_queue_head);
|
||||
bool ok = true;
|
||||
for (auto kv : m_delay_internalize) {
|
||||
if (ctx.is_relevant(kv.m_key) &&
|
||||
kv.m_value == internalize_mode::init_bits_i &&
|
||||
!check_delay_internalized(expr2enode(kv.m_key)))
|
||||
svector<std::pair<expr*, internalize_mode>> delay;
|
||||
for (auto kv : m_delay_internalize)
|
||||
delay.push_back(std::make_pair(kv.m_key, kv.m_value));
|
||||
flet<bool> _cheap1(m_cheap_axioms, true);
|
||||
for (auto kv : delay)
|
||||
if (!check_delay_internalized(kv.first))
|
||||
ok = false;
|
||||
}
|
||||
return ok ? sat::check_result::CR_DONE : sat::check_result::CR_CONTINUE;
|
||||
if (!ok)
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
|
||||
// if (repair_model()) return sat::check_result::DONE;
|
||||
|
||||
flet<bool> _cheap2(m_cheap_axioms, false);
|
||||
for (auto kv : delay)
|
||||
if (!check_delay_internalized(kv.first))
|
||||
ok = false;
|
||||
|
||||
if (!ok)
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
return sat::check_result::CR_DONE;
|
||||
}
|
||||
|
||||
void solver::push_core() {
|
||||
TRACE("bv", tout << "push: " << get_num_vars() << "@" << m_prop_queue_lim.size() << "\n";);
|
||||
th_euf_solver::push_core();
|
||||
m_prop_queue_lim.push_back(m_prop_queue.size());
|
||||
}
|
||||
|
@ -523,22 +535,18 @@ namespace bv {
|
|||
m_bits.shrink(old_sz);
|
||||
m_wpos.shrink(old_sz);
|
||||
m_zero_one_bits.shrink(old_sz);
|
||||
TRACE("bv", tout << "num vars " << old_sz << "@" << m_prop_queue_lim.size() << "\n";);
|
||||
}
|
||||
|
||||
void solver::pre_simplify() {}
|
||||
|
||||
void solver::simplify() {
|
||||
m_ackerman.propagate();
|
||||
}
|
||||
|
||||
bool solver::set_root(literal l, literal r) {
|
||||
atom* a = get_bv2a(l.var());
|
||||
atom* b = get_bv2a(r.var());
|
||||
if (!a || !a->is_bit())
|
||||
if (!a)
|
||||
return true;
|
||||
if (b && !b->is_bit())
|
||||
return false;
|
||||
for (auto vp : a->to_bit()) {
|
||||
for (auto vp : *a) {
|
||||
sat::literal l2 = m_bits[vp.first][vp.second];
|
||||
if (l2.var() == r.var())
|
||||
continue;
|
||||
|
@ -549,8 +557,8 @@ namespace bv {
|
|||
m_bits[vp.first][vp.second] = r2;
|
||||
set_bit_eh(vp.first, r2, vp.second);
|
||||
}
|
||||
ctx.push(bit_occs_trail(*this, a->to_bit()));
|
||||
a->to_bit().m_occs = nullptr;
|
||||
ctx.push(bit_occs_trail(*this, *a));
|
||||
a->m_occs = nullptr;
|
||||
// validate_atoms();
|
||||
return true;
|
||||
}
|
||||
|
@ -621,8 +629,7 @@ namespace bv {
|
|||
return out << "bv <- v" << v1 << "[" << cidx << "] != v" << v2 << "[" << cidx << "] " << m_bits[v1][cidx] << " != " << m_bits[v2][cidx];
|
||||
}
|
||||
case bv_justification::kind_t::ne2bit:
|
||||
return out << "bv <- " << m_bits[v1] << " != " << m_bits[v2] << " @" << cidx;
|
||||
break;
|
||||
return out << "bv <- " << m_bits[v1] << " != " << m_bits[v2] << " @" << cidx;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
@ -643,7 +650,7 @@ namespace bv {
|
|||
|
||||
sat::extension* solver::copy(sat::solver* s) { UNREACHABLE(); return nullptr; }
|
||||
|
||||
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
|
||||
euf::th_solver* solver::clone(sat::solver* s, euf::solver& ctx) {
|
||||
bv::solver* result = alloc(bv::solver, ctx, get_id());
|
||||
ast_translation tr(m, ctx.get_manager());
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
|
@ -664,22 +671,18 @@ namespace bv {
|
|||
if (!a)
|
||||
continue;
|
||||
|
||||
if (a->is_bit()) {
|
||||
bit_atom* new_a = new (result->get_region()) bit_atom();
|
||||
m_bool_var2atom.setx(i, new_a, nullptr);
|
||||
for (auto vp : a->to_bit())
|
||||
new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs);
|
||||
for (auto const& occ : a->to_bit().eqs()) {
|
||||
expr* e = occ.m_node->get_expr();
|
||||
expr_ref e2(tr(e), tr.to());
|
||||
euf::enode* n = ctx.get_enode(e2);
|
||||
new_a->m_eqs = new (result->get_region()) eq_occurs(occ.m_bv1, occ.m_bv2, occ.m_idx, occ.m_v1, occ.m_v2, occ.m_literal, n, new_a->m_eqs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
def_atom* new_a = new (result->get_region()) def_atom(a->to_def().m_var, a->to_def().m_def);
|
||||
m_bool_var2atom.setx(i, new_a, nullptr);
|
||||
atom* new_a = new (result->get_region()) atom();
|
||||
m_bool_var2atom.setx(i, new_a, nullptr);
|
||||
for (auto vp : *a)
|
||||
new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs);
|
||||
for (auto const& occ : a->eqs()) {
|
||||
expr* e = occ.m_node->get_expr();
|
||||
expr_ref e2(tr(e), tr.to());
|
||||
euf::enode* n = ctx.get_enode(e2);
|
||||
new_a->m_eqs = new (result->get_region()) eq_occurs(occ.m_bv1, occ.m_bv2, occ.m_idx, occ.m_v1, occ.m_v2, occ.m_literal, n, new_a->m_eqs);
|
||||
}
|
||||
new_a->m_def = a->m_def;
|
||||
new_a->m_var = a->m_var;
|
||||
validate_atoms();
|
||||
}
|
||||
return result;
|
||||
|
@ -783,9 +786,7 @@ namespace bv {
|
|||
return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
|
||||
}
|
||||
|
||||
|
||||
bool solver::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) {
|
||||
|
||||
m_stats.m_num_eq2bit++;
|
||||
SASSERT(ctx.s().value(antecedent) == l_true);
|
||||
SASSERT(m_bits[v2][idx].var() == consequent.var());
|
||||
|
@ -801,8 +802,8 @@ namespace bv {
|
|||
find_wpos(v2);
|
||||
bool_var cv = consequent.var();
|
||||
atom* a = get_bv2a(cv);
|
||||
if (a && a->is_bit())
|
||||
for (auto curr : a->to_bit())
|
||||
if (a)
|
||||
for (auto curr : *a)
|
||||
if (propagate_eqc || find(curr.first) != find(v2) || curr.second != idx)
|
||||
m_prop_queue.push_back(propagation_item(curr));
|
||||
return true;
|
||||
|
|
|
@ -139,26 +139,10 @@ namespace bv {
|
|||
eq_occurs_it end() const { return eq_occurs_it(nullptr); }
|
||||
};
|
||||
|
||||
struct bit_atom;
|
||||
struct def_atom;
|
||||
struct eq_atom;
|
||||
class atom {
|
||||
public:
|
||||
|
||||
atom() {}
|
||||
virtual ~atom() {}
|
||||
virtual bool is_bit() const { return false; }
|
||||
virtual bool is_eq() const { return false; }
|
||||
bit_atom& to_bit();
|
||||
def_atom& to_def();
|
||||
eq_atom& to_eq();
|
||||
|
||||
};
|
||||
|
||||
struct var_pos_occ {
|
||||
var_pos m_vp;
|
||||
var_pos_occ * m_next;
|
||||
var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_vp(v, idx), m_next(next) {}
|
||||
var_pos_occ* m_next;
|
||||
var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ* next = nullptr) :m_vp(v, idx), m_next(next) {}
|
||||
};
|
||||
|
||||
class var_pos_it {
|
||||
|
@ -172,37 +156,24 @@ namespace bv {
|
|||
bool operator!=(var_pos_it const& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
struct bit_atom : public atom {
|
||||
struct atom {
|
||||
eq_occurs* m_eqs;
|
||||
var_pos_occ * m_occs;
|
||||
bit_atom() :m_eqs(nullptr), m_occs(nullptr) {}
|
||||
~bit_atom() override {}
|
||||
bool is_bit() const override { return true; }
|
||||
svector<std::pair<atom*, eq_occurs*>> m_bit2occ;
|
||||
literal m_var { sat::null_literal };
|
||||
literal m_def { sat::null_literal };
|
||||
atom() :m_eqs(nullptr), m_occs(nullptr) {}
|
||||
~atom() { m_bit2occ.clear(); }
|
||||
var_pos_it begin() const { return var_pos_it(m_occs); }
|
||||
var_pos_it end() const { return var_pos_it(nullptr); }
|
||||
bool is_fresh() const { return !m_occs && !m_eqs; }
|
||||
eqs_iterator eqs() const { return eqs_iterator(m_eqs); }
|
||||
};
|
||||
|
||||
struct eq_atom : public atom {
|
||||
eq_atom(){}
|
||||
~eq_atom() override { m_eqs.clear(); }
|
||||
bool is_bit() const override { return false; }
|
||||
bool is_eq() const override { return true; }
|
||||
svector<std::pair<bit_atom*,eq_occurs*>> m_eqs;
|
||||
};
|
||||
|
||||
struct def_atom : public atom {
|
||||
literal m_var;
|
||||
literal m_def;
|
||||
def_atom(literal v, literal d):m_var(v), m_def(d) {}
|
||||
~def_atom() override {}
|
||||
eqs_iterator eqs() const { return eqs_iterator(m_eqs); }
|
||||
};
|
||||
|
||||
struct propagation_item {
|
||||
var_pos m_vp { var_pos(0, 0) };
|
||||
bit_atom* m_atom{ nullptr };
|
||||
explicit propagation_item(bit_atom* a) : m_atom(a) {}
|
||||
atom* m_atom{ nullptr };
|
||||
explicit propagation_item(atom* a) : m_atom(a) {}
|
||||
explicit propagation_item(var_pos const& vp) : m_vp(vp) {}
|
||||
};
|
||||
|
||||
|
@ -253,22 +224,21 @@ namespace bv {
|
|||
sat::status status() const { return sat::status::th(m_is_redundant, get_id()); }
|
||||
void register_true_false_bit(theory_var v, unsigned i);
|
||||
void add_bit(theory_var v, sat::literal lit);
|
||||
bit_atom* mk_bit_atom(sat::bool_var b);
|
||||
eq_atom* mk_eq_atom(sat::bool_var b);
|
||||
atom* mk_atom(sat::bool_var b);
|
||||
void eq_internalized(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, sat::literal eq, euf::enode* n);
|
||||
void del_eq_occurs(bit_atom* a, eq_occurs* occ);
|
||||
void del_eq_occurs(atom* a, eq_occurs* occ);
|
||||
|
||||
void set_bit_eh(theory_var v, literal l, unsigned idx);
|
||||
void init_bits(expr* e, expr_ref_vector const & bits);
|
||||
void mk_bits(theory_var v);
|
||||
void add_def(sat::literal def, sat::literal l);
|
||||
bool internalize_circuit(app* a, theory_var v);
|
||||
bool internalize_circuit(app* a);
|
||||
void internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn);
|
||||
void internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn);
|
||||
void internalize_ac_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn);
|
||||
void internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn);
|
||||
void internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn);
|
||||
void internalize_num(app * n, theory_var v);
|
||||
void internalize_num(app * n);
|
||||
void internalize_concat(app * n);
|
||||
void internalize_bv2int(app* n);
|
||||
void internalize_int2bv(app* n);
|
||||
|
@ -278,7 +248,9 @@ namespace bv {
|
|||
void internalize_sub(app* n);
|
||||
void internalize_extract(app* n);
|
||||
void internalize_bit2bool(app* n);
|
||||
template<bool Signed>
|
||||
void internalize_udiv(app* n);
|
||||
void internalize_udiv_i(app* n);
|
||||
template<bool Signed, bool Reverse, bool Negated>
|
||||
void internalize_le(app* n);
|
||||
void assert_bv2int_axiom(app * n);
|
||||
void assert_int2bv_axiom(app* n);
|
||||
|
@ -286,23 +258,34 @@ namespace bv {
|
|||
|
||||
// delay internalize
|
||||
enum class internalize_mode {
|
||||
delay_i,
|
||||
no_delay_i,
|
||||
init_bits_i
|
||||
init_bits_only_i
|
||||
};
|
||||
|
||||
obj_map<expr, internalize_mode> m_delay_internalize;
|
||||
bool m_cheap_axioms{ true };
|
||||
bool should_bit_blast(expr * n);
|
||||
bool check_delay_internalized(euf::enode* n);
|
||||
bool check_mul(euf::enode* n);
|
||||
bool check_eval(euf::enode* n);
|
||||
bool check_delay_internalized(expr* e);
|
||||
bool check_mul(app* e);
|
||||
bool check_mul_invertibility(app* n, expr_ref_vector const& arg_values, expr* value);
|
||||
bool check_mul_zero(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2);
|
||||
bool check_mul_one(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2);
|
||||
bool check_mul_low_bits(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2);
|
||||
bool check_umul_no_overflow(app* n, expr_ref_vector const& arg_values, expr* value);
|
||||
bool check_bv_eval(euf::enode* n);
|
||||
bool check_bool_eval(euf::enode* n);
|
||||
void encode_msb_tail(expr* x, expr_ref_vector& xs);
|
||||
void encode_lsb_tail(expr* x, expr_ref_vector& xs);
|
||||
internalize_mode get_internalize_mode(expr* e);
|
||||
void set_delay_internalize(expr* e, internalize_mode mode);
|
||||
void eval_args(euf::enode* n, vector<rational>& args);
|
||||
|
||||
expr_ref eval_args(euf::enode* n, expr_ref_vector& eargs);
|
||||
expr_ref eval_bv(euf::enode* n);
|
||||
|
||||
// solving
|
||||
theory_var find(theory_var v) const { return m_find.find(v); }
|
||||
void find_wpos(theory_var v);
|
||||
void find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx);
|
||||
void find_new_diseq_axioms(atom& a, theory_var v, unsigned idx);
|
||||
void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx);
|
||||
bool get_fixed_value(theory_var v, numeral& result) const;
|
||||
void add_fixed_eq(theory_var v1, theory_var v2);
|
||||
|
@ -332,8 +315,7 @@ namespace bv {
|
|||
void asserted(literal l) override;
|
||||
sat::check_result check() override;
|
||||
void push_core() override;
|
||||
void pop_core(unsigned n) override;
|
||||
void pre_simplify() override;
|
||||
void pop_core(unsigned n) override;
|
||||
void simplify() override;
|
||||
bool set_root(literal l, literal r) override;
|
||||
void flush_roots() override;
|
||||
|
@ -343,7 +325,7 @@ namespace bv {
|
|||
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
|
||||
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
|
||||
void collect_statistics(statistics& st) const override;
|
||||
euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override;
|
||||
euf::th_solver* clone(sat::solver* s, euf::solver& ctx) override;
|
||||
extension* copy(sat::solver* s) override;
|
||||
void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {}
|
||||
void gc() override {}
|
||||
|
|
|
@ -132,6 +132,7 @@ namespace euf {
|
|||
SASSERT(m_egraph.find(e)->bool_var() == v);
|
||||
return lit;
|
||||
}
|
||||
TRACE("euf", tout << "attach " << v << " " << mk_bounded_pp(e, m) << "\n";);
|
||||
m_var2expr[v] = e;
|
||||
m_var_trail.push_back(v);
|
||||
enode* n = m_egraph.find(e);
|
||||
|
@ -299,20 +300,18 @@ namespace euf {
|
|||
if (m.is_ite(n->get_expr()))
|
||||
return true;
|
||||
|
||||
theory_id th_id = null_theory_id;
|
||||
for (auto p : euf::enode_th_vars(n)) {
|
||||
if (th_id == null_theory_id)
|
||||
th_id = p.get_id();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
if (th_id == null_theory_id)
|
||||
return false;
|
||||
|
||||
// the variable is shared if the equivalence class of n
|
||||
// contains a parent application.
|
||||
|
||||
for (euf::enode* parent : euf::enode_parents(n)) {
|
||||
family_id th_id = m.get_basic_family_id();
|
||||
for (auto p : euf::enode_th_vars(n)) {
|
||||
if (m.get_basic_family_id() != p.get_id()) {
|
||||
th_id = p.get_id();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (enode* parent : euf::enode_parents(n)) {
|
||||
app* p = to_app(parent->get_expr());
|
||||
family_id fid = p->get_family_id();
|
||||
if (fid != th_id && fid != m.get_basic_family_id())
|
||||
|
@ -345,9 +344,13 @@ namespace euf {
|
|||
// the theories of (array int int) and (array (array int int) int).
|
||||
// Remark: The inconsistency is not going to be detected if they are
|
||||
// not marked as shared.
|
||||
return true;
|
||||
// TODO
|
||||
// return get_theory(th_id)->is_shared(l->get_var());
|
||||
|
||||
for (auto p : euf::enode_th_vars(n))
|
||||
if (fid2solver(p.get_id())->is_shared(p.get_var()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
expr_ref solver::mk_eq(expr* e1, expr* e2) {
|
||||
|
|
|
@ -44,20 +44,55 @@ namespace euf {
|
|||
|
||||
void solver::collect_dependencies(deps_t& deps) {
|
||||
for (enode* n : m_egraph.nodes()) {
|
||||
if (n->num_args() == 0) {
|
||||
deps.insert(n, nullptr);
|
||||
continue;
|
||||
}
|
||||
auto* mb = expr2solver(n->get_expr());
|
||||
auto* mb = sort2solver(m.get_sort(n->get_expr()));
|
||||
if (mb)
|
||||
mb->add_dep(n, deps);
|
||||
else
|
||||
deps.insert(n, nullptr);
|
||||
}
|
||||
|
||||
TRACE("euf",
|
||||
for (auto const& d : deps.deps())
|
||||
if (d.m_value) {
|
||||
tout << mk_bounded_pp(d.m_key->get_expr(), m) << ":\n";
|
||||
for (auto* n : *d.m_value)
|
||||
tout << " " << mk_bounded_pp(n->get_expr(), m) << "\n";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
class solver::user_sort {
|
||||
solver& s;
|
||||
ast_manager& m;
|
||||
model_ref& mdl;
|
||||
expr_ref_vector& values;
|
||||
user_sort_factory factory;
|
||||
scoped_ptr_vector<expr_ref_vector> sort_values;
|
||||
obj_map<sort, expr_ref_vector*> sort2values;
|
||||
public:
|
||||
user_sort(solver& s, expr_ref_vector& values, model_ref& mdl):
|
||||
s(s), m(s.m), mdl(mdl), values(values), factory(m) {}
|
||||
|
||||
~user_sort() {
|
||||
for (auto kv : sort2values)
|
||||
mdl->register_usort(kv.m_key, kv.m_value->size(), kv.m_value->c_ptr());
|
||||
}
|
||||
|
||||
void add(unsigned id, sort* srt) {
|
||||
expr_ref value(factory.get_fresh_value(srt), m);
|
||||
values.set(id, value);
|
||||
expr_ref_vector* vals = nullptr;
|
||||
if (!sort2values.find(srt, vals)) {
|
||||
vals = alloc(expr_ref_vector, m);
|
||||
sort2values.insert(srt, vals);
|
||||
sort_values.push_back(vals);
|
||||
}
|
||||
vals->push_back(value);
|
||||
}
|
||||
};
|
||||
|
||||
void solver::dependencies2values(deps_t& deps, expr_ref_vector& values, model_ref& mdl) {
|
||||
user_sort_factory user_sort(m);
|
||||
user_sort user_sort(*this, values, mdl);
|
||||
for (enode* n : deps.top_sorted()) {
|
||||
unsigned id = n->get_root_id();
|
||||
if (values.get(id, nullptr))
|
||||
|
@ -94,16 +129,15 @@ namespace euf {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
auto* mb = expr2solver(e);
|
||||
if (mb)
|
||||
mb->add_value(n, *mdl, values);
|
||||
else if (m.is_uninterp(m.get_sort(e))) {
|
||||
expr* v = user_sort.get_fresh_value(m.get_sort(e));
|
||||
values.set(id, v);
|
||||
}
|
||||
else if ((mb = sort2solver(m.get_sort(e))))
|
||||
mb->add_value(n, *mdl, values);
|
||||
else {
|
||||
TRACE("euf", tout << "value for " << mk_bounded_pp(e, m) << "\n";);
|
||||
sort* srt = m.get_sort(e);
|
||||
if (m.is_uninterp(srt))
|
||||
user_sort.add(id, srt);
|
||||
else if (auto* mbS = sort2solver(srt))
|
||||
mbS->add_value(n, *mdl, values);
|
||||
else if (auto* mbE = expr2solver(e))
|
||||
mbE->add_value(n, *mdl, values);
|
||||
else {
|
||||
IF_VERBOSE(1, verbose_stream() << "no model values created for " << mk_pp(e, m) << "\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,8 @@ namespace euf {
|
|||
}
|
||||
|
||||
void solver::add_root(unsigned n, sat::literal const* lits) {
|
||||
bool_var v = s().add_var(false);
|
||||
ensure_dual_solver();
|
||||
m_dual_solver->add_root(sat::literal(v, false), n, lits);
|
||||
m_dual_solver->add_root(n, lits);
|
||||
}
|
||||
|
||||
void solver::add_aux(unsigned n, sat::literal const* lits) {
|
||||
|
@ -70,9 +69,26 @@ namespace euf {
|
|||
m_relevant_expr_ids.setx(e->get_id(), true, false);
|
||||
if (!is_app(e))
|
||||
continue;
|
||||
expr* c = nullptr, *th = nullptr, *el = nullptr;
|
||||
if (m.is_ite(e, c, th, el)) {
|
||||
sat::literal lit = expr2literal(c);
|
||||
todo.push_back(c);
|
||||
switch (s().value(lit)) {
|
||||
case l_true:
|
||||
todo.push_back(th);
|
||||
break;
|
||||
case l_false:
|
||||
todo.push_back(el);
|
||||
break;
|
||||
default:
|
||||
todo.push_back(th);
|
||||
todo.push_back(el);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (expr* arg : *to_app(e))
|
||||
if (!visited.get(arg->get_id(), false))
|
||||
todo.push_back(arg);
|
||||
todo.push_back(arg);
|
||||
}
|
||||
|
||||
TRACE("euf",
|
||||
|
|
|
@ -22,6 +22,7 @@ Author:
|
|||
#include "sat/smt/ba_solver.h"
|
||||
#include "sat/smt/bv_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/smt/array_solver.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
|
@ -79,6 +80,7 @@ namespace euf {
|
|||
return nullptr;
|
||||
pb_util pb(m);
|
||||
bv_util bvu(m);
|
||||
array_util au(m);
|
||||
if (pb.get_family_id() == fid) {
|
||||
ext = alloc(sat::ba_solver, *this, fid);
|
||||
if (use_drat())
|
||||
|
@ -89,6 +91,11 @@ namespace euf {
|
|||
if (use_drat())
|
||||
s().get_drat().add_theory(fid, symbol("bv"));
|
||||
}
|
||||
else if (au.get_family_id() == fid) {
|
||||
ext = alloc(array::solver, *this, fid);
|
||||
if (use_drat())
|
||||
s().get_drat().add_theory(fid, symbol("array"));
|
||||
}
|
||||
if (ext) {
|
||||
ext->set_solver(m_solver);
|
||||
ext->push_scopes(s().num_scopes());
|
||||
|
@ -210,10 +217,11 @@ namespace euf {
|
|||
|
||||
void solver::asserted(literal l) {
|
||||
expr* e = m_var2expr.get(l.var(), nullptr);
|
||||
if (!e)
|
||||
if (!e) {
|
||||
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
|
||||
return;
|
||||
|
||||
TRACE("euf", tout << "asserted: " << mk_bounded_pp(e, m) << " := " << l << "@" << s().scope_lvl() << "\n";);
|
||||
}
|
||||
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " := " << mk_bounded_pp(e, m) << "\n";);
|
||||
euf::enode* n = m_egraph.find(e);
|
||||
if (!n)
|
||||
return;
|
||||
|
@ -370,18 +378,30 @@ namespace euf {
|
|||
m_egraph.push();
|
||||
}
|
||||
|
||||
void solver::user_push() {
|
||||
push();
|
||||
if (m_dual_solver)
|
||||
m_dual_solver->push();
|
||||
}
|
||||
|
||||
void solver::user_pop(unsigned n) {
|
||||
pop(n);
|
||||
if (m_dual_solver)
|
||||
m_dual_solver->pop(n);
|
||||
}
|
||||
|
||||
void solver::pop(unsigned n) {
|
||||
start_reinit(n);
|
||||
m_egraph.pop(n);
|
||||
m_trail.pop_scope(n);
|
||||
for (auto* e : m_solvers)
|
||||
e->pop(n);
|
||||
si.pop(n);
|
||||
m_egraph.pop(n);
|
||||
scope const & s = m_scopes[m_scopes.size() - n];
|
||||
for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; )
|
||||
m_var2expr[m_var_trail[i]] = nullptr;
|
||||
m_var_trail.shrink(s.m_var_lim);
|
||||
m_trail.pop_scope(n);
|
||||
m_scopes.shrink(m_scopes.size() - n);
|
||||
si.pop(n);
|
||||
SASSERT(m_egraph.num_scopes() == m_scopes.size());
|
||||
TRACE("euf", tout << "pop to: " << m_scopes.size() << "\n";);
|
||||
}
|
||||
|
@ -424,8 +444,9 @@ namespace euf {
|
|||
if (replay.m.empty())
|
||||
return;
|
||||
|
||||
TRACE("euf", for (auto const& kv : replay.m) tout << "replay: " << kv.m_value << " " << mk_bounded_pp(kv.m_key, m) << "\n";);
|
||||
TRACE("euf", for (auto const& kv : replay.m) tout << kv.m_value << "\n";);
|
||||
for (auto const& kv : replay.m) {
|
||||
TRACE("euf", tout << "replay: " << kv.m_value << " " << mk_bounded_pp(kv.m_key, m) << "\n";);
|
||||
sat::literal lit;
|
||||
expr* e = kv.m_key;
|
||||
if (si.is_bool_op(e))
|
||||
|
@ -557,7 +578,7 @@ namespace euf {
|
|||
for (unsigned i = 0; i < m_id2solver.size(); ++i) {
|
||||
auto* e = m_id2solver[i];
|
||||
if (e)
|
||||
r->add_solver(i, e->fresh(s, *r));
|
||||
r->add_solver(i, e->clone(s, *r));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace euf {
|
|||
class solver : public sat::extension, public th_internalizer, public th_decompile {
|
||||
typedef top_sort<euf::enode> deps_t;
|
||||
friend class ackerman;
|
||||
class user_sort;
|
||||
// friend class sat::ba_solver;
|
||||
struct stats {
|
||||
unsigned m_ackerman;
|
||||
|
@ -129,7 +130,7 @@ namespace euf {
|
|||
th_solver* func_decl2solver(func_decl* f) { return get_solver(f->get_family_id(), f); }
|
||||
th_solver* expr2solver(expr* e);
|
||||
th_solver* bool_var2solver(sat::bool_var v);
|
||||
th_solver* fid2solver(family_id fid) { return m_id2solver.get(fid, nullptr); }
|
||||
th_solver* fid2solver(family_id fid) const { return m_id2solver.get(fid, nullptr); }
|
||||
void add_solver(family_id fid, th_solver* th);
|
||||
void init_ackerman();
|
||||
|
||||
|
@ -234,6 +235,8 @@ namespace euf {
|
|||
sat::check_result check() override;
|
||||
void push() override;
|
||||
void pop(unsigned n) override;
|
||||
void user_push();
|
||||
void user_pop(unsigned n);
|
||||
void pre_simplify() override;
|
||||
void simplify() override;
|
||||
// have a way to replace l by r in all constraints
|
||||
|
|
|
@ -65,10 +65,11 @@ namespace sat {
|
|||
return literal(m_var2ext[lit.var()], lit.sign());
|
||||
}
|
||||
|
||||
void dual_solver::add_root(literal lit, unsigned sz, literal const* clause) {
|
||||
void dual_solver::add_root(unsigned sz, literal const* clause) {
|
||||
literal root(m_solver.mk_var(), false);
|
||||
for (unsigned i = 0; i < sz; ++i)
|
||||
m_solver.mk_clause(ext2lit(lit), ~ext2lit(clause[i]), status::input());
|
||||
m_roots.push_back(~ext2lit(lit));
|
||||
m_solver.mk_clause(root, ~ext2lit(clause[i]), status::input());
|
||||
m_roots.push_back(~root);
|
||||
}
|
||||
|
||||
void dual_solver::add_aux(unsigned sz, literal const* clause) {
|
||||
|
@ -89,7 +90,7 @@ namespace sat {
|
|||
if (is_sat == l_false)
|
||||
for (literal lit : m_solver.get_core())
|
||||
m_core.push_back(lit2ext(lit));
|
||||
TRACE("euf", m_solver.display(tout << m_core << "\n"););
|
||||
TRACE("euf", m_solver.display(tout << "is-sat: " << is_sat << " core: " << m_core << "\n"););
|
||||
m_solver.user_pop(1);
|
||||
return is_sat == l_false;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace sat {
|
|||
* Add a root clause from the input problem.
|
||||
* At least one literal has to be satisfied in every root.
|
||||
*/
|
||||
void add_root(literal lit, unsigned sz, literal const* clause);
|
||||
void add_root(unsigned sz, literal const* clause);
|
||||
|
||||
/*
|
||||
* Add auxiliary clauses that originate from compiling definitions.
|
||||
|
|
|
@ -116,23 +116,43 @@ namespace euf {
|
|||
pop_core(n);
|
||||
}
|
||||
|
||||
sat::status th_euf_solver::mk_status() {
|
||||
return sat::status::th(m_is_redundant, get_id());
|
||||
}
|
||||
|
||||
bool th_euf_solver::add_unit(sat::literal lit) {
|
||||
return !is_true(lit) && (ctx.s().add_clause(1, &lit, sat::status::th(m_is_redundant, get_id())), true);
|
||||
bool was_true = is_true(lit);
|
||||
ctx.s().add_clause(1, &lit, mk_status());
|
||||
return !was_true;
|
||||
}
|
||||
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {
|
||||
bool was_true = is_true(a, b);
|
||||
sat::literal lits[2] = { a, b };
|
||||
return !is_true(a, b) && (ctx.s().add_clause(2, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
ctx.s().add_clause(2, lits, mk_status());
|
||||
return !was_true;
|
||||
}
|
||||
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
|
||||
bool was_true = is_true(a, b, c);
|
||||
sat::literal lits[3] = { a, b, c };
|
||||
return !is_true(a, b, c) && (ctx.s().add_clause(3, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
ctx.s().add_clause(3, lits, mk_status());
|
||||
return !was_true;
|
||||
}
|
||||
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
|
||||
bool was_true = is_true(a, b, c, d);
|
||||
sat::literal lits[4] = { a, b, c, d };
|
||||
return !is_true(a, b, c, d) && (ctx.s().add_clause(4, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
ctx.s().add_clause(4, lits, mk_status());
|
||||
return !was_true;
|
||||
}
|
||||
|
||||
bool th_euf_solver::add_clause(sat::literal_vector const& lits) {
|
||||
bool was_true = false;
|
||||
for (auto lit : lits)
|
||||
was_true |= is_true(lit);
|
||||
s().add_clause(lits.size(), lits.c_ptr(), mk_status());
|
||||
return !was_true;
|
||||
}
|
||||
|
||||
bool th_euf_solver::is_true(sat::literal lit) {
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace euf {
|
|||
public:
|
||||
th_solver(ast_manager& m, euf::theory_id id): extension(id), m(m) {}
|
||||
|
||||
virtual th_solver* fresh(sat::solver* s, euf::solver& ctx) = 0;
|
||||
virtual th_solver* clone(sat::solver* s, euf::solver& ctx) = 0;
|
||||
|
||||
virtual void new_eq_eh(euf::th_eq const& eq) {}
|
||||
|
||||
|
@ -121,11 +121,13 @@ namespace euf {
|
|||
region& get_region();
|
||||
|
||||
|
||||
sat::status mk_status();
|
||||
bool add_unit(sat::literal lit);
|
||||
bool add_clause(sat::literal lit) { return add_unit(lit); }
|
||||
bool add_clause(sat::literal a, sat::literal b);
|
||||
bool add_clause(sat::literal a, sat::literal b, sat::literal c);
|
||||
bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
|
||||
bool add_clause(sat::literal_vector const& lits);
|
||||
|
||||
bool is_true(sat::literal lit);
|
||||
bool is_true(sat::literal a, sat::literal b) { return is_true(a) || is_true(b); }
|
||||
|
|
|
@ -146,7 +146,7 @@ namespace user {
|
|||
return display_justification(out, idx);
|
||||
}
|
||||
|
||||
euf::th_solver* solver::fresh(sat::solver* dst_s, euf::solver& dst_ctx) {
|
||||
euf::th_solver* solver::clone(sat::solver* dst_s, euf::solver& dst_ctx) {
|
||||
auto* result = alloc(solver, dst_ctx);
|
||||
result->set_solver(dst_s);
|
||||
ast_translation tr(m, dst_ctx.get_manager(), false);
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace user {
|
|||
std::ostream& display(std::ostream& out) const override;
|
||||
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
|
||||
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
|
||||
euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override;
|
||||
euf::th_solver* clone(sat::solver* s, euf::solver& ctx) override;
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -791,7 +791,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
sat::literal result = m_result_stack.back();
|
||||
m_result_stack.pop_back();
|
||||
if (!result.sign() && m_map.to_bool_var(n) == sat::null_bool_var)
|
||||
m_map.insert(n, result.var());
|
||||
m_map.insert(n, result.var());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue