Z3
 
Loading...
Searching...
No Matches
fixedpoint.go
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation 2025
2// Z3 Go API: Fixedpoint solver for Datalog and CHC (Constrained Horn Clauses)
3
4package z3
5
6/*
7#include "z3.h"
8#include <stdlib.h>
9*/
10import "C"
11import (
12 "runtime"
13 "unsafe"
14)
15
16// Fixedpoint represents a fixedpoint solver for Datalog/CHC queries
17type Fixedpoint struct {
18 ctx *Context
19 ptr C.Z3_fixedpoint
20}
21
22// newFixedpoint creates a new Fixedpoint solver with proper memory management
23func newFixedpoint(ctx *Context, ptr C.Z3_fixedpoint) *Fixedpoint {
24 fp := &Fixedpoint{ctx: ctx, ptr: ptr}
25 C.Z3_fixedpoint_inc_ref(ctx.ptr, ptr)
26 runtime.SetFinalizer(fp, func(f *Fixedpoint) {
27 C.Z3_fixedpoint_dec_ref(f.ctx.ptr, f.ptr)
28 })
29 return fp
30}
31
32// NewFixedpoint creates a new fixedpoint solver
33func (ctx *Context) NewFixedpoint() *Fixedpoint {
34 ptr := C.Z3_mk_fixedpoint(ctx.ptr)
35 return newFixedpoint(ctx, ptr)
36}
37
38// GetHelp returns a string describing all available fixedpoint solver parameters
39func (f *Fixedpoint) GetHelp() string {
40 cstr := C.Z3_fixedpoint_get_help(f.ctx.ptr, f.ptr)
41 return C.GoString(cstr)
42}
43
44// SetParams sets the fixedpoint solver parameters
45func (f *Fixedpoint) SetParams(params *Params) {
46 C.Z3_fixedpoint_set_params(f.ctx.ptr, f.ptr, params.ptr)
47}
48
49// GetParamDescrs retrieves parameter descriptions for the fixedpoint solver
50func (f *Fixedpoint) GetParamDescrs() *ParamDescrs {
51 ptr := C.Z3_fixedpoint_get_param_descrs(f.ctx.ptr, f.ptr)
52 return newParamDescrs(f.ctx, ptr)
53}
54
55// Assert adds a constraint into the fixedpoint solver
56func (f *Fixedpoint) Assert(constraint *Expr) {
57 C.Z3_fixedpoint_assert(f.ctx.ptr, f.ptr, constraint.ptr)
58}
59
60// RegisterRelation registers a predicate as a recursive relation
61func (f *Fixedpoint) RegisterRelation(funcDecl *FuncDecl) {
62 C.Z3_fixedpoint_register_relation(f.ctx.ptr, f.ptr, funcDecl.ptr)
63}
64
65// AddRule adds a rule (Horn clause) to the fixedpoint solver
66// The rule should be an implication of the form body => head
67// where head is a relation and body is a conjunction of relations
68func (f *Fixedpoint) AddRule(rule *Expr, name *Symbol) {
69 var namePtr C.Z3_symbol
70 if name != nil {
71 namePtr = name.ptr
72 } else {
73 namePtr = nil
74 }
75 C.Z3_fixedpoint_add_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
76}
77
78// AddFact adds a table fact to the fixedpoint solver
79func (f *Fixedpoint) AddFact(pred *FuncDecl, args []int) {
80 if len(args) == 0 {
81 C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, 0, nil)
82 return
83 }
84
85 cArgs := make([]C.uint, len(args))
86 for i, arg := range args {
87 cArgs[i] = C.uint(arg)
88 }
89 C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, C.uint(len(args)), &cArgs[0])
90}
91
92// Query queries the fixedpoint solver with a constraint
93// Returns Satisfiable if there is a derivation, Unsatisfiable if not
94func (f *Fixedpoint) Query(query *Expr) Status {
95 result := C.Z3_fixedpoint_query(f.ctx.ptr, f.ptr, query.ptr)
96 switch result {
97 case C.Z3_L_TRUE:
98 return Satisfiable
99 case C.Z3_L_FALSE:
100 return Unsatisfiable
101 default:
102 return Unknown
103 }
104}
105
106// QueryRelations queries the fixedpoint solver with an array of relations
107// Returns Satisfiable if any relation is non-empty, Unsatisfiable otherwise
108func (f *Fixedpoint) QueryRelations(relations []*FuncDecl) Status {
109 if len(relations) == 0 {
110 return Unknown
111 }
112
113 cRelations := make([]C.Z3_func_decl, len(relations))
114 for i, rel := range relations {
115 cRelations[i] = rel.ptr
116 }
117
118 result := C.Z3_fixedpoint_query_relations(f.ctx.ptr, f.ptr, C.uint(len(relations)), &cRelations[0])
119 switch result {
120 case C.Z3_L_TRUE:
121 return Satisfiable
122 case C.Z3_L_FALSE:
123 return Unsatisfiable
124 default:
125 return Unknown
126 }
127}
128
129// UpdateRule updates a named rule in the fixedpoint solver
130func (f *Fixedpoint) UpdateRule(rule *Expr, name *Symbol) {
131 var namePtr C.Z3_symbol
132 if name != nil {
133 namePtr = name.ptr
134 } else {
135 namePtr = nil
136 }
137 C.Z3_fixedpoint_update_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
138}
139
140// GetAnswer retrieves the satisfying instance or instances of solver,
141// or definitions for the recursive predicates that show unsatisfiability
142func (f *Fixedpoint) GetAnswer() *Expr {
143 ptr := C.Z3_fixedpoint_get_answer(f.ctx.ptr, f.ptr)
144 if ptr == nil {
145 return nil
146 }
147 return newExpr(f.ctx, ptr)
148}
149
150// GetReasonUnknown retrieves explanation why fixedpoint engine returned status Unknown
151func (f *Fixedpoint) GetReasonUnknown() string {
152 cstr := C.Z3_fixedpoint_get_reason_unknown(f.ctx.ptr, f.ptr)
153 return C.GoString(cstr)
154}
155
156// GetNumLevels retrieves the number of levels explored for a given predicate
157func (f *Fixedpoint) GetNumLevels(predicate *FuncDecl) int {
158 return int(C.Z3_fixedpoint_get_num_levels(f.ctx.ptr, f.ptr, predicate.ptr))
159}
160
161// GetCoverDelta retrieves the cover delta for a given predicate and level
162func (f *Fixedpoint) GetCoverDelta(level int, predicate *FuncDecl) *Expr {
163 ptr := C.Z3_fixedpoint_get_cover_delta(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr)
164 if ptr == nil {
165 return nil
166 }
167 return newExpr(f.ctx, ptr)
168}
169
170// AddCover adds a cover constraint to a predicate at a given level
171func (f *Fixedpoint) AddCover(level int, predicate *FuncDecl, property *Expr) {
172 C.Z3_fixedpoint_add_cover(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr, property.ptr)
173}
174
175// String returns the string representation of the fixedpoint solver
176func (f *Fixedpoint) String() string {
177 cstr := C.Z3_fixedpoint_to_string(f.ctx.ptr, f.ptr, 0, nil)
178 return C.GoString(cstr)
179}
180
181// GetStatistics retrieves statistics for the fixedpoint solver
182func (f *Fixedpoint) GetStatistics() *Statistics {
183 ptr := C.Z3_fixedpoint_get_statistics(f.ctx.ptr, f.ptr)
184 return newStatistics(f.ctx, ptr)
185}
186
187// GetRules retrieves the current rules as a string
188func (f *Fixedpoint) GetRules() string {
189 return f.String()
190}
191
192// GetAssertions retrieves the fixedpoint assertions as an AST vector
193func (f *Fixedpoint) GetAssertions() *ASTVector {
194 ptr := C.Z3_fixedpoint_get_assertions(f.ctx.ptr, f.ptr)
195 return newASTVector(f.ctx, ptr)
196}
197
198// SetPredicateRepresentation sets the predicate representation for a given relation
199func (f *Fixedpoint) SetPredicateRepresentation(funcDecl *FuncDecl, kinds []C.Z3_symbol) {
200 if len(kinds) == 0 {
201 C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, 0, nil)
202 return
203 }
204 C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, C.uint(len(kinds)), &kinds[0])
205}
206
207// FromString parses a Datalog program from a string
208func (f *Fixedpoint) FromString(s string) {
209 cstr := C.CString(s)
210 defer C.free(unsafe.Pointer(cstr))
211 C.Z3_fixedpoint_from_string(f.ctx.ptr, f.ptr, cstr)
212}
213
214// FromFile parses a Datalog program from a file
215func (f *Fixedpoint) FromFile(filename string) {
216 cstr := C.CString(filename)
217 defer C.free(unsafe.Pointer(cstr))
218 C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
219}
220
221// QueryFromLvl poses a query against the asserted rules at the given level.
222// This is a Spacer-specific function.
223func (f *Fixedpoint) QueryFromLvl(query *Expr, lvl uint) Status {
224 result := C.Z3_fixedpoint_query_from_lvl(f.ctx.ptr, f.ptr, query.ptr, C.uint(lvl))
225 switch result {
226 case C.Z3_L_TRUE:
227 return Satisfiable
228 case C.Z3_L_FALSE:
229 return Unsatisfiable
230 default:
231 return Unknown
232 }
233}
234
235// GetGroundSatAnswer retrieves a bottom-up sequence of ground facts.
236// The previous call to Query or QueryFromLvl must have returned Satisfiable.
237// This is a Spacer-specific function.
238func (f *Fixedpoint) GetGroundSatAnswer() *Expr {
239 ptr := C.Z3_fixedpoint_get_ground_sat_answer(f.ctx.ptr, f.ptr)
240 if ptr == nil {
241 return nil
242 }
243 return newExpr(f.ctx, ptr)
244}
245
246// GetRulesAlongTrace returns the list of rules along the counterexample trace.
247// This is a Spacer-specific function.
248func (f *Fixedpoint) GetRulesAlongTrace() *ASTVector {
249 return newASTVector(f.ctx, C.Z3_fixedpoint_get_rules_along_trace(f.ctx.ptr, f.ptr))
250}
251
252// GetRuleNamesAlongTrace returns the list of rule names along the counterexample trace.
253// This is a Spacer-specific function.
254func (f *Fixedpoint) GetRuleNamesAlongTrace() *Symbol {
255 return newSymbol(f.ctx, C.Z3_fixedpoint_get_rule_names_along_trace(f.ctx.ptr, f.ptr))
256}
257
258// AddInvariant adds an assumed invariant for the predicate pred.
259// This is a Spacer-specific function.
260func (f *Fixedpoint) AddInvariant(pred *FuncDecl, property *Expr) {
261 C.Z3_fixedpoint_add_invariant(f.ctx.ptr, f.ptr, pred.ptr, property.ptr)
262}
263
264// GetReachable retrieves the reachable states of a predicate.
265// This is a Spacer-specific function.
266func (f *Fixedpoint) GetReachable(pred *FuncDecl) *Expr {
267 ptr := C.Z3_fixedpoint_get_reachable(f.ctx.ptr, f.ptr, pred.ptr)
268 if ptr == nil {
269 return nil
270 }
271 return newExpr(f.ctx, ptr)
272}
273
274// Statistics represents statistics for Z3 solvers
275type Statistics struct {
276 ctx *Context
277 ptr C.Z3_stats
278}
279
280// newStatistics creates a new Statistics object with proper memory management
281func newStatistics(ctx *Context, ptr C.Z3_stats) *Statistics {
282 stats := &Statistics{ctx: ctx, ptr: ptr}
283 C.Z3_stats_inc_ref(ctx.ptr, ptr)
284 runtime.SetFinalizer(stats, func(s *Statistics) {
285 C.Z3_stats_dec_ref(s.ctx.ptr, s.ptr)
286 })
287 return stats
288}
289
290// String returns the string representation of statistics
291func (s *Statistics) String() string {
292 cstr := C.Z3_stats_to_string(s.ctx.ptr, s.ptr)
293 return C.GoString(cstr)
294}
295
296// Size returns the number of statistical data entries
297func (s *Statistics) Size() int {
298 return int(C.Z3_stats_size(s.ctx.ptr, s.ptr))
299}
300
301// GetKey returns the key (name) of a statistical data entry at the given index
302func (s *Statistics) GetKey(idx int) string {
303 cstr := C.Z3_stats_get_key(s.ctx.ptr, s.ptr, C.uint(idx))
304 return C.GoString(cstr)
305}
306
307// IsUint returns true if the statistical data at the given index is unsigned integer
308func (s *Statistics) IsUint(idx int) bool {
309 return bool(C.Z3_stats_is_uint(s.ctx.ptr, s.ptr, C.uint(idx)))
310}
311
312// IsDouble returns true if the statistical data at the given index is double
313func (s *Statistics) IsDouble(idx int) bool {
314 return bool(C.Z3_stats_is_double(s.ctx.ptr, s.ptr, C.uint(idx)))
315}
316
317// GetUintValue returns the unsigned integer value at the given index
318func (s *Statistics) GetUintValue(idx int) uint64 {
319 return uint64(C.Z3_stats_get_uint_value(s.ctx.ptr, s.ptr, C.uint(idx)))
320}
321
322// GetDoubleValue returns the double value at the given index
323func (s *Statistics) GetDoubleValue(idx int) float64 {
324 return float64(C.Z3_stats_get_double_value(s.ctx.ptr, s.ptr, C.uint(idx)))
325}