3
0
Fork 0
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:
Nikolaj Bjorner 2020-09-28 19:24:16 -07:00 committed by GitHub
parent 25724401cf
commit 367e5fdd52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 1343 additions and 924 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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));

View file

@ -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));
}

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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())

View file

@ -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);
}

View file

@ -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;

View file

@ -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 {}

View file

@ -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) {

View file

@ -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");
}
}

View file

@ -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",

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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.

View file

@ -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) {

View file

@ -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); }

View file

@ -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);

View file

@ -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;
};
};

View file

@ -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;
}