mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 00:55:31 +00:00
moving remaining qsat functionality over
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
296addf246
commit
20bbdfe31a
23 changed files with 3876 additions and 225 deletions
921
src/qe/nlqsat.cpp
Normal file
921
src/qe/nlqsat.cpp
Normal file
|
@ -0,0 +1,921 @@
|
|||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nlqsat.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Quantifier Satisfiability Solver for nlsat
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2015-10-17
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include "nlqsat.h"
|
||||
#include "nlsat_solver.h"
|
||||
#include "nlsat_explain.h"
|
||||
#include "nlsat_assignment.h"
|
||||
#include "qsat.h"
|
||||
#include "quant_hoist.h"
|
||||
#include "goal2nlsat.h"
|
||||
#include "expr2var.h"
|
||||
#include "uint_set.h"
|
||||
#include "ast_util.h"
|
||||
#include "tseitin_cnf_tactic.h"
|
||||
#include "expr_safe_replace.h"
|
||||
|
||||
namespace qe {
|
||||
|
||||
enum qsat_mode_t {
|
||||
qsat_t,
|
||||
elim_t,
|
||||
interp_t
|
||||
};
|
||||
|
||||
class nlqsat : public tactic {
|
||||
|
||||
typedef unsigned_vector assumption_vector;
|
||||
typedef nlsat::scoped_literal_vector clause;
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_rounds;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
ast_manager& m;
|
||||
qsat_mode_t m_mode;
|
||||
params_ref m_params;
|
||||
nlsat::solver m_solver;
|
||||
tactic_ref m_nftactic;
|
||||
nlsat::literal_vector m_asms;
|
||||
nlsat::literal_vector m_cached_asms;
|
||||
unsigned_vector m_cached_asms_lim;
|
||||
nlsat::literal m_is_true;
|
||||
nlsat::assignment m_rmodel;
|
||||
svector<lbool> m_bmodel;
|
||||
nlsat::assignment m_rmodel0;
|
||||
svector<lbool> m_bmodel0;
|
||||
bool m_valid_model;
|
||||
vector<nlsat::var_vector> m_bound_rvars;
|
||||
vector<svector<nlsat::bool_var> > m_bound_bvars;
|
||||
vector<nlsat::scoped_literal_vector> m_preds;
|
||||
svector<max_level> m_rvar2level;
|
||||
u_map<max_level> m_bvar2level;
|
||||
expr2var m_a2b, m_t2x;
|
||||
u_map<expr*> m_b2a, m_x2t;
|
||||
volatile bool m_cancel;
|
||||
stats m_stats;
|
||||
statistics m_st;
|
||||
obj_hashtable<expr> m_free_vars;
|
||||
expr_ref_vector m_answer;
|
||||
expr_safe_replace m_answer_simplify;
|
||||
nlsat::literal_vector m_assumptions;
|
||||
u_map<expr*> m_asm2fml;
|
||||
expr_ref_vector m_trail;
|
||||
|
||||
lbool check_sat() {
|
||||
while (true) {
|
||||
++m_stats.m_num_rounds;
|
||||
check_cancel();
|
||||
init_assumptions();
|
||||
lbool res = m_solver.check(m_asms);
|
||||
switch (res) {
|
||||
case l_true:
|
||||
TRACE("qe", display(tout); );
|
||||
save_model();
|
||||
push();
|
||||
break;
|
||||
case l_false:
|
||||
if (0 == level()) return l_false;
|
||||
if (1 == level() && m_mode == qsat_t) return l_true;
|
||||
project();
|
||||
break;
|
||||
case l_undef:
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void init_assumptions() {
|
||||
unsigned lvl = level();
|
||||
m_asms.reset();
|
||||
m_asms.push_back(is_exists()?m_is_true:~m_is_true);
|
||||
m_asms.append(m_assumptions);
|
||||
TRACE("qe", tout << "model valid: " << m_valid_model << " level: " << lvl << " ";
|
||||
display_assumptions(tout);
|
||||
m_solver.display(tout););
|
||||
|
||||
if (!m_valid_model) {
|
||||
m_asms.append(m_cached_asms);
|
||||
return;
|
||||
}
|
||||
unsave_model();
|
||||
if (lvl == 0) {
|
||||
SASSERT(m_cached_asms.empty());
|
||||
return;
|
||||
}
|
||||
if (lvl <= m_preds.size()) {
|
||||
for (unsigned j = 0; j < m_preds[lvl - 1].size(); ++j) {
|
||||
add_literal(m_cached_asms, m_preds[lvl - 1][j]);
|
||||
}
|
||||
}
|
||||
m_asms.append(m_cached_asms);
|
||||
|
||||
for (unsigned i = lvl + 1; i < m_preds.size(); i += 2) {
|
||||
for (unsigned j = 0; j < m_preds[i].size(); ++j) {
|
||||
nlsat::literal l = m_preds[i][j];
|
||||
max_level lv = m_bvar2level.find(l.var());
|
||||
bool use =
|
||||
(lv.m_fa == i && (lv.m_ex == UINT_MAX || lv.m_ex < lvl)) ||
|
||||
(lv.m_ex == i && (lv.m_fa == UINT_MAX || lv.m_fa < lvl));
|
||||
if (use) {
|
||||
add_literal(m_asms, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("qe", display(tout);
|
||||
for (unsigned i = 0; i < m_asms.size(); ++i) {
|
||||
m_solver.display(tout, m_asms[i]); tout << "\n";
|
||||
});
|
||||
save_model();
|
||||
}
|
||||
|
||||
void add_literal(nlsat::literal_vector& lits, nlsat::literal l) {
|
||||
lbool r = m_solver.value(l);
|
||||
switch (r) {
|
||||
case l_true:
|
||||
lits.push_back(l);
|
||||
break;
|
||||
case l_false:
|
||||
lits.push_back(~l);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class S, class T>
|
||||
void insert_set(S& set, T const& vec) {
|
||||
for (unsigned i = 0; i < vec.size(); ++i) {
|
||||
set.insert(vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mbp(unsigned level, nlsat::scoped_literal_vector& result) {
|
||||
nlsat::var_vector vars;
|
||||
uint_set fvars;
|
||||
extract_vars(level, vars, fvars);
|
||||
mbp(vars, fvars, result);
|
||||
}
|
||||
|
||||
void extract_vars(unsigned level, nlsat::var_vector& vars, uint_set& fvars) {
|
||||
for (unsigned i = 0; i < m_bound_rvars.size(); ++i) {
|
||||
if (i < level) {
|
||||
insert_set(fvars, m_bound_bvars[i]);
|
||||
}
|
||||
else {
|
||||
vars.append(m_bound_rvars[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mbp(nlsat::var_vector const& vars, uint_set const& fvars, clause& result) {
|
||||
//
|
||||
// Also project auxiliary variables from clausification.
|
||||
//
|
||||
unsave_model();
|
||||
nlsat::explain& ex = m_solver.get_explain();
|
||||
nlsat::scoped_literal_vector new_result(m_solver);
|
||||
result.reset();
|
||||
// project quantified Boolean variables.
|
||||
for (unsigned i = 0; i < m_asms.size(); ++i) {
|
||||
nlsat::literal lit = m_asms[i];
|
||||
if (!m_b2a.contains(lit.var()) || fvars.contains(lit.var())) {
|
||||
result.push_back(lit);
|
||||
}
|
||||
}
|
||||
TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";);
|
||||
// project quantified real variables.
|
||||
// They are sorted by size, so we project the largest variables first to avoid
|
||||
// renaming variables.
|
||||
for (unsigned i = vars.size(); i > 0;) {
|
||||
--i;
|
||||
new_result.reset();
|
||||
ex.project(vars[i], result.size(), result.c_ptr(), new_result);
|
||||
result.swap(new_result);
|
||||
TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";);
|
||||
}
|
||||
negate_clause(result);
|
||||
}
|
||||
|
||||
void negate_clause(clause& result) {
|
||||
for (unsigned i = 0; i < result.size(); ++i) {
|
||||
result.set(i, ~result[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void save_model() {
|
||||
m_solver.get_rvalues(m_rmodel);
|
||||
m_solver.get_bvalues(m_bmodel);
|
||||
m_valid_model = true;
|
||||
if (is_exists(level())) {
|
||||
m_rmodel0.copy(m_rmodel);
|
||||
m_bmodel0.reset();
|
||||
m_bmodel0.append(m_bmodel);
|
||||
}
|
||||
}
|
||||
|
||||
void unsave_model() {
|
||||
SASSERT(m_valid_model);
|
||||
m_solver.set_rvalues(m_rmodel);
|
||||
m_solver.set_bvalues(m_bmodel);
|
||||
}
|
||||
|
||||
void clear_model() {
|
||||
m_valid_model = false;
|
||||
m_rmodel.reset();
|
||||
m_bmodel.reset();
|
||||
m_solver.set_rvalues(m_rmodel);
|
||||
}
|
||||
|
||||
unsigned level() const {
|
||||
return m_cached_asms_lim.size();
|
||||
}
|
||||
|
||||
void enforce_parity(clause& cl) {
|
||||
cl.push_back(is_exists()?~m_is_true:m_is_true);
|
||||
}
|
||||
|
||||
void add_clause(clause& cl) {
|
||||
if (cl.empty()) {
|
||||
cl.push_back(~m_solver.mk_true());
|
||||
}
|
||||
SASSERT(!cl.empty());
|
||||
nlsat::literal_vector lits(cl.size(), cl.c_ptr());
|
||||
m_solver.mk_clause(lits.size(), lits.c_ptr());
|
||||
}
|
||||
|
||||
max_level get_level(clause const& cl) {
|
||||
return get_level(cl.size(), cl.c_ptr());
|
||||
}
|
||||
|
||||
max_level get_level(unsigned n, nlsat::literal const* ls) {
|
||||
max_level level;
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
level.merge(get_level(ls[i]));
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
max_level get_level(nlsat::literal l) {
|
||||
max_level level;
|
||||
if (m_bvar2level.find(l.var(), level)) {
|
||||
return level;
|
||||
}
|
||||
nlsat::var_vector vs;
|
||||
m_solver.vars(l, vs);
|
||||
for (unsigned i = 0; i < vs.size(); ++i) {
|
||||
level.merge(m_rvar2level[vs[i]]);
|
||||
}
|
||||
set_level(l.var(), level);
|
||||
return level;
|
||||
}
|
||||
|
||||
void set_level(nlsat::bool_var v, max_level const& level) {
|
||||
unsigned k = level.max();
|
||||
while (m_preds.size() <= k) {
|
||||
m_preds.push_back(nlsat::scoped_literal_vector(m_solver));
|
||||
}
|
||||
nlsat::literal l(v, false);
|
||||
m_preds[k].push_back(l);
|
||||
m_bvar2level.insert(v, level);
|
||||
TRACE("qe", m_solver.display(tout, l); tout << ": " << level << "\n";);
|
||||
}
|
||||
|
||||
void project() {
|
||||
TRACE("qe", display_assumptions(tout););
|
||||
if (!m_valid_model) {
|
||||
pop(1);
|
||||
return;
|
||||
}
|
||||
if (m_mode == elim_t) {
|
||||
project_qe();
|
||||
return;
|
||||
}
|
||||
SASSERT(level() >= 2);
|
||||
unsigned num_scopes;
|
||||
clause cl(m_solver);
|
||||
mbp(level()-1, cl);
|
||||
|
||||
max_level clevel = get_level(cl);
|
||||
enforce_parity(cl);
|
||||
add_clause(cl);
|
||||
|
||||
if (clevel.max() == UINT_MAX) {
|
||||
num_scopes = 2*(level()/2);
|
||||
}
|
||||
else {
|
||||
SASSERT(clevel.max() + 2 <= level());
|
||||
num_scopes = level() - clevel.max();
|
||||
if ((num_scopes % 2) != 0) {
|
||||
--num_scopes;
|
||||
}
|
||||
SASSERT(num_scopes >= 2);
|
||||
}
|
||||
|
||||
TRACE("qe", tout << "backtrack: " << num_scopes << "\n";);
|
||||
pop(num_scopes);
|
||||
}
|
||||
|
||||
void project_qe() {
|
||||
SASSERT(level() >= 1 && m_mode == elim_t && m_valid_model);
|
||||
clause cl(m_solver);
|
||||
mbp(std::max(1u, level()-1), cl);
|
||||
|
||||
expr_ref fml = clause2fml(cl);
|
||||
TRACE("qe", tout << level() << ": " << fml << "\n";);
|
||||
max_level clevel = get_level(cl);
|
||||
if (level() == 1 || clevel.max() == 0) {
|
||||
add_assumption_literal(cl, fml);
|
||||
}
|
||||
else {
|
||||
enforce_parity(cl);
|
||||
}
|
||||
add_clause(cl);
|
||||
|
||||
if (level() == 1) { // is_forall() && clevel.max() == 0
|
||||
add_to_answer(fml);
|
||||
}
|
||||
|
||||
if (level() == 1) {
|
||||
pop(1);
|
||||
}
|
||||
else {
|
||||
pop(2);
|
||||
}
|
||||
}
|
||||
|
||||
void add_to_answer(expr_ref& fml) {
|
||||
m_answer_simplify(fml);
|
||||
expr* e;
|
||||
if (m.is_not(fml, e)) {
|
||||
m_answer_simplify.insert(e, m.mk_false());
|
||||
}
|
||||
else {
|
||||
m_answer_simplify.insert(fml, m.mk_true());
|
||||
}
|
||||
m_answer.push_back(fml);
|
||||
}
|
||||
|
||||
expr_ref clause2fml(nlsat::scoped_literal_vector const& clause) {
|
||||
expr_ref_vector fmls(m);
|
||||
expr_ref fml(m);
|
||||
expr* t;
|
||||
nlsat2goal n2g(m);
|
||||
for (unsigned i = 0; i < clause.size(); ++i) {
|
||||
nlsat::literal l = clause[i];
|
||||
if (m_asm2fml.find(l.var(), t)) {
|
||||
fml = t;
|
||||
if (l.sign()) {
|
||||
fml = push_not(fml);
|
||||
}
|
||||
SASSERT(l.sign());
|
||||
fmls.push_back(fml);
|
||||
}
|
||||
else {
|
||||
fmls.push_back(n2g(m_solver, m_b2a, m_x2t, l));
|
||||
}
|
||||
}
|
||||
fml = mk_or(fmls);
|
||||
return fml;
|
||||
}
|
||||
|
||||
void add_assumption_literal(clause& clause, expr* fml) {
|
||||
nlsat::bool_var b = m_solver.mk_bool_var();
|
||||
clause.push_back(nlsat::literal(b, true));
|
||||
m_assumptions.push_back(nlsat::literal(b, false));
|
||||
m_asm2fml.insert(b, fml);
|
||||
m_trail.push_back(fml);
|
||||
m_bvar2level.insert(b, max_level());
|
||||
}
|
||||
|
||||
bool is_exists() const { return is_exists(level()); }
|
||||
bool is_forall() const { return is_forall(level()); }
|
||||
bool is_exists(unsigned level) const { return (level % 2) == 0; }
|
||||
bool is_forall(unsigned level) const { return is_exists(level+1); }
|
||||
|
||||
void check_cancel() {
|
||||
if (m_cancel) {
|
||||
throw tactic_exception(TACTIC_CANCELED_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
//m_solver.reset();
|
||||
m_asms.reset();
|
||||
m_cached_asms.reset();
|
||||
m_cached_asms_lim.reset();
|
||||
m_is_true = nlsat::null_literal;
|
||||
m_rmodel.reset();
|
||||
m_valid_model = false;
|
||||
m_bound_rvars.reset();
|
||||
m_bound_bvars.reset();
|
||||
m_preds.reset();
|
||||
m_rvar2level.reset();
|
||||
m_bvar2level.reset();
|
||||
m_t2x.reset();
|
||||
m_a2b.reset();
|
||||
m_b2a.reset();
|
||||
m_x2t.reset();
|
||||
m_cancel = false;
|
||||
m_st.reset();
|
||||
m_solver.collect_statistics(m_st);
|
||||
m_free_vars.reset();
|
||||
m_answer.reset();
|
||||
m_answer_simplify.reset();
|
||||
m_assumptions.reset();
|
||||
m_asm2fml.reset();
|
||||
m_trail.reset();
|
||||
}
|
||||
|
||||
void push() {
|
||||
m_cached_asms_lim.push_back(m_cached_asms.size());
|
||||
}
|
||||
|
||||
void pop(unsigned num_scopes) {
|
||||
clear_model();
|
||||
unsigned new_level = level() - num_scopes;
|
||||
m_cached_asms.shrink(m_cached_asms_lim[new_level]);
|
||||
m_cached_asms_lim.shrink(new_level);
|
||||
}
|
||||
|
||||
void display(std::ostream& out) {
|
||||
display_preds(out);
|
||||
display_assumptions(out);
|
||||
m_solver.display(out << "solver:\n");
|
||||
}
|
||||
|
||||
void display_assumptions(std::ostream& out) {
|
||||
m_solver.display(out << "assumptions: ", m_asms.size(), m_asms.c_ptr());
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void display_preds(std::ostream& out) {
|
||||
for (unsigned i = 0; i < m_preds.size(); ++i) {
|
||||
m_solver.display(out << i << ": ", m_preds[i].size(), m_preds[i].c_ptr());
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// expr -> nlsat::solver
|
||||
|
||||
void hoist(expr_ref& fml) {
|
||||
quantifier_hoister hoist(m);
|
||||
vector<app_ref_vector> qvars;
|
||||
app_ref_vector vars(m);
|
||||
bool is_forall = false;
|
||||
pred_abs abs(m);
|
||||
abs.get_free_vars(fml, vars);
|
||||
insert_set(m_free_vars, vars);
|
||||
qvars.push_back(vars);
|
||||
vars.reset();
|
||||
if (m_mode == elim_t) {
|
||||
is_forall = true;
|
||||
hoist.pull_quantifier(is_forall, fml, vars);
|
||||
qvars.push_back(vars);
|
||||
}
|
||||
else {
|
||||
hoist.pull_quantifier(is_forall, fml, vars);
|
||||
qvars.back().append(vars);
|
||||
}
|
||||
do {
|
||||
is_forall = !is_forall;
|
||||
vars.reset();
|
||||
hoist.pull_quantifier(is_forall, fml, vars);
|
||||
qvars.push_back(vars);
|
||||
}
|
||||
while (!vars.empty());
|
||||
SASSERT(qvars.back().empty());
|
||||
init_expr2var(qvars);
|
||||
|
||||
goal2nlsat g2s;
|
||||
|
||||
expr_ref is_true(m), fml1(m), fml2(m);
|
||||
is_true = m.mk_fresh_const("is_true", m.mk_bool_sort());
|
||||
fml = m.mk_iff(is_true, fml);
|
||||
goal_ref g = alloc(goal, m);
|
||||
g->assert_expr(fml);
|
||||
proof_converter_ref pc;
|
||||
expr_dependency_ref core(m);
|
||||
model_converter_ref mc;
|
||||
goal_ref_buffer result;
|
||||
(*m_nftactic)(g, result, mc, pc, core);
|
||||
SASSERT(result.size() == 1);
|
||||
TRACE("qe", result[0]->display(tout););
|
||||
g2s(*result[0], m_params, m_solver, m_a2b, m_t2x);
|
||||
|
||||
// insert variables and their levels.
|
||||
for (unsigned i = 0; i < qvars.size(); ++i) {
|
||||
m_bound_bvars.push_back(svector<nlsat::bool_var>());
|
||||
m_bound_rvars.push_back(nlsat::var_vector());
|
||||
max_level lvl;
|
||||
if (is_exists(i)) lvl.m_ex = i; else lvl.m_fa = i;
|
||||
for (unsigned j = 0; j < qvars[i].size(); ++j) {
|
||||
app* v = qvars[i][j].get();
|
||||
|
||||
if (m_a2b.is_var(v)) {
|
||||
SASSERT(m.is_bool(v));
|
||||
nlsat::bool_var b = m_a2b.to_var(v);
|
||||
m_bound_bvars.back().push_back(b);
|
||||
set_level(b, lvl);
|
||||
}
|
||||
else if (m_t2x.is_var(v)) {
|
||||
nlsat::var w = m_t2x.to_var(v);
|
||||
m_bound_rvars.back().push_back(w);
|
||||
m_rvar2level.setx(w, lvl, max_level());
|
||||
}
|
||||
}
|
||||
}
|
||||
init_var2expr();
|
||||
m_is_true = nlsat::literal(m_a2b.to_var(is_true), false);
|
||||
// insert literals from arithmetical sub-formulas
|
||||
nlsat::atom_vector const& atoms = m_solver.get_atoms();
|
||||
for (unsigned i = 0; i < atoms.size(); ++i) {
|
||||
if (atoms[i]) {
|
||||
get_level(nlsat::literal(i, false));
|
||||
}
|
||||
}
|
||||
TRACE("qe", tout << fml << "\n";);
|
||||
}
|
||||
|
||||
void init_expr2var(vector<app_ref_vector> const& qvars) {
|
||||
for (unsigned i = 0; i < qvars.size(); ++i) {
|
||||
init_expr2var(qvars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void init_expr2var(app_ref_vector const& qvars) {
|
||||
for (unsigned i = 0; i < qvars.size(); ++i) {
|
||||
app* v = qvars[i];
|
||||
if (m.is_bool(v)) {
|
||||
m_a2b.insert(v, m_solver.mk_bool_var());
|
||||
}
|
||||
else {
|
||||
// TODO: assert it is of type Real.
|
||||
m_t2x.insert(v, m_solver.mk_var(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_var2expr() {
|
||||
expr2var::iterator it = m_t2x.begin(), end = m_t2x.end();
|
||||
for (; it != end; ++it) {
|
||||
m_x2t.insert(it->m_value, it->m_key);
|
||||
}
|
||||
it = m_a2b.begin(), end = m_a2b.end();
|
||||
for (; it != end; ++it) {
|
||||
m_b2a.insert(it->m_value, it->m_key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return false if nlsat assigned noninteger value to an integer variable.
|
||||
// [copied from nlsat_tactic.cpp]
|
||||
bool mk_model(model_converter_ref & mc) {
|
||||
bool ok = true;
|
||||
model_ref md = alloc(model, m);
|
||||
arith_util util(m);
|
||||
expr2var::iterator it = m_t2x.begin(), end = m_t2x.end();
|
||||
for (; it != end; ++it) {
|
||||
nlsat::var x = it->m_value;
|
||||
expr * t = it->m_key;
|
||||
if (!is_uninterp_const(t) || !m_free_vars.contains(t))
|
||||
continue;
|
||||
expr * v;
|
||||
try {
|
||||
v = util.mk_numeral(m_rmodel0.value(x), util.is_int(t));
|
||||
}
|
||||
catch (z3_error & ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (z3_exception &) {
|
||||
v = util.mk_to_int(util.mk_numeral(m_rmodel0.value(x), false));
|
||||
ok = false;
|
||||
}
|
||||
md->register_decl(to_app(t)->get_decl(), v);
|
||||
}
|
||||
it = m_a2b.begin(), end = m_a2b.end();
|
||||
for (; it != end; ++it) {
|
||||
expr * a = it->m_key;
|
||||
nlsat::bool_var b = it->m_value;
|
||||
if (a == 0 || !is_uninterp_const(a) || b == m_is_true.var() || !m_free_vars.contains(a))
|
||||
continue;
|
||||
lbool val = m_bmodel0.get(b, l_undef);
|
||||
if (val == l_undef)
|
||||
continue; // don't care
|
||||
md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false());
|
||||
}
|
||||
mc = model2model_converter(md.get());
|
||||
return ok;
|
||||
}
|
||||
|
||||
public:
|
||||
nlqsat(ast_manager& m, qsat_mode_t mode, params_ref const& p):
|
||||
m(m),
|
||||
m_mode(mode),
|
||||
m_params(p),
|
||||
m_solver(m.limit(), p),
|
||||
m_nftactic(0),
|
||||
m_rmodel(m_solver.am()),
|
||||
m_rmodel0(m_solver.am()),
|
||||
m_valid_model(false),
|
||||
m_a2b(m),
|
||||
m_t2x(m),
|
||||
m_cancel(false),
|
||||
m_answer(m),
|
||||
m_answer_simplify(m),
|
||||
m_trail(m)
|
||||
{
|
||||
m_solver.get_explain().set_signed_project(true);
|
||||
m_nftactic = mk_tseitin_cnf_tactic(m);
|
||||
}
|
||||
|
||||
virtual ~nlqsat() {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
params_ref p2(p);
|
||||
p2.set_bool("factor", false);
|
||||
m_solver.updt_params(p2);
|
||||
}
|
||||
|
||||
void collect_param_descrs(param_descrs & r) {
|
||||
}
|
||||
|
||||
|
||||
void operator()(/* in */ goal_ref const & in,
|
||||
/* out */ goal_ref_buffer & result,
|
||||
/* out */ model_converter_ref & mc,
|
||||
/* out */ proof_converter_ref & pc,
|
||||
/* out */ expr_dependency_ref & core) {
|
||||
|
||||
tactic_report report("nlqsat-tactic", *in);
|
||||
|
||||
ptr_vector<expr> fmls;
|
||||
expr_ref fml(m);
|
||||
mc = 0; pc = 0; core = 0;
|
||||
in->get_formulas(fmls);
|
||||
fml = mk_and(m, fmls.size(), fmls.c_ptr());
|
||||
if (m_mode == elim_t) {
|
||||
fml = m.mk_not(fml);
|
||||
}
|
||||
reset();
|
||||
TRACE("qe", tout << fml << "\n";);
|
||||
hoist(fml);
|
||||
TRACE("qe", tout << "ex: " << fml << "\n";);
|
||||
lbool is_sat = check_sat();
|
||||
|
||||
switch (is_sat) {
|
||||
case l_false:
|
||||
in->reset();
|
||||
in->inc_depth();
|
||||
if (m_mode == elim_t) {
|
||||
fml = mk_and(m_answer);
|
||||
}
|
||||
else {
|
||||
fml = m.mk_false();
|
||||
}
|
||||
in->assert_expr(fml);
|
||||
result.push_back(in.get());
|
||||
break;
|
||||
case l_true:
|
||||
SASSERT(m_mode == qsat_t);
|
||||
in->reset();
|
||||
in->inc_depth();
|
||||
result.push_back(in.get());
|
||||
if (in->models_enabled()) {
|
||||
VERIFY(mk_model(mc));
|
||||
}
|
||||
break;
|
||||
case l_undef:
|
||||
result.push_back(in.get());
|
||||
std::string s = "search failed";
|
||||
throw tactic_exception(s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void collect_statistics(statistics & st) const {
|
||||
st.copy(m_st);
|
||||
st.update("qsat num rounds", m_stats.m_num_rounds);
|
||||
}
|
||||
|
||||
void reset_statistics() {
|
||||
m_stats.reset();
|
||||
m_solver.reset_statistics();
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void set_logic(symbol const & l) {
|
||||
}
|
||||
|
||||
void set_progress_callback(progress_callback * callback) {
|
||||
}
|
||||
|
||||
tactic * translate(ast_manager & m) {
|
||||
return alloc(nlqsat, m, m_mode, m_params);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
|
||||
Algorithm:
|
||||
I := true
|
||||
while there is M, such that M |= ~B & I:
|
||||
find P, such that M => P => exists y . ~B & I
|
||||
; forall y B => ~P
|
||||
C := core of P with respect to A
|
||||
; A => ~ C => ~ P
|
||||
I := I & ~C
|
||||
|
||||
|
||||
Alternative Algorithm:
|
||||
R := false
|
||||
while there is M, such that M |= A & ~R:
|
||||
find I, such that M => I => forall y . B
|
||||
R := R | I
|
||||
|
||||
*/
|
||||
|
||||
lbool interpolate(expr* a, expr* b, expr_ref& result) {
|
||||
SASSERT(m_mode == interp_t);
|
||||
|
||||
reset();
|
||||
app_ref enableA(m), enableB(m);
|
||||
expr_ref A(m), B(m), fml(m);
|
||||
expr_ref_vector fmls(m), answer(m);
|
||||
|
||||
// varsB are private to B.
|
||||
nlsat::var_vector vars;
|
||||
uint_set fvars;
|
||||
private_vars(a, b, vars, fvars);
|
||||
|
||||
enableA = m.mk_const(symbol("#A"), m.mk_bool_sort());
|
||||
enableB = m.mk_not(enableA);
|
||||
A = m.mk_implies(enableA, a);
|
||||
B = m.mk_implies(enableB, m.mk_not(b));
|
||||
fml = m.mk_and(A, B);
|
||||
hoist(fml);
|
||||
|
||||
|
||||
|
||||
nlsat::literal _enableB = nlsat::literal(m_a2b.to_var(enableB), false);
|
||||
nlsat::literal _enableA = ~_enableB;
|
||||
|
||||
while (true) {
|
||||
m_mode = qsat_t;
|
||||
// enable B
|
||||
m_assumptions.reset();
|
||||
m_assumptions.push_back(_enableB);
|
||||
lbool is_sat = check_sat();
|
||||
|
||||
switch (is_sat) {
|
||||
case l_undef:
|
||||
return l_undef;
|
||||
case l_true:
|
||||
break;
|
||||
case l_false:
|
||||
result = mk_and(answer);
|
||||
return l_true;
|
||||
}
|
||||
|
||||
// disable B, enable A
|
||||
m_assumptions.reset();
|
||||
m_assumptions.push_back(_enableA);
|
||||
// add blocking clause to solver.
|
||||
|
||||
nlsat::scoped_literal_vector core(m_solver);
|
||||
|
||||
m_mode = elim_t;
|
||||
|
||||
mbp(vars, fvars, core);
|
||||
|
||||
// minimize core.
|
||||
nlsat::literal_vector _core(core.size(), core.c_ptr());
|
||||
_core.push_back(_enableA);
|
||||
is_sat = m_solver.check(_core); // TBD: only for quantifier-free A. Generalize output of elim_t to be a core.
|
||||
switch (is_sat) {
|
||||
case l_undef:
|
||||
return l_undef;
|
||||
case l_true:
|
||||
UNREACHABLE();
|
||||
case l_false:
|
||||
core.reset();
|
||||
core.append(_core.size(), _core.c_ptr());
|
||||
break;
|
||||
}
|
||||
negate_clause(core);
|
||||
// keep polarity of enableA, such that clause is only
|
||||
// used when checking satisfiability of B.
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
if (core[i].var() == _enableA.var()) core.set(i, ~core[i]);
|
||||
}
|
||||
add_clause(core); // Invariant is added as assumption for B.
|
||||
answer.push_back(clause2fml(core)); // TBD: remove answer literal.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief extract variables that are private to a (not used in b).
|
||||
vars cover the real variables, and fvars cover the Boolean variables.
|
||||
|
||||
TBD: We want fvars to be the complement such that returned core contains
|
||||
Shared boolean variables, but not the ones private to B.
|
||||
*/
|
||||
void private_vars(expr* a, expr* b, nlsat::var_vector& vars, uint_set& fvars) {
|
||||
app_ref_vector varsA(m), varsB(m);
|
||||
obj_hashtable<expr> varsAS;
|
||||
pred_abs abs(m);
|
||||
abs.get_free_vars(a, varsA);
|
||||
abs.get_free_vars(b, varsB);
|
||||
insert_set(varsAS, varsA);
|
||||
for (unsigned i = 0; i < varsB.size(); ++i) {
|
||||
if (varsAS.contains(varsB[i].get())) {
|
||||
varsB[i] = varsB.back();
|
||||
varsB.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
for (unsigned j = 0; j < varsB.size(); ++j) {
|
||||
app* v = varsB[j].get();
|
||||
if (m_a2b.is_var(v)) {
|
||||
fvars.insert(m_a2b.to_var(v));
|
||||
}
|
||||
else if (m_t2x.is_var(v)) {
|
||||
vars.push_back(m_t2x.to_var(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbool maximize(app* _x, expr* _fml, scoped_anum& result, bool& unbounded) {
|
||||
expr_ref fml(_fml, m);
|
||||
reset();
|
||||
hoist(fml);
|
||||
nlsat::literal_vector lits;
|
||||
lbool is_sat = l_true;
|
||||
nlsat::var x = m_t2x.to_var(_x);
|
||||
m_mode = qsat_t;
|
||||
while (is_sat == l_true) {
|
||||
is_sat = check_sat();
|
||||
if (is_sat == l_true) {
|
||||
// m_asms is minimized set of literals that satisfy formula.
|
||||
nlsat::explain& ex = m_solver.get_explain();
|
||||
polynomial::manager& pm = m_solver.pm();
|
||||
anum_manager& am = m_solver.am();
|
||||
ex.maximize(x, m_asms.size(), m_asms.c_ptr(), result, unbounded);
|
||||
if (unbounded) {
|
||||
break;
|
||||
}
|
||||
// TBD: assert the new bound on x using the result.
|
||||
bool is_even = false;
|
||||
polynomial::polynomial_ref pr(pm);
|
||||
pr = pm.mk_polynomial(x);
|
||||
rational r;
|
||||
am.get_upper(result, r);
|
||||
// result is algebraic numeral, but polynomials are not defined over extension field.
|
||||
polynomial::polynomial* p = pr;
|
||||
nlsat::bool_var b = m_solver.mk_ineq_atom(nlsat::atom::GT, 1, &p, &is_even);
|
||||
nlsat::literal bound(b, false);
|
||||
m_solver.mk_clause(1, &bound);
|
||||
}
|
||||
}
|
||||
return is_sat;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p) {
|
||||
return alloc(qe::nlqsat, m, qe::qsat_t, p);
|
||||
}
|
||||
|
||||
tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p) {
|
||||
return alloc(qe::nlqsat, m, qe::elim_t, p);
|
||||
}
|
||||
|
||||
|
38
src/qe/nlqsat.h
Normal file
38
src/qe/nlqsat.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
nlqsat.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Quantifier Satisfiability Solver for nlsat
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2015-10-17
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef QE_NLQSAT_H__
|
||||
#define QE_NLQSAT_H__
|
||||
|
||||
#include "tactic.h"
|
||||
|
||||
|
||||
tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p = params_ref());
|
||||
tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p = params_ref());
|
||||
|
||||
|
||||
/*
|
||||
ADD_TACTIC("nlqsat", "apply a NL-QSAT solver.", "mk_nlqsat_tactic(m, p)")
|
||||
|
||||
*/
|
||||
|
||||
// TBD_TACTIC("nlqe", "apply a NL-QE solver.", "mk_nlqe_tactic(m, p)")
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
--*/
|
||||
|
||||
#include "qe_util.h"
|
||||
#include "bool_rewriter.h"
|
||||
|
||||
namespace qe {
|
||||
|
||||
expr_ref mk_and(expr_ref_vector const& fmls) {
|
||||
ast_manager& m = fmls.get_manager();
|
||||
expr_ref result(m);
|
||||
bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref mk_or(expr_ref_vector const& fmls) {
|
||||
ast_manager& m = fmls.get_manager();
|
||||
expr_ref result(m);
|
||||
bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
qe_util.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utilities for quantifiers.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef QE_UTIL_H_
|
||||
#define QE_UTIL_H_
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
namespace qe {
|
||||
|
||||
expr_ref mk_and(expr_ref_vector const& fmls);
|
||||
|
||||
expr_ref mk_or(expr_ref_vector const& fmls);
|
||||
|
||||
}
|
||||
#endif
|
1176
src/qe/qsat.cpp
Normal file
1176
src/qe/qsat.cpp
Normal file
File diff suppressed because it is too large
Load diff
135
src/qe/qsat.h
Normal file
135
src/qe/qsat.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
qsat.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Quantifier Satisfiability Solver.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2015-5-28
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef QE_QSAT_H__
|
||||
#define QE_QSAT_H__
|
||||
|
||||
#include "tactic.h"
|
||||
#include "filter_model_converter.h"
|
||||
|
||||
namespace qe {
|
||||
|
||||
struct max_level {
|
||||
unsigned m_ex, m_fa;
|
||||
max_level(): m_ex(UINT_MAX), m_fa(UINT_MAX) {}
|
||||
void merge(max_level const& other) {
|
||||
merge(m_ex, other.m_ex);
|
||||
merge(m_fa, other.m_fa);
|
||||
}
|
||||
static unsigned max(unsigned a, unsigned b) {
|
||||
if (a == UINT_MAX) return b;
|
||||
if (b == UINT_MAX) return a;
|
||||
return std::max(a, b);
|
||||
}
|
||||
unsigned max() const {
|
||||
return max(m_ex, m_fa);
|
||||
}
|
||||
void merge(unsigned& lvl, unsigned other) {
|
||||
lvl = max(lvl, other);
|
||||
}
|
||||
std::ostream& display(std::ostream& out) const {
|
||||
if (m_ex != UINT_MAX) out << "e" << m_ex << " ";
|
||||
if (m_fa != UINT_MAX) out << "a" << m_fa << " ";
|
||||
return out;
|
||||
}
|
||||
|
||||
bool operator==(max_level const& other) const {
|
||||
return
|
||||
m_ex == other.m_ex &&
|
||||
m_fa == other.m_fa;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, max_level const& lvl) {
|
||||
return lvl.display(out);
|
||||
}
|
||||
|
||||
class pred_abs {
|
||||
ast_manager& m;
|
||||
vector<app_ref_vector> m_preds;
|
||||
expr_ref_vector m_asms;
|
||||
unsigned_vector m_asms_lim;
|
||||
obj_map<expr, expr*> m_pred2lit; // maintain definitions of predicates.
|
||||
obj_map<expr, app*> m_lit2pred; // maintain reverse mapping to predicates
|
||||
obj_map<expr, app*> m_asm2pred; // maintain map from assumptions to predicates
|
||||
obj_map<expr, expr*> m_pred2asm; // predicates |-> assumptions
|
||||
expr_ref_vector m_trail;
|
||||
filter_model_converter_ref m_fmc;
|
||||
ptr_vector<expr> todo;
|
||||
obj_map<expr, max_level> m_elevel;
|
||||
obj_map<func_decl, max_level> m_flevel;
|
||||
|
||||
template <typename T>
|
||||
void dec_keys(obj_map<expr, T*>& map) {
|
||||
typename obj_map<expr, T*>::iterator it = map.begin(), end = map.end();
|
||||
for (; it != end; ++it) {
|
||||
m.dec_ref(it->m_key);
|
||||
}
|
||||
}
|
||||
void add_lit(app* p, app* lit);
|
||||
void add_asm(app* p, expr* lit);
|
||||
bool is_predicate(app* a, unsigned l);
|
||||
void mk_concrete(expr_ref_vector& fmls, obj_map<expr, expr*> const& map);
|
||||
public:
|
||||
|
||||
pred_abs(ast_manager& m);
|
||||
filter_model_converter* fmc();
|
||||
void reset();
|
||||
max_level compute_level(app* e);
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void insert(app* a, max_level const& lvl);
|
||||
void get_assumptions(model* mdl, expr_ref_vector& asms);
|
||||
void set_expr_level(app* v, max_level const& lvl);
|
||||
void set_decl_level(func_decl* v, max_level const& lvl);
|
||||
void abstract_atoms(expr* fml, max_level& level, expr_ref_vector& defs);
|
||||
void abstract_atoms(expr* fml, expr_ref_vector& defs);
|
||||
expr_ref mk_abstract(expr* fml);
|
||||
void pred2lit(expr_ref_vector& fmls);
|
||||
expr_ref pred2asm(expr* fml);
|
||||
void get_free_vars(expr* fml, app_ref_vector& vars);
|
||||
expr_ref mk_assumption_literal(expr* a, model* mdl, max_level const& lvl, expr_ref_vector& defs);
|
||||
void add_pred(app* p, app* lit);
|
||||
app_ref fresh_bool(char const* name);
|
||||
void display(std::ostream& out) const;
|
||||
void display(std::ostream& out, expr_ref_vector const& asms) const;
|
||||
void collect_statistics(statistics& st) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
tactic * mk_qsat_tactic(ast_manager & m, params_ref const& p = params_ref());
|
||||
|
||||
tactic * mk_qe2_tactic(ast_manager & m, params_ref const& p = params_ref());
|
||||
|
||||
tactic * mk_qe_rec_tactic(ast_manager & m, params_ref const& p = params_ref());
|
||||
|
||||
|
||||
/*
|
||||
ADD_TACTIC("qsat", "apply a QSAT solver.", "mk_qsat_tactic(m, p)")
|
||||
|
||||
ADD_TACTIC("qe2", "apply a QSAT based quantifier elimination.", "mk_qe2_tactic(m, p)")
|
||||
|
||||
ADD_TACTIC("qe_rec", "apply a QSAT based quantifier elimination recursively.", "mk_qe_rec_tactic(m, p)")
|
||||
|
||||
*/
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue