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

adding dt-solver (#4739)

* adding dt-solver

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* dt

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* move mbp to self-contained module

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* Create CMakeLists.txt

* dt

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* rename to bool_var2expr to indicate type class

* mbp

* na
This commit is contained in:
Nikolaj Bjorner 2020-10-18 15:28:21 -07:00 committed by GitHub
parent b77c57451f
commit 2f756da294
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 2309 additions and 1257 deletions

View file

@ -2,30 +2,25 @@ z3_add_component(qe
SOURCES
nlarith_util.cpp
nlqsat.cpp
qe_arith.cpp
qe_arith_plugin.cpp
qe_array_plugin.cpp
qe_arrays.cpp
qe_bool_plugin.cpp
qe_bv_plugin.cpp
qe_cmd.cpp
qe.cpp
qe_datatype_plugin.cpp
qe_datatypes.cpp
qe_dl_plugin.cpp
qe_lite.cpp
qe_mbp.cpp
qe_mbi.cpp
qe_solve_plugin.cpp
qe_mbp.cpp
qe_tactic.cpp
qe_term_graph.cpp
qsat.cpp
COMPONENT_DEPENDENCIES
nlsat_tactic
nlsat
sat
smt
tactic
mbp
TACTIC_HEADERS
nlqsat.h
qe_lite.h

12
src/qe/mbp/CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
z3_add_component(mbp
SOURCES
mbp_arith.cpp
mbp_arrays.cpp
mbp_datatypes.cpp
mbp_plugin.cpp
mbp_solve_plugin.cpp
mbp_term_graph.cpp
COMPONENT_DEPENDENCIES
model
simplex
)

View file

@ -19,20 +19,19 @@ Revision History:
--*/
#include "qe/qe_arith.h"
#include "qe/qe_mbp.h"
#include "qe/mbp/mbp_arith.h"
#include "ast/ast_util.h"
#include "ast/arith_decl_plugin.h"
#include "ast/ast_pp.h"
#include "model/model_v2_pp.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/expr_functors.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "math/simplex/model_based_opt.h"
#include "model/model_evaluator.h"
#include "model/model_smt2_pp.h"
#include "model/model_v2_pp.h"
namespace qe {
namespace mbp {
struct arith_project_plugin::imp {
@ -43,14 +42,13 @@ namespace qe {
void insert_mul(expr* x, rational const& v, obj_map<expr, rational>& ts) {
// TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";);
rational w;
if (ts.find(x, w)) {
ts.insert(x, w + v);
}
else {
ts.insert(x, v);
}
if (ts.find(x, w))
ts.insert(x, w + v);
else
ts.insert(x, v);
}
//
// extract linear inequalities from literal 'lit' into the model-based optimization manager 'mbo'.
// It uses the current model to choose values for conditionals and it primes mbo with the current
@ -227,37 +225,7 @@ namespace qe {
}
bool is_numeral(expr* t, rational& r) {
expr* t1, *t2;
rational r1, r2;
if (a.is_numeral(t, r)) {
// no-op
}
else if (a.is_uminus(t, t1) && is_numeral(t1, r)) {
r.neg();
}
else if (a.is_mul(t)) {
app* ap = to_app(t);
r = rational(1);
for (expr * arg : *ap) {
if (!is_numeral(arg, r1)) return false;
r *= r1;
}
}
else if (a.is_add(t)) {
app* ap = to_app(t);
r = rational(0);
for (expr * arg : *ap) {
if (!is_numeral(arg, r1)) return false;
r += r1;
}
}
else if (a.is_sub(t, t1, t2) && is_numeral(t1, r1) && is_numeral(t2, r2)) {
r = r1 - r2;
}
else {
return false;
}
return true;
return a.is_extended_numeral(t, r);
}
struct compare_second {
@ -280,10 +248,6 @@ namespace qe {
~imp() {}
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return false;
}
bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) {
app_ref_vector vs(m);
vs.push_back(v);
@ -308,9 +272,8 @@ namespace qe {
for (expr* v : vars) {
has_arith |= is_arith(v);
}
if (!has_arith) {
return vector<def>();
}
if (!has_arith)
return vector<def>();
model_evaluator eval(model);
TRACE("qe", tout << model;);
eval.set_model_completion(true);
@ -356,9 +319,8 @@ namespace qe {
}
}
if (m_check_purified) {
for (expr* fml : fmls) {
mark_rec(fmls_mark, fml);
}
for (expr* fml : fmls)
mark_rec(fmls_mark, fml);
for (auto& kv : tids) {
expr* e = kv.m_key;
if (!var_mark.is_marked(e)) {
@ -368,36 +330,57 @@ namespace qe {
}
ptr_vector<expr> index2expr;
for (auto& kv : tids) {
index2expr.setx(kv.m_value, kv.m_key, nullptr);
}
for (auto& kv : tids)
index2expr.setx(kv.m_value, kv.m_key, nullptr);
j = 0;
unsigned_vector real_vars;
for (app* v : vars) {
if (is_arith(v) && !fmls_mark.is_marked(v)) {
real_vars.push_back(tids.find(v));
}
else {
vars[j++] = v;
}
if (is_arith(v) && !fmls_mark.is_marked(v))
real_vars.push_back(tids.find(v));
else
vars[j++] = v;
}
vars.shrink(j);
TRACE("qe", tout << "remaining vars: " << vars << "\n";
for (unsigned v : real_vars) {
tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n";
}
for (unsigned v : real_vars) tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n";
mbo.display(tout););
vector<opt::model_based_opt::def> defs = mbo.project(real_vars.size(), real_vars.c_ptr(), compute_def);
TRACE("qe", mbo.display(tout << "mbo result\n");
for (auto const& d : defs) {
tout << "def: " << d << "\n";
}
);
for (auto const& d : defs) tout << "def: " << d << "\n";);
vector<row> rows;
mbo.get_live_rows(rows);
rows2fmls(rows, index2expr, fmls);
vector<def> result;
if (compute_def)
optdefs2mbpdef(defs, index2expr, real_vars, result);
return result;
}
void optdefs2mbpdef(vector<opt::model_based_opt::def> const& defs, ptr_vector<expr> const& index2expr, unsigned_vector const& real_vars, vector<def>& result) {
SASSERT(defs.size() == real_vars.size());
for (unsigned i = 0; i < defs.size(); ++i) {
auto const& d = defs[i];
expr* x = index2expr[real_vars[i]];
bool is_int = a.is_int(x);
expr_ref_vector ts(m);
expr_ref t(m);
for (var const& v : d.m_vars)
ts.push_back(var2expr(index2expr, v));
if (!d.m_coeff.is_zero())
ts.push_back(a.mk_numeral(d.m_coeff, is_int));
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));
result.push_back(def(expr_ref(x, m), t));
}
}
void rows2fmls(vector<row> const& rows, ptr_vector<expr> const& index2expr, expr_ref_vector& fmls) {
for (row const& r : rows) {
expr_ref_vector ts(m);
expr_ref t(m), s(m), val(m);
@ -418,8 +401,6 @@ namespace qe {
default: UNREACHABLE();
}
fmls.push_back(t);
val = eval(t);
CTRACE("qe", !m.is_true(val), tout << "Evaluated unit " << t << " to " << val << "\n";);
continue;
}
for (var const& v : r.m_vars) {
@ -435,7 +416,7 @@ namespace qe {
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);
}
@ -443,46 +424,11 @@ namespace qe {
break;
}
fmls.push_back(t);
val = eval(t);
CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";);
}
vector<def> result;
if (compute_def) {
SASSERT(defs.size() == real_vars.size());
for (unsigned i = 0; i < defs.size(); ++i) {
auto const& d = defs[i];
expr* x = index2expr[real_vars[i]];
bool is_int = a.is_int(x);
expr_ref_vector ts(m);
expr_ref t(m);
for (var const& v : d.m_vars) {
ts.push_back(var2expr(index2expr, v));
}
if (!d.m_coeff.is_zero())
ts.push_back(a.mk_numeral(d.m_coeff, is_int));
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));
}
result.push_back(def(expr_ref(x, m), t));
}
}
return result;
}
}
expr_ref mk_add(expr_ref_vector const& ts) {
switch (ts.size()) {
case 0:
return expr_ref(a.mk_int(0), m);
case 1:
return expr_ref(ts.get(0), m);
default:
return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m);
}
return a.mk_add_simplify(ts);
}
opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) {
@ -550,11 +496,11 @@ namespace qe {
bool validate_model(model_evaluator& eval, expr_ref_vector const& fmls) {
bool valid = true;
for (unsigned i = 0; i < fmls.size(); ++i) {
expr_ref val = eval(fmls[i]);
for (expr* fml : fmls) {
expr_ref val = eval(fml);
if (!m.is_true(val)) {
valid = false;
TRACE("qe", tout << mk_pp(fmls[i], m) << " := " << val << "\n";);
TRACE("qe", tout << mk_pp(fml, m) << " := " << val << "\n";);
}
}
return valid;
@ -586,7 +532,7 @@ namespace qe {
};
arith_project_plugin::arith_project_plugin(ast_manager& m) {
arith_project_plugin::arith_project_plugin(ast_manager& m):project_plugin(m) {
m_imp = alloc(imp, m);
}
@ -610,10 +556,6 @@ namespace qe {
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);
}
family_id arith_project_plugin::get_family_id() {
return m_imp->a.get_family_id();
}
@ -622,11 +564,6 @@ namespace qe {
return m_imp->maximize(fmls, mdl, t, ge, gt);
}
void arith_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {
UNREACHABLE();
}
bool arith_project(model& model, app* var, expr_ref_vector& lits) {
ast_manager& m = lits.get_manager();
arith_project_plugin ap(m);

View file

@ -7,11 +7,11 @@ Copyright (c) 2015 Microsoft Corporation
#pragma once
#include "model/model.h"
#include "ast/arith_decl_plugin.h"
#include "qe/qe_mbp.h"
#include "model/model.h"
#include "qe/mbp/mbp_plugin.h"
namespace qe {
namespace mbp {
/**
Loos-Weispfenning model-based projection for a basic conjunction.
@ -25,12 +25,13 @@ namespace qe {
public:
arith_project_plugin(ast_manager& m);
~arith_project_plugin() override;
bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; }
family_id get_family_id() override;
void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override { UNREACHABLE(); }
opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt);
@ -39,6 +40,7 @@ namespace qe {
* 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

@ -3,7 +3,7 @@ Copyright (c) 2015 Microsoft Corporation
Module Name:
qe_arrays.cpp
mbp_arrays.cpp
Abstract:
@ -27,8 +27,8 @@ Revision History:
#include "ast/ast_util.h"
#include "ast/ast_pp.h"
#include "model/model_evaluator.h"
#include "qe/qe_arrays.h"
#include "qe/qe_term_graph.h"
#include "qe/mbp/mbp_arrays.h"
#include "qe/mbp/mbp_term_graph.h"
namespace {
@ -154,7 +154,7 @@ namespace {
}
}
namespace qe {
namespace mbp {
static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) {
@ -1571,7 +1571,7 @@ namespace qe {
};
array_project_plugin::array_project_plugin(ast_manager& m) {
array_project_plugin::array_project_plugin(ast_manager& m):project_plugin(m) {
m_imp = alloc(imp, m);
}

View file

@ -3,7 +3,7 @@ Copyright (c) 2015 Microsoft Corporation
Module Name:
qe_arrays.h
mbp_arrays.h
Abstract:
@ -21,9 +21,9 @@ Revision History:
#pragma once
#include "ast/array_decl_plugin.h"
#include "qe/qe_mbp.h"
#include "qe/mbp/mbp_plugin.h"
namespace qe {
namespace mbp {
class array_project_plugin : public project_plugin {
struct imp;

View file

@ -17,16 +17,15 @@ Revision History:
--*/
#include "qe/qe_arith.h"
#include "ast/ast_pp.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/expr_functors.h"
#include "model/model_v2_pp.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "util/obj_pair_hashtable.h"
#include "qe/qe_datatypes.h"
#include "qe/mbp/mbp_datatypes.h"
namespace qe {
namespace mbp {
struct datatype_project_plugin::imp {
ast_manager& m;
@ -285,7 +284,8 @@ namespace qe {
};
datatype_project_plugin::datatype_project_plugin(ast_manager& m) {
datatype_project_plugin::datatype_project_plugin(ast_manager& m):
project_plugin(m) {
m_imp = alloc(imp, m);
}

View file

@ -21,9 +21,9 @@ Revision History:
#pragma once
#include "ast/datatype_decl_plugin.h"
#include "qe/qe_mbp.h"
#include "qe/mbp/mbp_plugin.h"
namespace qe {
namespace mbp {
class datatype_project_plugin : public project_plugin {
struct imp;

262
src/qe/mbp/mbp_plugin.cpp Normal file
View file

@ -0,0 +1,262 @@
/*++
Copyright (c) 2015 Microsoft Corporation
Module Name:
qe_mbp.cpp
Abstract:
Model-based projection utilities
Author:
Nikolaj Bjorner (nbjorner) 2015-5-29
Revision History:
--*/
#include "ast/rewriter/expr_safe_replace.h"
#include "ast/ast_pp.h"
#include "ast/ast_util.h"
#include "ast/occurs.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/expr_functors.h"
#include "ast/for_each_expr.h"
#include "ast/scoped_proof.h"
#include "qe/mbp/mbp_plugin.h"
#include "model/model_pp.h"
#include "model/model_evaluator.h"
namespace mbp {
struct noop_op_proc {
void operator()(expr*) {}
};
void project_plugin::mark_rec(expr_mark& visited, expr* e) {
for_each_expr_proc<noop_op_proc> fe;
for_each_expr(fe, visited, e);
}
void project_plugin::mark_rec(expr_mark& visited, expr_ref_vector const& es) {
for (expr* e : es)
mark_rec(visited, e);
}
/**
\brief return two terms that are equal in the model.
The distinct term t is false in model, so there
are at least two arguments of t that are equal in the model.
*/
expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) {
SASSERT(m.is_distinct(t));
expr_ref val(m);
expr_ref_vector vals(m);
obj_map<expr, expr*> val2expr;
app* alit = to_app(t);
if (alit->get_num_args() == 2) {
return expr_ref(m.mk_eq(alit->get_arg(0), alit->get_arg(1)), m);
}
for (expr* e1 : *alit) {
expr* e2;
val = model(e1);
TRACE("qe", tout << mk_pp(e1, m) << " |-> " << val << "\n";);
if (val2expr.find(val, e2)) {
return expr_ref(m.mk_eq(e1, e2), m);
}
val2expr.insert(val, e1);
vals.push_back(val);
}
for (unsigned i = 0; i < alit->get_num_args(); ++i) {
for (unsigned j = i + 1; j < alit->get_num_args(); ++j) {
expr* e1 = alit->get_arg(i);
expr* e2 = alit->get_arg(j);
val = m.mk_eq(e1, e2);
if (!model.is_false(val))
return expr_ref(m.mk_eq(e1, e2), m);
}
}
UNREACHABLE();
return expr_ref(nullptr, m);
}
void project_plugin::erase(expr_ref_vector& lits, unsigned& i) {
lits[i] = lits.back();
lits.pop_back();
--i;
}
void project_plugin::push_back(expr_ref_vector& lits, expr* e) {
if (!m.is_true(e))
lits.push_back(e);
}
void project_plugin::extract_literals(model& model, app_ref_vector const& vars, expr_ref_vector& fmls) {
m_bool_visited.reset();
expr_ref val(m);
model_evaluator eval(model);
eval.set_expand_array_equalities(true);
TRACE("qe", tout << fmls << "\n";);
for (unsigned i = 0; i < fmls.size(); ++i) {
expr* fml = fmls.get(i), * nfml, * f1, * f2, * f3;
SASSERT(m.is_bool(fml));
if (m.is_not(fml, nfml) && m.is_distinct(nfml))
fmls[i--] = mbp::project_plugin::pick_equality(m, model, nfml);
else if (m.is_or(fml)) {
for (expr* arg : *to_app(fml)) {
val = eval(arg);
if (m.is_true(val)) {
fmls[i] = arg;
--i;
break;
}
}
}
else if (m.is_and(fml)) {
fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) {
val = eval(f1);
if (m.is_false(val)) {
f1 = mk_not(m, f1);
f2 = mk_not(m, f2);
}
fmls[i--] = f1;
push_back(fmls, f2);
}
else if (m.is_implies(fml, f1, f2)) {
val = eval(f2);
if (m.is_true(val)) {
fmls[i] = f2;
}
else {
fmls[i] = mk_not(m, f1);
}
--i;
}
else if (m.is_ite(fml, f1, f2, f3)) {
val = eval(f1);
if (m.is_true(val)) {
push_back(fmls, f1);
push_back(fmls, f2);
}
else {
push_back(fmls, mk_not(m, f1));
push_back(fmls, f3);
}
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) {
push_back(fmls, nfml);
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_and(nfml)) {
for (expr* arg : *to_app(nfml)) {
val = eval(arg);
if (m.is_false(val)) {
fmls[i--] = mk_not(m, arg);
break;
}
}
}
else if (m.is_not(fml, nfml) && m.is_or(nfml)) {
for (expr* arg : *to_app(nfml))
push_back(fmls, mk_not(m, arg));
mbp::project_plugin::erase(fmls, i);
}
else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) {
val = eval(f1);
if (m.is_true(val)) {
f2 = mk_not(m, f2);
}
else {
f1 = mk_not(m, f1);
}
push_back(fmls, f1);
push_back(fmls, f2);
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) {
push_back(fmls, f1);
push_back(fmls, mk_not(m, f2));
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) {
val = eval(f1);
if (m.is_true(val)) {
push_back(fmls, f1);
push_back(fmls, mk_not(m, f2));
}
else {
push_back(fmls, mk_not(m, f1));
push_back(fmls, mk_not(m, f3));
}
mbp::project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml)) {
if (extract_bools(eval, fmls, nfml)) {
mbp::project_plugin::erase(fmls, i);
}
}
else if (extract_bools(eval, fmls, fml))
mbp::project_plugin::erase(fmls, i);
}
TRACE("qe", tout << fmls << "\n";);
}
bool project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) {
TRACE("qe", tout << "extract bools: " << mk_pp(fml, m) << "\n";);
ptr_vector<expr> todo;
expr_safe_replace sub(m);
m_visited.reset();
bool found_bool = false;
if (is_app(fml)) {
todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
}
while (!todo.empty() && m.inc()) {
expr* e = todo.back();
todo.pop_back();
if (m_visited.is_marked(e)) {
continue;
}
m_visited.mark(e);
if (m.is_bool(e) && !m.is_true(e) && !m.is_false(e)) {
expr_ref val = eval(e);
TRACE("qe", tout << "found: " << mk_pp(e, m) << " " << val << "\n";);
if (!m.inc())
continue;
if (!m.is_true(val) && !m.is_false(val) && contains_uninterpreted(val))
throw default_exception("could not evaluate Boolean in model");
SASSERT(m.is_true(val) || m.is_false(val));
if (!m_bool_visited.is_marked(e))
fmls.push_back(m.is_true(val) ? e : mk_not(m, e));
sub.insert(e, val);
m_bool_visited.mark(e);
found_bool = true;
}
else if (is_app(e)) {
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
else {
TRACE("qe", tout << "expression not handled " << mk_pp(e, m) << "\n";);
}
}
if (found_bool) {
expr_ref tmp(m);
sub(fml, tmp);
expr_ref val = eval(tmp);
if (!m.is_true(val) && !m.is_false(val))
return false;
fmls.push_back(m.is_true(val) ? tmp : mk_not(m, tmp));
}
return found_bool;
}
}

93
src/qe/mbp/mbp_plugin.h Normal file
View file

@ -0,0 +1,93 @@
/*++
Copyright (c) 2015 Microsoft Corporation
Module Name:
mbp_plugin.h
Abstract:
Model-based projection utilities
Author:
Nikolaj Bjorner (nbjorner) 2015-5-28
Revision History:
--*/
#pragma once
#include "ast/ast.h"
#include "util/params.h"
#include "model/model.h"
#include "math/simplex/model_based_opt.h"
namespace mbp {
struct cant_project {};
struct def {
expr_ref var, term;
def(const expr_ref& v, expr_ref& t): var(v), term(t) {}
};
class project_plugin {
ast_manager& m;
expr_mark m_visited;
expr_mark m_bool_visited;
bool extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml);
// over-approximation
bool contains_uninterpreted(expr* v) { return true; }
void push_back(expr_ref_vector& lits, expr* lit);
public:
project_plugin(ast_manager& m) :m(m) {}
virtual ~project_plugin() {}
virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return false; }
/**
\brief partial solver.
*/
virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; }
virtual family_id get_family_id() { return null_family_id; }
virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { };
/**
\brief project vars modulo model, return set of definitions for eliminated variables.
- vars in/out: returns variables that were not eliminated
- lits in/out: returns projected literals
- returns set of definitions
(TBD: in triangular form, the last definition can be substituted into definitions that come before)
*/
virtual vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return vector<def>(); }
/**
\brief model based saturation. Saturates theory axioms to equi-satisfiable literals over EUF,
such that 'shared' are not retained for EUF.
*/
virtual void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {}
/*
* extract top-level literals
*/
void extract_literals(model& model, app_ref_vector const& vars, expr_ref_vector& fmls);
/*
* Purify literals into linear inequalities or constraints without arithmetic variables.
*/
void purify(model& model, app_ref_vector const& vars, expr_ref_vector& fmls);
static expr_ref pick_equality(ast_manager& m, model& model, expr* t);
static void erase(expr_ref_vector& lits, unsigned& i);
static void mark_rec(expr_mark& visited, expr* e);
static void mark_rec(expr_mark& visited, expr_ref_vector const& es);
};
}

View file

@ -23,9 +23,9 @@ Revision History:
#include "ast/datatype_decl_plugin.h"
#include "ast/ast_util.h"
#include "ast/ast_pp.h"
#include "qe/qe_solve_plugin.h"
#include "qe/mbp/mbp_solve_plugin.h"
namespace qe {
namespace mbp {
expr_ref solve_plugin::operator()(expr* lit) {
if (m.is_not(lit, lit))

View file

@ -3,7 +3,7 @@ Copyright (c) 2018 Microsoft Corporation
Module Name:
qe_solve.h
mbp_solve_plugin.h
Abstract:
@ -21,10 +21,10 @@ Revision History:
#pragma once
#include "ast/ast.h"
#include "ast/is_variable_test.h"
#include "util/plugin_manager.h"
#include "qe/qe_vartest.h"
namespace qe {
namespace mbp {
class solve_plugin {
protected:

View file

@ -24,10 +24,10 @@ Notes:
#include "ast/ast_util.h"
#include "ast/for_each_expr.h"
#include "ast/occurs.h"
#include "qe/qe_term_graph.h"
#include "model/model_evaluator.h"
#include "qe/mbp/mbp_term_graph.h"
namespace qe {
namespace mbp {
static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) {
expr *t = nullptr;
@ -245,8 +245,8 @@ namespace qe {
bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); }
term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m), m_projector(nullptr) {
m_plugins.register_plugin(mk_basic_solve_plugin(m, m_is_var));
m_plugins.register_plugin(mk_arith_solve_plugin(m, m_is_var));
m_plugins.register_plugin(mbp::mk_basic_solve_plugin(m, m_is_var));
m_plugins.register_plugin(mbp::mk_arith_solve_plugin(m, m_is_var));
}
term_graph::~term_graph() {
@ -283,7 +283,7 @@ namespace qe {
for (unsigned i = 0; i < lits.size(); ++i) {
l = lits.get(i);
family_id fid = get_family_id(m, l);
qe::solve_plugin *pin = m_plugins.get_plugin(fid);
mbp::solve_plugin *pin = m_plugins.get_plugin(fid);
lit = pin ? (*pin)(l) : l;
if (m.is_and(lit)) {
lits.append(::to_app(lit)->get_num_args(), ::to_app(lit)->get_args());

View file

@ -3,7 +3,7 @@ Copyright (c) Arie Gurfinkel
Module Name:
qe_term_graph.h
mbp_term_graph.h
Abstract:
@ -19,12 +19,12 @@ Notes:
#pragma once
#include "ast/ast.h"
#include "ast/is_variable_test.h"
#include "util/plugin_manager.h"
#include "qe/qe_solve_plugin.h"
#include "qe/qe_vartest.h"
#include "qe/mbp/mbp_solve_plugin.h"
#include "model/model.h"
namespace qe {
namespace mbp {
class term;
@ -53,7 +53,7 @@ namespace qe {
ast_ref_vector m_pinned;
projector* m_projector;
u_map<expr*> m_term2app;
plugin_manager<qe::solve_plugin> m_plugins;
plugin_manager<solve_plugin> m_plugins;
ptr_hashtable<term, term_hash, term_eq> m_cg_table;
vector<std::pair<term*,term*>> m_merge;

View file

@ -32,7 +32,7 @@ Revision History:
#include "qe/nlarith_util.h"
#include "model/model_evaluator.h"
#include "smt/smt_kernel.h"
#include "qe/qe_arith.h"
#include "qe/mbp/mbp_arith.h"
namespace qe {

View file

@ -17,25 +17,24 @@ Revision History:
--*/
#include "qe/qe_lite.h"
#include "util/uint_set.h"
#include "ast/expr_abstract.h"
#include "ast/used_vars.h"
#include "ast/rewriter/rewriter_def.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_smt2_pp.h"
#include "tactic/tactical.h"
#include "ast/is_variable_test.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/rewriter/var_subst.h"
#include "util/uint_set.h"
#include "ast/ast_util.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/for_each_expr.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "ast/datatype_decl_plugin.h"
#include "qe/qe_vartest.h"
#include "qe/qe_solve_plugin.h"
#include "tactic/tactical.h"
#include "qe/mbp/mbp_solve_plugin.h"
#include "qe/qe_lite.h"
namespace qel {
@ -73,7 +72,7 @@ namespace qel {
beta_reducer m_subst;
expr_ref_vector m_subst_map;
expr_ref_vector m_new_exprs;
plugin_manager<qe::solve_plugin> m_solvers;
plugin_manager<mbp::solve_plugin> m_solvers;
ptr_vector<expr> m_map;
int_vector m_pos2var;
@ -296,7 +295,7 @@ namespace qel {
if (m.is_eq(e, lhs, rhs)) {
fid = get_sort(lhs)->get_family_id();
}
qe::solve_plugin* p = m_solvers.get_plugin(fid);
auto* p = m_solvers.get_plugin(fid);
if (p) {
expr_ref res = (*p)(e);
if (res != e && m.is_eq(res, lhs, rhs) && is_variable(lhs)) {
@ -703,9 +702,9 @@ namespace qel {
void set_is_variable_proc(is_variable_proc& proc) {
m_is_variable = &proc;
m_solvers.reset();
m_solvers.register_plugin(qe::mk_arith_solve_plugin(m, proc));
m_solvers.register_plugin(qe::mk_basic_solve_plugin(m, proc));
m_solvers.register_plugin(qe::mk_bv_solve_plugin(m, proc));
m_solvers.register_plugin(mbp::mk_arith_solve_plugin(m, proc));
m_solvers.register_plugin(mbp::mk_basic_solve_plugin(m, proc));
m_solvers.register_plugin(mbp::mk_bv_solve_plugin(m, proc));
}
void operator()(quantifier * q, expr_ref & r, proof_ref & pr) {

View file

@ -38,9 +38,9 @@ Notes:
#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"
#include "qe/qe_arrays.h"
#include "qe/mbp/mbp_term_graph.h"
#include "qe/mbp/mbp_arith.h"
#include "qe/mbp/mbp_arrays.h"
namespace qe {
@ -263,8 +263,8 @@ namespace qe {
return avars;
}
vector<def> uflia_mbi::arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits) {
arith_project_plugin ap(m);
vector<mbp::def> uflia_mbi::arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits) {
mbp::arith_project_plugin ap(m);
ap.set_check_purified(false);
return ap.project(*mdl.get(), avars, lits);
}
@ -308,7 +308,7 @@ namespace qe {
expr_ref_vector alits(m), uflits(m);
split_arith(lits, alits, uflits);
auto avars = get_arith_vars(lits);
vector<def> defs = arith_project(mdl, avars, alits);
vector<mbp::def> defs = arith_project(mdl, avars, alits);
for (auto const& d : defs) uflits.push_back(m.mk_eq(d.var, d.term));
TRACE("qe", tout << "uflits: " << uflits << "\n";);
project_euf(mdl, uflits);
@ -354,7 +354,7 @@ namespace qe {
\brief add difference certificates to formula.
*/
void uflia_mbi::add_dcert(model_ref& mdl, expr_ref_vector& lits) {
term_graph tg(m);
mbp::term_graph tg(m);
add_arith_dcert(*mdl.get(), lits);
func_decl_ref_vector shared(m_shared_trail);
tg.set_vars(shared, false);
@ -406,7 +406,7 @@ namespace qe {
* \brief project private symbols.
*/
void uflia_mbi::project_euf(model_ref& mdl, expr_ref_vector& lits) {
term_graph tg(m);
mbp::term_graph tg(m);
func_decl_ref_vector shared(m_shared_trail);
tg.set_vars(shared, false);
tg.add_lits(lits);

View file

@ -20,7 +20,8 @@ Revision History:
#pragma once
#include "qe/qe_arith.h"
#include "qe/mbp/mbp_arith.h"
#include "qe/mbp/mbp_plugin.h"
#include "util/lbool.h"
namespace qe {
@ -126,7 +127,7 @@ namespace qe {
void add_arith_dcert(model& mdl, expr_ref_vector& lits);
void add_arith_dcert(model& mdl, expr_ref_vector& lits, app* a, app* b);
app_ref_vector get_arith_vars(expr_ref_vector const& lits);
vector<def> arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits);
vector<::mbp::def> arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits);
void project_euf(model_ref& mdl, expr_ref_vector& lits);
void split_arith(expr_ref_vector const& lits,
expr_ref_vector& alits,

View file

@ -27,9 +27,9 @@ Revision History:
#include "ast/for_each_expr.h"
#include "ast/scoped_proof.h"
#include "qe/qe_mbp.h"
#include "qe/qe_arith.h"
#include "qe/qe_arrays.h"
#include "qe/qe_datatypes.h"
#include "qe/mbp/mbp_arith.h"
#include "qe/mbp/mbp_arrays.h"
#include "qe/mbp/mbp_datatypes.h"
#include "qe/qe_lite.h"
#include "model/model_pp.h"
#include "model/model_evaluator.h"
@ -37,93 +37,26 @@ Revision History:
using namespace qe;
struct noop_op_proc {
void operator()(expr*) {}
};
void project_plugin::mark_rec(expr_mark& visited, expr* e) {
for_each_expr_proc<noop_op_proc> fe;
for_each_expr(fe, visited, e);
}
void project_plugin::mark_rec(expr_mark& visited, expr_ref_vector const& es) {
for (unsigned i = 0; i < es.size(); ++i) {
mark_rec(visited, es[i]);
}
}
/**
\brief return two terms that are equal in the model.
The distinct term t is false in model, so there
are at least two arguments of t that are equal in the model.
*/
expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) {
SASSERT(m.is_distinct(t));
expr_ref val(m);
expr_ref_vector vals(m);
obj_map<expr, expr*> val2expr;
app* alit = to_app(t);
if (alit->get_num_args() == 2) {
return expr_ref(m.mk_eq(alit->get_arg(0), alit->get_arg(1)), m);
}
for (expr * e1 : *alit) {
expr *e2;
val = model(e1);
TRACE("qe", tout << mk_pp(e1, m) << " |-> " << val << "\n";);
if (val2expr.find(val, e2)) {
return expr_ref(m.mk_eq(e1, e2), m);
}
val2expr.insert(val, e1);
vals.push_back(val);
}
for (unsigned i = 0; i < alit->get_num_args(); ++i) {
for (unsigned j = i + 1; j < alit->get_num_args(); ++j) {
expr* e1 = alit->get_arg(i);
expr* e2 = alit->get_arg(j);
val = m.mk_eq(e1, e2);
if (!model.is_false(val))
return expr_ref(m.mk_eq(e1, e2), m);
}
}
UNREACHABLE();
return expr_ref(nullptr, m);
}
void project_plugin::erase(expr_ref_vector& lits, unsigned& i) {
lits[i] = lits.back();
lits.pop_back();
--i;
}
void project_plugin::push_back(expr_ref_vector& lits, expr* e) {
if (lits.get_manager().is_true(e)) return;
lits.push_back(e);
}
class mbp::impl {
class mbproj::impl {
ast_manager& m;
params_ref m_params;
th_rewriter m_rw;
ptr_vector<project_plugin> m_plugins;
expr_mark m_visited;
expr_mark m_bool_visited;
params_ref m_params;
th_rewriter m_rw;
ptr_vector<mbp::project_plugin> m_plugins;
// parameters
bool m_reduce_all_selects;
bool m_dont_sub;
void add_plugin(project_plugin* p) {
void add_plugin(mbp::project_plugin* p) {
family_id fid = p->get_family_id();
SASSERT(!m_plugins.get(fid, 0));
m_plugins.setx(fid, p, 0);
SASSERT(!m_plugins.get(fid, nullptr));
m_plugins.setx(fid, p, nullptr);
}
project_plugin* get_plugin(app* var) {
mbp::project_plugin* get_plugin(app* var) {
family_id fid = m.get_sort(var)->get_family_id();
return m_plugins.get(fid, 0);
return m_plugins.get(fid, nullptr);
}
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
@ -132,20 +65,20 @@ class mbp::impl {
return false;
}
bool reduced = false;
for (unsigned i = 0; i < vars.size(); ++i) {
is_var.mark(vars[i].get());
}
expr_ref tmp(m), t(m), v(m);
for (expr* v : vars)
is_var.mark(v);
expr_ref tmp(m), t(m), v(m);
for (unsigned i = 0; i < lits.size(); ++i) {
expr* e = lits[i].get(), *l, *r;
expr* e = lits.get(i), * l, * r;
if (m.is_eq(e, l, r) && reduce_eq(is_var, l, r, v, t)) {
reduced = true;
project_plugin::erase(lits, i);
mbp::project_plugin::erase(lits, i);
expr_safe_replace sub(m);
sub.insert(v, t);
is_rem.mark(v);
for (unsigned j = 0; j < lits.size(); ++j) {
sub(lits[j].get(), tmp);
sub(lits.get(j), tmp);
m_rw(tmp);
lits[j] = tmp;
}
@ -153,20 +86,17 @@ class mbp::impl {
}
if (reduced) {
unsigned j = 0;
for (app* v : vars) {
if (!is_rem.is_marked(v)) {
for (app* v : vars)
if (!is_rem.is_marked(v))
vars[j++] = v;
}
}
vars.shrink(j);
}
return reduced;
}
bool reduce_eq(expr_mark& is_var, expr* l, expr* r, expr_ref& v, expr_ref& t) {
if (is_var.is_marked(r)) {
std::swap(l, r);
}
if (is_var.is_marked(r))
std::swap(l, r);
if (is_var.is_marked(l)) {
contains_app cont(m, to_app(l));
if (!cont(r)) {
@ -178,340 +108,155 @@ class mbp::impl {
return false;
}
void filter_variables(model& model, app_ref_vector& vars, expr_ref_vector& lits, expr_ref_vector& unused_lits) {
expr_mark lit_visited;
project_plugin::mark_rec(lit_visited, lits);
mbp::project_plugin::mark_rec(lit_visited, lits);
unsigned j = 0;
for (app* var : vars) {
if (lit_visited.is_marked(var)) {
for (app* var : vars)
if (lit_visited.is_marked(var))
vars[j++] = var;
}
}
vars.shrink(j);
}
// over-approximation
bool contains_uninterpreted(expr* v) {
return true;
}
bool extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) {
TRACE("qe", tout << "extract bools: " << mk_pp(fml, m) << "\n";);
ptr_vector<expr> todo;
expr_safe_replace sub(m);
m_visited.reset();
bool found_bool = false;
if (is_app(fml)) {
todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
}
while (!todo.empty()) {
expr* e = todo.back();
todo.pop_back();
if (m_visited.is_marked(e)) {
continue;
}
m_visited.mark(e);
if (m.is_bool(e) && !m.is_true(e) && !m.is_false(e) && m.inc()) {
expr_ref val = eval(e);
TRACE("qe", tout << "found: " << mk_pp(e, m) << " " << val << "\n";);
if (!m.inc())
continue;
if (!m.is_true(val) && !m.is_false(val) && contains_uninterpreted(val)) {
throw default_exception("could not evaluate Boolean in model");
}
SASSERT(m.is_true(val) || m.is_false(val));
if (!m_bool_visited.is_marked(e)) {
fmls.push_back(m.is_true(val) ? e : mk_not(m, e));
}
sub.insert(e, val);
m_bool_visited.mark(e);
found_bool = true;
}
else if (is_app(e)) {
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
else {
TRACE("qe", tout << "expression not handled " << mk_pp(e, m) << "\n";);
}
}
if (found_bool) {
expr_ref tmp(m);
sub(fml, tmp);
expr_ref val = eval(tmp);
if (!m.is_true(val) && !m.is_false(val))
return false;
fmls.push_back(m.is_true(val) ? tmp : mk_not(m, tmp));
}
return found_bool;
}
void project_bools(model& mdl, app_ref_vector& vars, expr_ref_vector& fmls) {
expr_safe_replace sub(m);
expr_ref val(m);
model_evaluator eval(mdl, m_params);
model_evaluator eval(mdl, m_params);
eval.set_model_completion(true);
unsigned j = 0;
for (unsigned i = 0; i < vars.size(); ++i) {
app* var = vars[i].get();
if (m.is_bool(var)) {
sub.insert(var, eval(var));
}
else {
vars[j++] = var;
}
}
if (j == vars.size()) {
return;
for (app* var : vars) {
if (m.is_bool(var))
sub.insert(var, eval(var));
else
vars[j++] = var;
}
if (j == vars.size())
return;
vars.shrink(j);
j = 0;
for (unsigned i = 0; i < fmls.size(); ++i) {
expr* fml = fmls[i].get();
j = 0;
for (expr* fml : fmls) {
sub(fml, val);
m_rw(val);
if (!m.is_true(val)) {
m_rw(val);
if (!m.is_true(val)) {
TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";);
fmls[j++] = val;
}
}
fmls.shrink(j);
}
}
fmls.shrink(j);
}
void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) {
expr_safe_replace sub (m);
for (app * v : vars) {
sub.insert(v, eval(v));
}
expr_safe_replace sub(m);
for (app* v : vars)
sub.insert(v, eval(v));
sub(fml);
}
}
struct index_term_finder {
ast_manager& m;
ast_manager& m;
array_util m_array;
app_ref m_var;
expr_ref_vector& m_res;
index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res):
m(mgr), m_array (m), m_var (v, m), m_res (res) {}
index_term_finder(ast_manager& mgr, app* v, expr_ref_vector& res) :
m(mgr), m_array(m), m_var(v, m), m_res(res) {}
void operator() (var *n) {}
void operator() (quantifier *n) {}
void operator() (app *n) {
expr *e1, *e2;
if (m_array.is_select (n)) {
for (expr * arg : *n) {
if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var)
m_res.push_back (arg);
}
void operator() (var* n) {}
void operator() (quantifier* n) {}
void operator() (app* n) {
expr* e1, * e2;
if (m_array.is_select(n)) {
for (expr* arg : *n) {
if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var)
m_res.push_back(arg);
}
}
else if (m.is_eq(n, e1, e2)) {
if (e1 == m_var)
m_res.push_back(e2);
else if (e2 == m_var)
m_res.push_back(e1);
if (e1 == m_var)
m_res.push_back(e2);
else if (e2 == m_var)
m_res.push_back(e1);
}
}
};
bool project_var (model_evaluator& eval, app* var, expr_ref& fml) {
bool project_var(model_evaluator& eval, app* var, expr_ref& fml) {
expr_ref val = eval(var);
TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << fml << "\n";);
expr_ref_vector terms (m);
index_term_finder finder (m, var, terms);
for_each_expr (finder, fml);
TRACE ("mbqi_project_verbose", tout << "terms:\n" << terms;);
for (expr * term : terms) {
TRACE("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp(var, m) << "\n" << "fml: " << fml << "\n";);
expr_ref_vector terms(m);
index_term_finder finder(m, var, terms);
for_each_expr(finder, fml);
TRACE("mbqi_project_verbose", tout << "terms:\n" << terms;);
for (expr* term : terms) {
expr_ref tval = eval(term);
TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << tval << " val: " << val << "\n";);
TRACE("mbqi_project_verbose", tout << "term: " << mk_pp(term, m) << " tval: " << tval << " val: " << val << "\n";);
// -- if the term does not contain an occurrence of var
// -- and is in the same equivalence class in the model
if (tval == val && !occurs (var, term)) {
TRACE ("mbqi_project",
tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";);
if (tval == val && !occurs(var, term)) {
TRACE("mbqi_project",
tout << "MBQI: replacing " << mk_pp(var, m) << " with " << mk_pp(term, m) << "\n";);
expr_safe_replace sub(m);
sub.insert (var, term);
sub (fml);
sub.insert(var, term);
sub(fml);
return true;
}
}
TRACE ("mbqi_project",
tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";);
TRACE("mbqi_project",
tout << "MBQI: failed to eliminate " << mk_pp(var, m) << " from " << fml << "\n";);
return false;
}
void project_vars (model &M, app_ref_vector &vars, expr_ref &fml) {
void project_vars(model& M, app_ref_vector& vars, expr_ref& fml) {
model_evaluator eval(M);
eval.set_model_completion(false);
// -- evaluate to initialize eval cache
(void) eval (fml);
eval(fml);
unsigned j = 0;
for (app * v : vars) {
if (!project_var (eval, v, fml)) {
vars[j++] = v;
}
}
for (app* v : vars)
if (!project_var(eval, v, fml))
vars[j++] = v;
vars.shrink(j);
}
public:
opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) {
arith_project_plugin arith(m);
mbp::arith_project_plugin arith(m);
return arith.maximize(fmls, mdl, t, ge, gt);
}
void extract_literals(model& model, expr_ref_vector& fmls) {
expr_ref val(m);
model_evaluator eval(model);
eval.set_expand_array_equalities(true);
TRACE("qe", tout << fmls << "\n";);
for (unsigned i = 0; i < fmls.size(); ++i) {
expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3;
SASSERT(m.is_bool(fml));
if (m.is_not(fml, nfml) && m.is_distinct(nfml)) {
fmls[i] = project_plugin::pick_equality(m, model, nfml);
--i;
}
else if (m.is_or(fml)) {
for (unsigned j = 0; j < to_app(fml)->get_num_args(); ++j) {
val = eval(to_app(fml)->get_arg(j));
if (m.is_true(val)) {
fmls[i] = to_app(fml)->get_arg(j);
--i;
break;
}
}
}
else if (m.is_and(fml)) {
fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
project_plugin::erase(fmls, i);
}
else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) {
val = eval(f1);
if (m.is_false(val)) {
f1 = mk_not(m, f1);
f2 = mk_not(m, f2);
}
fmls[i] = f1;
project_plugin::push_back(fmls, f2);
--i;
}
else if (m.is_implies(fml, f1, f2)) {
val = eval(f2);
if (m.is_true(val)) {
fmls[i] = f2;
}
else {
fmls[i] = mk_not(m, f1);
}
--i;
}
else if (m.is_ite(fml, f1, f2, f3)) {
val = eval(f1);
if (m.is_true(val)) {
project_plugin::push_back(fmls, f1);
project_plugin::push_back(fmls, f2);
}
else {
project_plugin::push_back(fmls, mk_not(m, f1));
project_plugin::push_back(fmls, f3);
}
project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) {
project_plugin::push_back(fmls, nfml);
project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_and(nfml)) {
for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) {
val = eval(to_app(nfml)->get_arg(j));
if (m.is_false(val)) {
fmls[i] = mk_not(m, to_app(nfml)->get_arg(j));
--i;
break;
}
}
}
else if (m.is_not(fml, nfml) && m.is_or(nfml)) {
for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) {
project_plugin::push_back(fmls, mk_not(m, to_app(nfml)->get_arg(j)));
}
project_plugin::erase(fmls, i);
}
else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) {
val = eval(f1);
if (m.is_true(val)) {
f2 = mk_not(m, f2);
}
else {
f1 = mk_not(m, f1);
}
project_plugin::push_back(fmls, f1);
project_plugin::push_back(fmls, f2);
project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) {
project_plugin::push_back(fmls, f1);
project_plugin::push_back(fmls, mk_not(m, f2));
project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) {
val = eval(f1);
if (m.is_true(val)) {
project_plugin::push_back(fmls, f1);
project_plugin::push_back(fmls, mk_not(m, f2));
}
else {
project_plugin::push_back(fmls, mk_not(m, f1));
project_plugin::push_back(fmls, mk_not(m, f3));
}
project_plugin::erase(fmls, i);
}
else if (m.is_not(fml, nfml)) {
if (extract_bools(eval, fmls, nfml)) {
project_plugin::erase(fmls, i);
}
}
else {
if (extract_bools(eval, fmls, fml)) {
project_plugin::erase(fmls, i);
}
// TBD other Boolean operations.
}
}
TRACE("qe", tout << fmls << "\n";);
m_bool_visited.reset();
void extract_literals(model& model, app_ref_vector const& vars, expr_ref_vector& fmls) {
mbp::project_plugin proj(m);
proj.extract_literals(model, vars, fmls);
}
impl(ast_manager& m, params_ref const& p):m(m), m_params(p), m_rw(m) {
add_plugin(alloc(arith_project_plugin, m));
add_plugin(alloc(datatype_project_plugin, m));
add_plugin(alloc(array_project_plugin, m));
impl(ast_manager& m, params_ref const& p) :m(m), m_params(p), m_rw(m) {
add_plugin(alloc(mbp::arith_project_plugin, m));
add_plugin(alloc(mbp::datatype_project_plugin, m));
add_plugin(alloc(mbp::array_project_plugin, m));
updt_params(p);
}
~impl() {
std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc<project_plugin>());
std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc<mbp::project_plugin>());
}
void updt_params(params_ref const& p) {
m_params.append(p);
m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false);
m_dont_sub = m_params.get_bool("dont_sub", false);
m_dont_sub = m_params.get_bool("dont_sub", false);
}
void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) {
extract_literals(model, fmls);
extract_literals(model, vars, fmls);
bool change = true;
while (change && !vars.empty()) {
change = solve(model, vars, fmls);
@ -520,13 +265,13 @@ public:
change = true;
}
}
}
}
}
bool validate_model(model& model, expr_ref_vector const& fmls) {
expr_ref val(m);
model_evaluator eval(model);
for (expr * f : fmls) {
for (expr* f : fmls) {
VERIFY(!model.is_false(f));
}
return true;
@ -544,7 +289,7 @@ public:
while (progress && !vars.empty() && !fmls.empty() && m.limit().inc()) {
app_ref_vector new_vars(m);
progress = false;
for (project_plugin * p : m_plugins) {
for (mbp::project_plugin* p : m_plugins) {
if (p) {
(*p)(model, vars, fmls);
}
@ -552,7 +297,7 @@ public:
while (!vars.empty() && !fmls.empty() && m.limit().inc()) {
var = vars.back();
vars.pop_back();
project_plugin* p = get_plugin(var);
mbp::project_plugin* p = get_plugin(var);
if (p && (*p)(model, var, vars, fmls)) {
progress = true;
}
@ -566,19 +311,17 @@ public:
expr_safe_replace sub(m);
val = model(var);
sub.insert(var, val);
for (unsigned i = 0; i < fmls.size(); ++i) {
sub(fmls[i].get(), tmp);
unsigned j = 0;
for (expr* f : fmls) {
sub(f, tmp);
m_rw(tmp);
if (m.is_true(tmp)) {
project_plugin::erase(fmls, i);
}
else {
fmls[i] = tmp;
}
}
if (!m.is_true(tmp))
fmls[j++] = tmp;
}
fmls.shrink(j);
progress = true;
}
if (!m.limit().inc())
}
if (!m.limit().inc())
return;
vars.append(new_vars);
if (progress) {
@ -592,13 +335,13 @@ public:
SASSERT(validate_model(model, fmls));
TRACE("qe", tout << vars << " " << fmls << "\n";);
}
void do_qe_lite(app_ref_vector& vars, expr_ref& fml) {
qe_lite qe(m, m_params, false);
qe (vars, fml);
m_rw (fml);
TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars: " << vars << "\n";);
SASSERT (!m.is_false (fml));
qe(vars, fml);
m_rw(fml);
TRACE("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars: " << vars << "\n";);
SASSERT(!m.is_false(fml));
}
void do_qe_bool(model& mdl, app_ref_vector& vars, expr_ref& fml) {
@ -609,130 +352,122 @@ public:
}
void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) {
TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";);
TRACE("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";);
model_evaluator eval(mdl, m_params);
model_evaluator eval(mdl, m_params);
eval.set_model_completion(true);
app_ref_vector other_vars (m);
app_ref_vector array_vars (m);
array_util arr_u (m);
arith_util ari_u (m);
app_ref_vector other_vars(m);
app_ref_vector array_vars(m);
array_util arr_u(m);
arith_util ari_u(m);
flatten_and(fml);
while (!vars.empty()) {
do_qe_lite(vars, fml);
do_qe_bool(mdl, vars, fml);
// sort out vars into bools, arith (int/real), and arrays
for (app* v : vars) {
if (arr_u.is_array(v)) {
array_vars.push_back (v);
}
else {
other_vars.push_back (v);
array_vars.push_back(v);
}
}
TRACE ("qe", tout << "Array vars: " << array_vars << "\n";);
vars.reset ();
// project arrays
qe::array_project_plugin ap(m);
ap(mdl, array_vars, fml, vars, m_reduce_all_selects);
SASSERT (array_vars.empty ());
m_rw (fml);
SASSERT (!m.is_false (fml));
else {
other_vars.push_back(v);
}
}
TRACE ("qe",
tout << "extended model:\n" << mdl;
tout << "Vars: " << vars << "\n";
);
TRACE("qe", tout << "Array vars: " << array_vars << "\n";);
vars.reset();
// project arrays
mbp::array_project_plugin ap(m);
ap(mdl, array_vars, fml, vars, m_reduce_all_selects);
SASSERT(array_vars.empty());
m_rw(fml);
SASSERT(!m.is_false(fml));
TRACE("qe",
tout << "extended model:\n" << mdl;
tout << "Vars: " << vars << "\n";);
}
// project reals, ints and other variables.
if (!other_vars.empty ()) {
TRACE ("qe", tout << "Other vars: " << other_vars << "\n" << mdl;);
if (!other_vars.empty()) {
TRACE("qe", tout << "Other vars: " << other_vars << "\n" << mdl;);
expr_ref_vector fmls(m);
flatten_and (fml, fmls);
flatten_and(fml, fmls);
(*this)(false, other_vars, mdl, fmls);
fml = mk_and (fmls);
fml = mk_and(fmls);
m_rw(fml);
TRACE ("qe",
tout << "Projected other vars:\n" << fml << "\n";
tout << "Remaining other vars:\n" << other_vars << "\n";);
SASSERT (!m.is_false (fml));
TRACE("qe",
tout << "Projected other vars:\n" << fml << "\n";
tout << "Remaining other vars:\n" << other_vars << "\n";);
SASSERT(!m.is_false(fml));
}
if (!other_vars.empty ()) {
project_vars (mdl, other_vars, fml);
if (!other_vars.empty()) {
project_vars(mdl, other_vars, fml);
m_rw(fml);
}
// substitute any remaining other vars
if (!m_dont_sub && !other_vars.empty ()) {
subst_vars (eval, other_vars, fml);
TRACE ("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";);
if (!m_dont_sub && !other_vars.empty()) {
subst_vars(eval, other_vars, fml);
TRACE("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";);
// an extra round of simplification because subst_vars is not simplifying
m_rw(fml);
other_vars.reset();
}
SASSERT(!eval.is_false(fml));
vars.reset ();
vars.reset();
vars.append(other_vars);
}
}
};
mbp::mbp(ast_manager& m, params_ref const& p) {
scoped_no_proof _sp (m);
mbproj::mbproj(ast_manager& m, params_ref const& p) {
scoped_no_proof _sp(m);
m_impl = alloc(impl, m, p);
}
mbp::~mbp() {
mbproj::~mbproj() {
dealloc(m_impl);
}
void mbp::updt_params(params_ref const& p) {
void mbproj::updt_params(params_ref const& p) {
m_impl->updt_params(p);
}
void mbp::get_param_descrs(param_descrs & r) {
void mbproj::get_param_descrs(param_descrs& r) {
r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects");
r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables");
}
void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) {
scoped_no_proof _sp (fmls.get_manager());
void mbproj::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) {
scoped_no_proof _sp(fmls.get_manager());
(*m_impl)(force_elim, vars, mdl, fmls);
}
void mbp::spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) {
scoped_no_proof _sp (fml.get_manager());
void mbproj::spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) {
scoped_no_proof _sp(fml.get_manager());
m_impl->spacer(vars, mdl, fml);
}
void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) {
scoped_no_proof _sp (fmls.get_manager());
void mbproj::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) {
scoped_no_proof _sp(fmls.get_manager());
m_impl->preprocess_solve(model, vars, fmls);
}
void mbp::extract_literals(model& model, expr_ref_vector& lits) {
scoped_no_proof _sp (lits.get_manager());
m_impl->extract_literals(model, lits);
}
opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) {
scoped_no_proof _sp (fmls.get_manager());
opt::inf_eps mbproj::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) {
scoped_no_proof _sp(fmls.get_manager());
return m_impl->maximize(fmls, mdl, t, ge, gt);
}

View file

@ -28,55 +28,13 @@ Revision History:
namespace qe {
struct cant_project {};
struct def {
expr_ref var, term;
def(const expr_ref& v, expr_ref& t): var(v), term(t) {}
};
class project_plugin {
public:
virtual ~project_plugin() {}
virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0;
/**
\brief partial solver.
*/
virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0;
virtual family_id get_family_id() = 0;
virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { };
/**
\brief project vars modulo model, return set of definitions for eliminated variables.
- vars in/out: returns variables that were not eliminated
- lits in/out: returns projected literals
- returns set of definitions
(TBD: in triangular form, the last definition can be substituted into definitions that come before)
*/
virtual vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0;
/**
\brief model based saturation. Saturates theory axioms to equi-satisfiable literals over EUF,
such that 'shared' are not retained for EUF.
*/
virtual void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) = 0;
static expr_ref pick_equality(ast_manager& m, model& model, expr* t);
static void erase(expr_ref_vector& lits, unsigned& i);
static void push_back(expr_ref_vector& lits, expr* lit);
static void mark_rec(expr_mark& visited, expr* e);
static void mark_rec(expr_mark& visited, expr_ref_vector const& es);
};
class mbp {
class mbproj {
class impl;
impl * m_impl;
public:
mbp(ast_manager& m, params_ref const& p = params_ref());
mbproj(ast_manager& m, params_ref const& p = params_ref());
~mbp();
~mbproj();
void updt_params(params_ref const& p);
@ -95,12 +53,6 @@ namespace qe {
*/
void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits);
/**
\brief
Extract literals from formulas based on model.
*/
void extract_literals(model& model, expr_ref_vector& lits);
/**
\brief
Maximize objective t under current model for constraints in fmls.

View file

@ -1,62 +0,0 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
qe_vartest.h
Abstract:
Utilities for quantifiers.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#pragma once
#include "ast/ast.h"
#include "util/uint_set.h"
// TBD: move under qe namespace
class is_variable_proc : public std::unary_function<expr*,bool> {
public:
virtual bool operator()(const expr* e) const = 0;
};
class is_variable_test : public is_variable_proc {
enum is_var_kind { BY_VAR_SET, BY_VAR_SET_COMPLEMENT, BY_NUM_DECLS };
uint_set m_var_set;
unsigned m_num_decls;
is_var_kind m_var_kind;
public:
is_variable_test(uint_set const& vars, bool index_of_bound) :
m_var_set(vars),
m_num_decls(0),
m_var_kind(index_of_bound?BY_VAR_SET:BY_VAR_SET_COMPLEMENT) {}
is_variable_test(unsigned num_decls) :
m_num_decls(num_decls),
m_var_kind(BY_NUM_DECLS) {}
bool operator()(const expr* e) const override {
if (!is_var(e)) {
return false;
}
unsigned idx = to_var(e)->get_idx();
switch(m_var_kind) {
case BY_VAR_SET:
return m_var_set.contains(idx);
case BY_VAR_SET_COMPLEMENT:
return !m_var_set.contains(idx);
case BY_NUM_DECLS:
return idx < m_num_decls;
}
UNREACHABLE();
return false;
}
};

View file

@ -607,7 +607,7 @@ namespace qe {
params_ref m_params;
stats m_stats;
statistics m_st;
qe::mbp m_mbp;
qe::mbproj m_mbp;
kernel m_fa;
kernel m_ex;
pred_abs m_pred_abs;