3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-09-12 12:41:27 +00:00

Add comprehensive documentation and examples for UserPropagator quantifier instantiation callbacks

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-08-29 01:49:26 +00:00
parent f3f0171f35
commit 2e031bc7fc
6 changed files with 1809 additions and 0 deletions

View file

@ -0,0 +1,306 @@
#!/usr/bin/env python3
"""
Example demonstrating the UserPropagator callback for quantifier instantiations in Z3.
This feature was added in Z3 version 4.15.3 and allows user propagators to intercept
and control quantifier instantiations. The callback receives the quantifier and its
proposed instantiation, and can return False to discard the instantiation.
This provides fine-grained control over the quantifier instantiation process,
allowing users to:
1. Inspect and log instantiations
2. Filter out undesired instantiations
3. Implement custom instantiation strategies
4. Delay certain instantiations for performance reasons
"""
from z3 import *
class QuantifierInstantiationController(UserPropagateBase):
"""
A user propagator that controls quantifier instantiations.
This example demonstrates how to:
1. Register a quantifier instantiation callback
2. Inspect proposed instantiations
3. Selectively allow or discard instantiations
"""
def __init__(self, s=None, ctx=None):
UserPropagateBase.__init__(self, s, ctx)
self.instantiation_count = 0
self.allowed_instantiations = 0
self.blocked_instantiations = 0
self.instantiation_log = []
# Register the quantifier instantiation callback
self.add_on_binding(self.on_quantifier_instantiation)
def on_quantifier_instantiation(self, quantifier, instantiation):
"""
Callback invoked when Z3 wants to instantiate a quantifier.
Args:
quantifier: The quantifier being instantiated (Z3 AST)
instantiation: The proposed instantiation (Z3 AST)
Returns:
bool: True to allow the instantiation, False to discard it
"""
self.instantiation_count += 1
# Log the instantiation for inspection
q_str = str(quantifier)
inst_str = str(instantiation)
self.instantiation_log.append((q_str, inst_str))
print(f"Instantiation #{self.instantiation_count}:")
print(f" Quantifier: {q_str}")
print(f" Instantiation: {inst_str}")
# Example filtering logic: allow only the first 3 instantiations
# In practice, you might filter based on the structure of the instantiation,
# performance considerations, or other criteria
allow = self.instantiation_count <= 3
if allow:
self.allowed_instantiations += 1
print(f" -> ALLOWED (#{self.allowed_instantiations})")
else:
self.blocked_instantiations += 1
print(f" -> BLOCKED (#{self.blocked_instantiations})")
print()
return allow
def push(self):
# Required method for user propagators
pass
def pop(self, num_scopes):
# Required method for user propagators
pass
def fresh(self, new_ctx):
# Required method for user propagators
return QuantifierInstantiationController(ctx=new_ctx)
def get_statistics(self):
"""Return statistics about instantiation control."""
return {
'total_instantiation_attempts': self.instantiation_count,
'allowed_instantiations': self.allowed_instantiations,
'blocked_instantiations': self.blocked_instantiations,
'instantiation_log': self.instantiation_log
}
def example_basic_quantifier_control():
"""
Basic example showing how to control quantifier instantiations.
"""
print("=" * 60)
print("BASIC QUANTIFIER INSTANTIATION CONTROL EXAMPLE")
print("=" * 60)
# Create solver with user propagator
s = Solver()
controller = QuantifierInstantiationController(s)
# Create a simple quantified formula
x = Int('x')
f = Function('f', IntSort(), IntSort())
# Add a quantified axiom: forall x. f(x) >= 0
axiom = ForAll([x], f(x) >= 0)
s.add(axiom)
# Add constraints that might trigger instantiations
a, b, c = Ints('a b c')
s.add(f(a) < 10)
s.add(f(b) < 20)
s.add(f(c) < 30)
# Also add constraints that should conflict if all instantiations are allowed
s.add(a == 1)
s.add(b == 2)
s.add(c == 3)
print("Checking satisfiability with instantiation control...")
result = s.check()
print(f"Result: {result}")
if result == sat:
print(f"Model: {s.model()}")
# Print statistics
stats = controller.get_statistics()
print(f"\nInstantiation Statistics:")
print(f" Total attempts: {stats['total_instantiation_attempts']}")
print(f" Allowed: {stats['allowed_instantiations']}")
print(f" Blocked: {stats['blocked_instantiations']}")
def example_advanced_filtering():
"""
Advanced example showing more sophisticated instantiation filtering.
"""
print("\n" + "=" * 60)
print("ADVANCED INSTANTIATION FILTERING EXAMPLE")
print("=" * 60)
class AdvancedController(UserPropagateBase):
def __init__(self, s=None, ctx=None):
UserPropagateBase.__init__(self, s, ctx)
self.instantiations_by_term = {}
self.add_on_binding(self.smart_filter)
def smart_filter(self, quantifier, instantiation):
"""
Smart filtering based on instantiation content.
"""
# Extract information about the instantiation
q_str = str(quantifier)
inst_str = str(instantiation)
# Count instantiations per term pattern
if inst_str not in self.instantiations_by_term:
self.instantiations_by_term[inst_str] = 0
self.instantiations_by_term[inst_str] += 1
# Allow only up to 2 instantiations of the same pattern
allow = self.instantiations_by_term[inst_str] <= 2
print(f"Instantiation: {inst_str} (attempt #{self.instantiations_by_term[inst_str]})")
print(f" -> {'ALLOWED' if allow else 'BLOCKED'}")
return allow
def push(self):
pass
def pop(self, num_scopes):
pass
def fresh(self, new_ctx):
return AdvancedController(ctx=new_ctx)
# Create solver with advanced controller
s = Solver()
controller = AdvancedController(s)
# Create a more complex scenario
x, y = Ints('x y')
P = Function('P', IntSort(), IntSort(), BoolSort())
# Add quantified formula: forall x, y. P(x, y) => P(y, x)
axiom = ForAll([x, y], Implies(P(x, y), P(y, x)))
s.add(axiom)
# Add facts that will trigger instantiations
a, b, c = Ints('a b c')
s.add(P(a, b))
s.add(P(b, c))
s.add(P(c, a))
print("Checking satisfiability with advanced filtering...")
result = s.check()
print(f"Result: {result}")
if result == sat:
print(f"Model: {s.model()}")
def example_instantiation_logging():
"""
Example focused on logging and analyzing instantiation patterns.
"""
print("\n" + "=" * 60)
print("INSTANTIATION LOGGING AND ANALYSIS EXAMPLE")
print("=" * 60)
class LoggingController(UserPropagateBase):
def __init__(self, s=None, ctx=None):
UserPropagateBase.__init__(self, s, ctx)
self.log = []
self.add_on_binding(self.log_instantiation)
def log_instantiation(self, quantifier, instantiation):
"""
Log all instantiation attempts for later analysis.
"""
entry = {
'quantifier': str(quantifier),
'instantiation': str(instantiation),
'timestamp': len(self.log)
}
self.log.append(entry)
# For this example, allow all instantiations
return True
def push(self):
pass
def pop(self, num_scopes):
pass
def fresh(self, new_ctx):
return LoggingController(ctx=new_ctx)
def analyze_patterns(self):
"""Analyze logged instantiation patterns."""
print(f"Total instantiations logged: {len(self.log)}")
# Group by quantifier
by_quantifier = {}
for entry in self.log:
q = entry['quantifier']
if q not in by_quantifier:
by_quantifier[q] = []
by_quantifier[q].append(entry['instantiation'])
for q, instantiations in by_quantifier.items():
print(f"\nQuantifier: {q}")
print(f" Instantiations ({len(instantiations)}):")
for i, inst in enumerate(instantiations, 1):
print(f" {i}. {inst}")
# Create solver with logging controller
s = Solver()
controller = LoggingController(s)
# Create scenario with multiple quantifiers
x = Int('x')
f = Function('f', IntSort(), IntSort())
g = Function('g', IntSort(), IntSort())
# Add multiple quantified axioms
s.add(ForAll([x], f(x) >= x)) # f(x) is at least x
s.add(ForAll([x], g(x) <= x)) # g(x) is at most x
# Add constraints to trigger instantiations
a, b = Ints('a b')
s.add(f(a) < 5)
s.add(g(b) > 10)
s.add(a == 2)
s.add(b == 8)
print("Solving with full logging enabled...")
result = s.check()
print(f"Result: {result}")
# Analyze the logged instantiation patterns
print("\nInstantiation Analysis:")
controller.analyze_patterns()
if __name__ == "__main__":
# Run all examples
example_basic_quantifier_control()
example_advanced_filtering()
example_instantiation_logging()
print("\n" + "=" * 60)
print("All examples completed successfully!")
print("=" * 60)