3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 12:08:18 +00:00

user propagator

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-08-22 19:00:58 -07:00
parent 5e5ef50dbc
commit 96f10b8c1c
15 changed files with 219 additions and 127 deletions

View file

@ -1843,7 +1843,7 @@ _lib.Z3_set_error_handler.argtypes = [ContextObj, _error_handler_type]
push_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p) push_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p)
pop_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) 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) 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) final_eh_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)

View file

@ -116,6 +116,8 @@ namespace api {
DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name());); DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name()););
dealloc(val); dealloc(val);
} }
if (m_params.owns_manager())
m_manager.detach();
} }
context::set_interruptable::set_interruptable(context & ctx, event_handler & i): context::set_interruptable::set_interruptable(context & ctx, event_handler & i):

View file

@ -896,9 +896,15 @@ extern "C" {
Z3_TRY; Z3_TRY;
RESET_ERROR_CODE(); RESET_ERROR_CODE();
init_solver(c, s); init_solver(c, s);
std::function<void(void*)> _push = push_eh; solver::push_eh_t _push = push_eh;
std::function<void(void*,unsigned)> _pop = pop_eh; solver::pop_eh_t _pop = pop_eh;
std::function<void*(void*)> _fresh = fresh_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); to_solver_ref(s)->user_propagate_init(user_context, _push, _pop, _fresh);
Z3_CATCH; Z3_CATCH;
} }
@ -955,11 +961,11 @@ extern "C" {
Z3_CATCH_RETURN(0); 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; 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(); 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; Z3_CATCH;
} }

View file

@ -10506,30 +10506,39 @@ def TransitiveClosure(f):
class PropClosures: class PropClosures:
# import thread
def __init__(self): def __init__(self):
self.bases = {} 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): def get(self, ctx):
# self.lock.acquire() if self.lock: self.lock.acquire()
r = self.bases[ctx] r = self.bases[ctx]
# self.lock.release() if self.lock: self.lock.release()
return r return r
def set(self, ctx, r): def set(self, ctx, r):
# self.lock.acquire() if self.lock: self.lock.acquire()
self.bases[ctx] = r self.bases[ctx] = r
# self.lock.release() if self.lock: self.lock.release()
def insert(self, r): def insert(self, r):
# self.lock.acquire() if self.lock: self.lock.acquire()
id = len(self.bases) + 3 id = len(self.bases) + 3
self.bases[id] = r self.bases[id] = r
# self.lock.release() if self.lock: self.lock.release()
return id 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): def user_prop_push(ctx):
_prop_closures.get(ctx).push(); _prop_closures.get(ctx).push();
@ -10537,9 +10546,10 @@ def user_prop_push(ctx):
def user_prop_pop(ctx, num_scopes): def user_prop_pop(ctx, num_scopes):
_prop_closures.get(ctx).pop(num_scopes) _prop_closures.get(ctx).pop(num_scopes)
def user_prop_fresh(ctx): def user_prop_fresh(id, ctx):
prop = _prop_closures.get(ctx) prop = _prop_closures.get(id)
new_prop = UsePropagateBase(None, prop.ctx) _prop_closures.set_threaded()
new_prop = UsePropagateBase(None, ctx)
_prop_closures.set(new_prop.id, new_prop.fresh()) _prop_closures.set(new_prop.id, new_prop.fresh())
return ctypes.c_void_p(new_prop.id) return ctypes.c_void_p(new_prop.id)
@ -10577,66 +10587,104 @@ _user_prop_diseq = eq_eh_type(user_prop_diseq)
class UserPropagateBase: 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): def __init__(self, s, ctx = None):
assert s is None or ctx is None
ensure_prop_closures()
self.solver = s self.solver = s
self.ctx = s.ctx if s is not None else ctx self._ctx = None
self.cb = None self.cb = None
self.id = _prop_closures.insert(self) self.id = _prop_closures.insert(self)
self.fixed = None self.fixed = None
self.final = None self.final = None
self.eq = None self.eq = None
self.diseq = 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: if s:
Z3_solver_propagate_init(s.ctx.ref(), Z3_solver_propagate_init(self.ctx_ref(),
s.solver, s.solver,
ctypes.c_void_p(self.id), ctypes.c_void_p(self.id),
_user_prop_push, _user_prop_push,
_user_prop_pop, _user_prop_pop,
_user_prop_fresh) _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): def add_fixed(self, fixed):
assert not 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 self.fixed = fixed
def add_final(self, final): def add_final(self, final):
assert not 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 self.final = final
def add_eq(self, eq): def add_eq(self, eq):
assert not 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 self.eq = eq
def add_diseq(self, diseq): def add_diseq(self, diseq):
assert not 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 self.diseq = diseq
def push(self): def push(self):
raise Z3Exception("push has not been overwritten") raise Z3Exception("push needs to be overwritten")
def pop(self, num_scopes): def pop(self, num_scopes):
raise Z3Exception("pop has not been overwritten") raise Z3Exception("pop needs to be overwritten")
def fresh(self): def fresh(self):
raise Z3Exception("fresh has not been overwritten") raise Z3Exception("fresh needs to be overwritten")
def add(self, e): def add(self, e):
assert self.solver 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. # Propagation can only be invoked as during a fixed-callback.
# #
def propagate(self, ids, e): def propagate(self, e, ids, eqs = []):
sz = len(ids) num_fixed = len(ids)
_ids = (ctypes.c_uint * sz)() _ids = (ctypes.c_uint * num_fixed)()
for i in range(sz): for i in range(num_fixed):
_ids[i] = ids[i] _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): def conflict(self, ids):
self.propagate(ids, BoolVal(False, self.ctx)) self.propagate(ids, BoolVal(False, self.ctx_ref()))

