3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 03:07:07 +00:00
z3/lib/proto_model.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

609 lines
20 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
proto_model.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-03-08.
Revision History:
--*/
#include"proto_model.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"var_subst.h"
#include"front_end_params.h"
#include"array_decl_plugin.h"
#include"well_sorted.h"
#include"used_symbols.h"
#include"model_v2_pp.h"
proto_model::proto_model(ast_manager & m, simplifier & s, model_params const & p):
model_core(m),
m_params(p),
m_asts(m),
m_simplifier(s),
m_afid(m.get_family_id(symbol("array"))) {
register_factory(alloc(basic_factory, m));
m_user_sort_factory = alloc(user_sort_factory, m);
register_factory(m_user_sort_factory);
}
void proto_model::reset_finterp() {
decl2finterp::iterator it = m_finterp.begin();
decl2finterp::iterator end = m_finterp.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
}
proto_model::~proto_model() {
reset_finterp();
}
void proto_model::register_decl(func_decl * d, expr * v) {
SASSERT(d->get_arity() == 0);
if (m_interp.contains(d)) {
DEBUG_CODE({
expr * old_v = 0;
m_interp.find(d, old_v);
SASSERT(old_v == v);
});
return;
}
SASSERT(!m_interp.contains(d));
m_decls.push_back(d);
m_asts.push_back(d);
m_asts.push_back(v);
m_interp.insert(d, v);
m_const_decls.push_back(d);
}
void proto_model::register_decl(func_decl * d, func_interp * fi, bool aux) {
SASSERT(d->get_arity() > 0);
SASSERT(!m_finterp.contains(d));
m_decls.push_back(d);
m_asts.push_back(d);
m_finterp.insert(d, fi);
m_func_decls.push_back(d);
if (aux)
m_aux_decls.insert(d);
}
/**
\brief Set new_fi as the new interpretation for f.
If f_aux != 0, then assign the old interpretation of f to f_aux.
If f_aux == 0, then delete the old interpretation of f.
f_aux is marked as a auxiliary declaration.
*/
void proto_model::reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux) {
func_interp * fi = get_func_interp(f);
if (fi == 0) {
register_decl(f, new_fi);
}
else {
if (f_aux != 0) {
register_decl(f_aux, fi);
m_aux_decls.insert(f_aux);
}
else {
dealloc(fi);
}
m_finterp.insert(f, new_fi);
}
}
expr * proto_model::mk_some_interp_for(func_decl * d) {
SASSERT(!has_interpretation(d));
expr * r = get_some_value(d->get_range()); // if t is a function, then it will be the constant function.
if (d->get_arity() == 0) {
register_decl(d, r);
}
else {
func_interp * new_fi = alloc(func_interp, m_manager, d->get_arity());
new_fi->set_else(r);
register_decl(d, new_fi);
}
return r;
}
/**
\brief Evaluate the expression e in the current model, and store the result in \c result.
It returns \c true if succeeded, and false otherwise. If the evaluation fails,
then r contains a term that is simplified as much as possible using the interpretations
available in the model.
When model_completion == true, if the model does not assign an interpretation to a
declaration it will build one for it. Moreover, partial functions will also be completed.
So, if model_completion == true, the evaluator never fails if it doesn't contain quantifiers.
*/
bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
bool is_ok = true;
SASSERT(is_well_sorted(m_manager, e));
TRACE("model_eval", tout << mk_pp(e, m_manager) << "\n";
tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";);
obj_map<expr, expr*> eval_cache;
expr_ref_vector trail(m_manager);
sbuffer<std::pair<expr*, expr*>, 128> todo;
ptr_buffer<expr> args;
expr * null = static_cast<expr*>(0);
todo.push_back(std::make_pair(e, null));
expr * a;
expr * expanded_a;
while (!todo.empty()) {
std::pair<expr *, expr *> & p = todo.back();
a = p.first;
expanded_a = p.second;
if (expanded_a != 0) {
expr * r = 0;
eval_cache.find(expanded_a, r);
SASSERT(r != 0);
todo.pop_back();
eval_cache.insert(a, r);
TRACE("model_eval",
tout << "orig:\n" << mk_pp(a, m_manager) << "\n";
tout << "after beta reduction:\n" << mk_pp(expanded_a, m_manager) << "\n";
tout << "new:\n" << mk_pp(r, m_manager) << "\n";);
}
else {
switch(a->get_kind()) {
case AST_APP: {
app * t = to_app(a);
bool visited = true;
args.reset();
unsigned num_args = t->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = 0;
if (!eval_cache.find(t->get_arg(i), arg)) {
visited = false;
todo.push_back(std::make_pair(t->get_arg(i), null));
}
else {
args.push_back(arg);
}
}
if (!visited) {
continue;
}
SASSERT(args.size() == t->get_num_args());
expr_ref new_t(m_manager);
func_decl * f = t->get_decl();
if (!has_interpretation(f)) {
// the model does not assign an interpretation to f.
SASSERT(new_t.get() == 0);
if (f->get_family_id() == null_family_id) {
if (model_completion) {
// create an interpretation for f.
new_t = mk_some_interp_for(f);
}
else {
is_ok = false;
}
}
if (new_t.get() == 0) {
// t is interpreted or model completion is disabled.
m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t);
trail.push_back(new_t);
if (!is_app(new_t) || to_app(new_t)->get_decl() != f) {
// if the result is not of the form (f ...), then assume we must simplify it.
expr * new_new_t = 0;
if (!eval_cache.find(new_t.get(), new_new_t)) {
todo.back().second = new_t;
todo.push_back(std::make_pair(new_t, null));
continue;
}
else {
new_t = new_new_t;
}
}
}
}
else {
// the model has an interpretaion for f.
if (num_args == 0) {
// t is a constant
new_t = get_const_interp(f);
}
else {
// t is a function application
SASSERT(new_t.get() == 0);
// try to use function graph first
func_interp * fi = get_func_interp(f);
SASSERT(fi->get_arity() == num_args);
expr_ref r1(m_manager);
// fi may be partial...
if (!fi->eval(m_simplifier, args.c_ptr(), r1)) {
SASSERT(fi->is_partial()); // fi->eval only fails when fi is partial.
if (model_completion) {
expr * r = get_some_value(f->get_range());
fi->set_else(r);
SASSERT(!fi->is_partial());
new_t = r;
}
else {
// f is an uninterpreted function, there is no need to use m_simplifier.mk_app
new_t = m_manager.mk_app(f, num_args, args.c_ptr());
trail.push_back(new_t);
is_ok = false;
}
}
else {
SASSERT(r1);
trail.push_back(r1);
expr * r2 = 0;
if (!eval_cache.find(r1.get(), r2)) {
todo.back().second = r1;
todo.push_back(std::make_pair(r1, null));
continue;
}
else {
new_t = r2;
}
}
}
}
TRACE("model_eval",
tout << "orig:\n" << mk_pp(t, m_manager) << "\n";
tout << "new:\n" << mk_pp(new_t, m_manager) << "\n";);
todo.pop_back();
SASSERT(new_t.get() != 0);
eval_cache.insert(t, new_t);
break;
}
case AST_VAR:
SASSERT(a != 0);
eval_cache.insert(a, a);
todo.pop_back();
break;
case AST_QUANTIFIER:
is_ok = false; // evaluator does not handle quantifiers.
SASSERT(a != 0);
eval_cache.insert(a, a);
todo.pop_back();
break;
default:
UNREACHABLE();
break;
}
}
}
if (!eval_cache.find(e, a)) {
TRACE("model_eval", tout << "FAILED e: " << mk_bounded_pp(e, m_manager) << "\n";);
UNREACHABLE();
}
result = a;
TRACE("model_eval",
ast_ll_pp(tout << "original: ", m_manager, e);
ast_ll_pp(tout << "evaluated: ", m_manager, a);
ast_ll_pp(tout << "reduced: ", m_manager, result.get());
tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";
);
SASSERT(is_well_sorted(m_manager, result.get()));
return is_ok;
}
/**
\brief Replace uninterpreted constants occurring in fi->get_else()
by their interpretations.
*/
void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_aux_fs) {
if (fi->is_partial())
return;
expr * fi_else = fi->get_else();
TRACE("model_bug", tout << "cleaning up:\n" << mk_pp(fi_else, m_manager) << "\n";);
obj_map<expr, expr*> cache;
expr_ref_vector trail(m_manager);
ptr_buffer<expr, 128> todo;
ptr_buffer<expr> args;
todo.push_back(fi_else);
expr * a;
while (!todo.empty()) {
a = todo.back();
if (is_uninterp_const(a)) {
todo.pop_back();
func_decl * a_decl = to_app(a)->get_decl();
expr * ai = get_const_interp(a_decl);
if (ai == 0) {
ai = get_some_value(a_decl->get_range());
register_decl(a_decl, ai);
}
cache.insert(a, ai);
}
else {
switch(a->get_kind()) {
case AST_APP: {
app * t = to_app(a);
bool visited = true;
args.reset();
unsigned num_args = t->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = 0;
if (!cache.find(t->get_arg(i), arg)) {
visited = false;
todo.push_back(t->get_arg(i));
}
else {
args.push_back(arg);
}
}
if (!visited) {
continue;
}
func_decl * f = t->get_decl();
if (m_aux_decls.contains(f))
found_aux_fs.insert(f);
expr_ref new_t(m_manager);
m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t);
if (t != new_t.get())
trail.push_back(new_t);
todo.pop_back();
cache.insert(t, new_t);
break;
}
default:
SASSERT(a != 0);
cache.insert(a, a);
todo.pop_back();
break;
}
}
}
if (!cache.find(fi_else, a)) {
UNREACHABLE();
}
fi->set_else(a);
}
void proto_model::remove_aux_decls_not_in_set(ptr_vector<func_decl> & decls, func_decl_set const & s) {
unsigned sz = decls.size();
unsigned i = 0;
unsigned j = 0;
for (; i < sz; i++) {
func_decl * f = decls[i];
if (!m_aux_decls.contains(f) || s.contains(f)) {
decls[j] = f;
j++;
}
}
decls.shrink(j);
}
/**
\brief Replace uninterpreted constants occurring in the func_interp's get_else()
by their interpretations.
*/
void proto_model::cleanup() {
func_decl_set found_aux_fs;
decl2finterp::iterator it = m_finterp.begin();
decl2finterp::iterator end = m_finterp.end();
for (; it != end; ++it) {
func_interp * fi = (*it).m_value;
cleanup_func_interp(fi, found_aux_fs);
}
// remove auxiliary declarations that are not used.
if (found_aux_fs.size() != m_aux_decls.size()) {
remove_aux_decls_not_in_set(m_decls, found_aux_fs);
remove_aux_decls_not_in_set(m_func_decls, found_aux_fs);
func_decl_set::iterator it2 = m_aux_decls.begin();
func_decl_set::iterator end2 = m_aux_decls.end();
for (; it2 != end2; ++it2) {
func_decl * faux = *it2;
if (!found_aux_fs.contains(faux)) {
TRACE("cleanup_bug", tout << "eliminating " << faux->get_name() << "\n";);
func_interp * fi = 0;
m_finterp.find(faux, fi);
SASSERT(fi != 0);
m_finterp.erase(faux);
dealloc(fi);
}
}
m_aux_decls.swap(found_aux_fs);
}
}
value_factory * proto_model::get_factory(family_id fid) {
return m_factories.get_plugin(fid);
}
void proto_model::freeze_universe(sort * s) {
SASSERT(m_manager.is_uninterp(s));
m_user_sort_factory->freeze_universe(s);
}
/**
\brief Return the known universe of an uninterpreted sort.
*/
obj_hashtable<expr> const & proto_model::get_known_universe(sort * s) const {
SASSERT(m_manager.is_uninterp(s));
return m_user_sort_factory->get_known_universe(s);
}
ptr_vector<expr> const & proto_model::get_universe(sort * s) const {
ptr_vector<expr> & tmp = const_cast<proto_model*>(this)->m_tmp;
tmp.reset();
obj_hashtable<expr> const & u = get_known_universe(s);
obj_hashtable<expr>::iterator it = u.begin();
obj_hashtable<expr>::iterator end = u.end();
for (; it != end; ++it)
tmp.push_back(*it);
return tmp;
}
unsigned proto_model::get_num_uninterpreted_sorts() const {
return m_user_sort_factory->get_num_sorts();
}
sort * proto_model::get_uninterpreted_sort(unsigned idx) const {
SASSERT(idx < get_num_uninterpreted_sorts());
return m_user_sort_factory->get_sort(idx);
}
/**
\brief Return true if the given sort is uninterpreted and has a finite interpretation
in the model.
*/
bool proto_model::is_finite(sort * s) const {
return m_manager.is_uninterp(s) && m_user_sort_factory->is_finite(s);
}
expr * proto_model::get_some_value(sort * s) {
family_id fid = s->get_family_id();
if (fid == null_family_id) {
return m_user_sort_factory->get_some_value(s);
}
value_factory * f = get_factory(fid);
if (f)
return f->get_some_value(s);
// there is no factory for the family id, then assume s is uninterpreted.
return m_user_sort_factory->get_some_value(s);
}
bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) {
family_id fid = s->get_family_id();
if (fid == null_family_id) {
return m_user_sort_factory->get_some_values(s, v1, v2);
}
value_factory * f = get_factory(fid);
if (f)
return f->get_some_values(s, v1, v2);
else
return false;
}
expr * proto_model::get_fresh_value(sort * s) {
family_id fid = s->get_family_id();
if (fid == null_family_id)
return m_user_sort_factory->get_fresh_value(s);
value_factory * f = get_factory(fid);
if (f)
return f->get_fresh_value(s);
else
// Use user_sort_factory if the theory has no support for model construnction.
// This is needed when dummy theories are used for arithmetic or arrays.
return m_user_sort_factory->get_fresh_value(s);
}
void proto_model::register_value(expr * n) {
sort * s = m_manager.get_sort(n);
family_id fid = s->get_family_id();
if (fid == null_family_id) {
m_user_sort_factory->register_value(n);
}
else {
value_factory * f = get_factory(fid);
if (f)
f->register_value(n);
}
}
bool proto_model::is_array_value(expr * v) const {
return is_app_of(v, m_afid, OP_AS_ARRAY);
}
void proto_model::compress() {
ptr_vector<func_decl>::iterator it = m_func_decls.begin();
ptr_vector<func_decl>::iterator end = m_func_decls.end();
for (; it != end; ++it) {
func_decl * f = *it;
func_interp * fi = get_func_interp(f);
SASSERT(fi != 0);
fi->compress();
}
}
/**
\brief Complete the interpretation fi of f if it is partial.
If f does not have an interpretation in the given model, then this is a noop.
*/
void proto_model::complete_partial_func(func_decl * f) {
func_interp * fi = get_func_interp(f);
if (fi && fi->is_partial()) {
expr * else_value = 0;
#if 0
// For UFBV benchmarks, setting the "else" to false is not a good idea.
// TODO: find a permanent solution. A possibility is to add another option.
if (m_manager.is_bool(f->get_range())) {
else_value = m_manager.mk_false();
}
else {
else_value = fi->get_max_occ_result();
if (else_value == 0)
else_value = get_some_value(f->get_range());
}
#else
else_value = fi->get_max_occ_result();
if (else_value == 0)
else_value = get_some_value(f->get_range());
#endif
fi->set_else(else_value);
}
}
/**
\brief Set the (else) field of function interpretations...
*/
void proto_model::complete_partial_funcs() {
if (m_params.m_model_partial)
return;
ptr_vector<func_decl>::iterator it = m_func_decls.begin();
ptr_vector<func_decl>::iterator end = m_func_decls.end();
for (; it != end; ++it)
complete_partial_func(*it);
}
model * proto_model::mk_model() {
TRACE("proto_model", tout << "mk_model\n"; model_v2_pp(tout, *this););
model * m = alloc(model, m_manager);
decl2expr::iterator it1 = m_interp.begin();
decl2expr::iterator end1 = m_interp.end();
for (; it1 != end1; ++it1) {
m->register_decl(it1->m_key, it1->m_value);
}
decl2finterp::iterator it2 = m_finterp.begin();
decl2finterp::iterator end2 = m_finterp.end();
for (; it2 != end2; ++it2) {
m->register_decl(it2->m_key, it2->m_value);
}
m_finterp.reset(); // m took the ownership of the func_interp's
unsigned sz = get_num_uninterpreted_sorts();
for (unsigned i = 0; i < sz; i++) {
sort * s = get_uninterpreted_sort(i);
TRACE("proto_model", tout << "copying uninterpreted sorts...\n" << mk_pp(s, m_manager) << "\n";);
ptr_buffer<expr> buf;
obj_hashtable<expr> const & u = get_known_universe(s);
obj_hashtable<expr>::iterator it = u.begin();
obj_hashtable<expr>::iterator end = u.end();
for (; it != end; ++it) {
buf.push_back(*it);
}
m->register_usort(s, buf.size(), buf.c_ptr());
}
return m;
}