Z3
 
Loading...
Searching...
No Matches
propagator.go
Go to the documentation of this file.
1package z3
2
3/*
4#include "z3.h"
5#include <stdint.h>
6
7// Declarations for C helper functions defined in propagator_bridge.c
8extern void z3go_solver_propagate_init(Z3_context ctx, Z3_solver s, uintptr_t user_ctx);
9extern void z3go_solver_propagate_fixed(Z3_context ctx, Z3_solver s);
10extern void z3go_solver_propagate_final(Z3_context ctx, Z3_solver s);
11extern void z3go_solver_propagate_eq(Z3_context ctx, Z3_solver s);
12extern void z3go_solver_propagate_diseq(Z3_context ctx, Z3_solver s);
13extern void z3go_solver_propagate_created(Z3_context ctx, Z3_solver s);
14extern void z3go_solver_propagate_decide(Z3_context ctx, Z3_solver s);
15extern void z3go_solver_propagate_on_binding(Z3_context ctx, Z3_solver s);
16extern void z3go_solver_register_on_clause(Z3_context ctx, Z3_solver s, uintptr_t user_ctx);
17*/
18import "C"
19import (
20 "runtime/cgo"
21)
22
23// UserPropagator implements a custom theory propagator for Z3.
24// Embed this type and override callback methods to implement a propagator.
25//
26// Example usage:
27//
28// type MyPropagator struct {
29// z3.UserPropagator
30// }
31//
32// func (p *MyPropagator) Push() { ... }
33// func (p *MyPropagator) Pop(n uint) { ... }
34// func (p *MyPropagator) Fresh(ctx *z3.Context) z3.UserPropagatorCallbacks { return &MyPropagator{} }
35type UserPropagator struct {
36 ctx *Context
37 solver *Solver
38 cb C.Z3_solver_callback // current callback context, valid only during a callback
39 handle cgo.Handle // handle for passing to C as void* context
40 iface UserPropagatorCallbacks
41}
42
43// UserPropagatorCallbacks is the interface that a user propagator must implement.
44// Push, Pop, and Fresh are mandatory. The other callbacks are optional.
45type UserPropagatorCallbacks interface {
46 // Push is called when the solver creates a new backtracking scope.
47 Push()
48 // Pop is called when the solver backtracks n scopes.
49 Pop(n uint)
50 // Fresh is called when the solver spawns a new internal solver instance.
51 // Return a propagator for the new context. The callbacks registered on the
52 // original propagator will also be registered on the fresh one.
53 Fresh(ctx *Context) UserPropagatorCallbacks
54}
55
56// FixedHandler is implemented by propagators that handle fixed-value assignments.
57type FixedHandler interface {
58 Fixed(term *Expr, value *Expr)
59}
60
61// FinalHandler is implemented by propagators that handle the final check.
62// The final check is invoked when all decision variables have been assigned.
63type FinalHandler interface {
64 Final()
65}
66
67// EqHandler is implemented by propagators that handle expression equalities.
68type EqHandler interface {
69 Eq(a *Expr, b *Expr)
70}
71
72// DiseqHandler is implemented by propagators that handle expression disequalities.
73type DiseqHandler interface {
74 Diseq(a *Expr, b *Expr)
75}
76
77// CreatedHandler is implemented by propagators that handle term creation events.
78// Terms are created when they use a function declared with PropagatorDeclare.
79type CreatedHandler interface {
80 Created(t *Expr)
81}
82
83// DecideHandler is implemented by propagators that handle solver decision events.
84type DecideHandler interface {
85 Decide(t *Expr, idx uint, phase bool)
86}
87
88// OnBindingHandler is implemented by propagators that handle quantifier binding events.
89// Return false to block the instantiation.
90type OnBindingHandler interface {
91 OnBinding(q *Expr, inst *Expr) bool
92}
93
94// newUserPropagator creates a new UserPropagator wrapping the given callbacks.
95func newUserPropagator(ctx *Context, solver *Solver, iface UserPropagatorCallbacks) *UserPropagator {
96 p := &UserPropagator{
97 ctx: ctx,
98 solver: solver,
99 iface: iface,
100 }
101 p.handle = cgo.NewHandle(p)
102 C.z3go_solver_propagate_init(ctx.ptr, solver.ptr, C.uintptr_t(p.handle))
103 return p
104}
105
106// Close releases the resources associated with this propagator.
107// It must be called when the propagator is no longer needed.
108func (p *UserPropagator) Close() {
109 if p.handle != 0 {
110 p.handle.Delete()
111 p.handle = 0
112 }
113}
114
115// RegisterFixed registers the fixed-value callback.
116// The propagator's iface must implement FixedHandler.
117func (p *UserPropagator) RegisterFixed() {
118 C.z3go_solver_propagate_fixed(p.ctx.ptr, p.solver.ptr)
119}
120
121// RegisterFinal registers the final-check callback.
122// The propagator's iface must implement FinalHandler.
123func (p *UserPropagator) RegisterFinal() {
124 C.z3go_solver_propagate_final(p.ctx.ptr, p.solver.ptr)
125}
126
127// RegisterEq registers the equality callback.
128// The propagator's iface must implement EqHandler.
129func (p *UserPropagator) RegisterEq() {
130 C.z3go_solver_propagate_eq(p.ctx.ptr, p.solver.ptr)
131}
132
133// RegisterDiseq registers the disequality callback.
134// The propagator's iface must implement DiseqHandler.
135func (p *UserPropagator) RegisterDiseq() {
136 C.z3go_solver_propagate_diseq(p.ctx.ptr, p.solver.ptr)
137}
138
139// RegisterCreated registers the term-creation callback.
140// The propagator's iface must implement CreatedHandler.
141func (p *UserPropagator) RegisterCreated() {
142 C.z3go_solver_propagate_created(p.ctx.ptr, p.solver.ptr)
143}
144
145// RegisterDecide registers the decision callback.
146// The propagator's iface must implement DecideHandler.
147func (p *UserPropagator) RegisterDecide() {
148 C.z3go_solver_propagate_decide(p.ctx.ptr, p.solver.ptr)
149}
150
151// RegisterOnBinding registers the quantifier-binding callback.
152// The propagator's iface must implement OnBindingHandler.
153func (p *UserPropagator) RegisterOnBinding() {
154 C.z3go_solver_propagate_on_binding(p.ctx.ptr, p.solver.ptr)
155}
156
157// Add registers an expression for propagation.
158// Only Bool and BitVector expressions can be registered.
159// May be called during a callback (uses the solver callback) or outside (uses the solver directly).
160func (p *UserPropagator) Add(e *Expr) {
161 if p.cb != nil {
162 C.Z3_solver_propagate_register_cb(p.ctx.ptr, p.cb, e.ptr)
163 } else {
164 C.Z3_solver_propagate_register(p.ctx.ptr, p.solver.ptr, e.ptr)
165 }
166}
167
168// Consequence propagates a consequence based on fixed terms.
169// fixed is the list of fixed terms used as premises.
170// Returns true if the propagation was accepted.
171func (p *UserPropagator) Consequence(fixed []*Expr, consequence *Expr) bool {
172 return p.ConsequenceWithEqs(fixed, nil, nil, consequence)
173}
174
175// ConsequenceWithEqs propagates a consequence based on fixed values and equalities.
176// fixed are the premise fixed terms, lhs/rhs are equality premises, consequence is the result.
177// Returns true if the propagation was accepted.
178func (p *UserPropagator) ConsequenceWithEqs(fixed []*Expr, lhs []*Expr, rhs []*Expr, consequence *Expr) bool {
179 numFixed := C.uint(len(fixed))
180 numEqs := C.uint(len(lhs))
181 var fixedPtr *C.Z3_ast
182 var lhsPtr *C.Z3_ast
183 var rhsPtr *C.Z3_ast
184 if numFixed > 0 {
185 cFixed := make([]C.Z3_ast, numFixed)
186 for i, e := range fixed {
187 cFixed[i] = e.ptr
188 }
189 fixedPtr = &cFixed[0]
190 }
191 if numEqs > 0 {
192 cLhs := make([]C.Z3_ast, numEqs)
193 cRhs := make([]C.Z3_ast, numEqs)
194 for i := range lhs {
195 cLhs[i] = lhs[i].ptr
196 cRhs[i] = rhs[i].ptr
197 }
198 lhsPtr = &cLhs[0]
199 rhsPtr = &cRhs[0]
200 }
201 result := C.Z3_solver_propagate_consequence(p.ctx.ptr, p.cb,
202 numFixed, fixedPtr,
203 numEqs, lhsPtr, rhsPtr,
204 consequence.ptr)
205 return result == C.bool(true)
206}
207
208// NextSplit overrides the solver's next variable to split on.
209// This should be called during the Decide callback to override the decision.
210// phase: -1 for false, 0 for default, 1 for true (as Z3_lbool values).
211func (p *UserPropagator) NextSplit(e *Expr, idx uint, phase int) bool {
212 result := C.Z3_solver_next_split(p.ctx.ptr, p.cb, e.ptr, C.uint(idx), C.Z3_lbool(phase))
213 return result == C.bool(true)
214}
215
216// PropagatorDeclare creates an uninterpreted function declaration for the user propagator.
217// When expressions using this function are created, the Created callback is invoked.
218func (ctx *Context) PropagatorDeclare(name string, domain []*Sort, rangeSort *Sort) *FuncDecl {
219 sym := ctx.MkStringSymbol(name)
220 n := C.uint(len(domain))
221 var domainPtr *C.Z3_sort
222 if n > 0 {
223 cDomain := make([]C.Z3_sort, n)
224 for i, s := range domain {
225 cDomain[i] = s.ptr
226 }
227 domainPtr = &cDomain[0]
228 }
229 result := C.Z3_solver_propagate_declare(ctx.ptr, sym.ptr, n, domainPtr, rangeSort.ptr)
230 return newFuncDecl(ctx, result)
231}
232
233// NewUserPropagator attaches a user propagator to the solver.
234// The callbacks object must implement UserPropagatorCallbacks (Push, Pop, Fresh).
235// Optional callbacks (Fixed, Final, Eq, Diseq, Created, Decide, OnBinding)
236// are registered by calling the corresponding Register* methods on the returned propagator.
237//
238// The propagator must be closed by calling Close() when no longer needed.
239func (s *Solver) NewUserPropagator(callbacks UserPropagatorCallbacks) *UserPropagator {
240 return newUserPropagator(s.ctx, s, callbacks)
241}
242
243// OnClauseHandler is the callback function type for clause inference events.
244// proofHint is a partial derivation justifying the inference (may be nil).
245// deps contains dependency indices.
246// literals is the inferred clause as an AST vector.
247// The lifetime of proofHint and literals is limited to the callback scope.
248type OnClauseHandler func(proofHint *Expr, deps []uint, literals *ASTVector)
249
250// OnClause registers a callback for clause inferences during solving.
251// Useful for observing learned clauses, custom learning strategies,
252// clause sharing in parallel solvers, and proof extraction.
253// Call Close when the callback is no longer needed.
254type OnClause struct {
255 handle cgo.Handle
256 ctx *Context
257 handler OnClauseHandler
258}
259
260// NewOnClause registers a callback for clause inferences on the given solver.
261// The returned OnClause must be closed by calling Close() when done.
262func (s *Solver) NewOnClause(handler OnClauseHandler) *OnClause {
263 oc := &OnClause{
264 ctx: s.ctx,
265 handler: handler,
266 }
267 oc.handle = cgo.NewHandle(oc)
268 C.z3go_solver_register_on_clause(s.ctx.ptr, s.ptr, C.uintptr_t(oc.handle))
269 return oc
270}
271
272// Close releases the resources associated with this on-clause callback.
273func (oc *OnClause) Close() {
274 if oc.handle != 0 {
275 oc.handle.Delete()
276 oc.handle = 0
277 }
278}