View file

@ -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_push_eh(void* ctx);
typedef void Z3_pop_eh(void* ctx, unsigned num_scopes); 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_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_eq_eh(void* ctx, Z3_solver_callback cb, unsigned x, unsigned y);
typedef void Z3_final_eh(void* ctx, Z3_solver_callback cb); typedef void Z3_final_eh(void* ctx, Z3_solver_callback cb);
@ -6586,10 +6586,10 @@ extern "C" {
The callback adds a propagation consequence based on the fixed values of the The callback adds a propagation consequence based on the fixed values of the
\c ids. \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. \brief Check whether the assertions in a given solver are consistent or not.

View file

@ -24,19 +24,6 @@ Notes:
#include "solver/solver.h" #include "solver/solver.h"
context_params::context_params() { 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(); updt_params();
} }
@ -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() { ast_manager * context_params::mk_ast_manager() {
if (m_manager)
return m_manager;
ast_manager * r = alloc(ast_manager, ast_manager * r = alloc(ast_manager,
m_proof ? PGM_ENABLED : PGM_DISABLED, m_proof ? PGM_ENABLED : PGM_DISABLED,
m_trace ? m_trace_file_name.c_str() : nullptr); m_trace ? m_trace_file_name.c_str() : nullptr);

View file

@ -26,24 +26,24 @@ class context_params {
void set_bool(bool & opt, char const * param, char const * value); void set_bool(bool & opt, char const * param, char const * value);
void set_uint(unsigned & 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: public:
bool m_auto_config; bool m_auto_config { true };
bool m_proof; bool m_proof { false };
std::string m_dot_proof_file; std::string m_dot_proof_file;
bool m_interpolants; bool m_debug_ref_count { false };
bool m_debug_ref_count; bool m_trace { false };
bool m_trace;
std::string m_trace_file_name; std::string m_trace_file_name;
bool m_well_sorted_check; bool m_well_sorted_check { false };
bool m_model; bool m_model { true };
bool m_model_validate; bool m_model_validate { false };
bool m_dump_models; bool m_dump_models { false };
bool m_unsat_core; bool m_unsat_core { false };
bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. bool m_smtlib2_compliant { false }; // it must be here because it enable/disable the use of coercions in the ast_manager.
unsigned m_timeout; unsigned m_timeout { UINT_MAX } ;
bool m_statistics; bool m_statistics { false };
unsigned rlimit() const { return m_rlimit; } unsigned rlimit() const { return m_rlimit; }
context_params(); context_params();
@ -74,6 +74,9 @@ public:
\brief Create an AST manager using this configuration. \brief Create an AST manager using this configuration.
*/ */
ast_manager * mk_ast_manager(); ast_manager * mk_ast_manager();
void set_foreign_manager(ast_manager* m) { m_manager = m; }
bool owns_manager() const { return m_manager != nullptr; }
}; };

View file

@ -183,13 +183,14 @@ namespace smt {
dst_ctx.setup_context(dst_ctx.m_fparams.m_auto_config); dst_ctx.setup_context(dst_ctx.m_fparams.m_auto_config);
dst_ctx.internalize_assertions(); dst_ctx.internalize_assertions();
dst_ctx.copy_user_propagator(src_ctx);
TRACE("smt_context", TRACE("smt_context",
src_ctx.display(tout); src_ctx.display(tout);
dst_ctx.display(tout);); dst_ctx.display(tout););
} }
context::~context() { context::~context() {
flush(); flush();
m_asserted_formulas.finalize(); m_asserted_formulas.finalize();
@ -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 * context::mk_fresh(symbol const * l, smt_params * p, params_ref const& pa) {
context * new_ctx = alloc(context, m, p ? *p : m_fparams, pa); context * new_ctx = alloc(context, m, p ? *p : m_fparams, pa);
new_ctx->m_is_auxiliary = true; new_ctx->m_is_auxiliary = true;
@ -2950,10 +2964,10 @@ namespace smt {
} }
void context::user_propagate_init( void context::user_propagate_init(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh) { solver::fresh_eh_t& fresh_eh) {
setup_context(m_fparams.m_auto_config); setup_context(m_fparams.m_auto_config);
m_user_propagator = alloc(user_propagator, *this); m_user_propagator = alloc(user_propagator, *this);
m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh); m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
@ -2963,7 +2977,7 @@ namespace smt {
} }
bool context::watches_fixed(enode* n) const { 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) { void context::assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain) {

View file

@ -1573,6 +1573,8 @@ namespace smt {
void log_stats(); void log_stats();
void copy_user_propagator(context& src);
public: public:
context(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); context(ast_manager & m, smt_params & fp, params_ref const & p = params_ref());
@ -1688,10 +1690,10 @@ namespace smt {
* user-propagator * user-propagator
*/ */
void user_propagate_init( void user_propagate_init(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh); solver::fresh_eh_t& fresh_eh);
void user_propagate_register_final(solver::final_eh_t& final_eh) { void user_propagate_register_final(solver::final_eh_t& final_eh) {
if (!m_user_propagator) if (!m_user_propagator)

View file

@ -234,10 +234,10 @@ namespace smt {
} }
void user_propagate_init( void user_propagate_init(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh) { solver::fresh_eh_t& fresh_eh) {
m_kernel.user_propagate_init(ctx, push_eh, pop_eh, fresh_eh); m_kernel.user_propagate_init(ctx, push_eh, pop_eh, fresh_eh);
} }
@ -474,10 +474,10 @@ namespace smt {
} }
void kernel::user_propagate_init( void kernel::user_propagate_init(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh) { solver::fresh_eh_t& fresh_eh) {
m_imp->user_propagate_init(ctx, push_eh, pop_eh, fresh_eh); m_imp->user_propagate_init(ctx, push_eh, pop_eh, fresh_eh);
} }

View file

@ -290,9 +290,9 @@ namespace smt {
*/ */
void user_propagate_init( void user_propagate_init(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh); solver::fresh_eh_t& fresh_eh);
void user_propagate_register_fixed(solver::fixed_eh_t& fixed_eh); void user_propagate_register_fixed(solver::fixed_eh_t& fixed_eh);

View file

@ -212,7 +212,7 @@ namespace {
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, std::function<void(void*)>& push_eh,
std::function<void(void*, unsigned)>& pop_eh, std::function<void(void*, unsigned)>& pop_eh,
std::function<void*(void*)>& fresh_eh) override { solver::fresh_eh_t& fresh_eh) override {
m_context.user_propagate_init(ctx, push_eh, pop_eh, fresh_eh); m_context.user_propagate_init(ctx, push_eh, pop_eh, fresh_eh);
} }

View file

@ -23,11 +23,13 @@ Author:
using namespace smt; using namespace smt;
user_propagator::user_propagator(context& ctx): user_propagator::user_propagator(context& ctx):
theory(ctx, ctx.get_manager().mk_family_id("user_propagator")), theory(ctx, ctx.get_manager().mk_family_id("user_propagator"))
m_qhead(0),
m_num_scopes(0)
{} {}
user_propagator::~user_propagator() {
dealloc(m_api_context);
}
void user_propagator::force_push() { void user_propagator::force_push() {
for (; m_num_scopes > 0; --m_num_scopes) { for (; m_num_scopes > 0; --m_num_scopes) {
theory::push_scope_eh(); theory::push_scope_eh();
@ -36,9 +38,6 @@ void user_propagator::force_push() {
} }
} }
// TODO: check type of 'e', either Bool or Bit-vector.
//
unsigned user_propagator::add_expr(expr* e) { unsigned user_propagator::add_expr(expr* e) {
force_push(); force_push();
enode* n = ensure_enode(e); enode* n = ensure_enode(e);
@ -49,13 +48,13 @@ unsigned user_propagator::add_expr(expr* e) {
return v; return v;
} }
void user_propagator::propagate(unsigned sz, unsigned const* ids, expr* conseq) { void user_propagator::propagate(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr* conseq) {
m_prop.push_back(prop_info(sz, ids, expr_ref(conseq, m))); m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, expr_ref(conseq, m)));
} }
theory * user_propagator::mk_fresh(context * new_ctx) { theory * user_propagator::mk_fresh(context * new_ctx) {
auto* th = alloc(user_propagator, *new_ctx); auto* th = alloc(user_propagator, *new_ctx);
void* ctx = m_fresh_eh(m_user_context); void* ctx = m_fresh_eh(m_user_context, new_ctx->get_manager(), th->m_api_context);
th->add(ctx, m_push_eh, m_pop_eh, m_fresh_eh); th->add(ctx, m_push_eh, m_pop_eh, m_fresh_eh);
if ((bool)m_fixed_eh) th->register_fixed(m_fixed_eh); if ((bool)m_fixed_eh) th->register_fixed(m_fixed_eh);
if ((bool)m_final_eh) th->register_final(m_final_eh); if ((bool)m_final_eh) th->register_final(m_final_eh);
@ -110,9 +109,12 @@ void user_propagator::propagate() {
justification* js; justification* js;
while (qhead < m_prop.size() && !ctx.inconsistent()) { while (qhead < m_prop.size() && !ctx.inconsistent()) {
auto const& prop = m_prop[qhead]; auto const& prop = m_prop[qhead];
m_lits.reset(); m_lits.reset();
eqs.reset();
for (unsigned id : prop.m_ids) for (unsigned id : prop.m_ids)
m_lits.append(m_id2justification[id]); m_lits.append(m_id2justification[id]);
for (auto const& p : prop.m_eqs)
eqs.push_back(enode_pair(get_enode(p.first), get_enode(p.second)));
if (m.is_false(prop.m_conseq)) { if (m.is_false(prop.m_conseq)) {
js = ctx.mk_justification( js = ctx.mk_justification(
ext_theory_conflict_justification( ext_theory_conflict_justification(
@ -126,10 +128,16 @@ void user_propagator::propagate() {
get_id(), ctx.get_region(), m_lits.size(), m_lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit)); get_id(), ctx.get_region(), m_lits.size(), m_lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit));
ctx.assign(lit, js); ctx.assign(lit, js);
} }
++m_stats.m_num_propagations;
++qhead; ++qhead;
} }
ctx.push_trail(value_trail<context, unsigned>(m_qhead)); ctx.push_trail(value_trail<context, unsigned>(m_qhead));
m_qhead = qhead; m_qhead = qhead;
} }
void user_propagator::collect_statistics(::statistics & st) const {
st.update("user-propagations", m_stats.m_num_propagations);
st.update("user-watched", get_num_vars());
}

View file

@ -29,45 +29,59 @@ Notes:
namespace smt { namespace smt {
class user_propagator : public theory, public solver::propagate_callback { class user_propagator : public theory, public solver::propagate_callback {
void* m_user_context;
std::function<void(void*)> m_push_eh;
std::function<void(void*, unsigned)> m_pop_eh;
std::function<void*(void*)> m_fresh_eh;
solver::final_eh_t m_final_eh;
solver::fixed_eh_t m_fixed_eh;
solver::eq_eh_t m_eq_eh;
solver::eq_eh_t m_diseq_eh;
struct prop_info { struct prop_info {
unsigned_vector m_ids; unsigned_vector m_ids;
expr_ref m_conseq; expr_ref m_conseq;
prop_info(unsigned sz, unsigned const* ids, expr_ref const& c): svector<std::pair<unsigned, unsigned>> m_eqs;
m_ids(sz, ids), prop_info(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr_ref const& c):
m_ids(num_fixed, fixed_ids),
m_conseq(c) m_conseq(c)
{} {
for (unsigned i = 0; i < num_eqs; ++i)
m_eqs.push_back(std::make_pair(eq_lhs[i], eq_rhs[i]));
}
}; };
unsigned m_qhead;
struct stats {
unsigned m_num_propagations;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
void* m_user_context;
solver::push_eh_t m_push_eh;
solver::pop_eh_t m_pop_eh;
solver::fresh_eh_t m_fresh_eh;
solver::final_eh_t m_final_eh;
solver::fixed_eh_t m_fixed_eh;
solver::eq_eh_t m_eq_eh;
solver::eq_eh_t m_diseq_eh;
void* m_api_context { nullptr };
unsigned m_qhead { 0 };
vector<prop_info> m_prop; vector<prop_info> m_prop;
unsigned_vector m_prop_lim; unsigned_vector m_prop_lim;
vector<literal_vector> m_id2justification; vector<literal_vector> m_id2justification;
unsigned m_num_scopes; unsigned m_num_scopes { 0 };
literal_vector m_lits; literal_vector m_lits;
stats m_stats;
void force_push(); void force_push();
public: public:
user_propagator(context& ctx); user_propagator(context& ctx);
~user_propagator() override {} ~user_propagator() override;
/* /*
* \brief initial setup for user propagator. * \brief initial setup for user propagator.
*/ */
void add( void add(
void* ctx, void* ctx,
std::function<void(void*)>& push_eh, solver::push_eh_t& push_eh,
std::function<void(void*, unsigned)>& pop_eh, solver::pop_eh_t& pop_eh,
std::function<void*(void*)>& fresh_eh) { solver::fresh_eh_t& fresh_eh) {
m_user_context = ctx; m_user_context = ctx;
m_push_eh = push_eh; m_push_eh = push_eh;
m_pop_eh = pop_eh; m_pop_eh = pop_eh;
@ -81,7 +95,9 @@ namespace smt {
void register_eq(solver::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; } void register_eq(solver::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; }
void register_diseq(solver::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; } void register_diseq(solver::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; }
void propagate(unsigned sz, unsigned const* ids, expr* conseq) override; bool has_fixed() const { return (bool)m_fixed_eh; }
void propagate(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* lhs, unsigned const* rhs, expr* conseq) override;
void new_fixed_eh(theory_var v, expr* value, unsigned num_lits, literal const* jlits); void new_fixed_eh(theory_var v, expr* value, unsigned num_lits, literal const* jlits);
@ -99,7 +115,7 @@ namespace smt {
void push_scope_eh() override; void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override; void pop_scope_eh(unsigned num_scopes) override;
void restart_eh() override {} void restart_eh() override {}
void collect_statistics(::statistics & st) const override {} void collect_statistics(::statistics & st) const override;
model_value_proc * mk_value(enode * n, model_generator & mg) override { return nullptr; } model_value_proc * mk_value(enode * n, model_generator & mg) override { return nullptr; }
void init_model(model_generator & m) override {} void init_model(model_generator & m) override {}
bool include_func_interp(func_decl* f) override { return false; } bool include_func_interp(func_decl* f) override { return false; }

View file

@ -239,22 +239,26 @@ public:
virtual expr_ref get_implied_upper_bound(expr* e) = 0; virtual expr_ref get_implied_upper_bound(expr* e) = 0;
virtual void user_propagate_init(
void* ctx,
std::function<void(void*)>& push_eh,
std::function<void(void*, unsigned)>& pop_eh,
std::function<void*(void*)>& fresh_eh) {
throw default_exception("user-propagators are only supported on the SMT solver");
}
class propagate_callback { class propagate_callback {
public: public:
virtual void propagate(unsigned sz, unsigned const* ids, expr* conseq) = 0; virtual void propagate(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr* conseq) = 0;
}; };
typedef std::function<void(void*, solver::propagate_callback*)> final_eh_t; typedef std::function<void(void*, solver::propagate_callback*)> final_eh_t;
typedef std::function<void(void*, solver::propagate_callback*, unsigned, expr*)> fixed_eh_t; typedef std::function<void(void*, solver::propagate_callback*, unsigned, expr*)> fixed_eh_t;
typedef std::function<void(void*, solver::propagate_callback*, unsigned, unsigned)> eq_eh_t; typedef std::function<void(void*, solver::propagate_callback*, unsigned, unsigned)> eq_eh_t;
typedef std::function<void*(void*, ast_manager&, void*&)> fresh_eh_t;
typedef std::function<void(void*)> push_eh_t;
typedef std::function<void(void*,unsigned)> pop_eh_t;
virtual void user_propagate_init(
void* ctx,
push_eh_t& push_eh,
pop_eh_t& pop_eh,
fresh_eh_t& fresh_eh) {
throw default_exception("user-propagators are only supported on the SMT solver");
}
virtual void user_propagate_register_fixed(fixed_eh_t& fixed_eh) { virtual void user_propagate_register_fixed(fixed_eh_t& fixed_eh) {
throw default_exception("user-propagators are only supported on the SMT solver"); throw default_exception("user-propagators are only supported on the SMT solver");