Simplification is one of the most fundamental activities in interactive theorem proving. Mathlib provides a layered set of tactics for rewriting, normalizing, and rearranging expressions: the powerfulDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/leanprover-community/mathlib4/llms.txt
Use this file to discover all available pages before exploring further.
simp engine and its variants, coercion-handling tactics like norm_cast and push_cast, logical normalization with push_neg, and the congruence tactic gcongr for inequality reasoning. Understanding how these tactics work and when to reach for each one is key to writing efficient Mathlib proofs.
simp
simp is Mathlib’s central simplification tactic. It repeatedly applies rewrite rules — primarily lemmas tagged with the @[simp] attribute — until no more rewrites apply. It is used everywhere: to unfold definitions, reduce numeric expressions, simplify logical connectives, and normalize terms before other tactics take over.
- Basic forms
- Location variants
- Debugging
simp flags and options:
| Syntax | Effect |
|---|---|
simp | Full simp set, simplify goal |
simp only [l1, l2] | Use only the listed lemmas |
simp [h] | Add h on top of the default simp set |
simp [*] | Use all local hypotheses as rewrite rules |
simp at h | Simplify hypothesis h |
simp only [] at * | Apply listed lemmas everywhere |
simp? | Print the simp only call that succeeded |
simp (config := { … }) | Pass a Simp.Config for fine-grained control |
Adding your own simp lemmas
Adding your own simp lemmas
Tag a lemma with Use
@[simp] to include it in the default simp set:@[simp] for lemmas that:- Simplify a term to a structurally simpler one
- Have a single canonical direction (left-to-right)
- Do not introduce new variables on the right-hand side
@[simp] for lemmas that could create rewriting loops.simp_rw
simp_rw behaves like simp only but rewrites under binders — inside fun, ∀, ∃, and similar constructs. This is the main difference from simp only, which cannot rewrite under a lambda abstraction.
simp_all
simp_all is a powerful variant that simplifies the entire proof state — the goal and every hypothesis — simultaneously, using all hypotheses as rewrite rules for each other. This can produce shorter proofs than chaining simp at * calls, but it can also be slower and less predictable.
norm_cast and push_cast
Lean 4 frequently involves coercions between numeric types — ℕ → ℤ → ℚ → ℝ — and these coercions can clutter proof goals. The cast-handling tactics normalize or redirect these coercions.
norm_cast normalizes expressions involving coercions by applying congruence lemmas tagged @[norm_cast]. It tries to move all casts to the outermost position, canceling redundant coercions.
push_cast pushes coercions inward — towards the leaves of an expression tree. This is useful when you want to work with the cast applied to individual subterms.
pull_cast (inverse of push_cast) pulls coercions outward.
- norm_cast
- push_cast
- cast helpers
push_neg
push_neg pushes negations inward through quantifiers and logical connectives, transforming them according to the following rules:
| Input | Output |
|---|---|
¬ ∀ x, P x | ∃ x, ¬ P x |
¬ ∃ x, P x | ∀ x, ¬ P x |
¬ (P ∧ Q) | P → ¬ Q |
¬ (P → Q) | P ∧ ¬ Q |
¬ (a < b) | b ≤ a |
¬ (a ≤ b) | b < a |
¬ (a = b) | a ≠ b |
gcongr
gcongr (generalized congruence) applies congruence lemmas tagged with @[gcongr] to prove goals of the form f a ≤ f b, f a < f b, or f a = f b from hypotheses a ≤ b, a < b. It handles arithmetic operations, min, max, and many order-theoretic constructions.
gcongr generates side goals for the positivity/nonnegativity conditions it cannot discharge automatically. Remaining goals are typically closed by linarith, positivity, or norm_num.ring_nf (as a simplification tactic)
While ring_nf is primarily covered in the Arithmetic Tactics page, it plays a role in simplification workflows as well. After simp or push_cast, expressions may be left in a non-normal ring form. ring_nf normalizes them: