Skip to main content

Overview

Z3 simplifiers are lightweight formula transformations that reduce formula complexity before solving. Unlike tactics (which are heavy preprocessing pipelines), simplifiers are fast, composable transformations that can significantly improve solver performance.

Understanding Simplifiers

Simplifiers vs. Tactics

Simplifiers:
  • Lightweight transformations
  • Fast and incremental
  • Compose naturally with and-then
  • Applied before solving
  • Don’t change the solver strategy
Tactics:
  • Heavy preprocessing pipelines
  • Complete solving strategies
  • Transform goals into subgoals
  • May apply multiple techniques

When to Use Simplifiers

Use simplifiers when you want to:
  • Preprocess formulas before solving
  • Eliminate redundant constraints
  • Propagate constants and bounds
  • Simplify arithmetic expressions
  • Improve solver performance without changing strategy

Creating and Using Simplifiers

Basic Simplifier Usage

Listing Available Simplifiers

Common Simplifiers

Core Simplifiers

simplify

General-purpose simplification:

propagate-values

Propagates constants and simplifies based on known values:

solve-eqs

Solves for variables in equations and substitutes:

elim-uncnstr

Eliminates unconstrained variables:

Arithmetic Simplifiers

normalize-bounds

Normalizes arithmetic bounds:

lia2pb

Converts linear integer arithmetic to pseudo-boolean:

Boolean Simplifiers

propagate-ineqs

Propagates inequalities:

ctx-solver-simplify

Context-aware simplification using solver state:

Composing Simplifiers

Sequential Composition

Chain simplifiers using and-then:

Building a Simplification Pipeline

Configuring Simplifiers with Parameters

Setting Simplifier Parameters

Discovering Simplifier Parameters

Getting Simplifier Help

Domain-Specific Simplification

Bit-Vector Simplification

Arithmetic Simplification

Performance Considerations

Simplifier Overhead

Best Practices

When to Use Simplifiers

Use simplifiers when:
  • Formulas contain obvious redundancies
  • Constants can be propagated
  • Variables can be eliminated
  • Bounds can be normalized
  • You want to improve performance without changing solver strategy
Avoid simplifiers when:
  • Formulas are already in optimal form
  • Simplification overhead exceeds benefits
  • Debugging and need to preserve original formula structure
Lightweight preprocessing:
Aggressive preprocessing:

Common Pitfalls

  1. Adding simplifier after assertions: Simplifiers must be added before assertions
  2. Over-simplification: Too many simplifiers can increase overhead
  3. Wrong simplifier order: Order matters in composition
  4. Forgetting reference counting: Always pair inc_ref with dec_ref

Limitations

Simplifier Restrictions

Workaround: Add simplifier first, then make assertions:

API Reference

Simplifier Creation

  • Z3_mk_simplifier - Create simplifier by name (api_tactic.cpp:533)
  • Z3_simplifier_inc_ref - Increment reference counter (api_tactic.cpp:549)
  • Z3_simplifier_dec_ref - Decrement reference counter (api_tactic.cpp:557)

Simplifier Discovery

  • Z3_get_num_simplifiers - Get number of available simplifiers (api_tactic.cpp:565)
  • Z3_get_simplifier_name - Get simplifier name by index (api_tactic.cpp:573)
  • Z3_simplifier_get_descr - Get simplifier description (api_tactic.cpp:657)

Simplifier Composition

  • Z3_simplifier_and_then - Sequential composition (api_tactic.cpp:585)
  • Z3_simplifier_using_params - Add parameters to simplifier (api_tactic.cpp:601)

Simplifier Information

  • Z3_simplifier_get_help - Get help text (api_tactic.cpp:625)
  • Z3_simplifier_get_param_descrs - Get parameter descriptions (api_tactic.cpp:641)

Solver Integration

  • Z3_solver_add_simplifier - Attach simplifier to solver (api_solver.cpp:244)

See Also