3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 00:55:31 +00:00

first round for combined mbi

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2018-06-13 07:55:45 -07:00 committed by Arie Gurfinkel
parent 9109968e55
commit b62d73f209
7 changed files with 297 additions and 64 deletions

View file

@ -37,6 +37,7 @@ namespace qe {
ast_manager& m;
arith_util a;
bool m_check_purified; // check that variables are properly pure
void insert_mul(expr* x, rational const& v, obj_map<expr, rational>& ts) {
TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";);
@ -264,7 +265,7 @@ namespace qe {
}
imp(ast_manager& m):
m(m), a(m) {}
m(m), a(m), m_check_purified(true) {}
~imp() {}
@ -347,17 +348,23 @@ namespace qe {
tids.insert(v, mbo.add_var(r, a.is_int(v)));
}
}
for (expr* fml : fmls) {
mark_rec(fmls_mark, fml);
if (m_check_purified) {
for (expr* fml : fmls) {
mark_rec(fmls_mark, fml);
}
for (auto& kv : tids) {
expr* e = kv.m_key;
if (!var_mark.is_marked(e)) {
mark_rec(fmls_mark, e);
}
}
}
ptr_vector<expr> index2expr;
for (auto& kv : tids) {
expr* e = kv.m_key;
if (!var_mark.is_marked(e)) {
mark_rec(fmls_mark, e);
}
index2expr.setx(kv.m_value, e, 0);
index2expr.setx(kv.m_value, kv.m_key, 0);
}
j = 0;
unsigned_vector real_vars;
for (app* v : vars) {
@ -369,6 +376,7 @@ namespace qe {
}
}
vars.shrink(j);
TRACE("qe", tout << "remaining vars: " << vars << "\n";
for (unsigned v : real_vars) {
tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n";
@ -379,10 +387,9 @@ namespace qe {
vector<row> rows;
mbo.get_live_rows(rows);
for (unsigned i = 0; i < rows.size(); ++i) {
for (row const& r : rows) {
expr_ref_vector ts(m);
expr_ref t(m), s(m), val(m);
row const& r = rows[i];
if (r.m_vars.size() == 0) {
continue;
}
@ -411,25 +418,19 @@ namespace qe {
}
ts.push_back(t);
}
t = mk_add(ts);
s = a.mk_numeral(-r.m_coeff, a.is_int(t));
if (ts.size() == 1) {
t = ts[0].get();
}
else {
t = a.mk_add(ts.size(), ts.c_ptr());
}
switch (r.m_type) {
case opt::t_lt: t = a.mk_lt(t, s); break;
case opt::t_le: t = a.mk_le(t, s); break;
case opt::t_eq: t = a.mk_eq(t, s); break;
case opt::t_mod: {
case opt::t_mod:
if (!r.m_coeff.is_zero()) {
t = a.mk_sub(t, s);
}
t = a.mk_eq(a.mk_mod(t, a.mk_numeral(r.m_mod, true)), a.mk_int(0));
break;
}
}
fmls.push_back(t);
val = eval(t);
CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";);
@ -447,19 +448,29 @@ namespace qe {
ts.push_back(var2expr(index2expr, v));
}
ts.push_back(a.mk_numeral(d.m_coeff, is_int));
t = a.mk_add(ts.size(), ts.c_ptr());
t = mk_add(ts);
if (!d.m_div.is_one() && is_int) {
t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int));
}
else if (!d.m_div.is_one() && !is_int) {
t = a.mk_div(t, a.mk_numeral(d.m_div, is_int));
}
SASSERT(eval(t) == eval(x));
result.push_back(def(expr_ref(x, m), t));
}
}
return result;
}
expr_ref mk_add(expr_ref_vector const& ts) {
if (ts.size() == 1) {
return expr_ref(ts.get(0), m);
}
else {
return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m);
}
}
opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) {
SASSERT(a.is_real(t));
expr_ref_vector fmls(fmls0);
@ -577,6 +588,10 @@ namespace qe {
return m_imp->project(model, vars, lits);
}
void arith_project_plugin::set_check_purified(bool check_purified) {
m_imp->m_check_purified = check_purified;
}
bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return m_imp->solve(model, vars, lits);
}

View file

@ -33,6 +33,12 @@ namespace qe {
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt);
/**
* \brief check if formulas are purified, or leave it to caller to ensure that
* arithmetic variables nested under foreign functions are handled properly.
*/
void set_check_purified(bool check_purified);
};
bool arith_project(model& model, app* var, expr_ref_vector& lits);

View file

