mirror of
https://github.com/Z3Prover/z3
synced 2025-04-10 19:27:06 +00:00
707 lines
26 KiB
C++
707 lines
26 KiB
C++
/*++
|
|
Copyright (c) 2012 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
qe_sat_tactic.cpp
|
|
|
|
Abstract:
|
|
|
|
Procedure for quantifier satisfiability using quantifier projection.
|
|
Based on generalizations by Bjorner & Monniaux
|
|
(see tvm\papers\z3qe\altqe.tex)
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2012-02-24
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "qe_sat_tactic.h"
|
|
#include "quant_hoist.h"
|
|
#include "ast_pp.h"
|
|
#include "smt_solver.h"
|
|
#include "qe.h"
|
|
#include "cooperate.h"
|
|
#include "model_v2_pp.h"
|
|
#include "expr_replacer.h"
|
|
#include "th_rewriter.h"
|
|
#include "expr_context_simplifier.h"
|
|
|
|
// plugin registration.
|
|
// solver specific projection operators.
|
|
// connect goals to tactic
|
|
|
|
namespace qe {
|
|
|
|
class is_relevant_default : public i_expr_pred {
|
|
public:
|
|
bool operator()(expr* e) {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class mk_atom_default : public i_nnf_atom {
|
|
public:
|
|
virtual void operator()(expr* e, bool pol, expr_ref& result) {
|
|
if (pol) result = e;
|
|
else result = result.get_manager().mk_not(e);
|
|
}
|
|
};
|
|
|
|
class sat_tactic : public tactic {
|
|
|
|
// forall x . not forall y . not forall z . not forall u . fml.
|
|
|
|
ast_manager& m;
|
|
expr_ref m_false;
|
|
volatile bool m_cancel;
|
|
front_end_params m_fparams;
|
|
params_ref m_params;
|
|
unsigned m_extrapolate_strategy_param;
|
|
bool m_projection_mode_param;
|
|
bool m_strong_context_simplify_param;
|
|
bool m_ctx_simplify_local_param;
|
|
vector<app_ref_vector> m_vars;
|
|
ptr_vector<smt::solver> m_solvers;
|
|
smt::solver m_solver;
|
|
expr_ref m_fml;
|
|
expr_ref_vector m_Ms;
|
|
expr_ref_vector m_assignments;
|
|
is_relevant_default m_is_relevant;
|
|
mk_atom_default m_mk_atom;
|
|
th_rewriter m_rewriter;
|
|
expr_strong_context_simplifier m_ctx_rewriter;
|
|
|
|
class solver_context : public i_solver_context {
|
|
|
|
ast_manager& m;
|
|
sat_tactic& m_super;
|
|
smt::solver& m_solver;
|
|
atom_set m_pos;
|
|
atom_set m_neg;
|
|
app_ref_vector m_vars;
|
|
expr_ref m_fml;
|
|
ptr_vector<contains_app> m_contains_app;
|
|
bool m_projection_mode_param;
|
|
public:
|
|
solver_context(sat_tactic& s, unsigned idx):
|
|
m(s.m),
|
|
m_super(s),
|
|
m_solver(*s.m_solvers[idx+1]),
|
|
m_vars(m),
|
|
m_fml(m),
|
|
m_projection_mode_param(true) {}
|
|
|
|
virtual ~solver_context() {
|
|
std::for_each(m_contains_app.begin(), m_contains_app.end(), delete_proc<contains_app>());
|
|
}
|
|
|
|
void init(expr* fml, unsigned idx) {
|
|
m_fml = fml;
|
|
for (unsigned j = 0; j < m_super.vars(idx).size(); ++j) {
|
|
add_var(m_super.vars(idx)[j]);
|
|
}
|
|
get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg);
|
|
}
|
|
|
|
void set_projection_mode(bool p) { m_projection_mode_param = p; }
|
|
|
|
ast_manager& get_manager() { return m; }
|
|
|
|
expr* fml() { return m_fml; }
|
|
|
|
// set of atoms in current formula.
|
|
virtual atom_set const& pos_atoms() const { return m_pos; }
|
|
virtual atom_set const& neg_atoms() const { return m_neg; }
|
|
|
|
// Access current set of variables to solve
|
|
virtual unsigned get_num_vars() const { return m_vars.size(); }
|
|
virtual app* get_var(unsigned idx) const { return m_vars[idx]; }
|
|
virtual app*const* get_vars() const { return m_vars.c_ptr(); }
|
|
virtual bool is_var(expr* e, unsigned& idx) const {
|
|
for (unsigned i = 0; i < m_vars.size(); ++i) {
|
|
if (e == m_vars[i]) return (idx = i, true);
|
|
}
|
|
return false;
|
|
}
|
|
virtual contains_app& contains(unsigned idx) { return *m_contains_app[idx]; }
|
|
|
|
// callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml'
|
|
virtual void elim_var(unsigned idx, expr* fml, expr* def) {
|
|
m_fml = fml;
|
|
m_pos.reset();
|
|
m_neg.reset();
|
|
get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg);
|
|
m_vars.erase(idx);
|
|
dealloc(m_contains_app[idx]);
|
|
m_contains_app.erase(m_contains_app.c_ptr() + idx);
|
|
}
|
|
|
|
// callback to add new variable to branch.
|
|
virtual void add_var(app* x) {
|
|
m_vars.push_back(x);
|
|
m_contains_app.push_back(alloc(contains_app, m, x));
|
|
}
|
|
|
|
// callback to add constraints in branch.
|
|
virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) {
|
|
ptr_buffer<expr> args;
|
|
if (l1) args.push_back(l1);
|
|
if (l2) args.push_back(l2);
|
|
if (l3) args.push_back(l3);
|
|
expr_ref cnstr(m.mk_or(args.size(), args.c_ptr()), m);
|
|
m_solver.assert_expr(cnstr);
|
|
TRACE("qe", tout << "add_constraint " << mk_pp(cnstr,m) << "\n";);
|
|
}
|
|
|
|
// eliminate finite domain variable 'var' from fml.
|
|
virtual void blast_or(app* var, expr_ref& fml) {
|
|
expr_ref result(m);
|
|
expr_quant_elim qelim(m, m_super.m_fparams);
|
|
qe::mk_exists(1, &var, fml);
|
|
qelim(m.mk_true(), fml, result);
|
|
fml = result;
|
|
TRACE("qe", tout << mk_pp(var, m) << " " << mk_pp(fml, m) << "\n";);
|
|
}
|
|
|
|
void project_var_partial(unsigned i) {
|
|
app* x = get_var(i);
|
|
m_super.check_success(has_plugin(x));
|
|
qe_solver_plugin& p = plugin(m.get_sort(x)->get_family_id());
|
|
model_ref model;
|
|
m_solver.get_model(model);
|
|
m_super.check_success(p.project(contains(i), model, m_fml));
|
|
m_super.m_rewriter(m_fml);
|
|
TRACE("qe", model_v2_pp(tout, *model); tout << "\n";
|
|
tout << mk_pp(m_fml, m) << "\n";);
|
|
elim_var(i, m_fml, 0);
|
|
}
|
|
|
|
void project_var_full(unsigned i) {
|
|
expr_ref result(m);
|
|
app* x = get_var(i);
|
|
expr_quant_elim qelim(m, m_super.m_fparams);
|
|
qe::mk_exists(1, &x, m_fml);
|
|
qelim(m.mk_true(), m_fml, result);
|
|
m_fml = result;
|
|
m_super.m_rewriter(m_fml);
|
|
TRACE("qe", tout << mk_pp(m_fml, m) << "\n";);
|
|
elim_var(i, m_fml, 0);
|
|
}
|
|
|
|
void project_var(unsigned i) {
|
|
if (m_projection_mode_param) {
|
|
project_var_full(i);
|
|
}
|
|
else {
|
|
project_var_partial(i);
|
|
}
|
|
}
|
|
};
|
|
|
|
public:
|
|
sat_tactic(ast_manager& m, params_ref const& p = params_ref()):
|
|
m(m),
|
|
m_false(m.mk_false(), m),
|
|
m_cancel(false),
|
|
m_params(p),
|
|
m_extrapolate_strategy_param(0),
|
|
m_projection_mode_param(true),
|
|
m_strong_context_simplify_param(true),
|
|
m_ctx_simplify_local_param(false),
|
|
m_fml(m),
|
|
m_Ms(m),
|
|
m_assignments(m),
|
|
m_rewriter(m),
|
|
m_ctx_rewriter(m_fparams, m),
|
|
m_solver(m, m_fparams) {
|
|
m_fparams.m_model = true;
|
|
}
|
|
|
|
virtual tactic * translate(ast_manager & m) {
|
|
return alloc(sat_tactic, m);
|
|
}
|
|
|
|
~sat_tactic() {
|
|
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
|
dealloc(m_solvers[i]);
|
|
}
|
|
}
|
|
|
|
virtual void set_cancel(bool f) {
|
|
m_cancel = f;
|
|
// not thread-safe when solvers are reset.
|
|
// TBD: lock - this, reset() and init_Ms.
|
|
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
|
m_solvers[i]->set_cancel(f);
|
|
}
|
|
m_solver.set_cancel(f);
|
|
m_ctx_rewriter.set_cancel(f);
|
|
}
|
|
|
|
virtual void operator()(
|
|
goal_ref const& goal,
|
|
goal_ref_buffer& result,
|
|
model_converter_ref& mc,
|
|
proof_converter_ref & pc,
|
|
expr_dependency_ref& core)
|
|
{
|
|
checkpoint();
|
|
reset();
|
|
ptr_vector<expr> fmls;
|
|
goal->get_formulas(fmls);
|
|
m_fml = m.mk_and(fmls.size(), fmls.c_ptr());
|
|
TRACE("qe", tout << "input: " << mk_pp(m_fml,m) << "\n";);
|
|
simplify_rewriter_star rw(m);
|
|
expr_ref tmp(m);
|
|
rw(m_fml, tmp);
|
|
m_fml = tmp;
|
|
TRACE("qe", tout << "reduced: " << mk_pp(m_fml,m) << "\n";);
|
|
skolemize_existential_prefix();
|
|
extract_alt_form(m_fml);
|
|
model_ref model;
|
|
expr_ref res = qt(0, m.mk_true(), model);
|
|
goal->inc_depth();
|
|
if (m.is_false(res)) {
|
|
goal->assert_expr(res);
|
|
}
|
|
else {
|
|
goal->reset();
|
|
// equi-satisfiable. What to do with model?
|
|
mc = model2model_converter(&*model);
|
|
}
|
|
result.push_back(goal.get());
|
|
}
|
|
|
|
virtual void collect_statistics(statistics & st) const {
|
|
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
|
m_solvers[i]->collect_statistics(st);
|
|
}
|
|
m_solver.collect_statistics(st);
|
|
m_ctx_rewriter.collect_statistics(st);
|
|
}
|
|
|
|
virtual void reset_statistics() {
|
|
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
|
m_solvers[i]->reset_statistics();
|
|
}
|
|
m_solver.reset_statistics();
|
|
m_ctx_rewriter.reset_statistics();
|
|
}
|
|
|
|
virtual void cleanup() {}
|
|
|
|
virtual void updt_params(params_ref const & p) {
|
|
m_extrapolate_strategy_param = p.get_uint(":extrapolate-strategy", m_extrapolate_strategy_param);
|
|
m_projection_mode_param = p.get_bool(":projection-mode", m_projection_mode_param);
|
|
m_strong_context_simplify_param = p.get_bool(":strong-context-simplify", m_strong_context_simplify_param);
|
|
m_ctx_simplify_local_param = p.get_bool(":strong-context-simplify-local", m_ctx_simplify_local_param);
|
|
}
|
|
|
|
virtual void collect_param_descrs(param_descrs & r) {
|
|
r.insert(":extrapolate-strategy",CPK_UINT, "(default: 0 trivial extrapolation) 1 - nnf strengthening 2 - smt-test 3 - nnf_weakening");
|
|
r.insert(":projection-mode", CPK_BOOL, "(default: true - full) false - partial quantifier instantiation");
|
|
r.insert(":strong-context-simplify", CPK_BOOL, "(default: true) use strong context simplifier on result of quantifier elimination");
|
|
r.insert(":strong-context-simplify-local", CPK_BOOL, "(default: false) use strong context simplifier locally on the new formula only");
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
unsigned num_alternations() const { return m_vars.size(); }
|
|
|
|
void init_Ms() {
|
|
for (unsigned i = 0; i < num_alternations(); ++i) {
|
|
m_Ms.push_back(m.mk_true());
|
|
m_solvers.push_back(alloc(smt::solver, m, m_fparams, m_params));
|
|
}
|
|
m_Ms.push_back(m_fml);
|
|
m_solvers.push_back(alloc(smt::solver, m, m_fparams, m_params));
|
|
m_solvers.back()->assert_expr(m_fml);
|
|
}
|
|
|
|
expr* M(unsigned i) { return m_Ms[i].get(); }
|
|
|
|
app_ref_vector const& vars(unsigned i) { return m_vars[i]; }
|
|
|
|
smt::solver& solver(unsigned i) { return *m_solvers[i]; }
|
|
|
|
void reset() {
|
|
m_fml = 0;
|
|
m_Ms.reset();
|
|
m_vars.reset();
|
|
}
|
|
|
|
void skolemize_existential_prefix() {
|
|
quantifier_hoister hoist(m);
|
|
expr_ref result(m);
|
|
app_ref_vector vars(m);
|
|
hoist.pull_exists(m_fml, vars, result);
|
|
m_fml = result;
|
|
}
|
|
|
|
//
|
|
// fa x ex y fa z . phi
|
|
// fa x ! fa y ! fa z ! (!phi)
|
|
//
|
|
void extract_alt_form(expr* fml) {
|
|
quantifier_hoister hoist(m);
|
|
expr_ref result(m);
|
|
bool is_fa;
|
|
unsigned parity = 0;
|
|
m_fml = fml;
|
|
while (true) {
|
|
app_ref_vector vars(m);
|
|
hoist(m_fml, vars, is_fa, result);
|
|
if (vars.empty()) {
|
|
break;
|
|
}
|
|
SASSERT(((parity & 0x1) == 0) == is_fa);
|
|
++parity;
|
|
TRACE("qe", tout << "Hoist " << (is_fa?"fa":"ex") << "\n";
|
|
for (unsigned i = 0; i < vars.size(); ++i) {
|
|
tout << mk_pp(vars[i].get(), m) << " ";
|
|
}
|
|
tout << "\n";
|
|
tout << mk_pp(result, m) << "\n";);
|
|
|
|
m_vars.push_back(vars);
|
|
m_fml = result;
|
|
}
|
|
//
|
|
// negate formula if the last quantifier is universal.
|
|
// so that normal form fa x ! fa y ! fa z ! psi
|
|
// is obtained.
|
|
//
|
|
if ((parity & 0x1) == 1) {
|
|
m_fml = m.mk_not(m_fml);
|
|
}
|
|
init_Ms();
|
|
checkpoint();
|
|
}
|
|
|
|
/**
|
|
\brief Main quantifier test algorithm loop.
|
|
|
|
*/
|
|
expr_ref qt(unsigned i, expr* ctx, model_ref& model) {
|
|
model_ref model1;
|
|
while (true) {
|
|
IF_VERBOSE(1, verbose_stream() << "qt " << i << "\n";);
|
|
TRACE("qe",
|
|
tout << i << " " << mk_pp(ctx, m) << "\n";
|
|
display(tout););
|
|
checkpoint();
|
|
if (is_sat(i, ctx, model)) {
|
|
expr_ref ctxM = extrapolate(i, ctx, M(i));
|
|
if (i == num_alternations()) {
|
|
return ctxM;
|
|
}
|
|
expr_ref res = qt(i+1, ctxM, model1);
|
|
if (m.is_false(res)) {
|
|
return ctxM;
|
|
}
|
|
project(i, res);
|
|
}
|
|
else {
|
|
return m_false;
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
return expr_ref(m);
|
|
}
|
|
|
|
/**
|
|
\brief compute extrapolant
|
|
|
|
Assume A & B is sat.
|
|
Compute C, such that
|
|
1. A & C is sat,
|
|
2. not B & C is unsat.
|
|
(C strengthens B and is still satisfiable with A).
|
|
*/
|
|
expr_ref extrapolate(unsigned i, expr* A, expr* B) {
|
|
switch(m_extrapolate_strategy_param) {
|
|
case 0: return trivial_extrapolate(i, A, B);
|
|
case 1: return nnf_strengthening_extrapolate(i, A, B);
|
|
case 2: return smt_test_extrapolate(i, A, B);
|
|
case 3: return nnf_weakening_extrapolate(i, A, B);
|
|
default: return trivial_extrapolate(i, A, B);
|
|
}
|
|
}
|
|
|
|
expr_ref trivial_extrapolate(unsigned i, expr* A, expr* B) {
|
|
return expr_ref(B, m);
|
|
}
|
|
|
|
expr_ref conjunction_extrapolate(unsigned i, expr* A, expr* B) {
|
|
return expr_ref(m.mk_and(A, B), m);
|
|
}
|
|
|
|
/**
|
|
|
|
Set C = nnf(B), That is, C is the NNF version of B.
|
|
For each literal in C in order, replace the literal by False and
|
|
check the conditions for extrapolation:
|
|
1. not B & C is unsat,
|
|
2. A & C is sat.
|
|
The first check holds by construction, so it is redundant.
|
|
The second is not redundant.
|
|
Instead of replacing literals in an NNF formula,
|
|
one simply asserts the negation of that literal.
|
|
*/
|
|
expr_ref nnf_strengthening_extrapolate(unsigned i, expr* A, expr* B) {
|
|
expr_ref Bnnf(B, m);
|
|
atom_set pos, neg;
|
|
get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg);
|
|
expr_substitution sub(m);
|
|
|
|
remove_duplicates(pos, neg);
|
|
|
|
// Assumption: B is already asserted in solver[i].
|
|
smt::solver& solver = *m_solvers[i];
|
|
solver.push();
|
|
solver.assert_expr(A);
|
|
nnf_strengthen(solver, pos, m.mk_false(), sub);
|
|
nnf_strengthen(solver, neg, m.mk_true(), sub);
|
|
|
|
|
|
solver.pop(1);
|
|
scoped_ptr<expr_replacer> replace = mk_default_expr_replacer(m);
|
|
replace->set_substitution(&sub);
|
|
(*replace)(Bnnf);
|
|
m_rewriter(Bnnf);
|
|
TRACE("qe",
|
|
tout << "A: " << mk_pp(A, m) << "\n";
|
|
tout << "B: " << mk_pp(B, m) << "\n";
|
|
tout << "B': " << mk_pp(Bnnf.get(), m) << "\n";
|
|
// solver.display(tout);
|
|
);
|
|
DEBUG_CODE(
|
|
solver.push();
|
|
solver.assert_expr(m.mk_not(B));
|
|
solver.assert_expr(Bnnf);
|
|
lbool is_sat = solver.check();
|
|
TRACE("qe", tout << "is sat: " << is_sat << "\n";);
|
|
SASSERT(is_sat == l_false);
|
|
solver.pop(1););
|
|
DEBUG_CODE(
|
|
solver.push();
|
|
solver.assert_expr(A);
|
|
solver.assert_expr(Bnnf);
|
|
lbool is_sat = solver.check();
|
|
TRACE("qe", tout << "is sat: " << is_sat << "\n";);
|
|
SASSERT(is_sat == l_true);
|
|
solver.pop(1););
|
|
|
|
return Bnnf;
|
|
}
|
|
|
|
void nnf_strengthen(smt::solver& solver, atom_set& atoms, expr* value, expr_substitution& sub) {
|
|
atom_set::iterator it = atoms.begin(), end = atoms.end();
|
|
for (; it != end; ++it) {
|
|
solver.push();
|
|
solver.assert_expr(m.mk_iff(*it, value));
|
|
lbool is_sat = solver.check();
|
|
solver.pop(1);
|
|
if (is_sat == l_true) {
|
|
TRACE("qe", tout << "consistent with: " << mk_pp(*it, m) << " " << mk_pp(value, m) << "\n";);
|
|
sub.insert(*it, value);
|
|
solver.assert_expr(m.mk_iff(*it, value));
|
|
}
|
|
checkpoint();
|
|
}
|
|
}
|
|
|
|
void remove_duplicates(atom_set& pos, atom_set& neg) {
|
|
ptr_vector<app> to_delete;
|
|
atom_set::iterator it = pos.begin(), end = pos.end();
|
|
for (; it != end; ++it) {
|
|
if (neg.contains(*it)) {
|
|
to_delete.push_back(*it);
|
|
}
|
|
}
|
|
for (unsigned j = 0; j < to_delete.size(); ++j) {
|
|
pos.remove(to_delete[j]);
|
|
neg.remove(to_delete[j]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set C = nnf(B), That is, C is the NNF version of B.
|
|
For each literal in C in order, replace the literal by True and
|
|
check the conditions for extrapolation
|
|
1. not B & C is unsat,
|
|
2. A & C is sat.
|
|
The second holds by construction and is redundant.
|
|
The first is not redundant.
|
|
*/
|
|
expr_ref nnf_weakening_extrapolate(unsigned i, expr* A, expr* B) {
|
|
expr_ref Bnnf(B, m);
|
|
atom_set pos, neg;
|
|
get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg);
|
|
remove_duplicates(pos, neg);
|
|
expr_substitution sub(m);
|
|
|
|
m_solver.push();
|
|
m_solver.assert_expr(A);
|
|
m_solver.assert_expr(m.mk_not(B));
|
|
nnf_weaken(m_solver, Bnnf, pos, m.mk_true(), sub);
|
|
nnf_weaken(m_solver, Bnnf, neg, m.mk_false(), sub);
|
|
scoped_ptr<expr_replacer> replace = mk_default_expr_replacer(m);
|
|
replace->set_substitution(&sub);
|
|
(*replace)(Bnnf);
|
|
m_rewriter(Bnnf);
|
|
m_solver.pop(1);
|
|
return Bnnf;
|
|
}
|
|
|
|
void nnf_weaken(smt::solver& solver, expr_ref& B, atom_set& atoms, expr* value, expr_substitution& sub) {
|
|
atom_set::iterator it = atoms.begin(), end = atoms.end();
|
|
for (; it != end; ++it) {
|
|
solver.push();
|
|
expr_ref fml = B;
|
|
mk_default_expr_replacer(m)->apply_substitution(*it, value, fml);
|
|
solver.assert_expr(fml);
|
|
if (solver.check() == l_false) {
|
|
sub.insert(*it, value);
|
|
B = fml;
|
|
}
|
|
solver.pop(1);
|
|
checkpoint();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Use the model for A & B to extrapolate.
|
|
Initially, C is conjunction of literals from B
|
|
that are in model of A & B.
|
|
The model is a conjunction of literals.
|
|
Let us denote this set $C$. We see that the conditions
|
|
for extrapolation are satisfied. Furthermore,
|
|
C can be generalized by removing literals
|
|
from C as long as !B & A & C is unsatisfiable.
|
|
*/
|
|
|
|
expr_ref smt_test_extrapolate(unsigned i, expr* A, expr* B) {
|
|
expr_ref_vector proxies(m), core(m);
|
|
obj_map<expr, expr*> proxy_map;
|
|
|
|
checkpoint();
|
|
|
|
m_solver.push();
|
|
m_solver.assert_expr(m.mk_not(B));
|
|
for (unsigned i = 0; i < m_assignments.size(); ++i) {
|
|
proxies.push_back(m.mk_fresh_const("proxy",m.mk_bool_sort()));
|
|
proxy_map.insert(proxies.back(), m_assignments[i].get());
|
|
m_solver.assert_expr(m.mk_iff(proxies.back(), m_assignments[i].get()));
|
|
TRACE("qe", tout << "assignment: " << mk_pp(m_assignments[i].get(), m) << "\n";);
|
|
}
|
|
lbool is_sat = m_solver.check(proxies.size(), proxies.c_ptr());
|
|
SASSERT(is_sat == l_false);
|
|
unsigned core_size = m_solver.get_unsat_core_size();
|
|
for (unsigned i = 0; i < core_size; ++i) {
|
|
core.push_back(proxy_map.find(m_solver.get_unsat_core_expr(i)));
|
|
}
|
|
expr_ref result(m.mk_and(core.size(), core.c_ptr()), m);
|
|
TRACE("qe", tout << "core: " << mk_pp(result, m) << "\n";
|
|
tout << mk_pp(A, m) << "\n";
|
|
tout << mk_pp(B, m) << "\n";);
|
|
m_solver.pop(1);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
\brief project vars(idx) from fml relative to M(idx).
|
|
*/
|
|
void project(unsigned idx, expr* _fml) {
|
|
SASSERT(idx < num_alternations());
|
|
expr_ref fml(_fml, m);
|
|
if (m_strong_context_simplify_param && m_ctx_simplify_local_param) {
|
|
m_ctx_rewriter.push();
|
|
m_ctx_rewriter.assert(M(idx));
|
|
m_ctx_rewriter(fml);
|
|
m_ctx_rewriter.pop();
|
|
TRACE("qe", tout << mk_pp(_fml, m) << "\n-- context simplify -->\n" << mk_pp(fml, m) << "\n";);
|
|
}
|
|
solver_context ctx(*this, idx);
|
|
ctx.add_plugin(mk_arith_plugin(ctx, false, m_fparams));
|
|
ctx.add_plugin(mk_bool_plugin(ctx));
|
|
ctx.add_plugin(mk_bv_plugin(ctx));
|
|
ctx.init(fml, idx);
|
|
ctx.set_projection_mode(m_projection_mode_param);
|
|
m_solvers[idx+1]->push();
|
|
while (ctx.get_num_vars() > 0) {
|
|
VERIFY(l_true == m_solvers[idx+1]->check());
|
|
ctx.project_var(ctx.get_num_vars()-1);
|
|
}
|
|
m_solvers[idx+1]->pop(1);
|
|
expr_ref not_fml(m.mk_not(ctx.fml()), m);
|
|
m_rewriter(not_fml);
|
|
if (m_strong_context_simplify_param && !m_ctx_simplify_local_param) {
|
|
m_ctx_rewriter.push();
|
|
m_ctx_rewriter.assert(M(idx));
|
|
m_ctx_rewriter(not_fml);
|
|
m_ctx_rewriter.pop();
|
|
}
|
|
expr_ref tmp(m.mk_and(M(idx), not_fml), m);
|
|
m_rewriter(tmp);
|
|
m_Ms[idx] = tmp;
|
|
m_solvers[idx]->assert_expr(not_fml);
|
|
TRACE("qe",
|
|
tout << mk_pp(fml, m) << "\n--->\n";
|
|
tout << mk_pp(tmp, m) << "\n";);
|
|
}
|
|
|
|
void checkpoint() {
|
|
if (m_cancel) {
|
|
throw tactic_exception(TACTIC_CANCELED_MSG);
|
|
}
|
|
cooperate("qe-sat");
|
|
}
|
|
|
|
void check_success(bool ok) {
|
|
if (!ok) {
|
|
TRACE("qe", tout << "check failed\n";);
|
|
throw tactic_exception(TACTIC_CANCELED_MSG);
|
|
}
|
|
}
|
|
|
|
bool is_sat(unsigned i, expr* ctx, model_ref& model) {
|
|
smt::solver& solver = *m_solvers[i];
|
|
solver.push();
|
|
solver.assert_expr(ctx);
|
|
lbool r = solver.check();
|
|
m_assignments.reset();
|
|
solver.get_assignments(m_assignments);
|
|
solver.pop(1);
|
|
check_success(r != l_undef);
|
|
if (r == l_true && i == 0) solver.get_model(model);
|
|
return r == l_true;
|
|
}
|
|
|
|
void display(std::ostream& out) {
|
|
bool is_fa = true;
|
|
for (unsigned i = 0; i < num_alternations(); ++i) {
|
|
out << (is_fa?"fa ":"ex ");
|
|
for (unsigned j = 0; j < vars(i).size(); ++j) {
|
|
out << mk_pp(m_vars[i][j].get(), m) << " ";
|
|
}
|
|
out << "\n";
|
|
out << mk_pp(m_Ms[i+1].get(), m) << "\n";
|
|
is_fa = !is_fa;
|
|
}
|
|
}
|
|
};
|
|
|
|
tactic * mk_sat_tactic(ast_manager& m, params_ref const& p) {
|
|
return alloc(sat_tactic, m, p);
|
|
}
|
|
|
|
};
|