mirror of
https://github.com/Z3Prover/z3
synced 2025-09-08 02:31:24 +00:00
360 lines
No EOL
12 KiB
C
360 lines
No EOL
12 KiB
C
/**
|
|
* Example demonstrating the UserPropagator callback for quantifier instantiations in Z3 C API.
|
|
*
|
|
* This feature was added in Z3 version 4.15.3 and allows user propagators to intercept
|
|
* and control quantifier instantiations using the Z3_solver_propagate_on_binding API.
|
|
*
|
|
* The callback receives the quantifier and its proposed instantiation, and can return
|
|
* Z3_FALSE to discard the instantiation, providing fine-grained control over the
|
|
* quantifier instantiation process.
|
|
*
|
|
* To compile:
|
|
* gcc -I /path/to/z3/src/api quantifier_instantiation_callback.c \
|
|
* -L /path/to/z3/build -lz3 -o quantifier_instantiation_callback
|
|
*
|
|
* To run:
|
|
* LD_LIBRARY_PATH=/path/to/z3/build ./quantifier_instantiation_callback
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <z3.h>
|
|
|
|
// Global counters for demonstration
|
|
static int instantiation_counter = 0;
|
|
static int allowed_counter = 0;
|
|
static int blocked_counter = 0;
|
|
|
|
/**
|
|
* User-defined callback for quantifier instantiation control.
|
|
*
|
|
* This function is called by Z3 whenever it wants to instantiate a quantifier.
|
|
*
|
|
* @param ctx User context (can be used to pass custom data)
|
|
* @param cb Solver callback object
|
|
* @param q The quantifier being instantiated
|
|
* @param inst The proposed instantiation
|
|
* @return Z3_TRUE to allow the instantiation, Z3_FALSE to discard it
|
|
*/
|
|
Z3_bool quantifier_instantiation_callback(void* ctx, Z3_solver_callback cb, Z3_ast q, Z3_ast inst) {
|
|
// Get the Z3 context from the callback
|
|
Z3_context z3_ctx = Z3_solver_callback_get_context(cb);
|
|
|
|
instantiation_counter++;
|
|
|
|
// Get string representations for logging
|
|
Z3_string q_str = Z3_ast_to_string(z3_ctx, q);
|
|
Z3_string inst_str = Z3_ast_to_string(z3_ctx, inst);
|
|
|
|
printf("Instantiation #%d:\n", instantiation_counter);
|
|
printf(" Quantifier: %s\n", q_str);
|
|
printf(" Instantiation: %s\n", inst_str);
|
|
|
|
// Example filtering logic: allow only the first 3 instantiations
|
|
// In practice, you might implement more sophisticated filtering
|
|
Z3_bool allow = (instantiation_counter <= 3) ? Z3_TRUE : Z3_FALSE;
|
|
|
|
if (allow) {
|
|
allowed_counter++;
|
|
printf(" -> ALLOWED (#%d)\n", allowed_counter);
|
|
} else {
|
|
blocked_counter++;
|
|
printf(" -> BLOCKED (#%d)\n", blocked_counter);
|
|
}
|
|
|
|
printf("\n");
|
|
return allow;
|
|
}
|
|
|
|
/**
|
|
* Example demonstrating basic quantifier instantiation control.
|
|
*/
|
|
void example_basic_control() {
|
|
printf("============================================================\n");
|
|
printf("BASIC QUANTIFIER INSTANTIATION CONTROL EXAMPLE\n");
|
|
printf("============================================================\n");
|
|
|
|
// Create Z3 context and solver
|
|
Z3_config cfg = Z3_mk_config();
|
|
Z3_context ctx = Z3_mk_context(cfg);
|
|
Z3_del_config(cfg);
|
|
|
|
Z3_solver s = Z3_mk_solver(ctx);
|
|
Z3_solver_inc_ref(ctx, s);
|
|
|
|
// Register the quantifier instantiation callback
|
|
Z3_solver_propagate_on_binding(ctx, s, quantifier_instantiation_callback);
|
|
|
|
// Create sorts and symbols
|
|
Z3_sort int_sort = Z3_mk_int_sort(ctx);
|
|
Z3_symbol f_name = Z3_mk_string_symbol(ctx, "f");
|
|
Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x");
|
|
|
|
// Create function declaration f: Int -> Int
|
|
Z3_func_decl f = Z3_mk_func_decl(ctx, f_name, 1, &int_sort, int_sort);
|
|
|
|
// Create variables
|
|
Z3_ast x = Z3_mk_bound(ctx, 0, int_sort);
|
|
Z3_ast f_x = Z3_mk_app(ctx, f, 1, &x);
|
|
|
|
// Create axiom: forall x. f(x) >= 0
|
|
Z3_ast zero = Z3_mk_int(ctx, 0, int_sort);
|
|
Z3_ast f_x_geq_0 = Z3_mk_ge(ctx, f_x, zero);
|
|
|
|
Z3_ast axiom = Z3_mk_forall_const(ctx, 0, 1, &x, 0, NULL, f_x_geq_0);
|
|
Z3_solver_assert(ctx, s, axiom);
|
|
|
|
// Create constants and add constraints
|
|
Z3_symbol a_name = Z3_mk_string_symbol(ctx, "a");
|
|
Z3_symbol b_name = Z3_mk_string_symbol(ctx, "b");
|
|
Z3_symbol c_name = Z3_mk_string_symbol(ctx, "c");
|
|
|
|
Z3_ast a = Z3_mk_const(ctx, a_name, int_sort);
|
|
Z3_ast b = Z3_mk_const(ctx, b_name, int_sort);
|
|
Z3_ast c = Z3_mk_const(ctx, c_name, int_sort);
|
|
|
|
// Add constraints that might trigger instantiations
|
|
Z3_ast f_a = Z3_mk_app(ctx, f, 1, &a);
|
|
Z3_ast f_b = Z3_mk_app(ctx, f, 1, &b);
|
|
Z3_ast f_c = Z3_mk_app(ctx, f, 1, &c);
|
|
|
|
Z3_ast ten = Z3_mk_int(ctx, 10, int_sort);
|
|
Z3_ast twenty = Z3_mk_int(ctx, 20, int_sort);
|
|
Z3_ast thirty = Z3_mk_int(ctx, 30, int_sort);
|
|
|
|
Z3_solver_assert(ctx, s, Z3_mk_lt(ctx, f_a, ten));
|
|
Z3_solver_assert(ctx, s, Z3_mk_lt(ctx, f_b, twenty));
|
|
Z3_solver_assert(ctx, s, Z3_mk_lt(ctx, f_c, thirty));
|
|
|
|
// Add specific values
|
|
Z3_ast one = Z3_mk_int(ctx, 1, int_sort);
|
|
Z3_ast two = Z3_mk_int(ctx, 2, int_sort);
|
|
Z3_ast three = Z3_mk_int(ctx, 3, int_sort);
|
|
|
|
Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, a, one));
|
|
Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, b, two));
|
|
Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, c, three));
|
|
|
|
printf("Checking satisfiability with instantiation control...\n");
|
|
Z3_lbool result = Z3_solver_check(ctx, s);
|
|
|
|
switch (result) {
|
|
case Z3_L_TRUE:
|
|
printf("Result: SAT\n");
|
|
{
|
|
Z3_model model = Z3_solver_get_model(ctx, s);
|
|
if (model) {
|
|
Z3_model_inc_ref(ctx, model);
|
|
printf("Model: %s\n", Z3_model_to_string(ctx, model));
|
|
Z3_model_dec_ref(ctx, model);
|
|
}
|
|
}
|
|
break;
|
|
case Z3_L_FALSE:
|
|
printf("Result: UNSAT\n");
|
|
break;
|
|
case Z3_L_UNDEF:
|
|
printf("Result: UNKNOWN\n");
|
|
break;
|
|
}
|
|
|
|
// Print statistics
|
|
printf("\nInstantiation Statistics:\n");
|
|
printf(" Total attempts: %d\n", instantiation_counter);
|
|
printf(" Allowed: %d\n", allowed_counter);
|
|
printf(" Blocked: %d\n", blocked_counter);
|
|
|
|
// Cleanup
|
|
Z3_solver_dec_ref(ctx, s);
|
|
Z3_del_context(ctx);
|
|
}
|
|
|
|
// Structure for advanced callback context
|
|
typedef struct {
|
|
int max_instantiations_per_pattern;
|
|
char** patterns;
|
|
int* counts;
|
|
int num_patterns;
|
|
int capacity;
|
|
} AdvancedCallbackContext;
|
|
|
|
/**
|
|
* Find or add a pattern in the context.
|
|
*/
|
|
int find_or_add_pattern(AdvancedCallbackContext* context, const char* pattern) {
|
|
// Look for existing pattern
|
|
for (int i = 0; i < context->num_patterns; i++) {
|
|
if (strcmp(context->patterns[i], pattern) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// Add new pattern if space available
|
|
if (context->num_patterns < context->capacity) {
|
|
int index = context->num_patterns++;
|
|
context->patterns[index] = malloc(strlen(pattern) + 1);
|
|
strcpy(context->patterns[index], pattern);
|
|
context->counts[index] = 0;
|
|
return index;
|
|
}
|
|
|
|
return -1; // No space
|
|
}
|
|
|
|
/**
|
|
* Advanced callback that tracks instantiation patterns.
|
|
*/
|
|
Z3_bool advanced_instantiation_callback(void* ctx, Z3_solver_callback cb, Z3_ast q, Z3_ast inst) {
|
|
AdvancedCallbackContext* context = (AdvancedCallbackContext*)ctx;
|
|
|
|
// Get string representation of the instantiation
|
|
Z3_context z3_ctx = Z3_solver_callback_get_context(cb);
|
|
Z3_string inst_str = Z3_ast_to_string(z3_ctx, inst);
|
|
|
|
// Find or add this pattern
|
|
int pattern_index = find_or_add_pattern(context, inst_str);
|
|
if (pattern_index == -1) {
|
|
printf("Warning: Pattern storage full, allowing instantiation\n");
|
|
return Z3_TRUE;
|
|
}
|
|
|
|
// Increment count for this pattern
|
|
context->counts[pattern_index]++;
|
|
int count = context->counts[pattern_index];
|
|
|
|
// Allow only up to max_instantiations_per_pattern of the same pattern
|
|
Z3_bool allow = (count <= context->max_instantiations_per_pattern) ? Z3_TRUE : Z3_FALSE;
|
|
|
|
printf("Instantiation: %s (attempt #%d)\n", inst_str, count);
|
|
printf(" -> %s\n", allow ? "ALLOWED" : "BLOCKED");
|
|
|
|
return allow;
|
|
}
|
|
|
|
/**
|
|
* Example demonstrating advanced instantiation filtering.
|
|
*/
|
|
void example_advanced_filtering() {
|
|
printf("\n============================================================\n");
|
|
printf("ADVANCED INSTANTIATION FILTERING EXAMPLE\n");
|
|
printf("============================================================\n");
|
|
|
|
// Create callback context
|
|
AdvancedCallbackContext context;
|
|
context.max_instantiations_per_pattern = 2;
|
|
context.capacity = 100;
|
|
context.num_patterns = 0;
|
|
context.patterns = malloc(context.capacity * sizeof(char*));
|
|
context.counts = malloc(context.capacity * sizeof(int));
|
|
|
|
// Create Z3 context and solver
|
|
Z3_config cfg = Z3_mk_config();
|
|
Z3_context ctx = Z3_mk_context(cfg);
|
|
Z3_del_config(cfg);
|
|
|
|
Z3_solver s = Z3_mk_solver(ctx);
|
|
Z3_solver_inc_ref(ctx, s);
|
|
|
|
// Register the advanced callback with context
|
|
Z3_solver_propagate_on_binding(ctx, s, advanced_instantiation_callback);
|
|
|
|
// Create sorts and function declaration
|
|
Z3_sort int_sort = Z3_mk_int_sort(ctx);
|
|
Z3_sort bool_sort = Z3_mk_bool_sort(ctx);
|
|
Z3_sort P_domain[2] = {int_sort, int_sort};
|
|
|
|
Z3_symbol P_name = Z3_mk_string_symbol(ctx, "P");
|
|
Z3_func_decl P = Z3_mk_func_decl(ctx, P_name, 2, P_domain, bool_sort);
|
|
|
|
// Create bound variables for quantifier
|
|
Z3_ast x = Z3_mk_bound(ctx, 1, int_sort); // Note: index 1 for second variable in binding order
|
|
Z3_ast y = Z3_mk_bound(ctx, 0, int_sort); // Note: index 0 for first variable in binding order
|
|
|
|
// Create P(x, y) and P(y, x)
|
|
Z3_ast xy_args[2] = {x, y};
|
|
Z3_ast yx_args[2] = {y, x};
|
|
Z3_ast P_xy = Z3_mk_app(ctx, P, 2, xy_args);
|
|
Z3_ast P_yx = Z3_mk_app(ctx, P, 2, yx_args);
|
|
|
|
// Create implication: P(x, y) => P(y, x)
|
|
Z3_ast implication = Z3_mk_implies(ctx, P_xy, P_yx);
|
|
|
|
// Create quantified formula: forall x, y. P(x, y) => P(y, x)
|
|
Z3_ast vars[2] = {x, y};
|
|
Z3_ast axiom = Z3_mk_forall_const(ctx, 0, 2, vars, 0, NULL, implication);
|
|
Z3_solver_assert(ctx, s, axiom);
|
|
|
|
// Create constants
|
|
Z3_symbol a_name = Z3_mk_string_symbol(ctx, "a");
|
|
Z3_symbol b_name = Z3_mk_string_symbol(ctx, "b");
|
|
Z3_symbol c_name = Z3_mk_string_symbol(ctx, "c");
|
|
|
|
Z3_ast a = Z3_mk_const(ctx, a_name, int_sort);
|
|
Z3_ast b = Z3_mk_const(ctx, b_name, int_sort);
|
|
Z3_ast c = Z3_mk_const(ctx, c_name, int_sort);
|
|
|
|
// Add facts that will trigger instantiations
|
|
Z3_ast ab_args[2] = {a, b};
|
|
Z3_ast bc_args[2] = {b, c};
|
|
Z3_ast ca_args[2] = {c, a};
|
|
|
|
Z3_solver_assert(ctx, s, Z3_mk_app(ctx, P, 2, ab_args));
|
|
Z3_solver_assert(ctx, s, Z3_mk_app(ctx, P, 2, bc_args));
|
|
Z3_solver_assert(ctx, s, Z3_mk_app(ctx, P, 2, ca_args));
|
|
|
|
printf("Checking satisfiability with advanced filtering...\n");
|
|
Z3_lbool result = Z3_solver_check(ctx, s);
|
|
|
|
switch (result) {
|
|
case Z3_L_TRUE:
|
|
printf("Result: SAT\n");
|
|
{
|
|
Z3_model model = Z3_solver_get_model(ctx, s);
|
|
if (model) {
|
|
Z3_model_inc_ref(ctx, model);
|
|
printf("Model: %s\n", Z3_model_to_string(ctx, model));
|
|
Z3_model_dec_ref(ctx, model);
|
|
}
|
|
}
|
|
break;
|
|
case Z3_L_FALSE:
|
|
printf("Result: UNSAT\n");
|
|
break;
|
|
case Z3_L_UNDEF:
|
|
printf("Result: UNKNOWN\n");
|
|
break;
|
|
}
|
|
|
|
// Print pattern statistics
|
|
printf("\nInstantiation Pattern Statistics:\n");
|
|
for (int i = 0; i < context.num_patterns; i++) {
|
|
printf(" %s: %d attempts\n", context.patterns[i], context.counts[i]);
|
|
}
|
|
|
|
// Cleanup context
|
|
for (int i = 0; i < context.num_patterns; i++) {
|
|
free(context.patterns[i]);
|
|
}
|
|
free(context.patterns);
|
|
free(context.counts);
|
|
|
|
// Cleanup Z3 objects
|
|
Z3_solver_dec_ref(ctx, s);
|
|
Z3_del_context(ctx);
|
|
}
|
|
|
|
int main() {
|
|
printf("Z3 Quantifier Instantiation Callback Examples\n");
|
|
printf("==============================================\n\n");
|
|
|
|
// Run examples
|
|
example_basic_control();
|
|
example_advanced_filtering();
|
|
|
|
printf("\n============================================================\n");
|
|
printf("All examples completed successfully!\n");
|
|
printf("============================================================\n");
|
|
|
|
return 0;
|
|
} |