3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-09 15:43:25 +00:00

Added decide-callback to user-propagator (#5978)

* Fixed registering expressions in push/pop

* Reused existing function

* Reverted reusing can_propagate

* Added decide-callback to user-propagator

* Refactoring

* Fixed index
This commit is contained in:
Clemens Eisenhofer 2022-04-15 20:07:17 +02:00 committed by GitHub
parent 9ecd4f8406
commit e11496bc65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 284 additions and 87 deletions

View file

@ -975,6 +975,14 @@ extern "C" {
Z3_CATCH; Z3_CATCH;
} }
void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh) {
Z3_TRY;
RESET_ERROR_CODE();
user_propagator::decide_eh_t c = (void(*)(void*, user_propagator::callback*, expr*&, unsigned&, lbool&))decide_eh;
to_solver_ref(s)->user_propagate_register_decide(c);
Z3_CATCH;
}
Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range) { Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range) {
Z3_TRY; Z3_TRY;
LOG_Z3_solver_propagate_declare(c, name, n, domain, range); LOG_Z3_solver_propagate_declare(c, name, n, domain, range);

View file

@ -3943,11 +3943,13 @@ namespace z3 {
typedef std::function<void(void)> final_eh_t; typedef std::function<void(void)> final_eh_t;
typedef std::function<void(expr const&, expr const&)> eq_eh_t; typedef std::function<void(expr const&, expr const&)> eq_eh_t;
typedef std::function<void(expr const&)> created_eh_t; typedef std::function<void(expr const&)> created_eh_t;
typedef std::function<void(expr&, unsigned&, Z3_lbool&)> decide_eh_t;
final_eh_t m_final_eh; final_eh_t m_final_eh;
eq_eh_t m_eq_eh; eq_eh_t m_eq_eh;
fixed_eh_t m_fixed_eh; fixed_eh_t m_fixed_eh;
created_eh_t m_created_eh; created_eh_t m_created_eh;
decide_eh_t m_decide_eh;
solver* s; solver* s;
context* c; context* c;
std::vector<z3::context*> subcontexts; std::vector<z3::context*> subcontexts;
@ -4009,8 +4011,15 @@ namespace z3 {
expr e(p->ctx(), _e); expr e(p->ctx(), _e);
p->m_created_eh(e); p->m_created_eh(e);
} }
static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast& _val, unsigned& bit, Z3_lbool& is_pos) {
user_propagator_base* p = static_cast<user_propagator_base*>(_p);
scoped_cb _cb(p, cb);
expr val(p->ctx(), _val);
p->m_decide_eh(val, bit, is_pos);
_val = val;
}
public: public:
user_propagator_base(context& c) : s(nullptr), c(&c) {} user_propagator_base(context& c) : s(nullptr), c(&c) {}
@ -4119,6 +4128,22 @@ namespace z3 {
Z3_solver_propagate_created(ctx(), *s, created_eh); Z3_solver_propagate_created(ctx(), *s, created_eh);
} }
} }
void register_decide(decide_eh_t& c) {
m_decide_eh = c;
if (s) {
Z3_solver_propagate_decide(ctx(), *s, decide_eh);
}
}
void register_decide() {
m_decide_eh = [this](expr& val, unsigned& bit, Z3_lbool& is_pos) {
decide(val, bit, is_pos);
};
if (s) {
Z3_solver_propagate_decide(ctx(), *s, decide_eh);
}
}
virtual void fixed(expr const& /*id*/, expr const& /*e*/) { } virtual void fixed(expr const& /*id*/, expr const& /*e*/) { }
@ -4127,6 +4152,8 @@ namespace z3 {
virtual void final() { } virtual void final() { }
virtual void created(expr const& /*e*/) {} virtual void created(expr const& /*e*/) {}
virtual void decide(expr& /*val*/, unsigned& /*bit*/, Z3_lbool& /*is_pos*/) {}
/** /**
\brief tracks \c e by a unique identifier that is returned by the call. \brief tracks \c e by a unique identifier that is returned by the call.

View file

@ -1444,6 +1444,7 @@ Z3_DECLARE_CLOSURE(Z3_fixed_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as
Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast s, Z3_ast t)); Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast s, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb)); Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb));
Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t)); Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast&, unsigned&, Z3_lbool&));
/** /**
@ -6758,6 +6759,14 @@ extern "C" {
* The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare. * The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
*/ */
void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh); void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh);
/**
* \brief register a callback when a the solver decides to split on a registered expression
* The callback may set passed expression to another registered expression which will be selected instead.
* In case the expression is a bitvector the bit to split on is determined by the bit argument and the
* truth-value to try first is given by is_pos
*/
void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh);
/** /**
Create uninterpreted function declaration for the user propagator. Create uninterpreted function declaration for the user propagator.

View file

@ -1766,6 +1766,70 @@ namespace smt {
m_bvar_inc *= INV_ACTIVITY_LIMIT; m_bvar_inc *= INV_ACTIVITY_LIMIT;
} }
/**
\brief Returns a truth value for the given variable
*/
bool context::guess(bool_var var, lbool phase) {
if (is_quantifier(m_bool_var2expr[var])) {
// Overriding any decision on how to assign the quantifier.
// assigning a quantifier to false is equivalent to make it irrelevant.
phase = l_false;
}
literal l(var, false);
if (phase != l_undef)
return phase == l_true;
bool_var_data & d = m_bdata[var];
if (d.try_true_first())
return true;
switch (m_fparams.m_phase_selection) {
case PS_THEORY:
if (m_phase_cache_on && d.m_phase_available) {
return m_bdata[var].m_phase;
}
if (!m_phase_cache_on && d.is_theory_atom()) {
theory * th = m_theories.get_plugin(d.get_theory());
lbool th_phase = th->get_phase(var);
if (th_phase != l_undef) {
return th_phase == l_true;
}
}
if (track_occs()) {
if (m_lit_occs[l.index()] == 0) {
return false;
}
if (m_lit_occs[(~l).index()] == 0) {
return true;
}
}
return m_phase_default;
case PS_CACHING:
case PS_CACHING_CONSERVATIVE:
case PS_CACHING_CONSERVATIVE2:
if (m_phase_cache_on && d.m_phase_available) {
TRACE("phase_selection", tout << "using cached value, is_pos: " << m_bdata[var].m_phase << ", var: p" << var << "\n";);
return m_bdata[var].m_phase;
}
else {
TRACE("phase_selection", tout << "setting to false\n";);
return m_phase_default;
}
case PS_ALWAYS_FALSE:
return false;
case PS_ALWAYS_TRUE:
return true;
case PS_RANDOM:
return m_random() % 2 == 0;
case PS_OCCURRENCE: {
return m_lit_occs[l.index()] > m_lit_occs[(~l).index()];
}
default:
UNREACHABLE();
return false;
}
}
/** /**
\brief Execute next case split, return false if there are no \brief Execute next case split, return false if there are no
more case splits to be performed. more case splits to be performed.
@ -1807,81 +1871,15 @@ namespace smt {
TRACE("decide", tout << "splitting, lvl: " << m_scope_lvl << "\n";); TRACE("decide", tout << "splitting, lvl: " << m_scope_lvl << "\n";);
TRACE("decide_detail", tout << mk_pp(bool_var2expr(var), m) << "\n";); TRACE("decide_detail", tout << mk_pp(bool_var2expr(var), m) << "\n";);
bool is_pos; bool is_pos = guess(var, phase);
if (is_quantifier(m_bool_var2expr[var])) {
// Overriding any decision on how to assign the quantifier.
// assigning a quantifier to false is equivalent to make it irrelevant.
phase = l_false;
}
literal l(var, false); literal l(var, false);
if (phase != l_undef) { bool_var original_choice = var;
is_pos = phase == l_true;
} if (decide_user_interference(var, is_pos)) {
else { m_case_split_queue->unassign_var_eh(original_choice);
bool_var_data & d = m_bdata[var]; l = literal(var, false);
if (d.try_true_first()) {
is_pos = true;
}
else {
switch (m_fparams.m_phase_selection) {
case PS_THEORY:
if (m_phase_cache_on && d.m_phase_available) {
is_pos = m_bdata[var].m_phase;
break;
}
if (!m_phase_cache_on && d.is_theory_atom()) {
theory * th = m_theories.get_plugin(d.get_theory());
lbool th_phase = th->get_phase(var);
if (th_phase != l_undef) {
is_pos = th_phase == l_true;
break;
}
}
if (track_occs()) {
if (m_lit_occs[l.index()] == 0) {
is_pos = false;
break;
}
if (m_lit_occs[(~l).index()] == 0) {
is_pos = true;
break;
}
}
is_pos = m_phase_default;
break;
case PS_CACHING:
case PS_CACHING_CONSERVATIVE:
case PS_CACHING_CONSERVATIVE2:
if (m_phase_cache_on && d.m_phase_available) {
TRACE("phase_selection", tout << "using cached value, is_pos: " << m_bdata[var].m_phase << ", var: p" << var << "\n";);
is_pos = m_bdata[var].m_phase;
}
else {
TRACE("phase_selection", tout << "setting to false\n";);
is_pos = m_phase_default;
}
break;
case PS_ALWAYS_FALSE:
is_pos = false;
break;
case PS_ALWAYS_TRUE:
is_pos = true;
break;
case PS_RANDOM:
is_pos = (m_random() % 2 == 0);
break;
case PS_OCCURRENCE: {
is_pos = m_lit_occs[l.index()] > m_lit_occs[(~l).index()];
break;
}
default:
is_pos = false;
UNREACHABLE();
}
}
} }
if (!is_pos) l.neg(); if (!is_pos) l.neg();
@ -1889,7 +1887,7 @@ namespace smt {
assign(l, b_justification::mk_axiom(), true); assign(l, b_justification::mk_axiom(), true);
return true; return true;
} }
/** /**
\brief Update counter that is used to enable/disable phase caching. \brief Update counter that is used to enable/disable phase caching.
*/ */
@ -2906,6 +2904,14 @@ namespace smt {
return m_user_propagator && m_user_propagator->has_fixed() && 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;
} }
bool context::decide_user_interference(bool_var& var, bool& is_pos) {
if (!m_user_propagator || !m_user_propagator->has_decide())
return false;
bool_var old = var;
m_user_propagator->decide(var, is_pos);
return old != 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) {
theory_var v = n->get_th_var(m_user_propagator->get_family_id()); theory_var v = n->get_th_var(m_user_propagator->get_family_id());
m_user_propagator->new_fixed_eh(v, val, sz, explain); m_user_propagator->new_fixed_eh(v, val, sz, explain);
@ -3042,7 +3048,8 @@ namespace smt {
} }
} }
} }
} else { }
else {
literal_vector new_case_split; literal_vector new_case_split;
for (unsigned i = 0; i < num_lits; ++i) { for (unsigned i = 0; i < num_lits; ++i) {
literal l = lits[i]; literal l = lits[i];

View file

@ -1134,6 +1134,8 @@ namespace smt {
enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args);
bool guess(bool_var var, lbool phase);
protected: protected:
bool decide(); bool decide();
@ -1738,8 +1740,16 @@ namespace smt {
m_user_propagator->register_created(r); m_user_propagator->register_created(r);
} }
void user_propagate_register_decide(user_propagator::decide_eh_t& r) {
if (!m_user_propagator)
throw default_exception("user propagator must be initialized");
m_user_propagator->register_decide(r);
}
bool watches_fixed(enode* n) const; bool watches_fixed(enode* n) const;
bool decide_user_interference(bool_var& var, bool& is_pos);
void assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain); void assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain);
void assign_fixed(enode* n, expr* val, literal_vector const& explain) { void assign_fixed(enode* n, expr* val, literal_vector const& explain) {

View file

@ -284,4 +284,8 @@ namespace smt {
m_imp->m_kernel.user_propagate_register_created(r); m_imp->m_kernel.user_propagate_register_created(r);
} }
void kernel::user_propagate_register_decide(user_propagator::decide_eh_t& r) {
m_imp->m_kernel.user_propagate_register_decide(r);
}
}; };

