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

662 lines
23 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
user_smt_theory.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-05-22.
Revision History:
--*/
#include"smt_context.h"
#include"user_smt_theory.h"
#include"ast_pp.h"
#include"smt_model_generator.h"
#include"stats.h"
#include"warning.h"
namespace smt {
//
// value factory for user sorts.
//
// NB. This value factory for user theories
// does not address theories where model
// values are structured objects such as
// arrays, records, or data-types.
//
class user_smt_theory_factory : public simple_factory<unsigned> {
app* mk_value_core(unsigned const& val, sort* s) {
return m_manager.mk_model_value(val, s);
}
public:
user_smt_theory_factory(ast_manager& m, family_id fid):
simple_factory<unsigned>(m, fid)
{}
};
user_theory::user_theory(ast_manager & m, front_end_params const& p, void * ext_context, void * ext_data, char const * name, family_id fid, user_decl_plugin * dp, user_simplifier_plugin * sp):
theory(fid),
m_params(p),
m_ext_context(ext_context),
m_ext_data(ext_data),
m_name(name),
m_simplify_axioms(false),
m_decl_plugin(dp),
m_simplifier_plugin(sp),
m_find(*this),
m_trail_stack(*this),
m_asserted_axioms(m),
m_persisted_axioms(m),
m_persisted_axioms_qhead(0),
m_delete_fptr(0),
m_new_app_fptr(0),
m_new_elem_fptr(0),
m_init_search_fptr(0),
m_push_fptr(0),
m_pop_fptr(0),
m_restart_fptr(0),
m_reset_fptr(0),
m_final_check_fptr(0),
m_new_eq_fptr(0),
m_new_diseq_fptr(0),
m_new_assignment_fptr(0),
m_new_relevant_fptr(0),
m_mk_fresh_fptr(0),
m_delete_invoking(false),
m_new_app_invoking(false),
m_new_elem_invoking(false),
m_init_search_invoking(false),
m_push_invoking(false),
m_pop_invoking(false),
m_restart_invoking(false),
m_reset_invoking(false),
m_final_check_invoking(false),
m_new_eq_invoking(false),
m_new_diseq_invoking(false),
m_new_assignment_invoking(false),
m_new_relevant_invoking(false) {
}
user_theory::~user_theory() {
if (m_delete_fptr != 0) {
flet<bool> i(m_delete_invoking, true);
m_delete_fptr(this);
}
}
theory * user_theory::mk_fresh(context * new_ctx) {
if (m_mk_fresh_fptr == 0) {
throw default_exception("The mk_fresh_ext_data callback was not set for user theory, you must use Z3_theory_set_mk_fresh_ext_data_callback");
return 0;
}
user_simplifier_plugin * new_sp = static_cast<user_simplifier_plugin*>(new_ctx->get_simplifier().get_plugin(get_family_id()));
SASSERT(new_sp != 0);
user_theory * new_th = alloc(user_theory, get_manager(), new_ctx->get_fparams(), m_ext_context, m_mk_fresh_fptr(this), get_name(), get_family_id(),
m_decl_plugin, new_sp);
new_sp->set_owner(new_th);
new_th->m_delete_fptr = m_delete_fptr;
new_th->m_new_app_fptr = m_new_app_fptr;
new_th->m_new_elem_fptr = m_new_elem_fptr;
new_th->m_init_search_fptr = m_init_search_fptr;
new_th->m_push_fptr = m_push_fptr;
new_th->m_pop_fptr = m_pop_fptr;
new_th->m_restart_fptr = m_restart_fptr;
new_th->m_reset_fptr = m_reset_fptr;
new_th->m_final_check_fptr = m_final_check_fptr;
new_th->m_new_eq_fptr = m_new_eq_fptr;
new_th->m_new_diseq_fptr = m_new_diseq_fptr;
new_th->m_new_assignment_fptr = m_new_assignment_fptr;
new_th->m_new_relevant_fptr = m_new_relevant_fptr;
new_th->m_mk_fresh_fptr = m_mk_fresh_fptr;
return new_th;
}
void user_theory::assert_axiom_core(app* a) {
if (m_asserted_axiom_set.contains(a)) {
return;
}
m_asserted_axiom_set.insert(a);
m_asserted_axioms.push_back(a);
if (m_params.m_user_theory_persist_axioms) {
m_persisted_axioms.push_back(a);
}
}
/**
TODO: discuss semantics of assert_axiom.
Should we simplify axiom of not?
*/
void user_theory::assert_axiom(ast * axiom) {
++m_stats.m_num_user_axioms;
TRACE("user_smt_theory", tout << mk_pp(axiom, get_manager()) << "\n";);
if (!is_expr(axiom)) {
throw default_exception("invalid expression");
}
if (!get_manager().is_bool(to_expr(axiom))) {
throw default_exception("invalid theory axiom: axioms must have Boolean sort");
}
if (!m_new_eq_invoking &&
!m_new_diseq_invoking &&
!m_new_assignment_invoking &&
!m_new_relevant_invoking &&
!m_final_check_invoking) {
throw default_exception("theory axioms can only be invoked during callbacks "
"for new (dis)equalities/assignments and final check");
}
context & ctx = get_context();
ast_manager & m = get_manager();
if (!is_app(axiom) || !to_app(axiom)->is_ground() ||
ctx.get_fparams().m_user_theory_preprocess_axioms) {
asserted_formulas asf(m, ctx.get_fparams());
asf.assert_expr(to_app(axiom));
asf.reduce();
unsigned sz = asf.get_num_formulas();
unsigned qhead = asf.get_qhead();
while (qhead < sz) {
expr * f = asf.get_formula(qhead);
assert_axiom_core(to_app(f));
++qhead;
}
}
else {
if (!m_simplify_axioms) {
m_simplifier_plugin->enable(false);
}
expr_ref s_axiom(m);
proof_ref pr(m);
simplifier & s = ctx.get_simplifier();
s(to_app(axiom), s_axiom, pr);
if (!is_app(s_axiom)) {
throw default_exception("invalid theory axiom: axioms must be applications");
}
axiom = s_axiom;
m_simplifier_plugin->enable(true);
assert_axiom_core(to_app(axiom));
}
}
void user_theory::assume_eq(ast * _lhs, ast * _rhs) {
if (!is_expr(_lhs) || !is_expr(_rhs)) {
throw default_exception("assume_eq must take expressions as arguments");
}
expr* lhs = to_expr(_lhs);
expr* rhs = to_expr(_rhs);
ast_manager& m = get_manager();
context& ctx = get_context();
if (m.is_true(rhs)) {
std::swap(lhs, rhs);
}
if (m.is_true(lhs)) {
theory_var v2 = mk_var(rhs);
if (v2 == null_theory_var) {
throw default_exception("invalid assume eq: lhs or rhs is not a theory term");
}
bool_var bv = ctx.get_bool_var(rhs);
ctx.set_true_first_flag(bv);
ctx.mark_as_relevant(get_enode(v2));
return;
}
if (m.is_bool(lhs)) {
throw default_exception("assume_eq on Booleans must take 'true' as one of the arguments");
}
theory_var v1 = mk_var(lhs);
theory_var v2 = mk_var(rhs);
if (v1 == null_theory_var || v2 == null_theory_var) {
throw default_exception("invalid assume eq: lhs or rhs is not a theory term");
}
ctx.assume_eq(get_enode(v1), get_enode(v2));
}
void user_theory::reset_propagation_queues() {
m_new_eqs.reset();
m_new_diseqs.reset();
m_new_assignments.reset();
m_new_relevant_apps.reset();
}
theory_var user_theory::get_var(ast * n) const {
if (!is_app(n))
return null_theory_var;
context & ctx = get_context();
if (ctx.e_internalized(to_app(n))) {
enode * e = ctx.get_enode(to_app(n));
return e->get_th_var(get_id());
}
return null_theory_var;
}
theory_var user_theory::mk_var(ast * n) {
theory_var v = get_var(n);
if (v != null_theory_var || !is_app(n)) {
return v;
}
app* a = to_app(n);
if (a->get_family_id() == get_id() &&
internalize_term(a)) {
return mk_var(get_context().get_enode(a));
}
return v;
}
ast * user_theory::get_root(ast * n) const {
theory_var v = get_var(n);
if (v != null_theory_var) {
theory_var r = m_find.find(v);
return get_ast(r);
}
return n;
}
ast * user_theory::get_next(ast * n) const {
theory_var v = get_var(n);
if (v != null_theory_var) {
theory_var r = m_find.next(v);
return get_ast(r);
}
return n;
}
void user_theory::shrink_use_list(unsigned sz) {
SASSERT(m_use_list.size() >= sz);
std::for_each(m_use_list.begin() + sz, m_use_list.end(), delete_proc<ptr_vector<app> >());
m_use_list.shrink(sz);
}
ptr_vector<app> * user_theory::get_non_null_use_list(theory_var v) {
SASSERT(v != null_theory_var);
if (m_use_list[v] == 0)
m_use_list[v] = alloc(ptr_vector<app>);
return m_use_list[v];
}
unsigned user_theory::get_num_parents(ast * n) const {
theory_var v = get_var(n);
if (v != null_theory_var && m_use_list[v] != 0)
return m_use_list[v]->size();
return 0;
}
ast * user_theory::get_parent(ast * n, unsigned i) const {
theory_var v = get_var(n);
if (v != null_theory_var && m_use_list[v] != 0)
return m_use_list[v]->get(i, 0);
return 0;
}
theory_var user_theory::mk_var(enode * n) {
if (is_attached_to_var(n))
return n->get_th_var(get_id());
theory_var r = theory::mk_var(n);
theory_var r2 = m_find.mk_var();
m_use_list.push_back(0);
SASSERT(r == r2);
get_context().attach_th_var(n, this, r);
if (m_new_elem_fptr != 0) {
flet<bool> invoking(m_new_elem_invoking, true);
m_new_elem_fptr(this, n->get_owner());
}
return r;
}
bool user_theory::internalize_atom(app * atom, bool gate_ctx) {
return internalize_term(atom);
}
bool user_theory::internalize_term(app * term) {
context & ctx = get_context();
unsigned num_args = term->get_num_args();
for (unsigned i = 0; i < num_args; i++)
ctx.internalize(term->get_arg(i), false);
// the internalization of the arguments may trigger the internalization of term.
if (ctx.e_internalized(term))
return true;
m_parents.push_back(term);
enode * e = ctx.mk_enode(term, false, get_manager().is_bool(term), true);
if (get_manager().is_bool(term)) {
bool_var bv = ctx.mk_bool_var(term);
ctx.set_var_theory(bv, get_id());
ctx.set_enode_flag(bv, true);
}
// make sure every argument is attached to a theory variable...
for (unsigned i = 0; i < num_args; i++) {
enode * arg = e->get_arg(i);
theory_var v_arg = mk_var(arg);
ptr_vector<app> * arg_use_list = get_non_null_use_list(v_arg);
arg_use_list->push_back(term);
m_trail_stack.push(push_back_trail<user_theory, app *, false>(*arg_use_list));
}
if (m_new_app_fptr != 0) {
flet<bool> invoking(m_new_app_invoking, true);
m_new_app_fptr(this, term);
}
return true;
}
void user_theory::apply_sort_cnstr(enode * n, sort * s) {
mk_var(n);
}
void user_theory::assign_eh(bool_var v, bool is_true) {
m_new_assignments.push_back(v);
}
void user_theory::new_eq_eh(theory_var v1, theory_var v2) {
m_new_eqs.push_back(var_pair(v1, v2));
}
void user_theory::new_diseq_eh(theory_var v1, theory_var v2) {
m_new_diseqs.push_back(var_pair(v1, v2));
}
void user_theory::relevant_eh(app * n) {
m_new_relevant_apps.push_back(n);
}
void user_theory::push_scope_eh() {
SASSERT(m_new_assignments.empty());
SASSERT(m_new_eqs.empty());
SASSERT(m_new_diseqs.empty());
SASSERT(m_new_relevant_apps.empty());
theory::push_scope_eh();
m_trail_stack.push_scope();
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_asserted_axioms_old_sz = m_asserted_axioms.size();
s.m_parents_old_sz = m_parents.size();
if (m_push_fptr != 0) {
flet<bool> invoke(m_push_invoking, true);
m_push_fptr(this);
}
}
void user_theory::pop_scope_eh(unsigned num_scopes) {
reset_propagation_queues();
if (m_pop_fptr != 0) {
for (unsigned i = 0; i < num_scopes; i++) {
flet<bool> invoke(m_pop_invoking, true);
m_pop_fptr(this);
}
}
unsigned new_lvl = m_scopes.size() - num_scopes;
scope & s = m_scopes[new_lvl];
m_parents.shrink(s.m_parents_old_sz);
unsigned curr_sz = m_asserted_axioms.size();
unsigned old_sz = s.m_asserted_axioms_old_sz;
for (unsigned i = old_sz; i < curr_sz; i++) {
m_asserted_axiom_set.erase(m_asserted_axioms.get(i));
}
m_asserted_axioms.shrink(old_sz);
m_scopes.shrink(new_lvl);
m_trail_stack.pop_scope(num_scopes);
shrink_use_list(get_old_num_vars(num_scopes));
theory::pop_scope_eh(num_scopes);
}
void user_theory::restart_eh() {
if (m_restart_fptr != 0) {
flet<bool> invoke(m_restart_invoking, true);
m_restart_fptr(this);
}
}
void user_theory::init_search_eh() {
if (m_init_search_fptr != 0) {
flet<bool> invoke(m_init_search_invoking, true);
m_init_search_fptr(this);
}
}
final_check_status user_theory::final_check_eh() {
if (m_final_check_fptr != 0) {
unsigned old_sz = m_asserted_axioms.size();
flet<bool> invoke(m_final_check_invoking, true);
Z3_bool r = m_final_check_fptr(this);
if (old_sz != m_asserted_axioms.size()) {
assert_axioms_into_context(old_sz);
return r ? FC_CONTINUE : FC_GIVEUP;
}
return r ? FC_DONE : FC_GIVEUP;
}
return FC_DONE;
}
bool user_theory::can_propagate() {
return
(m_persisted_axioms.size() > m_persisted_axioms_qhead) ||
!m_new_eqs.empty() ||
!m_new_diseqs.empty() ||
!m_new_relevant_apps.empty() ||
!m_new_assignments.empty();
}
literal user_theory::internalize_literal(expr * arg) {
context & ctx = get_context();
ast_manager& m = get_manager();
if (is_app(arg) && m.is_not(arg)) {
expr * arg_arg = to_app(arg)->get_arg(0);
if (!ctx.b_internalized(arg_arg))
ctx.internalize(arg_arg, true);
return literal(ctx.get_bool_var(arg_arg), true);
}
else if (m.is_false(arg)) {
return false_literal;
}
else if (m.is_true(arg)) {
return true_literal;
}
else {
if (!ctx.b_internalized(arg))
ctx.internalize(arg, true);
return literal(ctx.get_bool_var(arg));
}
}
void user_theory::assert_axioms_into_context(unsigned old_sz) {
for (unsigned i = old_sz; i < m_asserted_axioms.size(); i++) {
expr * axiom = m_asserted_axioms.get(i);
assert_axiom_into_context(axiom);
}
}
void user_theory::mark_as_relevant(literal l) {
if (l == false_literal || l == true_literal) return;
get_context().mark_as_relevant(l);
}
void user_theory::assert_axiom_into_context(expr * axiom) {
TRACE("user_smt_theory", tout << mk_pp(axiom, get_manager()) << "\n";);
ast_manager & m = get_manager();
context & ctx = get_context();
if (m.is_or(axiom)) {
literal_buffer lits;
unsigned num_args = to_app(axiom)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
lits.push_back(internalize_literal(to_app(axiom)->get_arg(i)));
mark_as_relevant(lits.back());
}
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
}
else {
literal l = internalize_literal(axiom);
mark_as_relevant(l);
ctx.mk_th_axiom(get_id(), 1, &l);
}
}
void user_theory::propagate() {
unsigned old_sz = m_asserted_axioms.size();
if (m_persisted_axioms_qhead < m_persisted_axioms.size()) {
get_context().push_trail(value_trail<context, unsigned>(m_persisted_axioms_qhead));
for (; m_persisted_axioms_qhead < m_persisted_axioms.size(); ++m_persisted_axioms_qhead) {
m_asserted_axioms.push_back(m_persisted_axioms[m_persisted_axioms_qhead].get());
}
}
do {
for (unsigned i = 0; i < m_new_eqs.size(); i++) {
var_pair & p = m_new_eqs[i];
if (m_new_eq_fptr != 0) {
++m_stats.m_num_eq;
flet<bool> invoke(m_new_eq_invoking, true);
m_new_eq_fptr(this, get_app(p.first), get_app(p.second));
}
m_find.merge(p.first, p.second);
}
m_new_eqs.reset();
if (m_new_diseq_fptr != 0) {
for (unsigned i = 0; i < m_new_diseqs.size(); i++) {
++m_stats.m_num_diseq;
var_pair & p = m_new_diseqs[i];
flet<bool> invoke(m_new_diseq_invoking, true);
m_new_diseq_fptr(this, get_app(p.first), get_app(p.second));
}
}
m_new_diseqs.reset();
if (m_new_assignment_fptr != 0) {
context & ctx = get_context();
for (unsigned i = 0; i < m_new_assignments.size(); i++) {
++m_stats.m_num_assignment;
bool_var bv = m_new_assignments[i];
lbool val = ctx.get_assignment(bv);
SASSERT(val != l_undef);
flet<bool> invoke(m_new_assignment_invoking, true);
m_new_assignment_fptr(this, to_app(ctx.bool_var2expr(bv)), val == l_true);
}
}
m_new_assignments.reset();
if (m_new_relevant_fptr != 0) {
for (unsigned i = 0; i < m_new_relevant_apps.size(); i++) {
flet<bool> invoke(m_new_relevant_invoking, true);
m_new_relevant_fptr(this, m_new_relevant_apps[i]);
}
}
m_new_relevant_apps.reset();
assert_axioms_into_context(old_sz);
old_sz = m_asserted_axioms.size();
}
while (!m_new_eqs.empty() || !m_new_diseqs.empty() || !m_new_relevant_apps.empty() || !m_new_assignments.empty());
}
void user_theory::flush_eh() {
reset(false);
}
void user_theory::reset_eh() {
reset(true);
}
void user_theory::reset(bool full_reset) {
if (m_reset_fptr != 0) {
flet<bool> invoke(m_reset_invoking, true);
m_reset_fptr(this);
}
m_trail_stack.reset();
reset_propagation_queues();
m_asserted_axioms.reset();
m_asserted_axiom_set.reset();
shrink_use_list(0);
m_parents.reset();
m_scopes.reset();
m_persisted_axioms.reset();
m_persisted_axioms_qhead = 0;
m_stats.reset();
if (full_reset)
theory::reset_eh();
}
void user_theory::display_statistics(std::ostream & out) const {
print_stat(out, "num. user eqs: ", m_stats.m_num_eq);
print_stat(out, "num. user diseq: ", m_stats.m_num_diseq);
print_stat(out, "num. assignments: ", m_stats.m_num_assignment);
print_stat(out, "num. user axioms: ", m_stats.m_num_user_axioms);
}
void user_theory::display_istatistics(std::ostream & out) const {
out << "NUM_USER_EQS " << m_stats.m_num_eq << "\n";
out << "NUM_USER_DISEQ " << m_stats.m_num_diseq << "\n";
out << "NUM_ASSIGNMENTS " << m_stats.m_num_assignment << "\n";
out << "NUM_USER_AXIOMS " << m_stats.m_num_user_axioms << "\n";
}
class user_smt_model_value_proc : public model_value_proc {
func_decl_ref m_decl;
public:
user_smt_model_value_proc(ast_manager& m, func_decl* f) : m_decl(f, m) {}
virtual app * mk_value(model_generator & mg, ptr_vector<expr> & values) {
ast_manager& m = mg.get_manager();
return m.mk_app(m_decl, values.size(), values.c_ptr());
}
};
bool user_theory::build_models() const {
return true;
}
void user_theory::init_model(model_generator & m) {
m.register_factory(alloc(user_smt_theory_factory, get_manager(), get_id()));
}
void user_theory::finalize_model(model_generator &) {
// No-op
}
model_value_proc * user_theory::mk_value(enode * n, model_generator & mg) {
ast_manager& m = get_manager();
func_decl* f = n->get_decl();
if (m_decl_plugin->is_value(f)) {
return alloc(user_smt_model_value_proc, m, n->get_decl());
}
else {
return mg.mk_model_value(n);
}
}
bool user_theory::get_value(enode * n, expr_ref & r) {
return false;
}
char const * user_theory::get_name() const {
return m_name.c_str();
}
void user_theory::display(std::ostream & out) const {
out << "Theory " << get_name() << ":\n";
}
user_theory * mk_user_theory(solver & _s, void * ext_context, void * ext_data, char const * name) {
context & ctx = _s.kernel(); // HACK
symbol _name(name);
ast_manager & m = ctx.get_manager();
family_id fid = m.get_family_id(_name);
user_decl_plugin * dp = alloc(user_decl_plugin);
m.register_plugin(fid, dp);
simplifier & s = ctx.get_simplifier();
user_simplifier_plugin * sp = alloc(user_simplifier_plugin, _name, m);
s.register_plugin(sp);
user_theory * th = alloc(user_theory, m, ctx.get_fparams(), ext_context, ext_data, name, fid, dp, sp);
ctx.register_plugin(th);
sp->set_owner(th);
return th;
}
};