Tactics are Z3’s mechanism for composing symbolic reasoning steps into custom solving strategies. Think of tactics as transformations on goals that can be combined using tacticals (tactic combinators).From src/tactic/tactic.h:60:
Tactics process sets of formulas called Goals. When applied, four outcomes are possible:
from z3 import *x, y, z = Ints('x y z')g = Goal()g.add(x*x - y*y >= 0)# Use probe to check conditionp = Probe('num-consts')# If more than 2 constants, use simplify; else use factort = Cond(p > 2, Then('simplify'), Then('factor'))result = t(g)print(result)
Probes measure properties of goals to guide tactic selection:
from z3 import *x, y, z = Ints('x y z')g = Goal()g.add(x + y + z > 0)# Common probesnum_consts = Probe('num-consts')num_exprs = Probe('num-exprs')is_qflia = Probe('is-qflia') # Quantifier-free linear integer arithmeticprint(f"Constants: {num_consts(g)}")print(f"Expressions: {num_exprs(g)}")print(f"Is QF_LIA: {is_qflia(g)}")
from z3 import *x, y = Reals('x y')g = Goal()g.add(Or(x < 0, x > 0)) # Disjunctiong.add(x == y + 1)g.add(y < 0)# Split clause into multiple subgoalst = Tactic('split-clause')result = t(g)# Creates subgoals for each disjunctfor i, subgoal in enumerate(result): print(f"Subgoal {i}:") print(subgoal)
from z3 import *# Solve bounded integer arithmetic using SATt = Then( With('simplify', arith_lhs=True, som=True), 'normalize-bounds', 'lia2pb', # Linear integer arithmetic to pseudo-Boolean 'pb2bv', # Pseudo-Boolean to bit-vectors 'bit-blast', 'sat')s = t.solver()x, y, z = Ints('x y z')s.add(x > 0, x < 10)s.add(y > 0, y < 10)s.add(z > 0, z < 10)s.add(3*y + 2*x == z)print(s.check())print(s.model())
from z3 import *# Apply tactic to goalt = Then('simplify', 'normalize-bounds', 'solve-eqs')x, y, z = Ints('x y z')g = Goal()g.add(x > 10)g.add(y == x + 3)g.add(z > y)result = t(g)print("Transformed goal:")print(result)# Solve transformed subgoals = Solver()for formula in result[0]: s.add(formula)if s.check() == sat: m = s.model() print("Model for subgoal:", m) # Convert back to original goal original_model = result[0].convert_model(m) print("Model for original:", original_model)
from z3 import *x, y, z = Ints('x y z')g = Goal()g.add(x + y + z > 0)# Probe: number of constantsp = Probe('num-consts')print(f"Number of constants: {p(g)}")# Fail if goal has more than 2 constantst = Then( FailIf(p > 2), 'simplify')try: result = t(g) print(result)except Z3Exception as e: print(f"Tactic failed: {e}")
from z3 import *t = Then('qe', 'smt')s = t.solver()a, b, x = Ints('a b x')f = ForAll(x, Implies(x <= a, x < b))s.add(f)print(s.check())print(s.model())