Skip to content

Commit

Permalink
user propagator
Browse files Browse the repository at this point in the history
Signed-off-by: Nikolaj Bjorner <[email protected]>
  • Loading branch information
NikolajBjorner committed Aug 23, 2020
1 parent 5e5ef50 commit 96f10b8
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 127 deletions.
2 changes: 1 addition & 1 deletion scripts/update_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1843,7 +1843,7 @@ def _to_pystr(s):
push_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p)
pop_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint)
fresh_eh_type = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
fresh_eh_type = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
fixed_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p)
final_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
Expand Down
2 changes: 2 additions & 0 deletions src/api/api_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ namespace api {
DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name()););
dealloc(val);
}
if (m_params.owns_manager())
m_manager.detach();
}

context::set_interruptable::set_interruptable(context & ctx, event_handler & i):
Expand Down
18 changes: 12 additions & 6 deletions src/api/api_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,9 +896,15 @@ extern "C" {
Z3_TRY;
RESET_ERROR_CODE();
init_solver(c, s);
std::function<void(void*)> _push = push_eh;
std::function<void(void*,unsigned)> _pop = pop_eh;
std::function<void*(void*)> _fresh = fresh_eh;
solver::push_eh_t _push = push_eh;
solver::pop_eh_t _pop = pop_eh;
solver::fresh_eh_t _fresh = [&](void * user_ctx, ast_manager& m, void*& _ctx) {
context_params params;
params.set_foreign_manager(&m);
auto* ctx = reinterpret_cast<Z3_context>(alloc(api::context, &params, false));
_ctx = ctx;
return fresh_eh(user_ctx, ctx);
};
to_solver_ref(s)->user_propagate_init(user_context, _push, _pop, _fresh);
Z3_CATCH;
}
Expand Down Expand Up @@ -955,11 +961,11 @@ extern "C" {
Z3_CATCH_RETURN(0);
}

void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned sz, unsigned const* ids, Z3_ast conseq) {
void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, Z3_ast conseq) {
Z3_TRY;
LOG_Z3_solver_propagate_consequence(c, s, sz, ids, conseq);
LOG_Z3_solver_propagate_consequence(c, s, num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, conseq);
RESET_ERROR_CODE();
reinterpret_cast<solver::propagate_callback*>(s)->propagate(sz, ids, to_expr(conseq));
reinterpret_cast<solver::propagate_callback*>(s)->propagate(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, to_expr(conseq));
Z3_CATCH;
}

Expand Down
106 changes: 77 additions & 29 deletions src/api/python/z3/z3.py
Original file line number Diff line number Diff line change
Expand Up @@ -10506,40 +10506,50 @@ def TransitiveClosure(f):


class PropClosures:
# import thread
def __init__(self):
self.bases = {}
# self.lock = thread.Lock()
self.lock = None

def set_threaded():
if self.lock is None:
import threading
self.lock = threading.thread.Lock()

def get(self, ctx):
# self.lock.acquire()
if self.lock: self.lock.acquire()
r = self.bases[ctx]
# self.lock.release()
if self.lock: self.lock.release()
return r

def set(self, ctx, r):
# self.lock.acquire()
if self.lock: self.lock.acquire()
self.bases[ctx] = r
# self.lock.release()
if self.lock: self.lock.release()

def insert(self, r):
# self.lock.acquire()
if self.lock: self.lock.acquire()
id = len(self.bases) + 3
self.bases[id] = r
# self.lock.release()
if self.lock: self.lock.release()
return id

_prop_closures = PropClosures()
_prop_closures = None

def ensure_prop_closures():
global _prop_closures
if _prop_closures is None:
_prop_closures = PropClosures()

def user_prop_push(ctx):
_prop_closures.get(ctx).push();

def user_prop_pop(ctx, num_scopes):
_prop_closures.get(ctx).pop(num_scopes)

def user_prop_fresh(ctx):
prop = _prop_closures.get(ctx)
new_prop = UsePropagateBase(None, prop.ctx)
def user_prop_fresh(id, ctx):
prop = _prop_closures.get(id)
_prop_closures.set_threaded()
new_prop = UsePropagateBase(None, ctx)
_prop_closures.set(new_prop.id, new_prop.fresh())
return ctypes.c_void_p(new_prop.id)

Expand Down Expand Up @@ -10577,66 +10587,104 @@ def user_prop_diseq(ctx, cb, x, y):

class UserPropagateBase:

