3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-31 11:42:28 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2022-09-23 17:14:26 +02:00
commit 1df749ad33
368 changed files with 12363 additions and 6152 deletions

View file

@ -22,6 +22,7 @@ z3_add_component(sat_smt
euf_invariant.cpp
euf_model.cpp
euf_proof.cpp
euf_proof_checker.cpp
euf_relevancy.cpp
euf_solver.cpp
fpa_solver.cpp

View file

@ -264,11 +264,12 @@ namespace arith {
SASSERT(k1 != k2 || kind1 != kind2);
auto bin_clause = [&](sat::literal l1, sat::literal l2) {
sat::proof_hint* bound_params = nullptr;
arith_proof_hint* bound_params = nullptr;
if (ctx.use_drat()) {
bound_params = &m_farkas2;
m_farkas2.m_literals[0] = std::make_pair(rational(1), ~l1);
m_farkas2.m_literals[1] = std::make_pair(rational(1), ~l2);
m_arith_hint.set_type(ctx, hint_type::farkas_h);
m_arith_hint.add_lit(rational(1), ~l1);
m_arith_hint.add_lit(rational(1), ~l2);
bound_params = m_arith_hint.mk(ctx);
}
add_clause(l1, l2, bound_params);
};

View file

@ -15,6 +15,8 @@ Author:
--*/
#include "ast/ast_util.h"
#include "ast/scoped_proof.h"
#include "sat/smt/euf_solver.h"
#include "sat/smt/arith_solver.h"
@ -81,7 +83,6 @@ namespace arith {
}
void solver::explain_assumptions() {
m_arith_hint.reset();
unsigned i = 0;
for (auto const & ev : m_explanation) {
++i;
@ -91,14 +92,12 @@ namespace arith {
switch (m_constraint_sources[idx]) {
case inequality_source: {
literal lit = m_inequalities[idx];
m_arith_hint.m_literals.push_back({ev.coeff(), lit});
m_arith_hint.add_lit(ev.coeff(), lit);
break;
}
case equality_source: {
auto [u, v] = m_equalities[idx];
ctx.drat_log_expr(u->get_expr());
ctx.drat_log_expr(v->get_expr());
m_arith_hint.m_eqs.push_back({u->get_expr_id(), v->get_expr_id()});
m_arith_hint.add_eq(u, v);
break;
}
default:
@ -115,22 +114,65 @@ namespace arith {
* such that there is a r >= 1
* (r1*a1+..+r_k*a_k) = r*a, (r1*b1+..+r_k*b_k) <= r*b
*/
sat::proof_hint const* solver::explain(sat::hint_type ty, sat::literal lit) {
arith_proof_hint const* solver::explain(hint_type ty, sat::literal lit) {
if (!ctx.use_drat())
return nullptr;
m_arith_hint.m_ty = ty;
m_arith_hint.set_type(ctx, ty);
explain_assumptions();
if (lit != sat::null_literal)
m_arith_hint.m_literals.push_back({rational(1), ~lit});
return &m_arith_hint;
m_arith_hint.add_lit(rational(1), ~lit);
return m_arith_hint.mk(ctx);
}
sat::proof_hint const* solver::explain_implied_eq(euf::enode* a, euf::enode* b) {
arith_proof_hint const* solver::explain_implied_eq(euf::enode* a, euf::enode* b) {
if (!ctx.use_drat())
return nullptr;
m_arith_hint.m_ty = sat::hint_type::implied_eq_h;
m_arith_hint.set_type(ctx, hint_type::implied_eq_h);
explain_assumptions();
m_arith_hint.m_diseqs.push_back({a->get_expr_id(), b->get_expr_id()});
return &m_arith_hint;
m_arith_hint.add_diseq(a, b);
return m_arith_hint.mk(ctx);
}
expr* arith_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
family_id fid = m.get_family_id("arith");
arith_util arith(m);
solver& a = dynamic_cast<solver&>(*s.fid2solver(fid));
char const* name;
switch (m_ty) {
case hint_type::farkas_h:
name = "farkas";
break;
case hint_type::bound_h:
name = "bound";
break;
case hint_type::implied_eq_h:
name = "implied-eq";
break;
}
rational lc(1);
for (unsigned i = m_lit_head; i < m_lit_tail; ++i)
lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first));
expr_ref_vector args(m);
sort_ref_vector sorts(m);
for (unsigned i = m_lit_head; i < m_lit_tail; ++i) {
auto const& [coeff, lit] = a.m_arith_hint.lit(i);
args.push_back(arith.mk_int(abs(coeff*lc)));
args.push_back(s.literal2expr(lit));
}
for (unsigned i = m_eq_head; i < m_eq_tail; ++i) {
auto const& [x, y, is_eq] = a.m_arith_hint.eq(i);
expr_ref eq(m.mk_eq(x->get_expr(), y->get_expr()), m);
if (!is_eq) eq = m.mk_not(eq);
args.push_back(arith.mk_int(1));
args.push_back(eq);
}
for (expr* arg : args)
sorts.push_back(arg->get_sort());
sort* range = m.mk_proof_sort();
func_decl* d = m.mk_func_decl(symbol(name), args.size(), sorts.data(), range);
expr* r = m.mk_app(d, args);
return r;
}
}

View file

@ -1,5 +1,5 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Copyright (c) 2022 Microsoft Corporation
Module Name:
@ -11,7 +11,15 @@ Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2020-09-08
Nikolaj Bjorner (nbjorner) 2022-08-28
Notes:
The module assumes a limited repertoire of arithmetic proof rules.
- farkas - inequalities, equalities and disequalities with coefficients
- implied-eq - last literal is a disequality. The literals before imply the corresponding equality.
- bound - last literal is a bound. It is implied by prior literals.
--*/
#pragma once
@ -19,11 +27,12 @@ Author:
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/arith_decl_plugin.h"
#include "sat/smt/euf_proof_checker.h"
namespace arith {
class proof_checker {
class proof_checker : public euf::proof_checker_plugin {
struct row {
obj_map<expr, rational> m_coeffs;
rational m_coeff;
@ -32,16 +41,19 @@ namespace arith {
m_coeff = 0;
}
};
ast_manager& m;
arith_util a;
arith_util a;
vector<std::pair<rational, expr*>> m_todo;
bool m_strict = false;
row m_ineq;
row m_conseq;
vector<row> m_eqs;
vector<row> m_ineqs;
vector<row> m_diseqs;
bool m_strict = false;
row m_ineq;
row m_conseq;
vector<row> m_eqs;
vector<row> m_ineqs;
vector<row> m_diseqs;
symbol m_farkas;
symbol m_implied_eq;
symbol m_bound;
void add(row& r, expr* v, rational const& coeff) {
rational coeff1;
@ -131,11 +143,15 @@ namespace arith {
SASSERT(m_todo.empty());
m_todo.push_back({ mul, e });
rational coeff1;
expr* e1, *e2;
expr* e1, *e2, *e3;
for (unsigned i = 0; i < m_todo.size(); ++i) {
auto [coeff, e] = m_todo[i];
if (a.is_mul(e, e1, e2) && a.is_numeral(e1, coeff1))
m_todo.push_back({coeff*coeff1, e2});
else if (a.is_mul(e, e1, e2) && a.is_uminus(e1, e3) && a.is_numeral(e3, coeff1))
m_todo.push_back({-coeff*coeff1, e2});
else if (a.is_mul(e, e1, e2) && a.is_uminus(e2, e3) && a.is_numeral(e3, coeff1))
m_todo.push_back({ -coeff * coeff1, e1 });
else if (a.is_mul(e, e1, e2) && a.is_numeral(e2, coeff1))
m_todo.push_back({coeff*coeff1, e1});
else if (a.is_add(e))
@ -149,6 +165,8 @@ namespace arith {
}
else if (a.is_numeral(e, coeff1))
r.m_coeff += coeff*coeff1;
else if (a.is_uminus(e, e1) && a.is_numeral(e1, coeff1))
r.m_coeff -= coeff*coeff1;
else
add(r, e, coeff);
}
@ -296,10 +314,16 @@ namespace arith {
rows.push_back(row());
return rows.back();
}
public:
proof_checker(ast_manager& m): m(m), a(m) {}
proof_checker(ast_manager& m):
m(m),
a(m),
m_farkas("farkas"),
m_implied_eq("implied-eq"),
m_bound("bound") {}
~proof_checker() override {}
void reset() {
m_ineq.reset();
@ -313,7 +337,8 @@ namespace arith {
bool add_ineq(rational const& coeff, expr* e, bool sign) {
if (!m_diseqs.empty())
return add_literal(fresh(m_ineqs), abs(coeff), e, sign);
return add_literal(m_ineq, abs(coeff), e, sign);
else
return add_literal(m_ineq, abs(coeff), e, sign);
}
bool add_conseq(rational const& coeff, expr* e, bool sign) {
@ -350,6 +375,76 @@ namespace arith {
return out;
}
bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) override {
reset();
expr_mark pos, neg;
for (expr* e : clause)
if (m.is_not(e, e))
neg.mark(e, true);
else
pos.mark(e, true);
if (jst->get_name() != m_farkas &&
jst->get_name() != m_bound &&
jst->get_name() != m_implied_eq) {
IF_VERBOSE(0, verbose_stream() << "unhandled inference " << mk_pp(jst, m) << "\n");
return false;
}
bool is_bound = jst->get_name() == m_bound;
bool even = true;
rational coeff;
expr* x, * y;
unsigned j = 0;
for (expr* arg : *jst) {
if (even) {
if (!a.is_numeral(arg, coeff)) {
IF_VERBOSE(0, verbose_stream() << "not numeral " << mk_pp(jst, m) << "\n");
return false;
}
}
else {
bool sign = m.is_not(arg, arg);
if (a.is_le(arg) || a.is_lt(arg) || a.is_ge(arg) || a.is_gt(arg)) {
if (is_bound && j + 1 == jst->get_num_args())
add_conseq(coeff, arg, sign);
else
add_ineq(coeff, arg, sign);
}
else if (m.is_eq(arg, x, y)) {
if (sign)
add_diseq(x, y);
else
add_eq(x, y);
}
else {
IF_VERBOSE(0, verbose_stream() << "not a recognized arithmetical relation " << mk_pp(arg, m) << "\n");
return false;
}
if (sign && !pos.is_marked(arg)) {
units.push_back(m.mk_not(arg));
pos.mark(arg, false);
}
else if (!sign && !neg.is_marked(arg)) {
units.push_back(arg);
neg.mark(arg, false);
}
}
even = !even;
++j;
}
if (check())
return true;
IF_VERBOSE(0, verbose_stream() << "did not check condition\n" << mk_pp(jst, m) << "\n"; display(verbose_stream()); );
return false;
}
void register_plugins(euf::proof_checker& pc) override {
pc.register_plugin(m_farkas, this);
pc.register_plugin(m_bound, this);
pc.register_plugin(m_implied_eq, this);
}
};

View file

@ -39,8 +39,6 @@ namespace arith {
lp().settings().set_random_seed(get_config().m_random_seed);
m_lia = alloc(lp::int_solver, *m_solver.get());
m_farkas2.m_ty = sat::hint_type::farkas_h;
m_farkas2.m_literals.resize(2);
}
solver::~solver() {
@ -197,11 +195,12 @@ namespace arith {
reset_evidence();
m_core.push_back(lit1);
TRACE("arith", tout << lit2 << " <- " << m_core << "\n";);
sat::proof_hint* ph = nullptr;
arith_proof_hint* ph = nullptr;
if (ctx.use_drat()) {
ph = &m_farkas2;
m_farkas2.m_literals[0] = std::make_pair(rational(1), lit1);
m_farkas2.m_literals[1] = std::make_pair(rational(1), ~lit2);
m_arith_hint.set_type(ctx, hint_type::farkas_h);
m_arith_hint.add_lit(rational(1), lit1);
m_arith_hint.add_lit(rational(1), ~lit2);
ph = m_arith_hint.mk(ctx);
}
assign(lit2, m_core, m_eqs, ph);
++m_stats.m_bounds_propagations;
@ -262,7 +261,7 @@ namespace arith {
TRACE("arith", for (auto lit : m_core) tout << lit << ": " << s().value(lit) << "\n";);
DEBUG_CODE(for (auto lit : m_core) { VERIFY(s().value(lit) == l_true); });
++m_stats.m_bound_propagations1;
assign(lit, m_core, m_eqs, explain(sat::hint_type::bound_h, lit));
assign(lit, m_core, m_eqs, explain(hint_type::bound_h, lit));
}
if (should_refine_bounds() && first)
@ -378,7 +377,7 @@ namespace arith {
reset_evidence();
m_explanation.clear();
lp().explain_implied_bound(be, m_bp);
assign(bound, m_core, m_eqs, explain(sat::hint_type::farkas_h, bound));
assign(bound, m_core, m_eqs, explain(hint_type::farkas_h, bound));
}
@ -386,20 +385,16 @@ namespace arith {
TRACE("arith", tout << b << "\n";);
lp::constraint_index ci = b.get_constraint(is_true);
lp().activate(ci);
if (is_infeasible()) {
if (is_infeasible())
return;
}
lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true);
if (k == lp::LT || k == lp::LE) {
if (k == lp::LT || k == lp::LE)
++m_stats.m_assert_lower;
}
else {
else
++m_stats.m_assert_upper;
}
inf_rational value = b.get_value(is_true);
if (propagate_eqs() && value.is_rational()) {
if (propagate_eqs() && value.is_rational())
propagate_eqs(b.tv(), ci, k, b, value.get_rational());
}
#if 0
if (propagation_mode() != BP_NONE)
lp().mark_rows_for_bound_prop(b.tv().id());
@ -617,8 +612,7 @@ namespace arith {
verbose_stream() << eval << " " << value << " " << ctx.bpp(n) << "\n";
verbose_stream() << n->bool_var() << " " << n->value() << " " << get_phase(n->bool_var()) << " " << ctx.bpp(n) << "\n";
verbose_stream() << *b << "\n";);
IF_VERBOSE(0, ctx.display(verbose_stream()));
IF_VERBOSE(0, verbose_stream() << mdl << "\n");
IF_VERBOSE(0, ctx.display_validation_failure(verbose_stream(), mdl, n));
UNREACHABLE();
}
}
@ -1119,16 +1113,23 @@ namespace arith {
}
bool solver::check_delayed_eqs() {
for (auto p : m_delayed_eqs) {
bool found_diseq = false;
if (m_delayed_eqs_qhead == m_delayed_eqs.size())
return true;
force_push();
ctx.push(value_trail<unsigned>(m_delayed_eqs_qhead));
for (; m_delayed_eqs_qhead < m_delayed_eqs.size(); ++ m_delayed_eqs_qhead) {
auto p = m_delayed_eqs[m_delayed_eqs_qhead];
auto const& e = p.first;
if (p.second)
new_eq_eh(e);
else if (is_eq(e.v1(), e.v2())) {
mk_diseq_axiom(e);
return false;
found_diseq = true;
break;
}
}
return true;
}
return !found_diseq;
}
lbool solver::check_lia() {
@ -1178,7 +1179,7 @@ namespace arith {
app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper());
IF_VERBOSE(4, verbose_stream() << "cut " << b << "\n");
literal lit = expr2literal(b);
assign(lit, m_core, m_eqs, explain(sat::hint_type::bound_h, lit));
assign(lit, m_core, m_eqs, explain(hint_type::bound_h, lit));
lia_check = l_false;
break;
}
@ -1200,7 +1201,7 @@ namespace arith {
return lia_check;
}
void solver::assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, sat::proof_hint const* pma) {
void solver::assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, euf::th_proof_hint const* pma) {
if (core.size() < small_lemma_size() && eqs.empty()) {
m_core2.reset();
for (auto const& c : core)
@ -1247,7 +1248,7 @@ namespace arith {
for (literal& c : m_core)
c.neg();
add_clause(m_core, explain(sat::hint_type::farkas_h));
add_clause(m_core, explain(hint_type::farkas_h));
}
bool solver::is_infeasible() const {

View file

@ -48,8 +48,61 @@ namespace arith {
typedef sat::literal_vector literal_vector;
typedef lp_api::bound<sat::literal> api_bound;
enum class hint_type {
farkas_h,
bound_h,
implied_eq_h
};
struct arith_proof_hint : public euf::th_proof_hint {
hint_type m_ty;
unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail;
arith_proof_hint(hint_type t, unsigned lh, unsigned lt, unsigned eh, unsigned et):
m_ty(t), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {}
expr* get_hint(euf::solver& s) const override;
};
class arith_proof_hint_builder {
vector<std::pair<rational, literal>> m_literals;
svector<std::tuple<euf::enode*,euf::enode*,bool>> m_eqs;
hint_type m_ty;
unsigned m_lit_head = 0, m_lit_tail = 0, m_eq_head = 0, m_eq_tail = 0;
void reset() { m_lit_head = m_lit_tail; m_eq_head = m_eq_tail; }
void add(euf::enode* a, euf::enode* b, bool is_eq) {
if (m_eq_tail < m_eqs.size())
m_eqs[m_eq_tail] = std::tuple(a, b, is_eq);
else
m_eqs.push_back(std::tuple(a, b, is_eq));
m_eq_tail++;
}
public:
void set_type(euf::solver& ctx, hint_type ty) {
ctx.push(value_trail<unsigned>(m_eq_tail));
ctx.push(value_trail<unsigned>(m_lit_tail));
m_ty = ty;
reset();
}
void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); }
void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
void add_lit(rational const& coeff, literal lit) {
if (m_lit_tail < m_literals.size())
m_literals[m_lit_tail] = {coeff, lit};
else
m_literals.push_back({coeff, lit});
m_lit_tail++;
}
std::pair<rational, literal> const& lit(unsigned i) const { return m_literals[i]; }
std::tuple<enode*, enode*, bool> const& eq(unsigned i) const { return m_eqs[i]; }
arith_proof_hint* mk(euf::solver& s) {
return new (s.get_region()) arith_proof_hint(m_ty, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail);
}
};
class solver : public euf::th_euf_solver {
friend struct arith_proof_hint;
struct scope {
unsigned m_bounds_lim;
unsigned m_idiv_lim;
@ -165,6 +218,7 @@ namespace arith {
svector<euf::enode_pair> m_equalities; // asserted rows corresponding to equalities.
svector<theory_var> m_definitions; // asserted rows corresponding to definitions
svector<std::pair<euf::th_eq, bool>> m_delayed_eqs;
unsigned m_delayed_eqs_qhead = 0;
literal_vector m_asserted;
expr* m_not_handled{ nullptr };
@ -414,15 +468,15 @@ namespace arith {
void set_conflict();
void set_conflict_or_lemma(literal_vector const& core, bool is_conflict);
void set_evidence(lp::constraint_index idx);
void assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, sat::proof_hint const* pma);
void assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, euf::th_proof_hint const* pma);
void false_case_of_check_nla(const nla::lemma& l);
void dbg_finalize_model(model& mdl);
sat::proof_hint m_arith_hint;
sat::proof_hint m_farkas2;
sat::proof_hint const* explain(sat::hint_type ty, sat::literal lit = sat::null_literal);
sat::proof_hint const* explain_implied_eq(euf::enode* a, euf::enode* b);
arith_proof_hint_builder m_arith_hint;
arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal);
arith_proof_hint const* explain_implied_eq(euf::enode* a, euf::enode* b);
void explain_assumptions();

View file

@ -265,7 +265,8 @@ namespace array {
args1.push_back(e1);
args2.push_back(e2);
for (func_decl* f : funcs) {
expr* k = m.mk_app(f, e1, e2);
expr_ref k(m.mk_app(f, e1, e2), m);
rewrite(k);
args1.push_back(k);
args2.push_back(k);
}
@ -699,6 +700,23 @@ namespace array {
n->unmark1();
}
/**
* \brief check that lambda expressions are beta redexes.
* The array solver is not a decision procedure for lambdas that do not occur in beta
* redexes.
*/
bool solver::check_lambdas() {
unsigned num_vars = get_num_vars();
for (unsigned i = 0; i < num_vars; i++) {
auto* n = var2enode(i);
if (a.is_as_array(n->get_expr()) || is_lambda(n->get_expr()))
for (euf::enode* p : euf::enode_parents(n))
if (!ctx.is_beta_redex(p, n))
return false;
}
return true;
}
bool solver::is_shared_arg(euf::enode* r) {
SASSERT(r->is_root());
for (euf::enode* n : euf::enode_parents(r)) {

View file

@ -252,6 +252,8 @@ namespace array {
return p->get_arg(0)->get_root() == n->get_root();
if (a.is_map(p->get_expr()))
return true;
if (a.is_store(p->get_expr()))
return true;
return false;
}

View file

@ -108,7 +108,9 @@ namespace array {
if (m_delay_qhead < m_axiom_trail.size())
return sat::check_result::CR_CONTINUE;
if (!check_lambdas())
return sat::check_result::CR_GIVEUP;
// validate_check();
return sat::check_result::CR_DONE;
}

View file

@ -218,6 +218,7 @@ namespace array {
bool should_prop_upward(var_data const& d) const;
bool can_beta_reduce(euf::enode* n) const { return can_beta_reduce(n->get_expr()); }
bool can_beta_reduce(expr* e) const;
bool check_lambdas();
var_data& get_var_data(euf::enode* n) { return get_var_data(n->get_th_var(get_id())); }
var_data& get_var_data(theory_var v) { return *m_var_data[v]; }

View file

@ -49,16 +49,19 @@ namespace bv {
update_glue(*other);
vv::push_to_front(m_queue, other);
if (other == n) {
bool do_gc = other == n;
if (other == n)
new_tmp();
gc();
}
if (other->m_glue == 0) {
do_gc = false;
remove(other);
add_cc(v1, v2);
}
else if (other->m_count > m_propagate_high_watermark)
s.s().set_should_simplify();
else if (other->m_count > 2*m_propagate_high_watermark)
propagate();
if (do_gc)
gc();
}
void ackerman::used_diseq_eh(euf::theory_var v1, euf::theory_var v2) {
@ -76,8 +79,8 @@ namespace bv {
new_tmp();
gc();
}
if (other->m_count > m_propagate_high_watermark)
s.s().set_should_simplify();
if (other->m_count > 2*m_propagate_high_watermark)
propagate();
}
void ackerman::update_glue(vv& v) {
@ -137,6 +140,9 @@ namespace bv {
if (m_num_propagations_since_last_gc <= s.get_config().m_dack_gc)
return;
m_num_propagations_since_last_gc = 0;
if (m_table.size() > m_gc_threshold)
propagate();
while (m_table.size() > m_gc_threshold)
remove(m_queue->prev());
@ -147,7 +153,6 @@ namespace bv {
}
void ackerman::propagate() {
SASSERT(s.s().at_base_lvl());
auto* n = m_queue;
vv* k = nullptr;
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.get_config().m_dack_factor);

View file

@ -51,13 +51,13 @@ namespace bv {
solver& s;
table_t m_table;
vv* m_queue { nullptr };
vv* m_tmp_vv { nullptr };
vv* m_queue = nullptr;
vv* m_tmp_vv = nullptr;
unsigned m_gc_threshold { 100 };
unsigned m_propagate_high_watermark { 10000 };
unsigned m_propagate_low_watermark { 10 };
unsigned m_num_propagations_since_last_gc { 0 };
unsigned m_gc_threshold = 100;
unsigned m_propagate_high_watermark = 10000;
unsigned m_propagate_low_watermark = 10;
unsigned m_num_propagations_since_last_gc = 0;
bool_vector m_diff_levels;
void update_glue(vv& v);

View file

@ -47,7 +47,7 @@ namespace bv {
return true;
unsigned num_vars = e->get_num_args();
for (expr* arg : *e)
if (!m.is_value(arg))
if (m.is_value(arg))
--num_vars;
if (num_vars <= 1)
return true;
@ -72,6 +72,55 @@ namespace bv {
return expr_ref(bv.mk_numeral(val, get_bv_size(v)), m);
}
/**
\brief expose the multiplication circuit lazily.
It adds clauses for multiplier output one by one to enforce
the semantics of multipliers.
*/
bool solver::check_lazy_mul(app* e, expr* arg_value, expr* mul_value) {
SASSERT(e->get_num_args() >= 2);
expr_ref_vector args(m), new_args(m), new_out(m);
lazy_mul* lz = nullptr;
rational v0, v1;
unsigned sz, diff = 0;
VERIFY(bv.is_numeral(arg_value, v0, sz));
VERIFY(bv.is_numeral(mul_value, v1));
for (diff = 0; diff < sz; ++diff)
if (v0.get_bit(diff) != v1.get_bit(diff))
break;
SASSERT(diff < sz);
auto set_bits = [&](unsigned j, expr_ref_vector& bits) {
bits.reset();
for (unsigned i = 0; i < sz; ++i)
bits.push_back(bv.mk_bit2bool(e->get_arg(0), j));
};
if (!m_lazymul.find(e, lz)) {
set_bits(0, args);
for (unsigned j = 1; j < e->get_num_args(); ++j) {
new_out.reset();
set_bits(j, new_args);
m_bb.mk_multiplier(sz, args.data(), new_args.data(), new_out);
new_out.swap(args);
}
lz = alloc(lazy_mul, e, args);
m_lazymul.insert(e, lz);
ctx.push(new_obj_trail(lz));
ctx.push(insert_obj_map(m_lazymul, e));
}
if (lz->m_out.size() == lz->m_bits)
return false;
for (unsigned i = lz->m_bits; i <= diff; ++i) {
sat::literal bit1 = mk_literal(lz->m_out.get(i));
sat::literal bit2 = mk_literal(bv.mk_bit2bool(e, i));
add_equiv(bit1, bit2);
}
ctx.push(value_trail(lz->m_bits));
IF_VERBOSE(1, verbose_stream() << "expand lazy mul " << mk_pp(e, m) << " to " << diff << "\n");
lz->m_bits = diff;
return false;
}
bool solver::check_mul(app* e) {
SASSERT(e->get_num_args() >= 2);
expr_ref_vector args(m);
@ -96,6 +145,12 @@ namespace bv {
if (!check_mul_invertibility(e, args, r1))
return false;
#if 0
// unsound?
if (!check_lazy_mul(e, r1, r2))
return false;
#endif
// 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
@ -177,18 +232,17 @@ namespace bv {
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)) {
if (bv.is_zero(arg_value) && false) {
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.
args[i] = n->get_arg(i);
add_unit(eq_internalize(r, arg_value));
}
IF_VERBOSE(2, verbose_stream() << "delay internalize @" << s().scope_lvl() << "\n");
IF_VERBOSE(2, verbose_stream() << "delay internalize @" << s().scope_lvl() << " " << mk_pp(n, m) << "\n");
return false;
}
if (bv.is_zero(mul_value)) {

View file

@ -139,7 +139,9 @@ namespace bv {
return true;
SASSERT(!n || !n->is_attached_to(get_id()));
bool suppress_args = !reflect() && !m.is_considered_uninterpreted(a->get_decl());
bool suppress_args = !reflect()
&& !m.is_considered_uninterpreted(a->get_decl())
&& !bv.is_int2bv(e) && !bv.is_bv2int(e);
if (!n)
n = mk_enode(e, suppress_args);
@ -314,7 +316,6 @@ namespace bv {
euf::enode* n = bool_var2enode(l.var());
if (!n->is_attached_to(get_id()))
mk_var(n);
set_bit_eh(v, l, idx);
}
@ -435,7 +436,9 @@ namespace bv {
args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));
expr_ref sum(m_autil.mk_add(sz, args.data()), m);
sat::literal lit = eq_internalize(n, sum);
add_unit(lit);
m_bv2ints.push_back(expr2enode(n));
ctx.push(push_back_vector<euf::enode_vector>(m_bv2ints));
add_unit(lit);
}
void solver::internalize_int2bv(app* n) {
@ -454,6 +457,12 @@ namespace bv {
* Create the axioms:
* bit2bool(i,n) == ((e div 2^i) mod 2 != 0)
* for i = 0,.., sz-1
*
* Alternative axiomatization:
* e = sum bit2bool(i,n)*2^i + 2^n * (div(e, 2^n))
* possibly term div(e,2^n) is not correct with respect to adapted semantics?
* if not, use fresh variable or similar. Overall should be much beter.
* Note: based on superb question raised at workshop on 9/1/22.
*/
void solver::assert_int2bv_axiom(app* n) {
expr* e = nullptr;
@ -464,8 +473,8 @@ namespace bv {
unsigned sz = bv.get_bv_size(n);
numeral mod = power(numeral(2), sz);
rhs = m_autil.mk_mod(e, m_autil.mk_int(mod));
sat::literal eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
sat::literal eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
expr_ref_vector n_bits(m);
get_bits(n_enode, n_bits);
@ -476,8 +485,8 @@ namespace bv {
rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2));
rhs = mk_eq(rhs, m_autil.mk_int(1));
lhs = n_bits.get(i);
eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
eq_lit = eq_internalize(lhs, rhs);
add_unit(eq_lit);
}
}
@ -534,27 +543,27 @@ namespace bv {
internalize_binary(a, bin);
}
void solver::internalize_interp(app* n, std::function<expr*(expr*, expr*)>& ibin, std::function<expr*(expr*)>& iun) {
void solver::internalize_interp(app* n, std::function<expr* (expr*, expr*)>& ibin, std::function<expr* (expr*)>& iun) {
bv_rewriter_params p(s().params());
expr* arg1 = n->get_arg(0);
expr* arg2 = n->get_arg(1);
mk_bits(get_th_var(n));
sat::literal eq_lit;
sat::literal eq_lit;
if (p.hi_div0()) {
eq_lit = eq_internalize(n, ibin(arg1, arg2));
add_unit(eq_lit);
}
else {
unsigned sz = bv.get_bv_size(n);
expr_ref zero(bv.mk_numeral(0, sz), m);
sat::literal eqZ = eq_internalize(arg2, zero);
sat::literal eqU = eq_internalize(n, iun(arg1));
sat::literal eqI = eq_internalize(n, ibin(arg1, arg2));
add_clause(~eqZ, eqU);
add_clause(eqZ, eqI);
ctx.add_aux(~eqZ, eqU);
ctx.add_aux(eqZ, eqI);
}
add_unit(eq_lit);
}
else {
unsigned sz = bv.get_bv_size(n);
expr_ref zero(bv.mk_numeral(0, sz), m);
sat::literal eqZ = eq_internalize(arg2, zero);
sat::literal eqU = mk_literal(iun(arg1));
sat::literal eqI = mk_literal(ibin(arg1, arg2));
add_clause(~eqZ, eqU);
add_clause(eqZ, eqI);
ctx.add_aux(~eqZ, eqU);
ctx.add_aux(eqZ, eqI);
}
}
void solver::internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn) {
@ -574,11 +583,9 @@ namespace bv {
init_bits(n, bits);
}
void solver::internalize_binary(app* e, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn) {
SASSERT(e->get_num_args() >= 1);
expr_ref_vector bits(m), new_bits(m), arg_bits(m);
expr_ref_vector bits(m), new_bits(m), arg_bits(m);
get_arg_bits(e, 0, bits);
for (unsigned i = 1; i < e->get_num_args(); ++i) {
arg_bits.reset();
@ -658,7 +665,7 @@ namespace bv {
conc.push_back(arg);
expr_ref r(bv.mk_concat(conc), m);
mk_bits(get_th_var(e));
sat::literal eq_lit = eq_internalize(e, r);
sat::literal eq_lit = eq_internalize(e, r);
add_unit(eq_lit);
}
@ -667,9 +674,8 @@ namespace bv {
expr* arg = nullptr;
VERIFY(bv.is_bit2bool(n, arg, idx));
euf::enode* argn = expr2enode(arg);
if (!argn->is_attached_to(get_id())) {
mk_var(argn);
}
if (!argn->is_attached_to(get_id()))
mk_var(argn);
theory_var v_arg = argn->get_th_var(get_id());
SASSERT(idx < get_bv_size(v_arg));
sat::literal lit = expr2literal(n);
@ -771,7 +777,7 @@ namespace bv {
e1 = bv.mk_bit2bool(o1, i);
e2 = bv.mk_bit2bool(o2, i);
literal eq = eq_internalize(e1, e2);
add_clause(eq, ~oeq);
add_clause(eq, ~oeq);
eqs.push_back(~eq);
}
TRACE("bv", for (auto l : eqs) tout << mk_bounded_pp(literal2expr(l), m) << " "; tout << "\n";);

View file

@ -212,19 +212,32 @@ namespace bv {
return;
}
euf::enode* n1 = var2enode(eq.v1());
for (euf::enode* bv2int : euf::enode_class(n1)) {
if (!bv.is_bv2int(bv2int->get_expr()))
continue;
auto propagate_bv2int = [&](euf::enode* bv2int) {
euf::enode* bv2int_arg = bv2int->get_arg(0);
for (euf::enode* p : euf::enode_parents(n1->get_root())) {
if (bv.is_int2bv(p->get_expr()) && p->get_sort() == bv2int_arg->get_sort() && p->get_root() != bv2int_arg->get_root()) {
euf::enode_pair_vector eqs;
eqs.push_back({ n1, p->get_arg(0) });
eqs.push_back({ n1, bv2int });
ctx.propagate(p, bv2int_arg, euf::th_explain::propagate(*this, eqs, p, bv2int_arg));
theory_var v1 = get_th_var(p);
theory_var v2 = get_th_var(bv2int_arg);
SASSERT(v1 != euf::null_theory_var);
SASSERT(v2 != euf::null_theory_var);
ctx.propagate(p, bv2int_arg, mk_bv2int_justification(v1, v2, n1, p->get_arg(0), bv2int));
break;
}
}
};
if (m_bv2ints.size() < n1->class_size()) {
for (auto* bv2int : m_bv2ints) {
if (bv2int->get_root() == n1->get_root())
propagate_bv2int(bv2int);
}
}
else {
for (euf::enode* bv2int : euf::enode_class(n1)) {
if (bv.is_bv2int(bv2int->get_expr()))
propagate_bv2int(bv2int);
}
}
}
@ -282,6 +295,8 @@ namespace bv {
++m_stats.m_num_ne2bit;
s().assign(consequent, mk_ne2bit_justification(undef_idx, v1, v2, consequent, antecedent));
}
else if (!get_config().m_bv_eq_axioms)
;
else if (s().at_search_lvl()) {
force_push();
assert_ackerman(v1, v2);
@ -368,6 +383,11 @@ namespace bv {
r.push_back(b);
break;
}
case bv_justification::kind_t::bv2int: {
ctx.add_antecedent(c.a, c.b);
ctx.add_antecedent(c.a, c.c);
break;
}
}
if (!probing && ctx.use_drat())
log_drat(c);
@ -375,19 +395,26 @@ namespace bv {
void solver::log_drat(bv_justification const& c) {
// introduce dummy literal for equality.
sat::literal leq(s().num_vars() + 1, false);
expr_ref eq(m);
if (c.m_kind != bv_justification::kind_t::bit2ne) {
sat::literal leq1(s().num_vars() + 1, false);
sat::literal leq2(s().num_vars() + 2, false);
expr_ref eq1(m), eq2(m);
if (c.m_kind == bv_justification::kind_t::bv2int) {
eq1 = m.mk_eq(c.a->get_expr(), c.b->get_expr());
eq2 = m.mk_eq(c.a->get_expr(), c.c->get_expr());
ctx.set_tmp_bool_var(leq1.var(), eq1);
ctx.set_tmp_bool_var(leq2.var(), eq1);
}
else if (c.m_kind != bv_justification::kind_t::bit2ne) {
expr* e1 = var2expr(c.m_v1);
expr* e2 = var2expr(c.m_v2);
eq = m.mk_eq(e1, e2);
ctx.drat_eq_def(leq, eq);
eq1 = m.mk_eq(e1, e2);
ctx.set_tmp_bool_var(leq1.var(), eq1);
}
sat::literal_vector lits;
switch (c.m_kind) {
case bv_justification::kind_t::eq2bit:
lits.push_back(~leq);
lits.push_back(~leq1);
lits.push_back(~c.m_antecedent);
lits.push_back(c.m_consequent);
break;
@ -396,10 +423,10 @@ namespace bv {
lits.push_back(c.m_consequent);
break;
case bv_justification::kind_t::bit2eq:
get_antecedents(leq, c.to_index(), lits, true);
get_antecedents(leq1, c.to_index(), lits, true);
for (auto& lit : lits)
lit.neg();
lits.push_back(leq);
lits.push_back(leq1);
break;
case bv_justification::kind_t::bit2ne:
get_antecedents(c.m_consequent, c.to_index(), lits, true);
@ -407,9 +434,20 @@ namespace bv {
lit.neg();
lits.push_back(c.m_consequent);
break;
case bv_justification::kind_t::bv2int:
get_antecedents(leq1, c.to_index(), lits, true);
get_antecedents(leq2, c.to_index(), lits, true);
for (auto& lit : lits)
lit.neg();
lits.push_back(leq1);
lits.push_back(leq2);
break;
}
ctx.get_drat().add(lits, status());
// TBD, a proper way would be to delete the lemma after use.
ctx.set_tmp_bool_var(leq1.var(), nullptr);
ctx.set_tmp_bool_var(leq2.var(), nullptr);
}
void solver::asserted(literal l) {
@ -665,7 +703,9 @@ 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;
return out << "bv <- " << m_bits[v1] << " != " << m_bits[v2] << " @" << cidx;
case bv_justification::kind_t::bv2int:
return out << "bv <- v" << v1 << " == v" << v2 << " <== " << ctx.bpp(c.a) << " == " << ctx.bpp(c.b) << " == " << ctx.bpp(c.c);
default:
UNREACHABLE();
break;
@ -826,28 +866,41 @@ namespace bv {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2, c, a);
return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
auto jst = sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
TRACE("bv", tout << jst << " " << constraint << "\n");
return jst;
}
sat::ext_justification_idx solver::mk_bit2eq_justification(theory_var v1, theory_var v2) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2);
return constraint->to_index();
auto jst = constraint->to_index();
return jst;
}
sat::justification solver::mk_bit2ne_justification(unsigned idx, sat::literal c) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(idx, c);
return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
auto jst = sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
return jst;
}
sat::justification solver::mk_ne2bit_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(idx, v1, v2, c, a);
return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
auto jst = sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index());
return jst;
}
sat::ext_constraint_idx solver::mk_bv2int_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c) {
void* mem = get_region().allocate(bv_justification::get_obj_size());
sat::constraint_base::initialize(mem, this);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2, a, b, c);
auto jst = constraint->to_index();
return jst;
}
bool solver::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) {

View file

@ -27,6 +27,15 @@ namespace euf {
namespace bv {
struct lazy_mul {
expr_ref_vector m_out;
unsigned m_bits;
lazy_mul(app* a, expr_ref_vector& out):
m_out(out),
m_bits(0) {
}
};
class solver : public euf::th_euf_solver {
typedef rational numeral;
typedef euf::theory_var theory_var;
@ -52,13 +61,15 @@ namespace bv {
};
struct bv_justification {
enum kind_t { eq2bit, ne2bit, bit2eq, bit2ne };
enum kind_t { eq2bit, ne2bit, bit2eq, bit2ne, bv2int };
kind_t m_kind;
unsigned m_idx{ UINT_MAX };
theory_var m_v1{ euf::null_theory_var };
theory_var m_v2 { euf::null_theory_var };
unsigned m_idx = UINT_MAX;
theory_var m_v1 = euf::null_theory_var;
theory_var m_v2 = euf::null_theory_var;
sat::literal m_consequent;
sat::literal m_antecedent;
euf::enode* a, *b, *c;
bv_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) :
m_kind(bv_justification::kind_t::eq2bit), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {}
bv_justification(theory_var v1, theory_var v2):
@ -67,6 +78,8 @@ namespace bv {
m_kind(bv_justification::kind_t::bit2ne), m_idx(idx), m_consequent(c) {}
bv_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a) :
m_kind(bv_justification::kind_t::ne2bit), m_idx(idx), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {}
bv_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c):
m_kind(bv_justification::kind_t::bv2int), m_v1(v1), m_v2(v2), a(a), b(b), c(c) {}
sat::ext_constraint_idx to_index() const {
return sat::constraint_base::mem2base(this);
}
@ -80,6 +93,7 @@ namespace bv {
sat::ext_justification_idx mk_bit2eq_justification(theory_var v1, theory_var v2);
sat::justification mk_bit2ne_justification(unsigned idx, sat::literal c);
sat::justification mk_ne2bit_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a);
sat::ext_constraint_idx mk_bv2int_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c);
void log_drat(bv_justification const& c);
@ -210,8 +224,10 @@ namespace bv {
literal_vector m_tmp_literals;
svector<propagation_item> m_prop_queue;
unsigned_vector m_prop_queue_lim;
unsigned m_prop_queue_head { 0 };
sat::literal m_true { sat::null_literal };
unsigned m_prop_queue_head = 0;
sat::literal m_true = sat::null_literal;
euf::enode_vector m_bv2ints;
obj_map<app, lazy_mul*> m_lazymul;
// internalize
void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
@ -312,6 +328,7 @@ namespace bv {
bool m_cheap_axioms{ true };
bool should_bit_blast(app * n);
bool check_delay_internalized(expr* e);
bool check_lazy_mul(app* e, expr* mul_value, expr* arg_value);
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);

View file

@ -167,7 +167,7 @@ namespace euf {
lit = lit2;
}
TRACE("euf", tout << "attach " << v << " " << mk_bounded_pp(e, m) << "\n";);
TRACE("euf", tout << "attach v" << v << " " << mk_bounded_pp(e, m) << "\n";);
m_bool_var2expr.reserve(v + 1, nullptr);
if (m_bool_var2expr[v] && m_egraph.find(e)) {
if (m_egraph.find(e)->bool_var() != v) {
@ -264,7 +264,11 @@ namespace euf {
sat::status st = sat::status::th(m_is_redundant, m.get_basic_family_id());
if (sz <= 1)
return;
if (sz <= distinct_max_args) {
sort* srt = e->get_arg(0)->get_sort();
auto sort_sz = srt->get_num_elements();
if (sort_sz.is_finite() && sort_sz.size() < sz)
s().add_clause(0, nullptr, st);
else if (sz <= distinct_max_args) {
for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
@ -274,8 +278,7 @@ namespace euf {
}
}
else {
// dist-f(x_1) = v_1 & ... & dist-f(x_n) = v_n
sort* srt = e->get_arg(0)->get_sort();
// dist-f(x_1) = v_1 & ... & dist-f(x_n) = v_n
SASSERT(!m.is_bool(srt));
sort_ref u(m.mk_fresh_sort("distinct-elems"), m);
func_decl_ref f(m.mk_fresh_func_decl("dist-f", "", 1, &srt, u), m);

View file

@ -16,96 +16,26 @@ Author:
--*/
#include "sat/smt/euf_solver.h"
#include "ast/ast_util.h"
#include <iostream>
namespace euf {
void solver::init_drat() {
if (!m_drat_initialized) {
void solver::init_proof() {
if (!m_proof_initialized) {
get_drat().add_theory(get_id(), symbol("euf"));
get_drat().add_theory(m.get_basic_family_id(), symbol("bool"));
}
m_drat_initialized = true;
}
void solver::drat_log_expr1(expr* e) {
if (is_app(e)) {
app* a = to_app(e);
drat_log_decl(a->get_decl());
std::stringstream strm;
strm << mk_ismt2_func(a->get_decl(), m);
get_drat().def_begin('e', e->get_id(), strm.str());
for (expr* arg : *a)
get_drat().def_add_arg(arg->get_id());
get_drat().def_end();
if (!m_proof_out && s().get_config().m_drat &&
(get_config().m_lemmas2console || s().get_config().m_smt_proof.is_non_empty_string())) {
TRACE("euf", tout << "init-proof\n");
m_proof_out = alloc(std::ofstream, s().get_config().m_smt_proof.str(), std::ios_base::out);
if (get_config().m_lemmas2console)
get_drat().set_clause_eh(*this);
if (s().get_config().m_smt_proof.is_non_empty_string())
get_drat().set_clause_eh(*this);
}
else if (is_var(e)) {
var* v = to_var(e);
get_drat().def_begin('v', v->get_id(), "" + mk_pp(e->get_sort(), m));
get_drat().def_add_arg(v->get_idx());
get_drat().def_end();
}
else if (is_quantifier(e)) {
quantifier* q = to_quantifier(e);
std::stringstream strm;
strm << "(" << (is_forall(q) ? "forall" : (is_exists(q) ? "exists" : "lambda"));
for (unsigned i = 0; i < q->get_num_decls(); ++i)
strm << " (" << q->get_decl_name(i) << " " << mk_pp(q->get_decl_sort(i), m) << ")";
strm << ")";
get_drat().def_begin('q', q->get_id(), strm.str());
get_drat().def_add_arg(q->get_expr()->get_id());
get_drat().def_end();
}
else
UNREACHABLE();
m_drat_asts.insert(e);
push(insert_obj_trail<ast>(m_drat_asts, e));
}
void solver::drat_log_expr(expr* e) {
if (m_drat_asts.contains(e))
return;
ptr_vector<expr>::scoped_stack _sc(m_drat_todo);
m_drat_todo.push_back(e);
while (!m_drat_todo.empty()) {
e = m_drat_todo.back();
unsigned sz = m_drat_todo.size();
if (is_app(e))
for (expr* arg : *to_app(e))
if (!m_drat_asts.contains(arg))
m_drat_todo.push_back(arg);
if (is_quantifier(e)) {
expr* arg = to_quantifier(e)->get_expr();
if (!m_drat_asts.contains(arg))
m_drat_todo.push_back(arg);
}
if (m_drat_todo.size() != sz)
continue;
if (!m_drat_asts.contains(e))
drat_log_expr1(e);
m_drat_todo.pop_back();
}
}
void solver::drat_bool_def(sat::bool_var v, expr* e) {
if (!use_drat())
return;
drat_log_expr(e);
get_drat().bool_def(v, e->get_id());
}
void solver::drat_log_decl(func_decl* f) {
if (f->get_family_id() != null_family_id)
return;
if (m_drat_asts.contains(f))
return;
m_drat_asts.insert(f);
push(insert_obj_trail< ast>(m_drat_asts, f));
std::ostringstream strm;
smt2_pp_environment_dbg env(m);
ast_smt2_pp(strm, f, env);
get_drat().def_begin('f', f->get_small_id(), strm.str());
get_drat().def_end();
m_proof_initialized = true;
}
/**
@ -144,16 +74,20 @@ namespace euf {
}
}
void solver::set_tmp_bool_var(bool_var b, expr* e) {
m_bool_var2expr.setx(b, e, nullptr);
}
void solver::log_justification(literal l, th_explain const& jst) {
literal_vector lits;
unsigned nv = s().num_vars();
expr_ref_vector eqs(m);
unsigned nv = s().num_vars();
auto add_lit = [&](enode_pair const& eq) {
unsigned v = nv;
++nv;
literal lit(nv, false);
eqs.push_back(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()));
drat_eq_def(lit, eqs.back());
return lit;
set_tmp_bool_var(v, eqs.back());
return literal(v, false);
};
for (auto lit : euf::th_explain::lits(jst))
@ -167,18 +101,137 @@ namespace euf {
if (jst.eq_consequent().first != nullptr)
lits.push_back(add_lit(jst.eq_consequent()));
get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id(), jst.get_pragma()));
for (unsigned i = s().num_vars(); i < nv; ++i)
set_tmp_bool_var(i, nullptr);
}
void solver::drat_eq_def(literal lit, expr* eq) {
expr *a = nullptr, *b = nullptr;
VERIFY(m.is_eq(eq, a, b));
drat_log_expr(a);
drat_log_expr(b);
get_drat().def_begin('e', eq->get_id(), std::string("="));
get_drat().def_add_arg(a->get_id());
get_drat().def_add_arg(b->get_id());
get_drat().def_end();
get_drat().bool_def(lit.var(), eq->get_id());
void solver::on_clause(unsigned n, literal const* lits, sat::status st) {
TRACE("euf", tout << "on-clause " << n << "\n");
on_lemma(n, lits, st);
on_proof(n, lits, st);
}
void solver::on_proof(unsigned n, literal const* lits, sat::status st) {
if (!m_proof_out)
return;
flet<bool> _display_all_decls(m_display_all_decls, true);
std::ostream& out = *m_proof_out;
if (!visit_clause(out, n, lits))
return;
if (st.is_asserted())
display_redundant(out, n, lits, status2proof_hint(st));
else if (st.is_deleted())
display_deleted(out, n, lits);
else if (st.is_redundant())
display_redundant(out, n, lits, status2proof_hint(st));
else if (st.is_input())
display_assume(out, n, lits);
else
UNREACHABLE();
out.flush();
}
void solver::on_lemma(unsigned n, literal const* lits, sat::status st) {
if (!get_config().m_lemmas2console)
return;
if (!st.is_redundant() && !st.is_asserted())
return;
std::ostream& out = std::cout;
if (!visit_clause(out, n, lits))
return;
std::function<symbol(int)> ppth = [&](int th) {
return m.get_family_name(th);
};
if (!st.is_sat())
out << "; " << sat::status_pp(st, ppth) << "\n";
display_assert(out, n, lits);
}
void solver::on_instantiation(unsigned n, sat::literal const* lits, unsigned k, euf::enode* const* bindings) {
std::ostream& out = std::cout;
for (unsigned i = 0; i < k; ++i)
visit_expr(out, bindings[i]->get_expr());
VERIFY(visit_clause(out, n, lits));
out << "(instantiate";
display_literals(out, n, lits);
for (unsigned i = 0; i < k; ++i)
display_expr(out << " :binding ", bindings[i]->get_expr());
out << ")\n";
}
bool solver::visit_clause(std::ostream& out, unsigned n, literal const* lits) {
for (unsigned i = 0; i < n; ++i) {
expr* e = bool_var2expr(lits[i].var());
if (!e)
return false;
visit_expr(out, e);
}
return true;
}
void solver::display_assert(std::ostream& out, unsigned n, literal const* lits) {
display_literals(out << "(assert (or", n, lits) << "))\n";
}
void solver::display_assume(std::ostream& out, unsigned n, literal const* lits) {
display_literals(out << "(assume", n, lits) << ")\n";
}
void solver::display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) {
if (proof_hint)
visit_expr(out, proof_hint);
display_hint(display_literals(out << "(learn", n, lits), proof_hint) << ")\n";
}
void solver::display_deleted(std::ostream& out, unsigned n, literal const* lits) {
display_literals(out << "(del", n, lits) << ")\n";
}
std::ostream& solver::display_hint(std::ostream& out, expr* proof_hint) {
if (proof_hint)
return display_expr(out << " ", proof_hint);
else
return out;
}
expr_ref solver::status2proof_hint(sat::status st) {
if (st.is_sat())
return expr_ref(m.mk_const("rup", m.mk_proof_sort()), m); // provable by reverse unit propagation
auto* h = reinterpret_cast<euf::th_proof_hint const*>(st.get_hint());
if (!h)
return expr_ref(m);
expr* e = h->get_hint(*this);
if (e)
return expr_ref(e, m);
return expr_ref(m);
}
std::ostream& solver::display_literals(std::ostream& out, unsigned n, literal const* lits) {
for (unsigned i = 0; i < n; ++i) {
expr* e = bool_var2expr(lits[i].var());
if (lits[i].sign())
display_expr(out << " (not ", e) << ")";
else
display_expr(out << " ", e);
}
return out;
}
void solver::visit_expr(std::ostream& out, expr* e) {
m_clause_visitor.collect(e);
if (m_display_all_decls)
m_clause_visitor.display_decls(out);
else
m_clause_visitor.display_skolem_decls(out);
m_clause_visitor.define_expr(out, e);
}
std::ostream& solver::display_expr(std::ostream& out, expr* e) {
return m_clause_visitor.display_expr_def(out, e);
}
}

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
euf_proof_checker.cpp
Abstract:
Plugin manager for checking EUF proofs
Author:
Nikolaj Bjorner (nbjorner) 2020-08-25
--*/
#include "ast/ast_pp.h"
#include "sat/smt/euf_proof_checker.h"
#include "sat/smt/arith_proof_checker.h"
namespace euf {
proof_checker::proof_checker(ast_manager& m):
m(m) {
arith::proof_checker* apc = alloc(arith::proof_checker, m);
m_plugins.push_back(apc);
apc->register_plugins(*this);
(void)m;
}
proof_checker::~proof_checker() {}
void proof_checker::register_plugin(symbol const& rule, proof_checker_plugin* p) {
m_map.insert(rule, p);
}
bool proof_checker::check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units) {
if (!e || !is_app(e))
return false;
units.reset();
app* a = to_app(e);
proof_checker_plugin* p = nullptr;
if (m_map.find(a->get_decl()->get_name(), p))
return p->check(clause, a, units);
return false;
}
}

View file

@ -0,0 +1,46 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
euf_proof_checker.h
Abstract:
Plugin manager for checking EUF proofs
Author:
Nikolaj Bjorner (nbjorner) 2022-08-25
--*/
#pragma once
#include "util/map.h"
#include "util/scoped_ptr_vector.h"
#include "ast/ast.h"
namespace euf {
class proof_checker;
class proof_checker_plugin {
public:
virtual ~proof_checker_plugin() {}
virtual bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) = 0;
virtual void register_plugins(proof_checker& pc) = 0;
};
class proof_checker {
ast_manager& m;
scoped_ptr_vector<proof_checker_plugin> m_plugins;
map<symbol, proof_checker_plugin*, symbol_hash_proc, symbol_eq_proc> m_map;
public:
proof_checker(ast_manager& m);
~proof_checker();
void register_plugin(symbol const& rule, proof_checker_plugin*);
bool check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units);
};
}

View file

@ -49,7 +49,8 @@ namespace euf {
m_lookahead(nullptr),
m_to_m(&m),
m_to_si(&si),
m_values(m)
m_values(m),
m_clause_visitor(m)
{
updt_params(p);
m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2);
@ -347,8 +348,7 @@ namespace euf {
if (m_relevancy.enabled())
m_relevancy.propagate();
if (m_egraph.inconsistent()) {
unsigned lvl = s().scope_lvl();
s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index()));
set_conflict(conflict_constraint().to_index());
return true;
}
bool propagated1 = false;
@ -488,7 +488,8 @@ namespace euf {
};
if (merge_shared_bools())
cont = true;
for (auto* e : m_solvers) {
for (unsigned i = 0; i < m_solvers.size(); ++i) {
auto* e = m_solvers[i];
if (!m.inc())
return sat::check_result::CR_GIVEUP;
if (e == m_qsolver)
@ -519,7 +520,7 @@ namespace euf {
bool merged = false;
for (unsigned i = m_egraph.nodes().size(); i-- > 0; ) {
euf::enode* n = m_egraph.nodes()[i];
if (!is_shared(n) || !m.is_bool(n->get_expr()))
if (!m.is_bool(n->get_expr()) || !is_shared(n))
continue;
if (n->value() == l_true && !m.is_true(n->get_root()->get_expr())) {
m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
@ -616,15 +617,17 @@ namespace euf {
else
lit = si.internalize(e, false);
VERIFY(lit.var() == v);
if (!m_egraph.find(e) && (!m.is_iff(e) && !m.is_or(e) && !m.is_and(e) && !m.is_not(e))) {
if (!m_egraph.find(e) && !m.is_iff(e) && !m.is_or(e) && !m.is_and(e) && !m.is_not(e) && !m.is_implies(e) && !m.is_xor(e)) {
ptr_buffer<euf::enode> args;
if (is_app(e))
for (expr* arg : *to_app(e))
args.push_back(e_internalize(arg));
internalize(e, true);
if (!m_egraph.find(e))
mk_enode(e, args.size(), args.data());
}
attach_lit(lit, e);
else
attach_lit(lit, e);
}
if (relevancy_enabled())
@ -778,7 +781,7 @@ namespace euf {
}
for (auto const& thv : enode_th_vars(n)) {
auto* th = m_id2solver.get(thv.get_id(), nullptr);
if (th && !th->is_fixed(thv.get_var(), val, explain))
if (th && th->is_fixed(thv.get_var(), val, explain))
return true;
}
return false;
@ -863,7 +866,13 @@ namespace euf {
out << "bool-vars\n";
for (unsigned v : m_var_trail) {
expr* e = m_bool_var2expr[v];
out << v << (is_relevant(v)?"":"n") << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";
out << v << (is_relevant(v)?"":"n") << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1);
euf::enode* n = m_egraph.find(e);
if (n) {
for (auto const& th : enode_th_vars(n))
out << " " << m_id2solver[th.get_id()]->name();
}
out << "\n";
}
for (auto* e : m_solvers)
e->display(out);
@ -1067,10 +1076,7 @@ namespace euf {
user_propagator::fresh_eh_t& fresh_eh) {
m_user_propagator = alloc(user_solver::solver, *this);
m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
for (unsigned i = m_scopes.size(); i-- > 0; )
m_user_propagator->push();
m_solvers.push_back(m_user_propagator);
m_id2solver.setx(m_user_propagator->get_id(), m_user_propagator, nullptr);
add_solver(m_user_propagator);
}
bool solver::watches_fixed(enode* n) const {

View file

@ -60,11 +60,10 @@ namespace euf {
std::ostream& display(std::ostream& out) const;
};
class solver : public sat::extension, public th_internalizer, public th_decompile {
class solver : public sat::extension, public th_internalizer, public th_decompile, public sat::clause_eh {
typedef top_sort<euf::enode> deps_t;
friend class ackerman;
class user_sort;
// friend class sat::ba_solver;
struct stats {
unsigned m_ackerman;
unsigned m_final_checks;
@ -161,7 +160,6 @@ namespace euf {
void collect_dependencies(user_sort& us, deps_t& deps);
void values2model(deps_t const& deps, model_ref& mdl);
void validate_model(model& mdl);
void display_validation_failure(std::ostream& out, model& mdl, enode* n);
// solving
void propagate_literals();
@ -175,19 +173,22 @@ namespace euf {
void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
void log_antecedents(literal l, literal_vector const& r);
void log_justification(literal l, th_explain const& jst);
void drat_log_decl(func_decl* f);
void drat_log_expr1(expr* n);
ptr_vector<expr> m_drat_todo;
obj_hashtable<ast> m_drat_asts;
bool m_drat_initialized{ false };
void init_drat();
bool m_proof_initialized = false;
void init_proof();
ast_pp_util m_clause_visitor;
bool m_display_all_decls = false;
void on_clause(unsigned n, literal const* lits, sat::status st) override;
void on_lemma(unsigned n, literal const* lits, sat::status st);
void on_proof(unsigned n, literal const* lits, sat::status st);
std::ostream& display_literals(std::ostream& out, unsigned n, sat::literal const* lits);
void display_assume(std::ostream& out, unsigned n, literal const* lits);
void display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint);
void display_deleted(std::ostream& out, unsigned n, literal const* lits);
std::ostream& display_hint(std::ostream& out, expr* proof_hint);
expr_ref status2proof_hint(sat::status st);
// relevancy
//bool_vector m_relevant_expr_ids;
//bool_vector m_relevant_visited;
//ptr_vector<expr> m_relevant_todo;
//void init_relevant_expr_ids();
//void push_relevant(sat::bool_var v);
bool is_propagated(sat::literal lit);
// invariant
void check_eqc_bool_assignment() const;
@ -340,11 +341,16 @@ namespace euf {
// proof
bool use_drat() { return s().get_config().m_drat && (init_drat(), true); }
bool use_drat() { return s().get_config().m_drat && (init_proof(), true); }
sat::drat& get_drat() { return s().get_drat(); }
void drat_bool_def(sat::bool_var v, expr* n);
void drat_eq_def(sat::literal lit, expr* eq);
void drat_log_expr(expr* n);
void set_tmp_bool_var(sat::bool_var b, expr* e);
bool visit_clause(std::ostream& out, unsigned n, literal const* lits);
void display_assert(std::ostream& out, unsigned n, literal const* lits);
void visit_expr(std::ostream& out, expr* e);
std::ostream& display_expr(std::ostream& out, expr* e);
void on_instantiation(unsigned n, sat::literal const* lits, unsigned k, euf::enode* const* bindings);
scoped_ptr<std::ostream> m_proof_out;
// decompile
bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
@ -403,6 +409,7 @@ namespace euf {
obj_map<expr, enode*> const& values2root();
void model_updated(model_ref& mdl);
expr* node2value(enode* n) const;
void display_validation_failure(std::ostream& out, model& mdl, enode* n);
// diagnostics
func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; }

View file

@ -1430,10 +1430,9 @@ namespace pb {
IF_VERBOSE(0, verbose_stream() << *c << "\n");
VERIFY(c->well_formed());
if (m_solver && m_solver->get_config().m_drat) {
std::function<void(std::ostream& out)> fn = [&](std::ostream& out) {
out << "c ba constraint " << *c << " 0\n";
};
m_solver->get_drat().log_adhoc(fn);
auto * out = s().get_drat().out();
if (out)
*out << "c ba constraint " << *c << " 0\n";
}
}

View file

@ -372,16 +372,21 @@ namespace q {
}
void ematch::propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx) {
if (is_conflict) {
if (is_conflict)
++m_stats.m_num_conflicts;
ctx.set_conflict(j_idx);
}
else {
else
++m_stats.m_num_propagations;
auto& j = justification::from_index(j_idx);
auto lit = instantiate(j.m_clause, j.m_binding, j.m_clause[idx]);
ctx.propagate(lit, j_idx);
}
auto& j = justification::from_index(j_idx);
sat::literal_vector lits;
lits.push_back(~j.m_clause.m_literal);
for (unsigned i = 0; i < j.m_clause.size(); ++i)
lits.push_back(instantiate(j.m_clause, j.m_binding, j.m_clause[i]));
m_qs.log_instantiation(lits, &j);
euf::th_proof_hint* ph = nullptr;
if (ctx.use_drat())
ph = q_proof_hint::mk(ctx, j.m_clause.size(), j.m_binding);
m_qs.add_clause(lits, ph);
}
bool ematch::flush_prop_queue() {
@ -408,6 +413,7 @@ namespace q {
void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) {
m_evidence.reset();
ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes()));
m_qs.log_instantiation(~c.m_literal, lit);
}
sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) {

View file

@ -47,21 +47,21 @@ namespace q {
unsigned lim = m_indirect_nodes.size();
lit l = c[i];
lbool cmp = compare(n, binding, l.lhs, l.rhs, evidence);
TRACE("q", tout << l.lhs << " ~~ " << l.rhs << " is " << cmp << "\n";);
switch (cmp) {
case l_false:
case l_false:
m_indirect_nodes.shrink(lim);
if (!l.sign)
break;
c.m_watch = i;
return l_true;
case l_true:
case l_true:
m_indirect_nodes.shrink(lim);
if (l.sign)
break;
break;
c.m_watch = i;
return l_true;
case l_undef:
TRACE("q", tout << l.lhs << " ~~ " << l.rhs << " is undef\n";);
if (idx != UINT_MAX) {
idx = UINT_MAX;
return l_undef;

View file

@ -1961,7 +1961,7 @@ namespace q {
for (unsigned i = 0; i < num_args; i++)
m_args[i] = m_registers[pc->m_iregs[i]]->get_root();
for (enode* n : euf::enode_class(r)) {
if (n->get_decl() == f) {
if (n->get_decl() == f && num_args == n->num_args()) {
unsigned i = 0;
for (; i < num_args; i++) {
if (n->get_arg(i)->get_root() != m_args[i])

View file

@ -45,7 +45,7 @@ namespace q {
static mam * mk(euf::solver& ctx, ematch& em);
virtual ~mam() {}
virtual ~mam() = default;
virtual void add_pattern(quantifier * q, app * mp) = 0;

View file

@ -68,10 +68,21 @@ namespace q {
}
}
m_max_cex += ctx.get_config().m_mbqi_max_cexs;
for (auto const& [qlit, fml, generation] : m_instantiations) {
for (auto const& [qlit, fml, inst, generation] : m_instantiations) {
euf::solver::scoped_generation sg(ctx, generation + 1);
sat::literal lit = ctx.mk_literal(fml);
m_qs.add_clause(~qlit, ~lit);
euf::th_proof_hint* ph = nullptr;
if (!inst.empty()) {
ph = q_proof_hint::mk(ctx, inst.size(), inst.data());
sat::literal_vector lits;
lits.push_back(~qlit);
lits.push_back(~lit);
m_qs.add_clause(lits, ph);
}
else {
m_qs.add_clause(~qlit, ~lit);
}
m_qs.log_instantiation(~qlit, ~lit);
}
m_instantiations.reset();
if (result != l_true)
@ -223,10 +234,31 @@ namespace q {
TRACE("q", tout << "project: " << proj << "\n";);
IF_VERBOSE(11, verbose_stream() << "mbi:\n" << mk_pp(q, m) << "\n" << proj << "\n");
++m_stats.m_num_instantiations;
unsigned generation = ctx.get_max_generation(proj);
m_instantiations.push_back(instantiation_t(qlit, proj, generation));
unsigned generation = ctx.get_max_generation(proj);
expr_ref_vector inst = extract_binding(q);
m_instantiations.push_back(instantiation_t(qlit, proj, inst, generation));
}
expr_ref_vector mbqi::extract_binding(quantifier* q) {
if (!m_defs.empty()) {
expr_safe_replace sub(m);
for (unsigned i = m_defs.size(); i-- > 0; ) {
sub(m_defs[i].term);
sub.insert(m_defs[i].var, m_defs[i].term);
}
q_body* qb = q2body(q);
expr_ref_vector inst(m);
for (expr* v : qb->vars) {
expr_ref t(m);
sub(v, t);
inst.push_back(t);
}
return inst;
}
return expr_ref_vector(m);
}
void mbqi::add_universe_restriction(q_body& qb) {
for (app* v : qb.vars) {
sort* s = v->get_sort();
@ -283,6 +315,7 @@ namespace q {
expr_ref_vector fmls(qb.vbody);
app_ref_vector vars(qb.vars);
bool fmls_extracted = false;
m_defs.reset();
TRACE("q",
tout << "Project\n";
tout << fmls << "\n";
@ -313,16 +346,22 @@ namespace q {
fmls_extracted = true;
}
if (!p)
continue;
if (!(*p)(*m_model, vars, fmls))
return expr_ref(nullptr, m);
continue;
if (ctx.use_drat()) {
if (!p->project(*m_model, vars, fmls, m_defs))
return expr_ref(m);
}
else if (!(*p)(*m_model, vars, fmls))
return expr_ref(m);
}
for (app* v : vars) {
expr_ref term(m);
expr_ref val = (*m_model)(v);
val = m_model->unfold_as_array(val);
term = replace_model_value(val);
rep.insert(v, term);
rep.insert(v, term);
if (ctx.use_drat())
m_defs.push_back(mbp::def(expr_ref(v, m), term));
eqs.push_back(m.mk_eq(v, val));
}
rep(fmls);
@ -596,8 +635,8 @@ namespace q {
void mbqi::collect_statistics(statistics& st) const {
if (m_solver)
m_solver->collect_statistics(st);
st.update("q-num-instantiations", m_stats.m_num_instantiations);
st.update("q-num-checks", m_stats.m_num_checks);
st.update("q mbi instantiations", m_stats.m_num_instantiations);
st.update("q mbi num checks", m_stats.m_num_checks);
}
}

View file

@ -66,15 +66,17 @@ namespace q {
scoped_ptr_vector<obj_hashtable<expr>> m_values;
scoped_ptr_vector<mbp::project_plugin> m_plugins;
obj_map<quantifier, q_body*> m_q2body;
unsigned m_max_cex{ 1 };
unsigned m_max_quick_check_rounds { 100 };
unsigned m_max_unbounded_equalities { 10 };
unsigned m_max_choose_candidates { 10 };
unsigned m_generation_bound{ UINT_MAX };
unsigned m_generation_max { UINT_MAX };
typedef std::tuple<sat::literal, expr_ref, unsigned> instantiation_t;
unsigned m_max_cex = 1;
unsigned m_max_quick_check_rounds = 100;
unsigned m_max_unbounded_equalities = 10;
unsigned m_max_choose_candidates = 10;
unsigned m_generation_bound = UINT_MAX;
unsigned m_generation_max = UINT_MAX;
typedef std::tuple<sat::literal, expr_ref, expr_ref_vector, unsigned> instantiation_t;
vector<instantiation_t> m_instantiations;
vector<mbp::def> m_defs;
expr_ref_vector extract_binding(quantifier* q);
void restrict_to_universe(expr * sk, ptr_vector<expr> const & universe);
// void register_value(expr* e);
expr_ref replace_model_value(expr* e);

View file

@ -43,7 +43,7 @@ namespace q {
ast_manager& m;
public:
projection_function(ast_manager& m) : m(m) {}
virtual ~projection_function() {}
virtual ~projection_function() = default;
virtual expr* mk_lt(expr* a, expr* b) = 0;
expr* mk_le(expr* a, expr* b) { return m.mk_not(mk_lt(b, a)); }
virtual bool operator()(expr* a, expr* b) const = 0;

View file

@ -24,6 +24,7 @@ Author:
#include "sat/smt/euf_solver.h"
#include "sat/smt/sat_th.h"
#include "qe/lite/qe_lite.h"
#include <iostream>
namespace q {
@ -356,4 +357,43 @@ namespace q {
m_ematch.get_antecedents(l, idx, r, probing);
}
void solver::log_instantiation(unsigned n, sat::literal const* lits, justification* j) {
TRACE("q", for (unsigned i = 0; i < n; ++i) tout << literal2expr(lits[i]) << "\n";);
if (get_config().m_instantiations2console) {
ctx.on_instantiation(n, lits, j ? j->m_clause.num_decls() : 0, j ? j->m_binding : nullptr);
}
}
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned n, euf::enode* const* bindings) {
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n));
q_proof_hint* ph = new (mem) q_proof_hint();
ph->m_num_bindings = n;
for (unsigned i = 0; i < n; ++i)
ph->m_bindings[i] = bindings[i]->get_expr();
return ph;
}
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned n, expr* const* bindings) {
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n));
q_proof_hint* ph = new (mem) q_proof_hint();
ph->m_num_bindings = n;
for (unsigned i = 0; i < n; ++i)
ph->m_bindings[i] = bindings[i];
return ph;
}
expr* q_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
expr_ref_vector args(m);
sort_ref_vector sorts(m);
for (unsigned i = 0; i < m_num_bindings; ++i) {
args.push_back(m_bindings[i]);
sorts.push_back(args.back()->get_sort());
}
sort* range = m.mk_proof_sort();
func_decl* d = m.mk_func_decl(symbol("inst"), args.size(), sorts.data(), range);
expr* r = m.mk_app(d, args);
return r;
}
}

View file

@ -29,6 +29,16 @@ namespace euf {
namespace q {
struct q_proof_hint : public euf::th_proof_hint {
unsigned m_num_bindings;
expr* m_bindings[0];
q_proof_hint() {}
static size_t get_obj_size(unsigned num_bindings) { return sizeof(q_proof_hint) + num_bindings*sizeof(expr*); }
static q_proof_hint* mk(euf::solver& s, unsigned n, euf::enode* const* bindings);
static q_proof_hint* mk(euf::solver& s, unsigned n, expr* const* bindings);
expr* get_hint(euf::solver& s) const override;
};
class solver : public euf::th_euf_solver {
typedef obj_map<quantifier, quantifier*> flat_table;
@ -88,5 +98,9 @@ namespace q {
sat::literal_vector const& universal() const { return m_universal; }
quantifier* flatten(quantifier* q);
void log_instantiation(sat::literal q, sat::literal i, justification* j = nullptr) { sat::literal lits[2] = { q, i }; log_instantiation(2, lits, j); }
void log_instantiation(sat::literal_vector const& lits, justification* j) { log_instantiation(lits.size(), lits.data(), j); }
void log_instantiation(unsigned n, sat::literal const* lits, justification* j);
};
}

View file

@ -21,7 +21,7 @@ Author:
namespace sat {
class sat_internalizer {
public:
virtual ~sat_internalizer() {}
virtual ~sat_internalizer() = default;
virtual bool is_bool_op(expr* e) const = 0;
virtual literal internalize(expr* e, bool learned) = 0;
virtual bool_var to_bool_var(expr* e) = 0;

View file

@ -125,7 +125,7 @@ namespace euf {
pop_core(n);
}
sat::status th_euf_solver::mk_status(sat::proof_hint const* ps) {
sat::status th_euf_solver::mk_status(th_proof_hint const* ps) {
return sat::status::th(m_is_redundant, get_id(), ps);
}
@ -149,7 +149,7 @@ namespace euf {
return add_clause(2, lits);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::proof_hint const* ps) {
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps) {
sat::literal lits[2] = { a, b };
return add_clause(2, lits, ps);
}
@ -164,7 +164,7 @@ namespace euf {
return add_clause(4, lits);
}
bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, sat::proof_hint const* ps) {
bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps) {
bool was_true = false;
for (unsigned i = 0; i < n; ++i)
was_true |= is_true(lits[i]);
@ -226,13 +226,14 @@ namespace euf {
return ctx.s().rand()();
}
size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs, sat::proof_hint const* pma) {
return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs + (pma?pma->to_string().length()+1:1));
size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs) {
return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs);
}
th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, sat::proof_hint const* pma) {
th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, th_proof_hint const* pma) {
m_consequent = c;
m_eq = p;
m_proof_hint = pma;
m_num_literals = n_lits;
m_num_eqs = n_eqs;
char * base_ptr = reinterpret_cast<char*>(this) + sizeof(th_explain);
@ -244,33 +245,24 @@ namespace euf {
m_eqs = reinterpret_cast<enode_pair*>(base_ptr);
for (i = 0; i < n_eqs; ++i)
m_eqs[i] = eqs[i];
base_ptr += sizeof(enode_pair) * n_eqs;
m_pragma = reinterpret_cast<char*>(base_ptr);
i = 0;
if (pma) {
std::string s = pma->to_string();
for (i = 0; s[i]; ++i)
m_pragma[i] = s[i];
}
m_pragma[i] = 0;
}
th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, sat::proof_hint const* pma) {
th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma) {
region& r = th.ctx.get_region();
void* mem = r.allocate(get_obj_size(n_lits, n_eqs, pma));
void* mem = r.allocate(get_obj_size(n_lits, n_eqs));
sat::constraint_base::initialize(mem, &th);
return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y));
return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y), pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma) {
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
@ -313,8 +305,8 @@ namespace euf {
out << "--> " << m_consequent;
if (m_eq.first != nullptr)
out << "--> " << m_eq.first->get_expr_id() << " == " << m_eq.second->get_expr_id();
if (m_pragma != nullptr)
out << " p " << m_pragma;
if (m_proof_hint != nullptr)
out << " p ";
return out;
}

View file

@ -39,7 +39,7 @@ namespace euf {
virtual bool post_visit(expr* e, bool sign, bool root) { return false; }
public:
virtual ~th_internalizer() {}
virtual ~th_internalizer() = default;
virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0;
@ -58,9 +58,10 @@ namespace euf {
};
class th_decompile {
public:
virtual ~th_decompile() {}
virtual ~th_decompile() = default;
virtual bool to_formulas(std::function<expr_ref(sat::literal)>& lit2expr, expr_ref_vector& fmls) { return false; }
};
@ -68,7 +69,7 @@ namespace euf {
class th_model_builder {
public:
virtual ~th_model_builder() {}
virtual ~th_model_builder() = default;
/**
\brief compute the value for enode \c n and store the value in \c values
@ -138,6 +139,11 @@ namespace euf {
};
class th_proof_hint : public sat::proof_hint {
public:
virtual expr* get_hint(euf::solver& s) const = 0;
};
class th_euf_solver : public th_solver {
protected:
solver& ctx;
@ -150,16 +156,16 @@ namespace euf {
region& get_region();
sat::status mk_status(sat::proof_hint const* ps = nullptr);
sat::status mk_status(th_proof_hint const* ps = nullptr);
bool add_unit(sat::literal lit);
bool add_units(sat::literal_vector const& lits);
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::proof_hint const* ps);
bool add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps);
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, sat::proof_hint const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); }
bool add_clause(unsigned n, sat::literal* lits, sat::proof_hint const* ps = nullptr);
bool add_clause(sat::literal_vector const& lits, th_proof_hint const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); }
bool add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps = nullptr);
void add_equiv(sat::literal a, sat::literal b);
void add_equiv_and(sat::literal a, sat::literal_vector const& bs);
@ -220,16 +226,16 @@ namespace euf {
* that retrieve literals on demand.
*/
class th_explain {
sat::literal m_consequent = sat::null_literal; // literal consequent for propagations
enode_pair m_eq = enode_pair(); // equality consequent for propagations
sat::literal m_consequent = sat::null_literal; // literal consequent for propagations
enode_pair m_eq = enode_pair(); // equality consequent for propagations
th_proof_hint const* m_proof_hint;
unsigned m_num_literals;
unsigned m_num_eqs;
sat::literal* m_literals;
enode_pair* m_eqs;
char* m_pragma = nullptr;
static size_t get_obj_size(unsigned num_lits, unsigned num_eqs, sat::proof_hint const* pma);
th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, sat::proof_hint const* pma = nullptr);
static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, sat::proof_hint const* pma = nullptr);
static size_t get_obj_size(unsigned num_lits, unsigned num_eqs);
th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, th_proof_hint const* pma = nullptr);
static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma = nullptr);
public:
static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs);
@ -240,9 +246,9 @@ namespace euf {
static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
sat::ext_constraint_idx to_index() const {
return sat::constraint_base::mem2base(this);
@ -277,7 +283,7 @@ namespace euf {
enode_pair eq_consequent() const { return m_eq; }
sat::proof_hint const* get_pragma() const { return nullptr; } //*m_pragma ? m_pragma : nullptr; }
th_proof_hint const* get_pragma() const { return m_proof_hint; }
};

View file

@ -0,0 +1,79 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
xor_solver.h
Abstract:
XOR solver.
Interface outline.
--*/
#include "sat/smt/xor_solver.h"
#include "sat/sat_simplifier_params.hpp"
#include "sat/sat_xor_finder.h"
namespace xr {
solver::solver(euf::solver& ctx):
th_solver(ctx.get_manager(), symbol("xor-solver"), ctx.get_manager().get_family_id("xor-solver"))
{}
euf::th_solver* solver::clone(euf::solver& ctx) {
// and relevant copy internal state
return alloc(solver, ctx);
}
void solver::asserted(sat::literal l) {
}
bool solver::unit_propagate() {
return false;
}
void solver::get_antecedents(sat::literal l, sat::ext_justification_idx idx,
sat::literal_vector & r, bool probing) {
}
sat::check_result solver::check() {
return sat::check_result::CR_DONE;
}
void solver::push() {
}
void solver::pop(unsigned n) {
}
// inprocessing
// pre_simplify: decompile all xor constraints to allow other in-processing.
// simplify: recompile clauses to xor constraints
// literals that get added to xor constraints are tagged with the theory.
void solver::pre_simplify() {
}
void solver::simplify() {
}
std::ostream& solver::display(std::ostream& out) const {
return out;
}
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const {
return out;
}
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const {
return out;
}
}

48
src/sat/smt/xor_solver.h Normal file
View file

@ -0,0 +1,48 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
xor_solver.h
Abstract:
XOR solver.
Interface outline.
--*/
#pragma once
#include "sat/smt/euf_solver.h"
namespace xr {
class solver : public euf::th_solver {
public:
solver(euf::solver& ctx);
th_solver* clone(euf::solver& ctx) override;
sat::literal internalize(expr* e, bool sign, bool root, bool redundant) override { UNREACHABLE(); return sat::null_literal; }
void internalize(expr* e, bool redundant) override { UNREACHABLE(); }
void asserted(sat::literal l) override;
bool unit_propagate() override;
void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override;
void pre_simplify() override;
void simplify() override;
sat::check_result check() override;
void push() override;
void pop(unsigned n) override;
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;
};
}