Skip to main content

Overview

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:
  1. Proves the goal satisfiable
  2. Proves the goal unsatisfiable
  3. Produces a sequence of subgoals
  4. Fails

Goals

A goal is a set of formulas to be solved or transformed:

Basic Tactics

Z3 provides many built-in tactics:

simplify

Algebraic simplification and constant propagation

solve-eqs

Gaussian elimination for variable solving

split-clause

Split disjunctions into separate subgoals

smt

Main SMT solver as a tactic

bit-blast

Convert bit-vectors to Boolean formulas

sat

SAT solver for Boolean formulas

qe

Quantifier elimination

propagate-values

Propagate constant values

Applying Tactics

From examples/c++/example.cpp:515:

Tactic Combinators (Tacticals)

Tacticals combine tactics into more complex strategies:

Sequential Composition: Then

Apply tactics in sequence:

Parallel Composition: OrElse

Try first tactic; if it fails, try second:

Parallel: ParOr

Run tactics in parallel, return first success:

Repeat: Repeat

Repeatedly apply tactic until no progress:

Conditional: Cond

From examples/c++/example.cpp:751:

Probes

Probes measure properties of goals to guide tactic selection:

Probe Combinators

Split-Clause Example

From examples/c++/example.cpp:549:

Creating Custom Solvers

From examples/c++/example.cpp:599:

Bit-Vector Solver

Integer Arithmetic via SAT

From examples/c++/example.cpp:649:

Tactic Parameters

Configure tactics using With:

Using Tactics with Goals and Model Conversion

From examples/c++/example.cpp:680:

Fail-If with Probes

From examples/c++/example.cpp:715:

When Combinator

Try-For (Timeout)

List Available Tactics

Common Tactic Pipelines

Quantifier Elimination

Simplification Pipeline

Parallel Split Strategy

Tactic Help

Best Practices

Begin with built-in tactics before creating complex pipelines.
Probes help select appropriate tactics based on problem structure.
Measure performance to find bottlenecks:
For simple problems, the default Solver() is often sufficient.

Limitations

  • Tactics don’t support all solver features (e.g., push/pop)
  • Model conversion can be expensive for complex pipelines
  • Some tactics may not preserve equivalence (only equisatisfiability)
  • Interactive features like check-sat-assuming are limited

Solvers

Standard solver interface

SMT Solving

Core SMT concepts

Models

Working with solutions

References

  • Tactic framework: src/tactic/tactic.h
  • Tactic API: src/api/api_tactic.cpp
  • Built-in tactics: src/tactic/*/
  • Examples: examples/c++/example.cpp:515-782
  • Paper: “The Strategy Challenge in SMT Solving”