View file

@ -311,6 +311,8 @@ namespace smt {
void user_propagate_register_created(user_propagator::created_eh_t& r); void user_propagate_register_created(user_propagator::created_eh_t& r);
void user_propagate_register_decide(user_propagator::decide_eh_t& r);
/** /**
\brief Return a reference to smt::context. \brief Return a reference to smt::context.
This breaks abstractions. This breaks abstractions.

View file

@ -244,6 +244,10 @@ namespace {
m_context.user_propagate_register_created(c); m_context.user_propagate_register_created(c);
} }
void user_propagate_register_decide(user_propagator::decide_eh_t& c) override {
m_context.user_propagate_register_decide(c);
}
struct scoped_minimize_core { struct scoped_minimize_core {
smt_solver& s; smt_solver& s;
expr_ref_vector m_assumptions; expr_ref_vector m_assumptions;

View file

@ -322,6 +322,7 @@ public:
user_propagator::eq_eh_t m_eq_eh; user_propagator::eq_eh_t m_eq_eh;
user_propagator::eq_eh_t m_diseq_eh; user_propagator::eq_eh_t m_diseq_eh;
user_propagator::created_eh_t m_created_eh; user_propagator::created_eh_t m_created_eh;
user_propagator::decide_eh_t m_decide_eh;
void user_propagate_delay_init() { void user_propagate_delay_init() {
@ -333,6 +334,7 @@ public:
if (m_eq_eh) m_ctx->user_propagate_register_eq(m_eq_eh); if (m_eq_eh) m_ctx->user_propagate_register_eq(m_eq_eh);
if (m_diseq_eh) m_ctx->user_propagate_register_diseq(m_diseq_eh); if (m_diseq_eh) m_ctx->user_propagate_register_diseq(m_diseq_eh);
if (m_created_eh) m_ctx->user_propagate_register_created(m_created_eh); if (m_created_eh) m_ctx->user_propagate_register_created(m_created_eh);
if (m_decide_eh) m_ctx->user_propagate_register_decide(m_decide_eh);
for (expr* v : m_vars) for (expr* v : m_vars)
m_ctx->user_propagate_register_expr(v); m_ctx->user_propagate_register_expr(v);

View file

@ -531,7 +531,6 @@ namespace smt {
return true; return true;
} }
bool theory_bv::get_fixed_value(theory_var v, numeral & result) const { bool theory_bv::get_fixed_value(theory_var v, numeral & result) const {
result.reset(); result.reset();
unsigned i = 0; unsigned i = 0;
@ -1821,6 +1820,39 @@ namespace smt {
st.update("bv dynamic eqs", m_stats.m_num_eq_dynamic); st.update("bv dynamic eqs", m_stats.m_num_eq_dynamic);
} }
theory_bv::var_enode_pos theory_bv::get_bv_with_theory(bool_var v, theory_id id) const {
atom* a = get_bv2a(v);
svector<var_enode_pos> vec;
if (!a->is_bit())
return var_enode_pos(nullptr, UINT32_MAX);
bit_atom * b = static_cast<bit_atom*>(a);
var_pos_occ * curr = b->m_occs;
while (curr) {
enode* n = get_enode(curr->m_var);
if (n->get_th_var(id) != null_theory_var)
return var_enode_pos(n, curr->m_idx);
curr = curr->m_next;
}
return var_enode_pos(nullptr, UINT32_MAX);
}
bool_var theory_bv::get_first_unassigned(unsigned start_bit, enode* n) const {
theory_var v = n->get_th_var(get_family_id());
auto& bits = m_bits[v];
unsigned sz = bits.size();
for (unsigned i = start_bit; i < sz; ++i) {
if (ctx.get_assignment(bits[i].var()) != l_undef)
return bits[i].var();
}
for (unsigned i = 0; i < start_bit; ++i) {
if (ctx.get_assignment(bits[i].var()) != l_undef)
return bits[i].var();
}
return null_bool_var;
}
bool theory_bv::check_assignment(theory_var v) { bool theory_bv::check_assignment(theory_var v) {
if (!is_root(v)) if (!is_root(v))
return true; return true;

View file

@ -260,6 +260,9 @@ namespace smt {
smt_params const& params() const; smt_params const& params() const;
public: public:
typedef std::pair<enode*, unsigned> var_enode_pos;
theory_bv(context& ctx); theory_bv(context& ctx);
~theory_bv() override; ~theory_bv() override;
@ -284,6 +287,9 @@ namespace smt {
bool get_fixed_value(app* x, numeral & result) const; bool get_fixed_value(app* x, numeral & result) const;
bool is_fixed_propagated(theory_var v, expr_ref& val, literal_vector& explain) override; bool is_fixed_propagated(theory_var v, expr_ref& val, literal_vector& explain) override;
var_enode_pos get_bv_with_theory(bool_var v, theory_id id) const;
bool_var get_first_unassigned(unsigned start_bit, enode* n) const;
bool check_assignment(theory_var v); bool check_assignment(theory_var v);
bool check_invariant(); bool check_invariant();
bool check_zero_one_bits(theory_var v); bool check_zero_one_bits(theory_var v);

View file

@ -17,6 +17,7 @@ Author:
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "smt/theory_bv.h"
#include "smt/theory_user_propagator.h" #include "smt/theory_user_propagator.h"
#include "smt/smt_context.h" #include "smt/smt_context.h"
@ -116,6 +117,7 @@ theory * theory_user_propagator::mk_fresh(context * new_ctx) {
if ((bool)m_eq_eh) th->register_eq(m_eq_eh); if ((bool)m_eq_eh) th->register_eq(m_eq_eh);
if ((bool)m_diseq_eh) th->register_diseq(m_diseq_eh); if ((bool)m_diseq_eh) th->register_diseq(m_diseq_eh);
if ((bool)m_created_eh) th->register_created(m_created_eh); if ((bool)m_created_eh) th->register_created(m_created_eh);
if ((bool)m_decide_eh) th->register_decide(m_decide_eh);
return th; return th;
} }
@ -154,6 +156,73 @@ void theory_user_propagator::new_fixed_eh(theory_var v, expr* value, unsigned nu
} }
} }
void theory_user_propagator::decide(bool_var& var, bool& is_pos) {
const bool_var_data& d = ctx.get_bdata(var);
if (!d.is_theory_atom())
return;
theory* th = ctx.get_theory(d.get_theory());
bv_util bv(m);
enode* original_enode = nullptr;
unsigned original_bit = 0;
if (d.is_enode() && th->get_family_id() == get_family_id()) {
// variable is just a registered expression
original_enode = ctx.bool_var2enode(var);
}
else if (th->get_family_id() == bv.get_fid()) {
// it might be a registered bit-vector
auto registered_bv = ((theory_bv*)th)->get_bv_with_theory(var, get_family_id());
if (!registered_bv.first)
// there is no registered bv associated with the bit
return;
original_enode = registered_bv.first;
original_bit = registered_bv.second;
}
else
return;
// call the registered callback
unsigned new_bit = original_bit;
lbool phase = is_pos ? l_true : l_false;
expr* e = var2expr(original_enode->get_th_var(get_family_id()));
m_decide_eh(m_user_context, this, e, new_bit, phase);
enode* new_enode = ctx.get_enode(e);
// check if the callback changed something
if (original_enode == new_enode && (new_enode->is_bool() || original_bit == new_bit)) {
if (phase != l_undef)
// it only affected the truth value
is_pos = phase == l_true;
return;
}
bool_var old_var = var;
if (new_enode->is_bool()) {
// expression was set to a boolean
bool_var new_var = ctx.enode2bool_var(new_enode);
if (ctx.get_assignment(new_var) == l_undef) {
var = new_var;
}
}
else {
// expression was set to a bit-vector
auto th_bv = (theory_bv*)ctx.get_theory(bv.get_fid());
bool_var new_var = th_bv->get_first_unassigned(new_bit, new_enode);
if (new_var != null_bool_var) {
var = new_var;
}
}
// in case the callback did not decide on a truth value -> let Z3 decide
is_pos = ctx.guess(var, phase);
}
void theory_user_propagator::push_scope_eh() { void theory_user_propagator::push_scope_eh() {
++m_num_scopes; ++m_num_scopes;
} }

View file

@ -56,7 +56,7 @@ namespace smt {
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
}; };
void* m_user_context = nullptr; void* m_user_context = nullptr;
user_propagator::push_eh_t m_push_eh; user_propagator::push_eh_t m_push_eh;
user_propagator::pop_eh_t m_pop_eh; user_propagator::pop_eh_t m_pop_eh;
user_propagator::fresh_eh_t m_fresh_eh; user_propagator::fresh_eh_t m_fresh_eh;
@ -65,6 +65,7 @@ namespace smt {
user_propagator::eq_eh_t m_eq_eh; user_propagator::eq_eh_t m_eq_eh;
user_propagator::eq_eh_t m_diseq_eh; user_propagator::eq_eh_t m_diseq_eh;
user_propagator::created_eh_t m_created_eh; user_propagator::created_eh_t m_created_eh;
user_propagator::decide_eh_t m_decide_eh;
user_propagator::context_obj* m_api_context = nullptr; user_propagator::context_obj* m_api_context = nullptr;
unsigned m_qhead = 0; unsigned m_qhead = 0;
@ -121,13 +122,16 @@ namespace smt {
void register_eq(user_propagator::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; } void register_eq(user_propagator::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; }
void register_diseq(user_propagator::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; } void register_diseq(user_propagator::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; }
void register_created(user_propagator::created_eh_t& created_eh) { m_created_eh = created_eh; } void register_created(user_propagator::created_eh_t& created_eh) { m_created_eh = created_eh; }
void register_decide(user_propagator::decide_eh_t& decide_eh) { m_decide_eh = decide_eh; }
bool has_fixed() const { return (bool)m_fixed_eh; } bool has_fixed() const { return (bool)m_fixed_eh; }
bool has_decide() const { return (bool)m_decide_eh; }
void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override;
void register_cb(expr* e) override; void register_cb(expr* e) 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);
void decide(bool_var& var, bool& is_pos);
theory * mk_fresh(context * new_ctx) override; theory * mk_fresh(context * new_ctx) override;
bool internalize_atom(app* atom, bool gate_ctx) override; bool internalize_atom(app* atom, bool gate_ctx) override;

View file

@ -116,6 +116,10 @@ public:
m_tactic->user_propagate_register_created(created_eh); m_tactic->user_propagate_register_created(created_eh);
} }
void user_propagate_register_decide(user_propagator::decide_eh_t& created_eh) override {
m_tactic->user_propagate_register_decide(created_eh);
}
void user_propagate_clear() override { void user_propagate_clear() override {
if (m_tactic) if (m_tactic)
m_tactic->user_propagate_clear(); m_tactic->user_propagate_clear();

View file

@ -204,6 +204,10 @@ public:
m_t2->user_propagate_register_created(created_eh); m_t2->user_propagate_register_created(created_eh);
} }
void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override {
m_t2->user_propagate_register_decide(decide_eh);
}
}; };
tactic * and_then(tactic * t1, tactic * t2) { tactic * and_then(tactic * t1, tactic * t2) {

View file

@ -2,6 +2,7 @@
#pragma once #pragma once
#include "ast/ast.h" #include "ast/ast.h"
#include "util/lbool.h"
namespace user_propagator { namespace user_propagator {
@ -17,14 +18,14 @@ namespace user_propagator {
virtual ~context_obj() = default; virtual ~context_obj() = default;
}; };
typedef std::function<void(void*, callback*)> final_eh_t; typedef std::function<void(void*, callback*)> final_eh_t;
typedef std::function<void(void*, callback*, expr*, expr*)> fixed_eh_t; typedef std::function<void(void*, callback*, expr*, expr*)> fixed_eh_t;
typedef std::function<void(void*, callback*, expr*, expr*)> eq_eh_t; typedef std::function<void(void*, callback*, expr*, expr*)> eq_eh_t;
typedef std::function<void*(void*, ast_manager&, context_obj*&)> fresh_eh_t; typedef std::function<void*(void*, ast_manager&, context_obj*&)> fresh_eh_t;
typedef std::function<void(void*, callback*)> push_eh_t; typedef std::function<void(void*, callback*)> push_eh_t;
typedef std::function<void(void*, callback*, unsigned)> pop_eh_t; typedef std::function<void(void*, callback*, unsigned)> pop_eh_t;
typedef std::function<void(void*, callback*, expr*)> created_eh_t; typedef std::function<void(void*, callback*, expr*)> created_eh_t;
typedef std::function<void(void*, callback*, expr*&, unsigned&, lbool&)> decide_eh_t;
class plugin : public decl_plugin { class plugin : public decl_plugin {
public: public:
@ -85,6 +86,10 @@ namespace user_propagator {
throw default_exception("user-propagators are only supported on the SMT solver"); throw default_exception("user-propagators are only supported on the SMT solver");
} }
virtual void user_propagate_register_decide(decide_eh_t& r) {
throw default_exception("user-propagators are only supported on the SMT solver");
}
virtual void user_propagate_clear() { virtual void user_propagate_clear() {
} }