#
# Either solver is set or ctx is set.
# Propagators that are created throuh callbacks
# to "fresh" inherit the context of that is supplied
# as argument to the callback.
# This context should not be deleted. It is owned by the solver.
#
def __init__(self, s, ctx = None):
assert s is None or ctx is None
ensure_prop_closures()
self.solver = s
self.ctx = s.ctx if s is not None else ctx
self._ctx = None
self.cb = None
self.id = _prop_closures.insert(self)
self.fixed = None
self.final = None
self.eq = None
self.diseq = None
if ctx:
self._ctx = Context()
Z3_del_context(self._ctx.ctx)
self._ctx.ctx = ctx
self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
if s:
Z3_solver_propagate_init(s.ctx.ref(),
Z3_solver_propagate_init(self.ctx_ref(),
s.solver,
ctypes.c_void_p(self.id),
_user_prop_push,
_user_prop_pop,
_user_prop_fresh)


def __del__(self):
if self._ctx:
self._ctx.ctx = None

def ctx(self):
if self._ctx:
return self._ctx
else:
return self.solver.ctx

def ctx_ref(self):
return self.ctx().ref()

def add_fixed(self, fixed):
assert not self.fixed
Z3_solver_propagate_fixed(self.ctx.ref(), self.solver.solver, _user_prop_fixed)
assert not self._ctx
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
self.fixed = fixed

def add_final(self, final):
assert not self.final
Z3_solver_propagate_final(self.ctx.ref(), self.solver.solver, _user_prop_final)
assert not self._ctx
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
self.final = final

def add_eq(self, eq):
assert not self.eq
Z3_solver_propagate_eq(self.ctx.ref(), self.solver.solver, _user_prop_eq)
assert not self._ctx
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
self.eq = eq

def add_diseq(self, diseq):
assert not self.diseq
Z3_solver_propagate_diseq(self.ctx.ref(), self.solver.solver, _user_prop_diseq)
assert not self._ctx
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
self.diseq = diseq

def push(self):
raise Z3Exception("push has not been overwritten")
raise Z3Exception("push needs to be overwritten")

def pop(self, num_scopes):
raise Z3Exception("pop has not been overwritten")
raise Z3Exception("pop needs to be overwritten")

def fresh(self):
raise Z3Exception("fresh has not been overwritten")
raise Z3Exception("fresh needs to be overwritten")

def add(self, e):
assert self.solver
return Z3_solver_propagate_register(self.ctx.ref(), self.solver.solver, e.ast)
assert not self._ctx
return Z3_solver_propagate_register(self.ctx_ref(), self.solver.solver, e.ast)

#
# Propagation can only be invoked as during a fixed-callback.
#
def propagate(self, ids, e):
sz = len(ids)
_ids = (ctypes.c_uint * sz)()
for i in range(sz):
def propagate(self, e, ids, eqs = []):
num_fixed = len(ids)
_ids = (ctypes.c_uint * num_fixed)()
for i in range(num_fixed):
_ids[i] = ids[i]
Z3_solver_propagate_consequence(self.ctx.ref(), ctypes.c_void_p(self.cb), sz, _ids, e.ast)
num_eqs = len(eqs)
_lhs = (ctypes.c_uint * num_eqs)()
_rhs = (ctypes.c_uint * num_eqs)()
for i in range(num_eqs):
_lhs[i] = eqs[i][0]
_rhs[i] = eqs[i][1]
Z3_solver_propagate_consequence(self.ctx_ref(), ctypes.c_void_p(self.cb), num_fixed, _ids, num_eqs, _lhs, _rhs, e.ast)

def conflict(self, ids):
self.propagate(ids, BoolVal(False, self.ctx))
self.propagate(ids, BoolVal(False, self.ctx_ref()))
6 changes: 3 additions & 3 deletions src/api/z3_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ typedef void Z3_error_handler(Z3_context c, Z3_error_code e);
*/
typedef void Z3_push_eh(void* ctx);
typedef void Z3_pop_eh(void* ctx, unsigned num_scopes);
typedef void* Z3_fresh_eh(void* ctx);
typedef void* Z3_fresh_eh(void* ctx, Z3_context new_context);
typedef void Z3_fixed_eh(void* ctx, Z3_solver_callback cb, unsigned id, Z3_ast value);
typedef void Z3_eq_eh(void* ctx, Z3_solver_callback cb, unsigned x, unsigned y);
typedef void Z3_final_eh(void* ctx, Z3_solver_callback cb);
Expand Down Expand Up @@ -6586,10 +6586,10 @@ extern "C" {
The callback adds a propagation consequence based on the fixed values of the
\c ids.
def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, UINT), _in(AST)))
def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, UINT), _in(UINT), _in_array(4, UINT), _in_array(4, UINT), _in(AST)))
*/

void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned sz, unsigned const* ids, Z3_ast conseq);
void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, Z3_ast conseq);

