mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 00:55:31 +00:00
mbp (#4741)
* 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 * add projection * na * na * na * na * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * deps Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * testing arith/q * na * newline for model printing Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
e5cc613bf1
commit
72d407a49f
51 changed files with 903 additions and 618 deletions
|
@ -168,28 +168,23 @@ namespace mbp {
|
|||
expr* t1, *t2, *t3;
|
||||
rational mul1;
|
||||
expr_ref val(m);
|
||||
if (a.is_mul(t, t1, t2) && is_numeral(t1, mul1)) {
|
||||
linearize(mbo, eval, mul* mul1, t2, c, fmls, ts, tids);
|
||||
}
|
||||
else if (a.is_mul(t, t1, t2) && is_numeral(t2, mul1)) {
|
||||
linearize(mbo, eval, mul* mul1, t1, c, fmls, ts, tids);
|
||||
}
|
||||
if (a.is_mul(t, t1, t2) && is_numeral(t1, mul1))
|
||||
linearize(mbo, eval, mul* mul1, t2, c, fmls, ts, tids);
|
||||
else if (a.is_mul(t, t1, t2) && is_numeral(t2, mul1))
|
||||
linearize(mbo, eval, mul* mul1, t1, c, fmls, ts, tids);
|
||||
else if (a.is_uminus(t, t1))
|
||||
linearize(mbo, eval, -mul, t1, c, fmls, ts, tids);
|
||||
else if (a.is_numeral(t, mul1))
|
||||
c += mul * mul1;
|
||||
else if (a.is_add(t)) {
|
||||
app* ap = to_app(t);
|
||||
for (expr* arg : *ap) {
|
||||
linearize(mbo, eval, mul, arg, c, fmls, ts, tids);
|
||||
}
|
||||
for (expr* arg : *to_app(t))
|
||||
linearize(mbo, eval, mul, arg, c, fmls, ts, tids);
|
||||
}
|
||||
else if (a.is_sub(t, t1, t2)) {
|
||||
linearize(mbo, eval, mul, t1, c, fmls, ts, tids);
|
||||
linearize(mbo, eval, -mul, t2, c, fmls, ts, tids);
|
||||
}
|
||||
else if (a.is_uminus(t, t1)) {
|
||||
linearize(mbo, eval, -mul, t1, c, fmls, ts, tids);
|
||||
}
|
||||
else if (a.is_numeral(t, mul1)) {
|
||||
c += mul*mul1;
|
||||
}
|
||||
}
|
||||
|
||||
else if (m.is_ite(t, t1, t2, t3)) {
|
||||
val = eval(t1);
|
||||
SASSERT(m.is_true(val) || m.is_false(val));
|
||||
|
@ -269,9 +264,8 @@ namespace mbp {
|
|||
|
||||
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) {
|
||||
bool has_arith = false;
|
||||
for (expr* v : vars) {
|
||||
has_arith |= is_arith(v);
|
||||
}
|
||||
for (expr* v : vars)
|
||||
has_arith |= is_arith(v);
|
||||
if (!has_arith)
|
||||
return vector<def>();
|
||||
model_evaluator eval(model);
|
||||
|
@ -517,7 +511,6 @@ namespace mbp {
|
|||
expr_ref val = eval(v);
|
||||
if (!a.is_numeral(val, r)) {
|
||||
TRACE("qe", tout << eval.get_model() << "\n";);
|
||||
|
||||
throw default_exception("mbp evaluation was only partial");
|
||||
}
|
||||
id = mbo.add_var(r, a.is_int(v));
|
||||
|
|
|
@ -3,19 +3,18 @@ Copyright (c) 2015 Microsoft Corporation
|
|||
|
||||
Module Name:
|
||||
|
||||
qe_mbp.cpp
|
||||
|
||||
mpb_plugin.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Model-based projection utilities
|
||||
Model-based projection plugin utilities
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2015-5-29
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
|
@ -58,9 +57,9 @@ namespace mbp {
|
|||
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);
|
||||
}
|
||||
SASSERT(alit->get_num_args() > 1);
|
||||
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);
|
||||
|
@ -97,6 +96,7 @@ namespace mbp {
|
|||
}
|
||||
|
||||
void project_plugin::extract_literals(model& model, app_ref_vector const& vars, expr_ref_vector& fmls) {
|
||||
m_cache.reset();
|
||||
m_bool_visited.reset();
|
||||
expr_ref val(m);
|
||||
model_evaluator eval(model);
|
||||
|
@ -106,7 +106,7 @@ namespace mbp {
|
|||
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);
|
||||
fmls[i--] = pick_equality(m, model, nfml);
|
||||
else if (m.is_or(fml)) {
|
||||
for (expr* arg : *to_app(fml)) {
|
||||
val = eval(arg);
|
||||
|
@ -119,7 +119,7 @@ namespace mbp {
|
|||
}
|
||||
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);
|
||||
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);
|
||||
|
@ -150,11 +150,11 @@ namespace mbp {
|
|||
push_back(fmls, mk_not(m, f1));
|
||||
push_back(fmls, f3);
|
||||
}
|
||||
mbp::project_plugin::erase(fmls, i);
|
||||
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);
|
||||
erase(fmls, i);
|
||||
}
|
||||
else if (m.is_not(fml, nfml) && m.is_and(nfml)) {
|
||||
for (expr* arg : *to_app(nfml)) {
|
||||
|
@ -168,24 +168,22 @@ namespace mbp {
|
|||
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);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
erase(fmls, i);
|
||||
}
|
||||
else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) {
|
||||
val = eval(f1);
|
||||
|
@ -197,66 +195,187 @@ namespace mbp {
|
|||
push_back(fmls, mk_not(m, f1));
|
||||
push_back(fmls, mk_not(m, f3));
|
||||
}
|
||||
mbp::project_plugin::erase(fmls, i);
|
||||
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);
|
||||
else if (m.is_not(fml, nfml))
|
||||
extract_bools(eval, fmls, i, nfml, false);
|
||||
else
|
||||
extract_bools(eval, fmls, i, fml, true);
|
||||
}
|
||||
TRACE("qe", tout << fmls << "\n";);
|
||||
}
|
||||
|
||||
bool project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) {
|
||||
void project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, unsigned idx, expr* fml, bool is_true) {
|
||||
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());
|
||||
if (!is_app(fml))
|
||||
return;
|
||||
m_to_visit.reset();
|
||||
m_to_visit.append(to_app(fml)->get_num_args(), to_app(fml)->get_args());
|
||||
while (!m_to_visit.empty()) {
|
||||
if (!m.inc())
|
||||
return;
|
||||
expr* e = m_to_visit.back();
|
||||
if (m_cache.get(e->get_id(), nullptr)) {
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
else if (!is_app(e)) {
|
||||
m_cache.setx(e->get_id(), e);
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
else if (visit_ite(eval, e, fmls))
|
||||
continue;
|
||||
else if (visit_bool(eval, e, fmls))
|
||||
continue;
|
||||
else
|
||||
visit_app(e);
|
||||
}
|
||||
while (!todo.empty() && m.inc()) {
|
||||
expr* e = todo.back();
|
||||
todo.pop_back();
|
||||
if (m_visited.is_marked(e)) {
|
||||
|
||||
SASSERT(m_to_visit.empty());
|
||||
m_to_visit.push_back(fml);
|
||||
visit_app(fml);
|
||||
SASSERT(m_to_visit.empty());
|
||||
expr* new_fml = m_cache.get(fml->get_id(), nullptr);
|
||||
SASSERT(new_fml);
|
||||
if (new_fml != fml)
|
||||
fmls[idx] = is_true ? new_fml : mk_not(m, new_fml);
|
||||
}
|
||||
|
||||
bool project_plugin::is_true(model_evaluator& eval, expr* e) {
|
||||
expr_ref val = eval(e);
|
||||
bool tt = m.is_true(val);
|
||||
if (!tt && !m.is_false(val) && contains_uninterpreted(val))
|
||||
throw default_exception("could not evaluate Boolean in model");
|
||||
SASSERT(tt || m.is_false(val));
|
||||
return tt;
|
||||
}
|
||||
|
||||
bool project_plugin::visit_ite(model_evaluator& eval, expr* e, expr_ref_vector& fmls) {
|
||||
expr* c = nullptr, * th = nullptr, * el = nullptr;
|
||||
if (m.is_ite(e, c, th, el)) {
|
||||
bool tt = is_true(eval, c);
|
||||
if (!m_bool_visited.is_marked(c))
|
||||
fmls.push_back(tt ? c : mk_not(m, c));
|
||||
m_bool_visited.mark(c);
|
||||
expr* s = tt ? th : el;
|
||||
expr* t = m_cache.get(s->get_id(), nullptr);
|
||||
if (t) {
|
||||
m_to_visit.pop_back();
|
||||
m_cache.setx(e->get_id(), t);
|
||||
}
|
||||
else
|
||||
m_to_visit.push_back(s);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool project_plugin::visit_bool(model_evaluator& eval, expr* e, expr_ref_vector& fmls) {
|
||||
if (m.is_bool(e) && !m.is_true(e) && !m.is_false(e)) {
|
||||
bool tt = is_true(eval, e);
|
||||
if (!m_bool_visited.is_marked(e))
|
||||
fmls.push_back(tt ? e : mk_not(m, e));
|
||||
m_bool_visited.mark(e);
|
||||
m_cache.setx(e->get_id(), m.mk_bool_val(tt));
|
||||
m_to_visit.pop_back();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void project_plugin::visit_app(expr* e) {
|
||||
unsigned sz = m_to_visit.size();
|
||||
m_args.reset();
|
||||
bool diff = false;
|
||||
for (expr* arg : *to_app(e)) {
|
||||
expr* new_arg = m_cache.get(arg->get_id(), nullptr);
|
||||
diff |= new_arg != arg;
|
||||
if (new_arg == nullptr)
|
||||
m_to_visit.push_back(arg);
|
||||
else
|
||||
m_args.push_back(new_arg);
|
||||
}
|
||||
if (sz == m_to_visit.size()) {
|
||||
m_cache.setx(e->get_id(), diff ? m.mk_app(to_app(e)->get_decl(), m_args) : e);
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void project_plugin::mark_non_ground(expr* e) {
|
||||
m_to_visit.push_back(e);
|
||||
while (!m_to_visit.empty()) {
|
||||
expr* e = m_to_visit.back();
|
||||
if (!is_app(e)) {
|
||||
m_visited.mark(e);
|
||||
m_to_visit.pop_back();
|
||||
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));
|
||||
unsigned n = m_to_visit.size();
|
||||
for (expr* arg : *to_app(e)) {
|
||||
if (!m_visited.is_marked(arg))
|
||||
m_to_visit.push_back(arg);
|
||||
else if (m_non_ground.is_marked(arg))
|
||||
m_non_ground.mark(e);
|
||||
}
|
||||
if (m_to_visit.size() == n) {
|
||||
m_visited.mark(e);
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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";);
|
||||
void project_plugin::purify(euf_inverter& inv, model& mdl, app_ref_vector const& vars, expr_ref_vector& lits) {
|
||||
TRACE("mbp", tout << lits << "\n" << mdl << "\n";);
|
||||
extract_literals(mdl, vars, lits);
|
||||
if (!m.inc())
|
||||
return;
|
||||
model_evaluator eval(mdl);
|
||||
eval.set_expand_array_equalities(true);
|
||||
m_non_ground.reset();
|
||||
m_to_visit.reset();
|
||||
m_visited.reset();
|
||||
m_cache.reset();
|
||||
m_pure_eqs.reset();
|
||||
for (expr* v : vars)
|
||||
m_non_ground.mark(v);
|
||||
for (unsigned i = 0; m.inc() && i < lits.size(); ++i)
|
||||
lits[i] = purify(inv, eval, lits.get(i), lits);
|
||||
lits.append(m_pure_eqs);
|
||||
TRACE("mbp", tout << lits << "\n";);
|
||||
}
|
||||
|
||||
expr* project_plugin::purify(euf_inverter& inv, model_evaluator& eval, expr* e, expr_ref_vector& lits) {
|
||||
mark_non_ground(e);
|
||||
m_to_visit.push_back(e);
|
||||
while (!m_to_visit.empty()) {
|
||||
expr* t = m_to_visit.back();
|
||||
if (m_cache.get(t->get_id(), nullptr))
|
||||
m_to_visit.pop_back();
|
||||
else if (!is_app(t) || !m_non_ground.is_marked(t)) {
|
||||
m_cache.setx(t->get_id(), t);
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
else
|
||||
purify_app(inv, eval, to_app(t), lits);
|
||||
}
|
||||
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 m_cache.get(e->get_id());
|
||||
}
|
||||
|
||||
void project_plugin::purify_app(euf_inverter& inv, model_evaluator& eval, app* t, expr_ref_vector& lits) {
|
||||
if (is_uninterp(t) && t->get_num_args() > 0) {
|
||||
expr_ref t_value = eval(t);
|
||||
expr* s = inv.invert_app(t, t_value);
|
||||
m_cache.setx(t->get_id(), s);
|
||||
if (s != t)
|
||||
m_pure_eqs.push_back(m.mk_eq(t, s));
|
||||
unsigned i = 0;
|
||||
for (expr* arg : *t)
|
||||
push_back(lits, inv.invert_arg(t, i++, eval(arg)));
|
||||
m_to_visit.pop_back();
|
||||
}
|
||||
return found_bool;
|
||||
else
|
||||
visit_app(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,18 +35,43 @@ namespace mbp {
|
|||
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;
|
||||
/***
|
||||
* An EUF inverter provides two services:
|
||||
* 1. It inverts an uninterpreted function application f(s1,s2) with 'value' to a ground term evaluating to the same
|
||||
* 2. It adds constraints for arguments to s_i with 'value' to be within the bounds of the model value.
|
||||
*/
|
||||
class euf_inverter {
|
||||
public:
|
||||
virtual expr* invert_app(app* t, expr* value) = 0;
|
||||
virtual expr* invert_arg(app* t, unsigned i, expr* value) = 0;
|
||||
};
|
||||
|
||||
class project_plugin {
|
||||
ast_manager& m;
|
||||
expr_mark m_visited;
|
||||
ptr_vector<expr> m_to_visit;
|
||||
expr_mark m_bool_visited;
|
||||
expr_mark m_non_ground;
|
||||
expr_ref_vector m_cache, m_args, m_pure_eqs;
|
||||
|
||||
void extract_bools(model_evaluator& eval, expr_ref_vector& fmls, unsigned i, expr* fml, bool is_true);
|
||||
void visit_app(expr* e);
|
||||
bool visit_ite(model_evaluator& eval, expr* e, expr_ref_vector& fmls);
|
||||
bool visit_bool(model_evaluator& eval, expr* e, expr_ref_vector& fmls);
|
||||
bool is_true(model_evaluator& eval, expr* e);
|
||||
|
||||
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);
|
||||
|
||||
void mark_non_ground(expr* e);
|
||||
|
||||
expr* purify(euf_inverter& inv, model_evaluator& eval, expr* e, expr_ref_vector& lits);
|
||||
void purify_app(euf_inverter& inv, model_evaluator& eval, app* t, expr_ref_vector& lits);
|
||||
|
||||
public:
|
||||
project_plugin(ast_manager& m) :m(m) {}
|
||||
project_plugin(ast_manager& m) :m(m), m_cache(m), m_args(m), m_pure_eqs(m) {}
|
||||
virtual ~project_plugin() {}
|
||||
virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return false; }
|
||||
/**
|
||||
|
@ -81,7 +106,8 @@ namespace mbp {
|
|||
/*
|
||||
* Purify literals into linear inequalities or constraints without arithmetic variables.
|
||||
*/
|
||||
void purify(model& model, app_ref_vector const& vars, expr_ref_vector& fmls);
|
||||
|
||||
void purify(euf_inverter& inv, 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);
|
||||
|
|
|
@ -290,9 +290,8 @@ public:
|
|||
app_ref_vector new_vars(m);
|
||||
progress = false;
|
||||
for (mbp::project_plugin* p : m_plugins) {
|
||||
if (p) {
|
||||
if (p)
|
||||
(*p)(model, vars, fmls);
|
||||
}
|
||||
}
|
||||
while (!vars.empty() && !fmls.empty() && m.limit().inc()) {
|
||||
var = vars.back();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue