3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-13 12:28:44 +00:00
z3/src/smt/smt_model_generator.cpp
Nikolaj Bjorner 0e881e7abb fix #7584
2025-03-15 13:33:08 -07:00

535 lines
22 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smt_model_generator.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-10-29.
Revision History:
--*/
#include "util/ref_util.h"
#include "ast/for_each_expr.h"
#include "ast/ast_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/array_decl_plugin.h"
#include "smt/smt_context.h"
#include "smt/smt_model_generator.h"
#include "smt/proto_model/proto_model.h"
#include "model/model_v2_pp.h"
namespace smt {
void fresh_value_proc::get_dependencies(buffer<model_value_dependency>& result) {
result.push_back(model_value_dependency(m_value));
}
std::ostream& operator<<(std::ostream& out, model_value_dependency const& src) {
if (src.is_fresh_value()) return out << "fresh!" << src.get_value()->get_idx() << "#" << src.get_value()->get_src()->get_id();
else return out << "#" << src.get_enode()->get_owner_id();
}
model_generator::model_generator(ast_manager & m):
m(m),
m_context(nullptr),
m_fresh_idx(1),
m_asts(m),
m_model(nullptr) {
}
model_generator::~model_generator() {
dec_ref_collection_values(m, m_hidden_ufs);
}
void model_generator::reset() {
m_extra_fresh_values.reset();
m_fresh_idx = 1;
m_root2value.reset();
m_asts.reset();
m_model = nullptr;
}
void model_generator::init_model() {
SASSERT(!m_model);
// PARAM-TODO smt_params ---> params_ref
m_model = alloc(proto_model, m); // , m_context->get_fparams());
for (theory* th : m_context->theories()) {
TRACE("model_generator_bug", tout << "init_model for theory: " << th->get_name() << "\n";);
th->init_model(*this);
}
}
/**
\brief Create the boolean assignment.
*/
void model_generator::mk_bool_model() {
unsigned sz = m_context->get_num_b_internalized();
for (unsigned i = 0; i < sz; i++) {
expr * p = m_context->get_b_internalized(i);
if (is_uninterp_const(p) && m_context->is_relevant(p)) {
SASSERT(m.is_bool(p));
func_decl * d = to_app(p)->get_decl();
lbool val = m_context->get_assignment(p);
expr * v = val == l_true ? m.mk_true() : m.mk_false();
m_model->register_decl(d, v);
}
}
}
/**
\brief Create the mapping root2proc: enode-root -> model_value_proc, and roots.
Store the new model_value_proc at procs.
*/
void model_generator::mk_value_procs(obj_map<enode, model_value_proc *> & root2proc, ptr_vector<enode> & roots,
ptr_vector<model_value_proc> & procs) {
for (enode * r : m_context->enodes()) {
if (r == r->get_root() && (m_context->is_relevant(r) || m.is_value(r->get_expr()))) {
roots.push_back(r);
sort * s = r->get_sort();
model_value_proc * proc = nullptr;
if (m.is_bool(s)) {
CTRACE("model", m_context->get_assignment(r) == l_undef,
tout << mk_pp(r->get_expr(), m) << "\n";);
if (m_context->get_assignment(r) == l_true)
proc = alloc(expr_wrapper_proc, m.mk_true());
else
proc = alloc(expr_wrapper_proc, m.mk_false());
}
else if (m.is_model_value(r->get_expr()))
proc = alloc(expr_wrapper_proc, r->get_expr());
else {
family_id fid = s->get_family_id();
theory * th = m_context->get_theory(fid);
if (th && th->build_models()) {
if (r->get_th_var(th->get_id()) != null_theory_var) {
proc = th->mk_value(r, *this);
SASSERT(proc);
}
else {
TRACE("model", tout << "creating fresh value for #"
<< r->get_expr_id() << " "
<< mk_bounded_pp(r->get_expr(), m) << " "
<< mk_pp(r->get_sort(), m) << "\n";);
proc = alloc(fresh_value_proc, mk_extra_fresh_value(r->get_expr(), r->get_sort()));
}
}
else {
proc = mk_model_value(r);
SASSERT(proc);
}
}
SASSERT(proc);
procs.push_back(proc);
root2proc.insert(r, proc);
}
}
}
model_value_proc* model_generator::mk_model_value(enode* r) {
SASSERT(r == r->get_root());
expr * n = r->get_expr();
if (!m.is_model_value(n)) {
sort * s = r->get_sort();
n = m_model->get_fresh_value(s);
CTRACE("model", n == 0,
tout << mk_pp(r->get_expr(), m) << "\nsort:\n" << mk_pp(s, m) << "\n";
tout << "is_finite: " << m_model->is_finite(s) << "\n";);
}
return alloc(expr_wrapper_proc, to_app(n));
}
#define White 0
#define Grey 1
#define Black 2
static int get_color(source2color const & colors, source const & s) {
int color;
if (colors.find(s, color))
return color;
return White;
}
static void set_color(source2color & colors, source const & s, int c) {
colors.insert(s, c);
}
static void visit_child(source const & s, source2color & colors, svector<source> & todo, bool & visited) {
if (get_color(colors, s) == White) {
todo.push_back(s);
visited = false;
}
}
bool model_generator::visit_children(source const & src,
ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors,
obj_hashtable<sort> & already_traversed,
svector<source> & todo) {
if (src.is_fresh_value()) {
// there is an implicit dependency between a fresh value stub of
// sort S and the root enodes of sort S that are not associated with fresh values.
//
sort * s = src.get_value()->get_sort();
if (already_traversed.contains(s))
return true;
bool visited = true;
TRACE("mg_top_sort", tout << "fresh value of sort " << mk_pp(s, m) << "\n";
for (enode* r : roots)
if (r->get_sort() == s)
tout << mk_pp(r->get_expr(), m) << "\n";
);
for (enode * r : roots) {
if (r->get_sort() != s)
continue;
SASSERT(r == r->get_root());
if (root2proc[r]->is_fresh())
continue; // r is associated with a fresh value...
TRACE("mg_top_sort", tout << "fresh!" << src.get_value()->get_idx() << " -> #" << r->get_owner_id() << " " << mk_pp(r->get_sort(), m) << "\n";);
visit_child(source(r), colors, todo, visited);
TRACE("mg_top_sort", tout << "visited: " << visited << ", todo.size(): " << todo.size() << "\n";);
}
already_traversed.insert(s);
return visited;
}
SASSERT(!src.is_fresh_value());
enode * n = src.get_enode();
SASSERT(n == n->get_root());
bool visited = true;
model_value_proc * proc = root2proc[n];
buffer<model_value_dependency> dependencies;
proc->get_dependencies(dependencies);
for (model_value_dependency const& dep : dependencies) {
visit_child(dep, colors, todo, visited);
}
TRACE("mg_top_sort",
tout << "src: " << src << " ";
tout << mk_pp(n->get_expr(), m) << "\n";
for (model_value_dependency const& dep : dependencies) {
tout << "#" << n->get_owner_id() << " -> " << dep << " already visited: " << visited << "\n";
}
);
return visited;
}
void model_generator::process_source(source const & src,
ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
source2color & colors,
obj_hashtable<sort> & already_traversed,
svector<source> & todo,
svector<source> & sorted_sources) {
TRACE("mg_top_sort", tout << "process source, is_fresh: " << src.is_fresh_value() << " ";
tout << src << ", todo.size(): " << todo.size() << "\n";);
int color = get_color(colors, src);
SASSERT(color != Grey);
if (color == Black)
return;
SASSERT(color == White);
todo.push_back(src);
while (!todo.empty()) {
source curr = todo.back();
TRACE("mg_top_sort", tout << "current source, is_fresh: " << curr.is_fresh_value() << " ";
tout << curr << ", todo.size(): " << todo.size() << "\n";);
switch (get_color(colors, curr)) {
case White:
set_color(colors, curr, Grey);
visit_children(curr, roots, root2proc, colors, already_traversed, todo);
break;
case Grey:
// SASSERT(visit_children(curr, roots, root2proc, colors, already_traversed, todo));
set_color(colors, curr, Black);
TRACE("mg_top_sort", tout << "append " << curr << "\n";);
sorted_sources.push_back(curr);
break;
case Black:
todo.pop_back();
break;
default:
UNREACHABLE();
}
}
TRACE("mg_top_sort", tout << "END process_source, todo.size(): " << todo.size() << "\n";);
}
/**
\brief Topological sort of 'sources'. Store result in sorted_sources.
*/
void model_generator::top_sort_sources(ptr_vector<enode> const & roots,
obj_map<enode, model_value_proc *> const & root2proc,
svector<source> & sorted_sources) {
svector<source> todo;
source2color colors;
// The following 'set' of sorts is used to avoid traversing roots looking for enodes of sort S.
// That is, a sort S is in already_traversed, if all enodes of sort S in roots were already traversed.
obj_hashtable<sort> already_traversed;
// topological sort
// traverse all extra fresh values...
for (extra_fresh_value * f : m_extra_fresh_values) {
process_source(source(f), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
// traverse all enodes that are associated with fresh values...
for (enode* r : roots) {
if (root2proc[r]->is_fresh()) {
process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
}
for (enode * r : roots) {
process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources);
}
}
void model_generator::mk_values() {
obj_map<enode, model_value_proc *> root2proc;
ptr_vector<enode> roots;
ptr_vector<model_value_proc> procs;
scoped_reset _scoped_reset(*this, procs);
svector<source> sources;
buffer<model_value_dependency> dependencies;
expr_ref_vector dependency_values(m);
mk_value_procs(root2proc, roots, procs);
top_sort_sources(roots, root2proc, sources);
TRACE("sorted_sources",
for (source const& curr : sources) {
if (curr.is_fresh_value()) {
tout << curr << " " << mk_pp(curr.get_value()->get_sort(), m) << "\n";
}
else {
enode * n = curr.get_enode();
sort* s = n->get_sort();
SASSERT(n->get_root() == n);
tout << curr << " " << mk_pp(s, m);
tout << mk_bounded_pp(n->get_expr(), m) << " ";
tout << " is_fresh: " << root2proc[n]->is_fresh() << " - deps: ";
dependencies.reset();
model_value_proc* proc = root2proc[n];
SASSERT(proc);
proc->get_dependencies(dependencies);
for (auto const& d : dependencies)
tout << d << " ";
tout << "\n";
}
}
// m_context->display(tout);
);
for (source const& curr : sources) {
if (curr.is_fresh_value()) {
sort * s = curr.get_value()->get_sort();
TRACE("model_fresh_bug", tout << curr << " : " << mk_pp(s, m) << " " << curr.get_value()->get_value() << "\n";);
expr * val = m_model->get_fresh_value(s);
TRACE("model_fresh_bug", tout << curr << " := #" << (val == nullptr ? UINT_MAX : val->get_id()) << "\n";);
m_asts.push_back(val);
curr.get_value()->set_value(val);
}
else {
enode * n = curr.get_enode();
SASSERT(n->get_root() == n);
TRACE("mg_top_sort", tout << curr << "\n";);
app* val = nullptr;
if (m.is_value(n->get_expr()))
val = to_app(n->get_expr());
else {
dependencies.reset();
dependency_values.reset();
model_value_proc * proc = root2proc[n];
SASSERT(proc);
proc->get_dependencies(dependencies);
for (model_value_dependency const& d : dependencies) {
if (d.is_fresh_value()) {
expr* val = d.get_value()->get_value();
CTRACE("mg_top_sort", !val,
tout << "#" << n->get_owner_id() << " " <<
mk_pp(n->get_expr(), m) << " -> " << d << "\n";);
// there is a cyclic dependency for default(A), where A
// is an array of a datatype with the datatype using A.
if (!val)
val = m_model->get_some_value(d.get_value()->get_sort());
SASSERT(val);
dependency_values.push_back(val);
}
else {
enode * child = d.get_enode();
TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_expr(), m) << "): "
<< mk_pp(child->get_expr(), m) << " " << mk_pp(child->get_root()->get_expr(), m) << "\n";);
child = child->get_root();
dependency_values.push_back(m_root2value[child]);
}
}
val = proc->mk_value(*this, dependency_values);
}
register_value(val);
m_asts.push_back(val);
m_root2value.insert(n, val);
}
}
// send model
for (enode * n : m_context->enodes()) {
if (is_uninterp_const(n->get_expr()) && m_context->is_relevant(n)) {
func_decl * d = n->get_expr()->get_decl();
TRACE("mg_top_sort", tout << d->get_name() << " " << (m_hidden_ufs.contains(d)?"hidden":"visible") << "\n";);
if (m_hidden_ufs.contains(d)) continue;
expr * val = get_value(n);
m_model->register_decl(d, val);
}
}
}
model_generator::scoped_reset::scoped_reset(model_generator& mg, ptr_vector<model_value_proc>& procs):
mg(mg), procs(procs) {}
model_generator::scoped_reset::~scoped_reset() {
std::for_each(procs.begin(), procs.end(), delete_proc<model_value_proc>());
std::for_each(mg.m_extra_fresh_values.begin(), mg.m_extra_fresh_values.end(), delete_proc<extra_fresh_value>());
mg.m_extra_fresh_values.reset();
}
app * model_generator::get_value(enode * n) const {
return m_root2value[n->get_root()];
}
/**
\brief Return true if the interpretation of the function should be included in the model.
*/
bool model_generator::include_func_interp(func_decl * f) const {
family_id fid = f->get_family_id();
if (fid == null_family_id) return !m_hidden_ufs.contains(f);
if (fid == m.get_basic_family_id()) return false;
theory * th = m_context->get_theory(fid);
return !th || th->include_func_interp(f);
}
/**
\brief Create (partial) interpretation of function symbols.
The "else" is missing.
*/
void model_generator::mk_func_interps() {
unsigned sz = m_context->get_num_e_internalized();
for (unsigned i = 0; i < sz; i++) {
expr * t = m_context->get_e_internalized(i);
if (!m_context->is_relevant(t))
continue;
enode * n = m_context->get_enode(t);
unsigned num_args = n->get_num_args();
func_decl * f = n->get_decl();
if (num_args == 0 && include_func_interp(f)) {
m_model->register_decl(f, get_value(n));
}
else if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) {
ptr_buffer<expr> args;
expr * result = get_value(n);
SASSERT(result);
for (unsigned j = 0; j < num_args; j++) {
app * arg = get_value(n->get_arg(j));
SASSERT(arg);
args.push_back(arg);
}
func_interp * fi = m_model->get_func_interp(f);
if (fi == nullptr) {
fi = alloc(func_interp, m, f->get_arity());
m_model->register_decl(f, fi);
}
SASSERT(m_model->has_interpretation(f));
SASSERT(m_model->get_func_interp(f) == fi);
// The entry must be new because n->get_cg() == n
TRACE("model",
tout << "insert new entry for:\n" << mk_ismt2_pp(n->get_expr(), m) << "\nargs: ";
for (unsigned i = 0; i < num_args; i++) {
tout << "#" << n->get_arg(i)->get_owner_id() << " ";
}
tout << "\n";
for (expr* arg : args) {
tout << mk_pp(arg, m) << " ";
}
tout << "\n";
tout << "value: #" << n->get_owner_id() << "\n" << mk_ismt2_pp(result, m) << "\n";);
if (fi->get_entry(args.data()) == nullptr)
fi->insert_new_entry(args.data(), result);
}
}
}
extra_fresh_value * model_generator::mk_extra_fresh_value(expr * src, sort* s) {
extra_fresh_value * r = alloc(extra_fresh_value, src, s, m_fresh_idx);
m_fresh_idx++;
m_extra_fresh_values.push_back(r);
return r;
}
expr * model_generator::get_some_value(sort * s) {
SASSERT(m_model);
return m_model->get_some_value(s);
}
void model_generator::register_value(expr * val) {
SASSERT(m_model);
m_model->register_value(val);
}
void model_generator::finalize_theory_models() {
for (theory* th : m_context->theories())
th->finalize_model(*this);
}
void model_generator::register_existing_model_values() {
for (enode * r : m_context->enodes()) {
if (r == r->get_root() && m_context->is_relevant(r)) {
expr * n = r->get_expr();
if (m.is_model_value(n)) {
register_value(n);
}
}
}
}
void model_generator::register_factory(value_factory * f) {
m_model->register_factory(f);
}
void model_generator::register_macros() {
unsigned num = m_context->get_num_macros();
TRACE("model", tout << "num. macros: " << num << "\n";);
expr_ref v(m);
for (unsigned i = 0; i < num; i++) {
func_decl * f = m_context->get_macro_interpretation(i, v);
func_interp * fi = alloc(func_interp, m, f->get_arity());
fi->set_else(v);
TRACE("model", tout << f->get_name() << "\n" << mk_pp(v, m) << "\n";);
m_model->register_decl(f, fi);
}
}
proto_model * model_generator::mk_model() {
SASSERT(!m_model);
TRACE("model_verbose", m_context->display(tout););
init_model();
register_existing_model_values();
mk_bool_model();
mk_values();
mk_func_interps();
finalize_theory_models();
register_macros();
TRACE("model", model_v2_pp(tout, *m_model, true););
return m_model.get();
}
};