3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-01-20 01:03:20 +00:00

Add missing API methods across language bindings (#8150)

* Initial plan

* Add API coherence improvements for C#, Python, C++, and TypeScript

- C#: Add SubstituteFuns method to Expr class
- Python: Add update method to ExprRef class
- C++: Add update method to expr class
- TypeScript: Add complete Statistics API with Statistics interface, StatisticsEntry interface, StatisticsImpl class, and statistics() methods for Solver, Optimize, and Fixedpoint

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

* Add Z3_stats import and Statistics types to TypeScript bindings

- Add Z3_stats to imports in types.ts and high-level.ts
- Add Statistics and StatisticsEntry to type imports in high-level.ts
- Fixes missing type references identified in code review

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>
This commit is contained in:
Copilot 2026-01-11 10:01:04 -08:00 committed by GitHub
parent 6c90b7ec3f
commit 319db5dbb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 258 additions and 0 deletions

View file

@ -30,6 +30,7 @@ import {
Z3_solver,
Z3_sort,
Z3_sort_kind,
Z3_stats,
Z3_symbol,
Z3_symbol_kind,
Z3_tactic,
@ -100,6 +101,8 @@ import {
Solver,
Sort,
SortToExprMap,
Statistics,
StatisticsEntry,
Tactic,
Goal,
ApplyResult,
@ -1867,6 +1870,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new ModelImpl(check(Z3.solver_get_model(contextPtr, this.ptr)));
}
statistics(): Statistics<Name> {
return new StatisticsImpl(check(Z3.solver_get_statistics(contextPtr, this.ptr)));
}
reasonUnknown(): string {
return check(Z3.solver_get_reason_unknown(contextPtr, this.ptr));
}
@ -2008,6 +2015,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new ModelImpl(check(Z3.optimize_get_model(contextPtr, this.ptr)));
}
statistics(): Statistics<Name> {
return new StatisticsImpl(check(Z3.optimize_get_statistics(contextPtr, this.ptr)));
}
toString() {
return check(Z3.optimize_to_string(contextPtr, this.ptr));
}
@ -2167,6 +2178,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new AstVectorImpl(av);
}
statistics(): Statistics<Name> {
return new StatisticsImpl(check(Z3.fixedpoint_get_statistics(contextPtr, this.ptr)));
}
release() {
Z3.fixedpoint_dec_ref(contextPtr, this.ptr);
this._ptr = null;
@ -2378,6 +2393,81 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
}
}
class StatisticsImpl implements Statistics<Name> {
declare readonly __typename: Statistics['__typename'];
readonly ctx: Context<Name>;
private _ptr: Z3_stats | null;
get ptr(): Z3_stats {
_assertPtr(this._ptr);
return this._ptr;
}
constructor(ptr: Z3_stats) {
this.ctx = ctx;
this._ptr = ptr;
Z3.stats_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.stats_dec_ref(contextPtr, ptr), this);
}
size(): number {
return Z3.stats_size(contextPtr, this.ptr);
}
keys(): string[] {
const result: string[] = [];
const sz = this.size();
for (let i = 0; i < sz; i++) {
result.push(Z3.stats_get_key(contextPtr, this.ptr, i));
}
return result;
}
get(key: string): number {
const sz = this.size();
for (let i = 0; i < sz; i++) {
if (Z3.stats_get_key(contextPtr, this.ptr, i) === key) {
if (Z3.stats_is_uint(contextPtr, this.ptr, i)) {
return Z3.stats_get_uint_value(contextPtr, this.ptr, i);
} else {
return Z3.stats_get_double_value(contextPtr, this.ptr, i);
}
}
}
throw new Error(`Statistics key not found: ${key}`);
}
entries(): StatisticsEntry<Name>[] {
const result: StatisticsEntry<Name>[] = [];
const sz = this.size();
for (let i = 0; i < sz; i++) {
const key = Z3.stats_get_key(contextPtr, this.ptr, i);
const isUint = Z3.stats_is_uint(contextPtr, this.ptr, i);
const isDouble = Z3.stats_is_double(contextPtr, this.ptr, i);
const value = isUint
? Z3.stats_get_uint_value(contextPtr, this.ptr, i)
: Z3.stats_get_double_value(contextPtr, this.ptr, i);
result.push({
__typename: 'StatisticsEntry' as const,
key,
value,
isUint,
isDouble,
});
}
return result;
}
[Symbol.iterator](): Iterator<StatisticsEntry<Name>> {
return this.entries()[Symbol.iterator]();
}
release() {
Z3.stats_dec_ref(contextPtr, this.ptr);
this._ptr = null;
cleanup.unregister(this);
}
}
class FuncEntryImpl implements FuncEntry<Name> {
declare readonly __typename: FuncEntry['__typename'];

View file

@ -16,6 +16,7 @@ import {
Z3_optimize,
Z3_sort,
Z3_sort_kind,
Z3_stats,
Z3_tactic,
Z3_goal,
Z3_apply_result,
@ -958,6 +959,27 @@ export interface Solver<Name extends string = 'main'> {
model(): Model<Name>;
/**
* Retrieve statistics for the solver.
* Returns performance metrics, memory usage, decision counts, and other diagnostic information.
*
* @returns A Statistics object containing solver metrics
*
* @example
* ```typescript
* const solver = new Solver();
* const x = Int.const('x');
* solver.add(x.gt(0));
* await solver.check();
* const stats = solver.statistics();
* console.log('Statistics size:', stats.size());
* for (const entry of stats) {
* console.log(`${entry.key}: ${entry.value}`);
* }
* ```
*/
statistics(): Statistics<Name>;
/**
* Return a string describing why the last call to {@link check} returned `'unknown'`.
*
@ -1150,6 +1172,8 @@ export interface Optimize<Name extends string = 'main'> {
model(): Model<Name>;
statistics(): Statistics<Name>;
/**
* Manually decrease the reference count of the optimize
* This is automatically done when the optimize is garbage collected,
@ -1297,6 +1321,13 @@ export interface Fixedpoint<Name extends string = 'main'> {
*/
fromFile(file: string): AstVector<Name, Bool<Name>>;
/**
* Retrieve statistics for the fixedpoint solver.
* Returns performance metrics and diagnostic information.
* @returns A Statistics object containing solver metrics
*/
statistics(): Statistics<Name>;
/**
* Manually decrease the reference count of the fixedpoint
* This is automatically done when the fixedpoint is garbage collected,
@ -1442,6 +1473,77 @@ export interface Model<Name extends string = 'main'> extends Iterable<FuncDecl<N
release(): void;
}
/**
* Statistics entry representing a single key-value pair from solver statistics
*/
export interface StatisticsEntry<Name extends string = 'main'> {
/** @hidden */
readonly __typename: 'StatisticsEntry';
/** The key/name of this statistic */
readonly key: string;
/** The numeric value of this statistic */
readonly value: number;
/** True if this statistic is stored as an unsigned integer */
readonly isUint: boolean;
/** True if this statistic is stored as a double */
readonly isDouble: boolean;
}
export interface StatisticsCtor<Name extends string> {
new (): Statistics<Name>;
}
/**
* Statistics for solver operations
*
* Provides access to performance metrics, memory usage, decision counts,
* and other diagnostic information from solver operations.
*/
export interface Statistics<Name extends string = 'main'> extends Iterable<StatisticsEntry<Name>> {
/** @hidden */
readonly __typename: 'Statistics';
readonly ctx: Context<Name>;
readonly ptr: Z3_stats;
/**
* Return the number of statistical data points
* @returns The number of statistics entries
*/
size(): number;
/**
* Return the keys of all statistical data
* @returns Array of statistic keys
*/
keys(): string[];
/**
* Return a specific statistic value by key
* @param key - The key of the statistic to retrieve
* @returns The numeric value of the statistic
* @throws Error if the key doesn't exist
*/
get(key: string): number;
/**
* Return all statistics as an array of entries
* @returns Array of all statistics entries
*/
entries(): StatisticsEntry<Name>[];
/**
* Manually decrease the reference count of the statistics object
* This is automatically done when the statistics is garbage collected,
* but calling this eagerly can help release memory sooner.
*/
release(): void;
}
/**
* Part of {@link Context}. Used to declare uninterpreted sorts
*