/**
\brief Check whether the assertions in a given solver are consistent or not.
Expand Down
15 changes: 2 additions & 13 deletions src/cmd_context/context_params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,6 @@ Module Name:
#include "solver/solver.h"

context_params::context_params() {
m_unsat_core = false;
m_model = true;
m_model_validate = false;
m_dump_models = false;
m_auto_config = true;
m_proof = false;
m_trace = false;
m_debug_ref_count = false;
m_smtlib2_compliant = false;
m_well_sorted_check = false;
m_timeout = UINT_MAX;
m_rlimit = 0;
m_statistics = false;
updt_params();
}

Expand Down Expand Up @@ -204,6 +191,8 @@ void context_params::get_solver_params(ast_manager const & m, params_ref & p, bo
}

ast_manager * context_params::mk_ast_manager() {
if (m_manager)
return m_manager;
ast_manager * r = alloc(ast_manager,
m_proof ? PGM_ENABLED : PGM_DISABLED,
m_trace ? m_trace_file_name.c_str() : nullptr);
Expand Down
31 changes: 17 additions & 14 deletions src/cmd_context/context_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ class context_params {
void set_bool(bool & opt, char const * param, char const * value);
void set_uint(unsigned & opt, char const * param, char const * value);

unsigned m_rlimit;
unsigned m_rlimit { 0 };
ast_manager* m_manager { nullptr };

public:
bool m_auto_config;
bool m_proof;
bool m_auto_config { true };
bool m_proof { false };
std::string m_dot_proof_file;
bool m_interpolants;
bool m_debug_ref_count;
bool m_trace;
bool m_debug_ref_count { false };
bool m_trace { false };
std::string m_trace_file_name;
bool m_well_sorted_check;
bool m_model;
bool m_model_validate;
bool m_dump_models;
bool m_unsat_core;
bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager.
unsigned m_timeout;
bool m_statistics;
bool m_well_sorted_check { false };
bool m_model { true };
bool m_model_validate { false };
bool m_dump_models { false };
bool m_unsat_core { false };
bool m_smtlib2_compliant { false }; // it must be here because it enable/disable the use of coercions in the ast_manager.
unsigned m_timeout { UINT_MAX } ;
bool m_statistics { false };

unsigned rlimit() const { return m_rlimit; }
context_params();
Expand Down Expand Up @@ -74,6 +74,9 @@ class context_params {
\brief Create an AST manager using this configuration.
*/
ast_manager * mk_ast_manager();

void set_foreign_manager(ast_manager* m) { m_manager = m; }
bool owns_manager() const { return m_manager != nullptr; }
};


26 changes: 20 additions & 6 deletions src/smt/smt_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,14 @@ namespace smt {

dst_ctx.setup_context(dst_ctx.m_fparams.m_auto_config);
dst_ctx.internalize_assertions();

dst_ctx.copy_user_propagator(src_ctx);

TRACE("smt_context",
src_ctx.display(tout);
dst_ctx.display(tout););
}


context::~context() {
flush();
m_asserted_formulas.finalize();
Expand All @@ -205,6 +206,19 @@ namespace smt {
}
}

void context::copy_user_propagator(context& src_ctx) {
if (!src_ctx.m_user_propagator)
return;
ast_translation tr(src_ctx.m, m, false);
auto* p = get_theory(m.mk_family_id("user_propagator"));
m_user_propagator = reinterpret_cast<user_propagator*>(p);
SASSERT(m_user_propagator);
for (unsigned i = 0; i < src_ctx.m_user_propagator->get_num_vars(); ++i) {
app* e = src_ctx.m_user_propagator->get_expr(i);
m_user_propagator->add_expr(tr(e));
}
}

context * context::mk_fresh(symbol const * l, smt_params * p, params_ref const& pa) {
context * new_ctx = alloc(context, m, p ? *p : m_fparams, pa);
new_ctx->m_is_auxiliary = true;
Expand Down Expand Up @@ -2950,10 +2964,10 @@ namespace smt {
}

void context::user_propagate_init(
void* ctx,
std::function<void(void*)>& push_eh,
std::function<void(void*, unsigned)>& pop_eh,
std::function<void*(void*)>& fresh_eh) {
void* ctx,
solver::push_eh_t& push_eh,
solver::pop_eh_t& pop_eh,
solver::fresh_eh_t& fresh_eh) {
setup_context(m_fparams.m_auto_config);
m_user_propagator = alloc(user_propagator, *this);
m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
Expand All @@ -2963,7 +2977,7 @@ namespace smt {
}

bool context::watches_fixed(enode* n) const {
return m_user_propagator && n->get_th_var(m_user_propagator->get_family_id()) != null_theory_var;
return m_user_propagator && m_user_propagator->has_fixed() && n->get_th_var(m_user_propagator->get_family_id()) != null_theory_var;
}

void context::assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain) {
Expand Down
Loading

0 comments on commit 96f10b8

Please sign in to comment.