mirror of
https://github.com/Z3Prover/z3
synced 2025-04-06 17:44:08 +00:00
663 lines
23 KiB
C++
663 lines
23 KiB
C++
/*++
|
|
Copyright (c) 2020 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
q_mbi.cpp
|
|
|
|
Abstract:
|
|
|
|
Model-based quantifier instantiation plugin
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2020-09-29
|
|
|
|
--*/
|
|
|
|
#include "ast/ast_trail.h"
|
|
#include "ast/ast_util.h"
|
|
#include "ast/for_each_expr.h"
|
|
#include "ast/rewriter/var_subst.h"
|
|
#include "ast/rewriter/expr_safe_replace.h"
|
|
#include "qe/mbp/mbp_arith.h"
|
|
#include "qe/mbp/mbp_arrays.h"
|
|
#include "qe/mbp/mbp_datatypes.h"
|
|
#include "sat/smt/sat_th.h"
|
|
#include "sat/smt/q_mbi.h"
|
|
#include "sat/smt/q_solver.h"
|
|
#include "sat/smt/euf_solver.h"
|
|
|
|
|
|
namespace q {
|
|
|
|
mbqi::mbqi(euf::solver& ctx, solver& s) :
|
|
ctx(ctx),
|
|
m_qs(s),
|
|
m(s.get_manager()),
|
|
m_model_fixer(ctx, m_qs) {
|
|
auto* ap = alloc(mbp::arith_project_plugin, m);
|
|
ap->set_check_purified(false);
|
|
ap->set_apply_projection(true);
|
|
add_plugin(ap);
|
|
add_plugin(alloc(mbp::datatype_project_plugin, m));
|
|
add_plugin(alloc(mbp::array_project_plugin, m));
|
|
|
|
}
|
|
|
|
lbool mbqi::operator()() {
|
|
lbool result = l_true;
|
|
m_model = nullptr;
|
|
ctx.save_model(m_model);
|
|
m_instantiations.reset();
|
|
for (sat::literal lit : m_qs.m_universal) {
|
|
quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var()));
|
|
if (!ctx.is_relevant(lit.var()))
|
|
continue;
|
|
init_model();
|
|
switch (check_forall(q)) {
|
|
case l_false:
|
|
result = l_false;
|
|
break;
|
|
case l_undef:
|
|
if (result == l_true)
|
|
result = l_undef;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
m_max_cex += ctx.get_config().m_mbqi_max_cexs;
|
|
for (auto const& [qlit, fml, inst, generation] : m_instantiations) {
|
|
euf::solver::scoped_generation sg(ctx, generation + 1);
|
|
sat::literal lit = ~ctx.mk_literal(fml);
|
|
auto* ph = ctx.use_drat()? q_proof_hint::mk(ctx, ~qlit, lit, inst.size(), inst.data()) : nullptr;
|
|
m_qs.add_clause(~qlit, lit, ph);
|
|
m_qs.log_instantiation(~qlit, lit);
|
|
}
|
|
m_instantiations.reset();
|
|
if (result != l_true)
|
|
m_model = nullptr;
|
|
ctx.save_model(m_model);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Restrict solutions to sk to be among a finite set of expressions drawn from
|
|
* 'universe'. Include only expressions that are equal to some term created
|
|
* using at most 'm_generation_bound' quantifier instantiations.
|
|
*/
|
|
void mbqi::restrict_to_universe(expr* sk, ptr_vector<expr> const& universe) {
|
|
SASSERT(!universe.empty());
|
|
expr_ref_vector eqs(m);
|
|
euf::enode* n = nullptr;
|
|
unsigned generation_min = UINT_MAX;
|
|
for (expr* e : universe) {
|
|
if (ctx.values2root().find(e, n)) {
|
|
unsigned g = n->class_generation();
|
|
generation_min = std::min(generation_min, g);
|
|
m_generation_max = std::max(m_generation_max, g);
|
|
if (g > m_generation_bound)
|
|
continue;
|
|
}
|
|
eqs.push_back(m.mk_eq(sk, e));
|
|
}
|
|
if (eqs.empty()) {
|
|
for (expr* e : universe)
|
|
if (ctx.values2root().find(e, n) && n->class_generation() <= generation_min)
|
|
eqs.push_back(m.mk_eq(sk, e));
|
|
}
|
|
m_solver->assert_expr(mk_or(eqs));
|
|
}
|
|
|
|
expr_ref mbqi::replace_model_value(expr* e) {
|
|
auto const& v2r = ctx.values2root();
|
|
euf::enode* r = nullptr;
|
|
if (m.is_bool(e))
|
|
return expr_ref(e, m);
|
|
if (v2r.find(e, r))
|
|
return choose_term(r);
|
|
if (is_app(e) && to_app(e)->get_num_args() > 0) {
|
|
expr_ref_vector args(m);
|
|
for (expr* arg : *to_app(e))
|
|
args.push_back(replace_model_value(arg));
|
|
return expr_ref(m.mk_app(to_app(e)->get_decl(), args), m);
|
|
}
|
|
if (m.is_model_value(e))
|
|
return expr_ref(m.mk_model_value(0, e->get_sort()), m);
|
|
|
|
expr_ref e1 = m_model->unfold_as_array(e);
|
|
if (e1 == e)
|
|
return e1;
|
|
return replace_model_value(e1);
|
|
}
|
|
|
|
expr_ref mbqi::choose_term(euf::enode* r) {
|
|
unsigned gen = r->generation() + 1;
|
|
unsigned count = 0;
|
|
for (euf::enode* n : euf::enode_class(r)) {
|
|
if (n->generation() < gen) {
|
|
count = 0;
|
|
r = n;
|
|
}
|
|
else if (n->generation() == gen) {
|
|
if ((m_qs.random() % ++count) == 0 && has_quantifiers(n->get_expr()))
|
|
r = n;
|
|
}
|
|
if (count > m_max_choose_candidates)
|
|
break;
|
|
}
|
|
return expr_ref(r->get_expr(), m);
|
|
}
|
|
|
|
lbool mbqi::check_forall(quantifier* q) {
|
|
quantifier* q_flat = m_qs.flatten(q);
|
|
init_solver();
|
|
|
|
auto* qb = specialize(q_flat);
|
|
if (!qb)
|
|
return l_undef;
|
|
if (m.is_false(qb->mbody))
|
|
return l_true;
|
|
if (quick_check(q, q_flat, *qb))
|
|
return l_false;
|
|
|
|
m_generation_bound = 0;
|
|
m_generation_max = 0;
|
|
unsigned inc = 1;
|
|
while (true) {
|
|
::solver::scoped_push _sp(*m_solver);
|
|
add_universe_restriction(*qb);
|
|
m_solver->assert_expr(qb->mbody);
|
|
++m_stats.m_num_checks;
|
|
lbool r = m_solver->check_sat(0, nullptr);
|
|
if (r == l_undef)
|
|
return r;
|
|
if (r == l_true) {
|
|
model_ref mdl;
|
|
m_solver->get_model(mdl);
|
|
if (check_forall_subst(q, *qb, *mdl))
|
|
return l_false;
|
|
if (check_forall_default(q, *qb, *mdl))
|
|
return l_false;
|
|
else
|
|
return l_undef;
|
|
}
|
|
if (m_generation_bound >= m_generation_max)
|
|
return l_true;
|
|
m_generation_bound += inc;
|
|
inc += 1;
|
|
}
|
|
return l_true;
|
|
}
|
|
|
|
bool mbqi::check_forall_default(quantifier* q, q_body& qb, model& mdl) {
|
|
expr_ref_vector eqs(m);
|
|
add_domain_bounds(mdl, qb);
|
|
auto proj = solver_project(mdl, qb, eqs, false);
|
|
CTRACE("q", !proj, tout << "could not project " << qb.mbody << " " << eqs << "\n" << mdl);
|
|
if (!proj)
|
|
return false;
|
|
add_instantiation(q, proj);
|
|
return true;
|
|
}
|
|
|
|
bool mbqi::check_forall_subst(quantifier* q, q_body& qb, model& mdl0) {
|
|
if (qb.var_args.empty())
|
|
return false;
|
|
model_ref mdl1;
|
|
expr_ref_vector eqs(m);
|
|
unsigned i = 0;
|
|
::solver::scoped_push _sp(*m_solver);
|
|
add_domain_eqs(mdl0, qb);
|
|
for (; i < m_max_cex; ++i) {
|
|
++m_stats.m_num_checks;
|
|
if (l_true != m_solver->check_sat(0, nullptr))
|
|
break;
|
|
m_solver->get_model(mdl1);
|
|
auto proj = solver_project(*mdl1, qb, eqs, true);
|
|
if (!proj)
|
|
break;
|
|
add_instantiation(q, proj);
|
|
m_solver->assert_expr(m.mk_not(mk_and(eqs)));
|
|
}
|
|
return i > 0;
|
|
}
|
|
|
|
void mbqi::add_instantiation(quantifier* q, expr_ref& proj) {
|
|
sat::literal qlit = ctx.expr2literal(q);
|
|
if (is_exists(q))
|
|
qlit.neg();
|
|
ctx.rewrite(proj);
|
|
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);
|
|
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) {
|
|
SASSERT(!ctx.use_drat() || !m_defs.empty());
|
|
if (m_defs.empty())
|
|
return expr_ref_vector(m);
|
|
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;
|
|
}
|
|
|
|
|
|
void mbqi::add_universe_restriction(q_body& qb) {
|
|
for (app* v : qb.vars) {
|
|
sort* s = v->get_sort();
|
|
if (m_model->has_uninterpreted_sort(s))
|
|
restrict_to_universe(v, m_model->get_universe(s));
|
|
}
|
|
}
|
|
|
|
mbqi::q_body* mbqi::specialize(quantifier* q) {
|
|
var_subst subst(m);
|
|
q_body* result = q2body(q);
|
|
expr_ref& mbody = result->mbody;
|
|
if (!m_model->eval_expr(q->get_expr(), mbody, true))
|
|
return nullptr;
|
|
|
|
mbody = subst(mbody, result->vars);
|
|
if (is_forall(q))
|
|
mbody = mk_not(m, mbody);
|
|
TRACE("q", tout << "specialize " << mbody << "\n";);
|
|
return result;
|
|
}
|
|
|
|
mbqi::q_body* mbqi::q2body(quantifier* q) {
|
|
q_body* result = nullptr;
|
|
if (!m_q2body.find(q, result)) {
|
|
unsigned sz = q->get_num_decls();
|
|
var_subst subst(m);
|
|
result = alloc(q_body, m);
|
|
m_q2body.insert(q, result);
|
|
ctx.push(new_obj_trail<q_body>(result));
|
|
ctx.push(insert_obj_map<quantifier, q_body*>(m_q2body, q));
|
|
app_ref_vector& vars = result->vars;
|
|
vars.resize(sz, nullptr);
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
sort* s = q->get_decl_sort(i);
|
|
vars[i] = m.mk_fresh_const(q->get_decl_name(i), s, false);
|
|
}
|
|
expr_ref fml = subst(q->get_expr(), vars);
|
|
if (is_forall(q))
|
|
fml = m.mk_not(fml);
|
|
flatten_and(fml, result->vbody);
|
|
extract_free_vars(q, *result);
|
|
extract_var_args(q->get_expr(), *result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
expr_ref mbqi::solver_project(model& mdl, q_body& qb, expr_ref_vector& eqs, bool use_inst) {
|
|
eqs.reset();
|
|
model::scoped_model_completion _sc(mdl, true);
|
|
m_model->reset_eval_cache();
|
|
for (app* v : qb.vars)
|
|
m_model->register_decl(v->get_decl(), mdl(v));
|
|
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";
|
|
tout << "model\n";
|
|
tout << *m_model << "\n";
|
|
tout << "model of projection\n" << mdl << "\n";
|
|
tout << "var args: " << qb.var_args.size() << "\n";
|
|
tout << "domain eqs: " << qb.domain_eqs << "\n";
|
|
for (expr* f : fmls)
|
|
if (m_model->is_false(f))
|
|
tout << mk_pp(f, m) << " := false\n";
|
|
tout << "vars: " << vars << "\n";);
|
|
|
|
expr_safe_replace rep(m);
|
|
for (unsigned i = 0; !use_inst && i < vars.size(); ++i) {
|
|
app* v = vars.get(i);
|
|
auto* p = get_plugin(v);
|
|
if (p && !fmls_extracted) {
|
|
TRACE("q", tout << "domain eqs\n" << qb.domain_eqs << "\n";);
|
|
|
|
fmls.append(qb.domain_eqs);
|
|
eliminate_nested_vars(fmls, qb);
|
|
for (expr* e : fmls)
|
|
if (!m_model->is_true(e)) {
|
|
TRACE("q", tout << "not true: " << mk_pp(e, m) << " := " << (*m_model)(e) << "\n");
|
|
return expr_ref(nullptr, m);
|
|
}
|
|
mbp::project_plugin proj(m);
|
|
proj.extract_literals(*m_model, vars, fmls);
|
|
fmls_extracted = true;
|
|
}
|
|
if (!p)
|
|
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)) {
|
|
TRACE("q", tout << "theory projection failed\n");
|
|
return expr_ref(m);
|
|
}
|
|
}
|
|
for (app* v : vars) {
|
|
expr_ref term(m);
|
|
expr_ref val = (*m_model)(v);
|
|
term = replace_model_value(val);
|
|
TRACE("euf", tout << "replaced model value " << term << "\nfrom\n" << val << "\n");
|
|
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);
|
|
TRACE("q", tout << "generated formulas\n" << fmls << "\ngenerated eqs:\n" << eqs << "\n";
|
|
for (auto const& [v,t] : m_defs) tout << v << " := " << t << "\n");
|
|
return mk_and(fmls);
|
|
}
|
|
|
|
/**
|
|
* Add disjunctions to m_solver that restrict the possible values of
|
|
* arguments to uninterpreted functions. The disjunctions added to the solver
|
|
* are specialized with respect to m_model.
|
|
* Add also disjunctions to the quantifier "domain_eqs", to track the constraints
|
|
* added to the solver.
|
|
*/
|
|
void mbqi::add_domain_eqs(model& mdl, q_body& qb) {
|
|
qb.domain_eqs.reset();
|
|
var_subst subst(m);
|
|
|
|
for (auto [t, idx] : qb.var_args) {
|
|
expr_ref bounds = m_model_fixer.restrict_arg(t, idx);
|
|
if (m.is_true(bounds))
|
|
continue;
|
|
expr_ref vbounds = subst(bounds, qb.vars);
|
|
expr_ref mbounds(m);
|
|
if (!m_model->eval_expr(bounds, mbounds, true))
|
|
return;
|
|
mbounds = subst(mbounds, qb.vars);
|
|
m_solver->assert_expr(mbounds);
|
|
qb.domain_eqs.push_back(vbounds);
|
|
}
|
|
|
|
unsigned sz = qb.vars.size();
|
|
for (unsigned idx = 0; idx < sz; ++idx) {
|
|
if (!qb.is_free(idx))
|
|
continue;
|
|
expr* v = qb.vars.get(qb.vars.size() - idx - 1);
|
|
sort* srt = v->get_sort();
|
|
expr_ref_vector veqs(m), meqs(m);
|
|
auto const& nodes = ctx.get_egraph().nodes();
|
|
unsigned sz = nodes.size();
|
|
unsigned bound = m_max_unbounded_equalities;
|
|
expr_mark visited;
|
|
for (unsigned i = 0; i < sz && 0 < bound; ++i) {
|
|
auto* n = nodes[i];
|
|
expr* e = n->get_expr();
|
|
expr* val = ctx.node2value(n);
|
|
if (val && e->get_sort() == srt &&
|
|
!m.is_value(e) &&
|
|
!visited.is_marked(val)) {
|
|
visited.mark(val);
|
|
expr_ref value = replace_model_value(val);
|
|
veqs.push_back(m.mk_eq(v, e));
|
|
meqs.push_back(m.mk_eq(v, value));
|
|
--bound;
|
|
}
|
|
}
|
|
if (veqs.empty())
|
|
continue;
|
|
expr_ref meq = mk_or(meqs);
|
|
expr_ref veq = mk_or(veqs);
|
|
m_solver->assert_expr(meq);
|
|
qb.domain_eqs.push_back(veq);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add bounds to sub-terms under uninterpreted functions for projection.
|
|
*/
|
|
void mbqi::add_domain_bounds(model& mdl, q_body& qb) {
|
|
qb.domain_eqs.reset();
|
|
m_model->reset_eval_cache();
|
|
{
|
|
model::scoped_model_completion _sc(mdl, true);
|
|
for (app* v : qb.vars)
|
|
m_model->register_decl(v->get_decl(), mdl(v));
|
|
}
|
|
ctx.model_updated(m_model);
|
|
if (qb.var_args.empty())
|
|
return;
|
|
var_subst subst(m);
|
|
for (auto [t, idx] : qb.var_args) {
|
|
expr_ref _term = subst(t, qb.vars);
|
|
app_ref term(to_app(_term), m);
|
|
expr_ref value = (*m_model)(term->get_arg(idx));
|
|
if (m.is_value(value))
|
|
m_model_fixer.invert_arg(term, idx, value, qb.domain_eqs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove occurrences of free functions that contain variables.
|
|
* Add top-level equalities for those occurrences.
|
|
*
|
|
* F[g(t)] -> F[s] & g(t) = s
|
|
*
|
|
* where
|
|
* - eval(g(t)) = eval(s),
|
|
* - t contains bound variables,
|
|
* - s is ground.
|
|
*/
|
|
void mbqi::eliminate_nested_vars(expr_ref_vector& fmls, q_body& qb) {
|
|
if (qb.var_args.empty())
|
|
return;
|
|
expr_safe_replace rep(m);
|
|
var_subst subst(m);
|
|
expr_ref_vector eqs(m);
|
|
expr_mark visited;
|
|
for (auto p : qb.var_args) {
|
|
expr* e = p.first;
|
|
if (visited.is_marked(e))
|
|
continue;
|
|
visited.mark(e);
|
|
expr_ref _term = subst(e, qb.vars);
|
|
app_ref term(to_app(_term), m);
|
|
expr_ref value = (*m_model)(term);
|
|
value = replace_model_value(value);
|
|
expr* s = m_model_fixer.invert_app(term, value);
|
|
rep.insert(term, s);
|
|
expr_ref eq(m.mk_eq(term, s), m);
|
|
if (m_model->is_false(eq)) {
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << mk_pp(s, m) << " := " << (*m_model)(s) << "\n";
|
|
verbose_stream() << mk_pp(term, m) << " := " << (*m_model)(term) << "\n";
|
|
verbose_stream() << value << " -> " << (*m_model)(ctx.values2root()[value]->get_expr()) << "\n";
|
|
verbose_stream() << (*m_model)(s) << " -> " << (*m_model)(ctx.values2root()[(*m_model)(s)]->get_expr()) << "\n";
|
|
verbose_stream() << *m_model << "\n";);
|
|
}
|
|
eqs.push_back(eq);
|
|
}
|
|
rep(fmls);
|
|
fmls.append(eqs);
|
|
}
|
|
|
|
/*
|
|
* Add domain restrictions for every non-ground arguments to uninterpreted functions.
|
|
*/
|
|
void mbqi::extract_var_args(expr* _t, q_body& qb) {
|
|
expr_ref t(_t, m);
|
|
for (expr* s : subterms::ground(t)) {
|
|
if (is_ground(s))
|
|
continue;
|
|
if (is_uninterp(s) && to_app(s)->get_num_args() > 0) {
|
|
unsigned i = 0;
|
|
for (expr* arg : *to_app(s)) {
|
|
if (!is_ground(arg) && !is_uninterp(arg) && !qb.is_free(arg))
|
|
qb.var_args.push_back(std::make_pair(to_app(s), i));
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Extract variable positions that are free.
|
|
*/
|
|
void mbqi::extract_free_vars(quantifier* q, q_body& qb) {
|
|
expr_ref fml(q->get_expr(), m);
|
|
expr_ref_vector fmls(m);
|
|
if (is_exists(q))
|
|
fml = m.mk_not(fml);
|
|
flatten_or(fml, fmls);
|
|
expr* a = nullptr, *b = nullptr;
|
|
for (expr* f : fmls) {
|
|
if (!m.is_eq(f, a, b))
|
|
continue;
|
|
if (is_var(a) && !is_var(b))
|
|
qb.set_free(to_var(a)->get_idx());
|
|
if (is_var(b) && !is_var(a))
|
|
qb.set_free(to_var(b)->get_idx());
|
|
}
|
|
}
|
|
|
|
bool mbqi::quick_check(quantifier* q, quantifier* q_flat, q_body& qb) {
|
|
unsigned_vector offsets;
|
|
if (!first_offset(offsets, qb.vars))
|
|
return false;
|
|
var_subst subst(m);
|
|
expr_ref body(m);
|
|
unsigned max_rounds = m_max_quick_check_rounds;
|
|
unsigned num_bindings = 0;
|
|
expr_ref_vector binding(m);
|
|
|
|
for (unsigned i = 0; i < max_rounds && num_bindings < m_max_cex; ++i) {
|
|
set_binding(offsets, qb.vars, binding);
|
|
if (m_model->is_true(qb.vbody)) {
|
|
body = subst(q_flat->get_expr(), binding);
|
|
if (is_forall(q))
|
|
body = ::mk_not(m, body);
|
|
if (ctx.use_drat()) {
|
|
m_defs.reset();
|
|
for (unsigned i = 0; i < binding.size(); ++i) {
|
|
expr_ref v(qb.vars.get(i), m);
|
|
expr_ref t(binding.get(i), m);
|
|
m_defs.push_back(mbp::def(v, t));
|
|
}
|
|
}
|
|
add_instantiation(q, body);
|
|
++num_bindings;
|
|
}
|
|
if (!next_offset(offsets, qb.vars))
|
|
break;
|
|
}
|
|
return num_bindings > 0;
|
|
}
|
|
|
|
bool mbqi::next_offset(unsigned_vector& offsets, app_ref_vector const& vars, unsigned index, unsigned start) {
|
|
auto* v = vars[index];
|
|
sort* srt = v->get_sort();
|
|
auto const& nodes = ctx.get_egraph().nodes();
|
|
unsigned sz = nodes.size();
|
|
for (unsigned i = start; i < sz; ++i) {
|
|
euf::enode* n = nodes[i];
|
|
if (n->generation() > 0)
|
|
return false;
|
|
expr* e = n->get_expr();
|
|
if (e->get_sort() == srt && !m.is_value(e)) {
|
|
offsets[index] = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void mbqi::set_binding(unsigned_vector const& offsets, app_ref_vector const& vars, expr_ref_vector& binding) {
|
|
binding.reset();
|
|
auto const& nodes = ctx.get_egraph().nodes();
|
|
m_model->reset_eval_cache();
|
|
model::scoped_model_completion _sc(*m_model, true);
|
|
for (unsigned j = 0; j < offsets.size(); ++j) {
|
|
unsigned offset = offsets[j];
|
|
binding.push_back(nodes[offset]->get_expr());
|
|
m_model->register_decl(vars[j]->get_decl(), (*m_model)(binding.get(j)));
|
|
}
|
|
}
|
|
|
|
bool mbqi::first_offset(unsigned_vector& offsets, app_ref_vector const& vars) {
|
|
offsets.reset();
|
|
offsets.resize(vars.size(), 0);
|
|
for (unsigned i = 0; i < vars.size(); ++i)
|
|
if (!next_offset(offsets, vars, i, 0))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool mbqi::next_offset(unsigned_vector& offsets, app_ref_vector const& vars) {
|
|
for (unsigned i = 0; i < vars.size(); ++i) {
|
|
if (next_offset(offsets, vars, i, offsets[i] + 1))
|
|
return true;
|
|
for (unsigned j = 0; j <= i; ++j)
|
|
if (!next_offset(offsets, vars, j, 0))
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void mbqi::init_model() {
|
|
if (m_model)
|
|
return;
|
|
m_model = alloc(model, m);
|
|
ctx.update_model(m_model);
|
|
}
|
|
|
|
void mbqi::init_solver() {
|
|
if (!m_solver)
|
|
m_solver = mk_smt2_solver(m, m_no_drat_params);
|
|
}
|
|
|
|
void mbqi::init_search() {
|
|
m_max_cex = ctx.get_config().m_mbqi_max_cexs;
|
|
}
|
|
|
|
void mbqi::finalize_model(model& mdl) {
|
|
m_model_fixer(mdl);
|
|
}
|
|
|
|
mbp::project_plugin* mbqi::get_plugin(app* var) {
|
|
family_id fid = var->get_sort()->get_family_id();
|
|
return m_plugins.get(fid, nullptr);
|
|
}
|
|
|
|
void mbqi::add_plugin(mbp::project_plugin* p) {
|
|
family_id fid = p->get_family_id();
|
|
m_plugins.reserve(fid + 1);
|
|
SASSERT(!m_plugins.get(fid, nullptr));
|
|
m_plugins.set(fid, p);
|
|
}
|
|
|
|
void mbqi::collect_statistics(statistics& st) const {
|
|
if (m_solver)
|
|
m_solver->collect_statistics(st);
|
|
st.update("q mbi instantiations", m_stats.m_num_instantiations);
|
|
st.update("q mbi num checks", m_stats.m_num_checks);
|
|
}
|
|
|
|
}
|