@ -77,10 +77,12 @@ Notes:
#include "ast/ast_util.h"
#include "ast/for_each_expr.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/arith_decl_plugin.h"
#include "model/model_evaluator.h"
#include "solver/solver.h"
#include "qe/qe_mbi.h"
#include "qe/qe_term_graph.h"
#include "qe/qe_arith.h"
namespace qe {
@ -141,7 +143,7 @@ namespace qe {
}
// -------------------------------
// euf_mbi, TBD
// euf_mbi
struct euf_mbi_plugin::is_atom_proc {
ast_manager& m;
@ -235,6 +237,140 @@ namespace qe {
}
// -------------------------------
// euf_arith_mbi
struct euf_arith_mbi_plugin::is_atom_proc {
ast_manager& m;
expr_ref_vector& m_atoms;
is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {}
void operator()(app* a) {
if (m.is_eq(a)) {
m_atoms.push_back(a);
}
else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) {
m_atoms.push_back(a);
}
}
void operator()(expr*) {}
};
struct euf_arith_mbi_plugin::is_arith_var_proc {
ast_manager& m;
app_ref_vector& m_vars;
arith_util arith;
obj_hashtable<func_decl> m_exclude;
is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& excl):
m(vars.m()), m_vars(vars), arith(m) {
for (func_decl* f : excl) m_exclude.insert(f);
}
void operator()(app* a) {
if (arith.is_int_real(a) && a->get_family_id() != a->get_family_id() && !m_exclude.contains(a->get_decl())) {
m_vars.push_back(a);
}
}
void operator()(expr*) {}
};
euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot):
m(s->get_manager()),
m_atoms(m),
m_solver(s),
m_dual_solver(sNot) {
params_ref p;
p.set_bool("core.minimize", true);
m_solver->updt_params(p);
m_dual_solver->updt_params(p);
expr_ref_vector fmls(m);
m_solver->get_assertions(fmls);
expr_fast_mark1 marks;
is_atom_proc proc(m_atoms);
for (expr* e : fmls) {
quick_for_each_expr(proc, marks, e);
}
}
mbi_result euf_arith_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) {
lbool r = m_solver->check_sat(lits);
// populate set of arithmetic variables to be projected.
app_ref_vector avars(m);
is_arith_var_proc _proc(avars, vars);
for (expr* l : lits) quick_for_each_expr(_proc, l);
switch (r) {
case l_false:
lits.reset();
m_solver->get_unsat_core(lits);
// optionally minimize core using superposition.
return mbi_unsat;
case l_true: {
m_solver->get_model(mdl);
model_evaluator mev(*mdl.get());
lits.reset();
for (expr* e : m_atoms) {
if (mev.is_true(e)) {
lits.push_back(e);
}
else if (mev.is_false(e)) {
lits.push_back(m.mk_not(e));
}
}
TRACE("qe", tout << "atoms from model: " << lits << "\n";);
r = m_dual_solver->check_sat(lits);
expr_ref_vector core(m);
term_graph tg(m);
switch (r) {
case l_false: {
// use the dual solver to find a 'small' implicant
m_dual_solver->get_unsat_core(core);
TRACE("qe", tout << "core: " << core << "\n";);
lits.reset();
lits.append(core);
arith_util a(m);
// 1. project arithmetic variables using mdl that satisfies core.
// ground any remaining arithmetic variables using model.
arith_project_plugin ap(m);
ap.set_check_purified(false);
auto defs = ap.project(*mdl.get(), avars, lits);
// 2. Add the projected definitions to the remaining (euf) literals
for (auto const& def : defs) {
lits.push_back(m.mk_eq(def.var, def.term));
}
// 3. Project the remaining literals with respect to EUF.
tg.set_vars(vars, false);
tg.add_lits(lits);
lits.reset();
lits.append(tg.project(*mdl));
TRACE("qe", tout << "project: " << lits << "\n";);
return mbi_sat;
}
case l_undef:
return mbi_undef;
case l_true:
UNREACHABLE();
return mbi_undef;
}
return mbi_sat;
}
default:
// TBD: if not running solver to completion, then:
// 1. extract unit literals from m_solver.
// 2. run a cc over the units
// 3. extract equalities or other assignments over the congruence classes
// 4. ensure that at least some progress is made over original lits.
return mbi_undef;
}
}
void euf_arith_mbi_plugin::block(expr_ref_vector const& lits) {
m_solver->assert_expr(mk_not(mk_and(lits)));
}
/** --------------------------------------------------------------
* ping-pong interpolation of Gurfinkel & Vizel
* compute a binary interpolant.
@ -318,4 +454,5 @@ namespace qe {
}
}
}
};

View file

@ -86,29 +86,29 @@ namespace qe {
void block(expr_ref_vector const& lits) override;
};
class euf_arith_mbi_plugin : mbi_plugin {
ast_manager& m;
expr_ref_vector m_atoms;
solver_ref m_solver;
solver_ref m_dual_solver;
struct is_atom_proc;
struct is_arith_var_proc;
public:
euf_arith_mbi_plugin(solver* s, solver* sNot);
~euf_arith_mbi_plugin() override {}
mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override;
void block(expr_ref_vector const& lits) override;
};
/**
* use cases for interpolation.
*/
class interpolator {
ast_manager& m;
ast_manager& m;
public:
interpolator(ast_manager& m):m(m) {}
lbool pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp);
lbool pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp);
};
#if 0
TBD some other scenario, e.g., Farkas, Cute/Beautiful/maybe even Selfless
class solver_mbi_plugin : public mbi_plugin {
solver_ref m_solver;
public:
solver_mbi_plugin(solver* s);
~solver_mbi_plugin() override {}
mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override;
void block(expr_ref_vector const& lits) override;
};
#endif
};