3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-27 13:39:49 +00:00

Parallel solving (#7771)

* very basic setup

* ensure solve_eqs is fully disabled when smt.solve_eqs=false, #7743

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* respect smt configuration parameter in elim_unconstrained simplifier

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* indentation

* add bash files for test runs

* add option to selectively disable variable solving for only ground expressions

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remove verbose output

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* fix #7745

axioms for len(substr(...)) escaped due to nested rewriting

* ensure atomic constraints are processed by arithmetic solver

* #7739 optimization

add simplification rule for at(x, offset) = ""

Introducing j just postpones some rewrites that prevent useful simplifications. Z3 already uses common sub-expressions.
The example highlights some opportunities for simplification, noteworthy at(..) = "".
The example is solved in both versions after adding this simplification.

* fix unsound len(substr) axiom

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* FreshConst is_sort (#7748)

* #7750

add pre-processing simplification

* Add parameter validation for selected API functions

* updates to ac-plugin

fix incrementality bugs by allowing destructive updates during saturation at the cost of redoing saturation after a pop.

* enable passive, add check for bloom up-to-date

* add top-k fixed-sized min-heap priority queue for top scoring literals

* set up worker thread batch manager for multithreaded batch cubes paradigm, need to debug as I am getting segfault still

* fix bug in parallel solving batch setup

* fix bug

* allow for internalize implies

* disable pre-processing during cubing

* debugging

* remove default constructor

* remove a bunch of string copies

* Update euf_ac_plugin.cpp

include reduction rules in forward simplification

* Update euf_completion.cpp

try out restricting scope of equalities added by instantation

* Update smt_parallel.cpp

Drop non-relevant units from shared structures.

* process cubes as lists of individual lits

* merge

* Add support for Algebraic Datatypes in JavaScript/TypeScript bindings (#7734)

* Initial plan

* Add datatype type definitions to types.ts (work in progress)

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Complete datatype type definitions with working TypeScript compilation

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Implement core datatype functionality with TypeScript compilation success

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Complete datatype implementation with full Context integration and tests

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* chipping away at the new code structure

* comments

* debug infinite recursion and split cubes on existing split atoms that aren't in the cube

* share lemmas, learn from unsat core, try to debug a couple of things, there was a subtle bug that i have a hard time repro'ing

* merge

* fix #7603: race condition in Ctrl-C handling (#7755)

* fix #7603: race condition in Ctrl-C handling

* fix race in cancel_eh

* fix build

* add arithemtic saturation

* add an option to register callback on quantifier instantiation

Suppose a user propagator encodes axioms using quantifiers and uses E-matching for instantiation. If it wants to implement a custom priority scheme or drop some instances based on internal checks it can register a callback with quantifier instantiation

* missing new closure

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* add Z3_solver_propagate_on_binding to ml callback declarations

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* add python file

Signed-off-by: Lev Nachmanson <levnach@Levs-MacBook-Pro.local>

* debug under defined calls

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* more untangle params

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* precalc parameters to define the eval order

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove a printout

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* rename a Python file

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add on_binding callbacks across APIs

update release notes,
add to Java, .Net, C++

* use jboolean in Native interface

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* register on_binding attribute

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* fix java build for java bindings

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* avoid interferring side-effects in function calls

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remove theory_str and classes that are only used by it

* remove automata from python build

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remove ref to theory_str

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* get the finest factorizations before project

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* rename add_lcs to add_lc

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* resolve bad bug about l2g and g2l translators using wrong global context. add some debug prints

* initial attempt at dynamically switching from greedy to frugal splitting strategy in return_cubes. need to test. also there is some bug where the threads take forever to cancel?

* Update RELEASE_NOTES.md

* resolve bug about not translating managers correctly for the second phase of the greedy cubing, and the frugal fallback

---------

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Signed-off-by: Lev Nachmanson <levnach@Levs-MacBook-Pro.local>
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Co-authored-by: humnrdble <83878671+humnrdble@users.noreply.github.com>
Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Ilana Shapiro 2025-08-11 09:14:20 -07:00 committed by GitHub
parent 2169364b6d
commit 6044389446
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 474 additions and 15087 deletions

View file

@ -800,12 +800,11 @@ extern "C" {
unsigned timeout = p.get_uint("timeout", mk_c(c)->get_timeout());
bool use_ctrl_c = p.get_bool("ctrl_c", false);
th_rewriter m_rw(m, p);
m_rw.set_solver(alloc(api::seq_expr_solver, m, p));
expr_ref result(m);
cancel_eh<reslimit> eh(m.limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
try {
m_rw(a, result);

View file

@ -57,23 +57,6 @@ namespace smt2 {
namespace api {
class seq_expr_solver : public expr_solver {
ast_manager& m;
params_ref const& p;
solver_ref s;
public:
seq_expr_solver(ast_manager& m, params_ref const& p): m(m), p(p) {}
lbool check_sat(expr* e) override {
if (!s) {
s = mk_smt_solver(m, p, symbol("ALL"));
}
s->push();
s->assert_expr(e);
lbool r = s->check_sat();
s->pop(1);
return r;
}
};
class context : public tactic_manager {

View file

@ -287,7 +287,7 @@ extern "C" {
cancel_eh<reslimit> eh(mk_c(c)->m().limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
scoped_timer timer(timeout, &eh);
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
try {
r = to_fixedpoint_ref(d)->ctx().query(to_expr(q));
}

View file

@ -160,9 +160,6 @@ extern "C" {
model * _m = to_model_ref(m);
params_ref p;
ast_manager& mgr = mk_c(c)->m();
if (!_m->has_solver()) {
_m->set_solver(alloc(api::seq_expr_solver, mgr, p));
}
expr_ref result(mgr);
model::scoped_model_completion _scm(*_m, model_completion);
result = (*_m)(to_expr(t));

View file

@ -154,7 +154,7 @@ extern "C" {
bool use_ctrl_c = to_optimize_ptr(o)->get_params().get_bool("ctrl_c", true);
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {

View file

@ -650,7 +650,7 @@ extern "C" {
api::context::set_interruptable si(*(mk_c(c)), eh);
lbool result = l_undef;
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {
@ -748,7 +748,7 @@ extern "C" {
cancel_eh<reslimit> eh(mk_c(c)->m().limit());
to_solver(s)->set_eh(&eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {
@ -871,7 +871,7 @@ extern "C" {
to_solver(s)->set_eh(&eh);
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {
@ -919,7 +919,7 @@ extern "C" {
to_solver(s)->set_eh(&eh);
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
try {
@ -1160,6 +1160,14 @@ extern "C" {
Z3_CATCH;
}
void Z3_API Z3_solver_propagate_on_binding(Z3_context c, Z3_solver s, Z3_on_binding_eh binding_eh) {
Z3_TRY;
RESET_ERROR_CODE();
user_propagator::binding_eh_t c = (bool(*)(void*, user_propagator::callback*, expr*, expr*))binding_eh;
to_solver_ref(s)->user_propagate_register_on_binding(c);
Z3_CATCH;
}
bool Z3_API Z3_solver_next_split(Z3_context c, Z3_solver_callback cb, Z3_ast t, unsigned idx, Z3_lbool phase) {
Z3_TRY;
LOG_Z3_solver_next_split(c, cb, t, idx, phase);

View file

@ -427,7 +427,7 @@ extern "C" {
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
scoped_ctrl_c ctrlc(eh, use_ctrl_c);
scoped_timer timer(timeout, &eh);
try {
exec(*to_tactic_ref(t), new_goal, ref->m_subgoals);

View file

@ -4295,12 +4295,14 @@ namespace z3 {
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, unsigned, bool)> decide_eh_t;
typedef std::function<bool(expr const&, expr const&)> on_binding_eh_t;
final_eh_t m_final_eh;
eq_eh_t m_eq_eh;
fixed_eh_t m_fixed_eh;
created_eh_t m_created_eh;
decide_eh_t m_decide_eh;
on_binding_eh_t m_on_binding_eh;
solver* s;
context* c;
std::vector<z3::context*> subcontexts;
@ -4372,6 +4374,13 @@ namespace z3 {
expr val(p->ctx(), _val);
p->m_decide_eh(val, bit, is_pos);
}
static bool on_binding_eh(void* _p, Z3_solver_callback cb, Z3_ast _q, Z3_ast _inst) {
user_propagator_base* p = static_cast<user_propagator_base*>(_p);
scoped_cb _cb(p, cb);
expr q(p->ctx(), _q), inst(p->ctx(), _inst);
return p->m_on_binding_eh(q, inst);
}
public:
user_propagator_base(context& c) : s(nullptr), c(&c) {}
@ -4498,6 +4507,14 @@ namespace z3 {
}
}
void register_on_binding() {
m_on_binding_eh = [this](expr const& q, expr const& inst) {
return on_binding(q, inst);
};
if (s)
Z3_solver_propagate_on_binding(ctx(), *s, on_binding_eh);
}
virtual void fixed(expr const& /*id*/, expr const& /*e*/) { }
virtual void eq(expr const& /*x*/, expr const& /*y*/) { }
@ -4508,6 +4525,8 @@ namespace z3 {
virtual void decide(expr const& /*val*/, unsigned /*bit*/, bool /*is_pos*/) {}
virtual bool on_binding(expr const& /*q*/, expr const& /*inst*/) { return true; }
bool next_split(expr const& e, unsigned idx, Z3_lbool phase) {
assert(cb);
return Z3_solver_next_split(ctx(), cb, e, idx, phase);

View file

@ -64,7 +64,15 @@ namespace Microsoft.Z3
/// <param name="idx">If the term is a bit-vector, then an index into the bit-vector being branched on</param>
/// <param name="phase">The tentative truth-value</param>
public delegate void DecideEh(Expr term, uint idx, bool phase);
/// <summary>
/// Delegate type for callback when a quantifier is bound to an instance.
/// </summary>
/// <param name="q">Quantifier</param>
/// <param name="inst">Instance</param>
/// <returns>true if binding is allowed to take effect in the solver, false if blocked by callback</returns>
public delegate bool OnBindingEh(Expr q, Expr inst);
// access managed objects through a static array.
// thread safety is ignored for now.
GCHandle gch;
@ -78,6 +86,7 @@ namespace Microsoft.Z3
EqEh diseq_eh;
CreatedEh created_eh;
DecideEh decide_eh;
OnBindingEh on_binding_eh;
Native.Z3_push_eh push_eh;
Native.Z3_pop_eh pop_eh;
@ -89,6 +98,7 @@ namespace Microsoft.Z3
Native.Z3_eq_eh diseq_wrapper;
Native.Z3_decide_eh decide_wrapper;
Native.Z3_created_eh created_wrapper;
Native.Z3_on_binding_eh on_binding_wrapper;
void Callback(Action fn, Z3_solver_callback cb)
{
@ -175,6 +185,19 @@ namespace Microsoft.Z3
prop.Callback(() => prop.decide_eh(t, idx, phase), cb);
}
static bool _on_binding(voidp _ctx, Z3_solver_callback cb, Z3_ast _q, Z3_ast _inst)
{
var prop = (UserPropagator)GCHandle.FromIntPtr(_ctx).Target;
using var q = Expr.Create(prop.ctx, _q);
using var inst = Expr.Create(prop.ctx, _inst);
bool result = true;
prop.Callback(() => {
if (prop.on_binding_wrapper != null)
result = prop.on_binding_eh(q, inst);
}, cb);
return result;
}
/// <summary>
/// Propagator constructor from a solver class.
/// </summary>
@ -362,6 +385,20 @@ namespace Microsoft.Z3
}
}
/// <summary>
/// Set binding callback
/// </summary>
public OnBindingEh OnBinding
{
set
{
this.on_binding_wrapper = _on_binding;
this.on_binding_eh = value;
if (solver != null)
Native.Z3_solver_propagate_on_binding(ctx.nCtx, solver.NativeObject, on_binding_wrapper);
}
}
/// <summary>
/// Set the next decision
@ -378,6 +415,8 @@ namespace Microsoft.Z3
return Native.Z3_solver_next_split(ctx.nCtx, this.callback, e?.NativeObject ?? IntPtr.Zero, idx, phase) != 0;
}
/// <summary>
/// Track assignments to a term
/// </summary>

View file

@ -92,6 +92,7 @@ struct JavaInfo {
jmethodID eq = nullptr;
jmethodID final = nullptr;
jmethodID decide = nullptr;
jmethodID on_binding = nullptr;
Z3_solver_callback cb = nullptr;
};
@ -153,6 +154,12 @@ static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast _val, unsigned bit
info->jenv->CallVoidMethod(info->jobj, info->decide, (jlong)_val, bit, is_pos);
}
static jboolean on_binding_eh(void* _p, Z3_solver_callback cb, Z3_ast _q, Z3_ast _inst) {
JavaInfo *info = static_cast<JavaInfo*>(_p);
ScopedCB scoped(info, cb);
return info->jenv->CallBooleanMethod(info->jobj, info->on_binding, (jlong)_q, (jlong)_inst);
}
DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) {
JavaInfo *info = new JavaInfo;
@ -167,6 +174,7 @@ DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEn
info->eq = jenv->GetMethodID(jcls, "eqWrapper", "(JJ)V");
info->final = jenv->GetMethodID(jcls, "finWrapper", "()V");
info->decide = jenv->GetMethodID(jcls, "decideWrapper", "(JIZ)V");
info->on_binding = jenv->GetMethodID(jcls, "onBindingWrapper", "(JIZ)V");
if (!info->push || !info->pop || !info->fresh || !info->created || !info->fixed || !info->eq || !info->final || !info->decide) {
assert(false);

View file

@ -43,6 +43,13 @@ public abstract class UserPropagatorBase extends Native.UserPropagatorBase {
eq(x, y);
}
@Override
protected final boolean onBindingWrapper(long lq, long linst) {
Expr q = new Expr(ctx, lq);
Expr inst = new Expr(ctx, linst);
return on_binding(q, inst);
}
@Override
protected final UserPropagatorBase freshWrapper(long lctx) {
return fresh(new Context(lctx));
@ -77,6 +84,8 @@ public abstract class UserPropagatorBase extends Native.UserPropagatorBase {
public void fixed(Expr<?> var, Expr<?> value) {}
public void eq(Expr<?> x, Expr<?> y) {}
public boolean on_binding(Expr<?> q, Expr<?> inst) { return true; }
public void decide(Expr<?> var, int bit, boolean is_pos) {}

View file

@ -56,6 +56,7 @@ const types = {
Z3_final_eh: 'Z3_final_eh',
Z3_created_eh: 'Z3_created_eh',
Z3_decide_eh: 'Z3_decide_eh',
Z3_on_binding_eh: 'Z3_on_binding_eh',
Z3_on_clause_eh: 'Z3_on_clause_eh',
} as unknown as Record<string, string>;

View file

@ -11814,6 +11814,16 @@ def user_prop_decide(ctx, cb, t_ref, idx, phase):
t = _to_expr_ref(to_Ast(t_ref), prop.ctx())
prop.decide(t, idx, phase)
prop.cb = old_cb
def user_prop_binding(ctx, cb, q_ref, inst_ref):
prop = _prop_closures.get(ctx)
old_cb = prop.cb
prop.cb = cb
q = _to_expr_ref(to_Ast(q_ref), prop.ctx())
inst = _to_expr_ref(to_Ast(inst_ref), prop.ctx())
r = prop.binding(q, inst)
prop.cb = old_cb
return r
_user_prop_push = Z3_push_eh(user_prop_push)
@ -11825,6 +11835,7 @@ _user_prop_final = Z3_final_eh(user_prop_final)
_user_prop_eq = Z3_eq_eh(user_prop_eq)
_user_prop_diseq = Z3_eq_eh(user_prop_diseq)
_user_prop_decide = Z3_decide_eh(user_prop_decide)
_user_prop_binding = Z3_on_binding_eh(user_prop_binding)
def PropagateFunction(name, *sig):
@ -11873,6 +11884,7 @@ class UserPropagateBase:
self.diseq = None
self.decide = None
self.created = None
self.binding = None
if ctx:
self.fresh_ctx = ctx
if s:
@ -11936,7 +11948,14 @@ class UserPropagateBase:
assert not self._ctx
if self.solver:
Z3_solver_propagate_decide(self.ctx_ref(), self.solver.solver, _user_prop_decide)
self.decide = decide
self.decide = decide
def add_on_binding(self, binding):
assert not self.binding
assert not self._ctx
if self.solver:
Z3_solver_propagate_on_binding(self.ctx_ref(), self.solver.solver, _user_prop_binding)
self.binding = binding
def push(self):
raise Z3Exception("push needs to be overwritten")

View file

@ -1440,6 +1440,7 @@ Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as
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_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t, unsigned idx, bool phase));
Z3_DECLARE_CLOSURE(Z3_on_binding_eh, bool, (void* ctx, Z3_solver_callback cb, Z3_ast q, Z3_ast inst));
Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, unsigned n, unsigned const* deps, Z3_ast_vector literals));
@ -7225,6 +7226,17 @@ extern "C" {
*/
void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh);
/**
\brief register a callback when the solver instantiates a quantifier.
If the callback returns false, the actual instantiation of the quantifier is blocked.
This allows the user propagator selectively prioritize instantiations without relying on default
or configured weights.
def_API('Z3_solver_propagate_on_binding', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_on_binding_eh)))
*/
void Z3_API Z3_solver_propagate_on_binding(Z3_context c, Z3_solver s, Z3_on_binding_eh on_binding_eh);
/**
Sets the next (registered) expression to split on.
The function returns false and ignores the given expression in case the expression is already assigned internally