The Lean Language Reference

16.11. Interactive Mode🔗

Because grind relies on the current set of annotated lemmas, proofs may stop working or take longer to process when library annotations change. Additionally, as it searches for a proof, grind may attempt many steps that don't contribute to the final result, which can take time. A script can replace grind's search, leading it directly down the successful proof path and instantiating precisely those lemmas that actually contribute to the proof. Rechecking such a proof is both faster and more resilient to library changes. This script feature is called grind's interactive mode, and scripts are written in a special-purpose tactic language.

A script for grind's interactive mode consists of Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind => followed by an indented block of grind tactics. Each of these tactics runs one of grind's engines, instantiates a theorem, performs a case split, inspects the shared state, or manages the remaining goals. The steps operate on the same “whiteboard” that the automatic search uses: the equalities, inequalities, and Boolean facts that all prior steps have discovered, together with the equivalence classes derived from them.

Interactive mode can also be used to understand why grind is unable to complete a proof. The inspection tactics reveal the current state of the whiteboard, which may make it possible to notice a missing theorem instantiation or case split that a few explicit steps or an additional annotation in a library can supply. An explicit script can also make it easier for readers to understand why grind succeeds.

Reading a grind? Proof

The structure Two can be thought of as a two-element array, indexed by Bool instead of Nat.

structure Two α where fst : α snd : α namespace Two def get (xs : Two α) (i : Bool) : α := if i then xs.fst else xs.snd

The API for Two includes a number of lemmas, annotated for use with grind:

def both (x : α) : Two α := x, x def map (f : α β) (xs : Two α) : Two β := f xs.fst, f xs.snd @[ext, grind ext] theorem ext_get {xs ys : Two α} (h : i, xs.get i = ys.get i) : xs = ys := α:Sort u_1xs:Two αys:Two αh: (i : Bool), xs.get i = ys.get ixs = ys α:Sort u_1ys:Two αfst✝:αsnd✝:αh: (i : Bool), { fst := fst✝, snd := snd✝ }.get i = ys.get i{ fst := fst✝, snd := snd✝ } = ys; α:Sort u_1fst✝¹:αsnd✝¹:αfst✝:αsnd✝:αh: (i : Bool), { fst := fst✝¹, snd := snd✝¹ }.get i = { fst := fst✝, snd := snd✝ }.get i{ fst := fst✝¹, snd := snd✝¹ } = { fst := fst✝, snd := snd✝ } All goals completed! 🐙 @[grind =] theorem get_def {xs : Two α} {i} : xs.get i = if i then xs.fst else xs.snd := rfl @[grind =] theorem map_fst {f : α β} {xs} : (map f xs).fst = f xs.fst := rfl @[grind =] theorem map_snd {f : α β} {xs} : (map f xs).snd = f xs.snd := rfl @[grind =] theorem both_fst {x : α} : (both x).fst = x := rfl @[grind =] theorem both_snd {x : α} : (both x).snd = x := rfl @[grind =] theorem both_fst_eq_snd {x : α} : (both x).fst = (both x).snd := rfl

Using these lemmas, grind can prove that Two.map distributes over Two.both. Calling grind? results in multiple suggestions for more specific calls to grind, one of which is an interactive script:

theorem map_both {x : α} {f : α β} : (both x).map f = both (f x) := α:Sort u_1β:Sort u_2x:αf:α βmap f (both x) = both (f x) Try these: [apply] grind only [= get_def, = both_fst, = map_fst, = both_snd, = map_snd, #1362, #ba8b] [apply] grind only [= get_def, = both_fst, = map_fst, = both_snd, = map_snd] [apply] grind => cases #1362 instantiate only [= get_def] cases #ba8b · instantiate only [= both_fst, = map_fst] instantiate only [= both_fst] · instantiate only [= both_snd, = map_snd] instantiate only [= both_snd]All goals completed! 🐙
Try these:
  [apply] grind only [= get_def, = both_fst, = map_fst, = both_snd, = map_snd, #1362, #ba8b]
  [apply] grind only [= get_def, = both_fst, = map_fst, = both_snd, = map_snd]
  [apply] grind =>
    cases #1362
    instantiate only [= get_def]
    cases #ba8b
    · instantiate only [= both_fst, = map_fst]
      instantiate only [= both_fst]
    · instantiate only [= both_snd, = map_snd]
      instantiate only [= both_snd]

The script makes the proof's two internal case splits explicit.

During its search, grind also instantiates Two.both_fst_eq_snd, even though that equation contributes nothing to the proof. Because it does not end up contributing to the result, none of the suggested grind calls mention it; when rechecking these suggested versions, the overhead of this needless instantiation is avoided. Setting the diagnostics option reports how many times each theorem was instantiated:

set_option diagnostics true in set_option diagnostics.threshold 1 in
[diag] Diagnostics
  • [reduction] unfolded reducible declarations (max: 47, num: 3):
  • [def_eq] heuristic for solving `f a =?= f b` (max: 5, num: 1):
    • [def_eq] Not5
  • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
example {x : α} {f : α β} : (both x).map f = both (f x) := α:Sort u_1β:Sort u_2x:αf:α βmap f (both x) = both (f x)
[grind] Diagnostics
  • [thm] E-Matching instances
  • [split] Case splits
  • [app] Applications
    • [app] snd5
    • [app] Eq3
    • [app] fst3
    • [app] ite2
    • [app] both2
    • [app] get2
    • [app] Exists1
    • [app] Two1
    • [app] map1
  • [grind] Simplifier
    • [simp] tried theorems (max: 15, num: 1):
      • [simp] eq_self15 ❌️
    • [simp] tried congruence theorems (max: 2, num: 1):
      • [simp] ite_congr2
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
All goals completed! 🐙
[grind] Diagnostics
  • [thm] E-Matching instances
  • [split] Case splits
  • [app] Applications
    • [app] snd5
    • [app] Eq3
    • [app] fst3
    • [app] ite2
    • [app] both2
    • [app] get2
    • [app] Exists1
    • [app] Two1
    • [app] map1
  • [grind] Simplifier
    • [simp] tried theorems (max: 15, num: 1):
      • [simp] eq_self15 ❌️
    • [simp] tried congruence theorems (max: 2, num: 1):
      • [simp] ite_congr2
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters

The suggested interactive-mode grind does not instantiate Two.both_fst_eq_snd:

set_option diagnostics true in set_option diagnostics.threshold 1 in
[diag] Diagnostics
  • [reduction] unfolded reducible declarations (max: 74, num: 3):
  • [def_eq] heuristic for solving `f a =?= f b` (max: 5, num: 1):
    • [def_eq] Not5
  • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
example {x : α} {f : α β} : (both x).map f = both (f x) := α:Sort u_1β:Sort u_2x:αf:α βmap f (both x) = both (f x)
[grind] Diagnostics
  • [thm] E-Matching instances
  • [split] Case splits
  • [app] Applications
    • [app] Eq3
    • [app] fst3
    • [app] snd3
    • [app] ite2
    • [app] both2
    • [app] get2
    • [app] Exists1
    • [app] Two1
    • [app] map1
  • [grind] Simplifier
    • [simp] tried theorems (max: 13, num: 1):
      • [simp] eq_self13 ❌️
    • [simp] tried congruence theorems (max: 2, num: 1):
      • [simp] ite_congr2
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
grind => α:Sort u_1β:Sort u_2x:αf:α βh✝¹:¬map f (both x) = both (f x)w✝:Boolh✝:¬(map f (both x)).get w✝ = (both (f x)).get w✝False α:Sort u_1β:Sort u_2x:αf:α βh✝¹:¬map f (both x) = both (f x)w✝:Boolh✝:¬(map f (both x)).get w✝ = (both (f x)).get w✝False α:Sort u_1β:Sort u_2x:αf:α βh✝²:¬map f (both x) = both (f x)w✝:Boolh✝¹:¬(map f (both x)).get w✝ = (both (f x)).get w✝h✝:w✝ = trueFalseα:Sort u_1β:Sort u_2x:αf:α βh✝²:¬map f (both x) = both (f x)w✝:Boolh✝¹:¬(map f (both x)).get w✝ = (both (f x)).get w✝h✝:¬w✝ = trueFalse · α:Sort u_1β:Sort u_2x:αf:α βh✝²:¬map f (both x) = both (f x)w✝:Boolh✝¹:¬(map f (both x)).get w✝ = (both (f x)).get w✝h✝:w✝ = trueFalse All goals completed! 🐙 · α:Sort u_1β:Sort u_2x:αf:α βh✝²:¬map f (both x) = both (f x)w✝:Boolh✝¹:¬(map f (both x)).get w✝ = (both (f x)).get w✝h✝:¬w✝ = trueFalse All goals completed! 🐙
[grind] Diagnostics
  • [thm] E-Matching instances
  • [split] Case splits
  • [app] Applications
    • [app] Eq3
    • [app] fst3
    • [app] snd3
    • [app] ite2
    • [app] both2
    • [app] get2
    • [app] Exists1
    • [app] Two1
    • [app] map1
  • [grind] Simplifier
    • [simp] tried theorems (max: 13, num: 1):
      • [simp] eq_self13 ❌️
    • [simp] tried congruence theorems (max: 2, num: 1):
      • [simp] ite_congr2
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters

16.11.1. Entry Points🔗

There are two versions of grind's interactive mode. The first, introduced with Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind =>, is designed for controlling grind and automatically takes certain steps that are needed for most proofs. The other, introduced with sym, allows a higher degree of manual control and is intended for use in symbolic simulation workflows.

When used with a script, grind initializes the proof by introducing all hypotheses and then invoking proof by contradiction so the initial goal is always False. It then runs the script.

syntaxInteractive grind
tactic ::= ...
    | `grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**:
every time grind discovers a new equality, inequality, or logical fact,
it writes it on the board, groups together terms known to be equal,
and lets each reasoning engine read from and contribute to the shared workspace.
These engines work together to handle equality reasoning, apply known theorems,
propagate new facts, perform case analysis, and run specialized solvers
for domains like linear arithmetic and commutative rings.

See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information.

`grind` is *not* designed for goals whose search space explodes combinatorially,
think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards,
or a 200‑variable Sudoku encoded as Boolean constraints.  Such encodings require
 thousands (or millions) of case‑splits that overwhelm `grind`’s branching search.

For **bit‑level or combinatorial problems**, consider using **`bv_decide`**.
`bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a
*compact, machine‑checkable certificate*.

### Equality reasoning

`grind` uses **congruence closure** to track equalities between terms.
When two terms are known to be equal, congruence closure automatically deduces
equalities between more complex expressions built from them.
For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b`
for any function `f`. This forms the foundation for efficient equality reasoning in `grind`.
Here is an example:
```
example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by
  grind
```

### Applying theorems using E-matching

To apply existing theorems, `grind` uses a technique called **E-matching**,
which finds matches for known theorem patterns while taking equalities into account.
Combined with congruence closure, E-matching helps `grind` discover
non-obvious consequences of theorems and equalities automatically.

Consider the following functions and theorems:
```
def f (a : Nat) : Nat :=
  a + 1

def g (a : Nat) : Nat :=
  a - 1

@[grind =]
theorem gf (x : Nat) : g (f x) = x := by
  simp [f, g]
```
The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`.
The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation,
`g (f x)`, as a pattern for E-matching.
Suppose we now have a goal involving:
```
example {a b} (h : f b = a) : g a = b := by
  grind
```
Although `g a` is not an instance of the pattern `g (f x)`,
it becomes one modulo the equation `f b = a`. By substituting `a`
with `f b` in `g a`, we obtain the term `g (f b)`,
which matches the pattern `g (f x)` with the assignment `x := b`.
Thus, the theorem `gf` is instantiated with `x := b`,
and the new equality `g (f b) = b` is asserted.
`grind` then uses congruence closure to derive the implied equality
`g a = g (f b)` and completes the proof.

The pattern used to instantiate theorems affects the effectiveness of `grind`.
For example, the pattern `g (f x)` is too restrictive in the following case:
the theorem `gf` will not be instantiated because the goal does not even
contain the function symbol `g`.

```
example (h₁ : f b = a) (h₂ : f c = a) : b = c := by
  grind
```

You can use the command `grind_pattern` to manually select a pattern for a given theorem.
In the following example, we instruct `grind` to use `f x` as the pattern,
allowing it to solve the goal automatically:
```
grind_pattern gf => f x

example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by
  grind
```
You can enable the option `trace.grind.ematch.instance` to make `grind` print a
trace message for each theorem instance it generates.

You can also specify a **multi-pattern** to control when `grind` should apply a theorem.
A multi-pattern requires that all specified patterns are matched in the current context
before the theorem is applied. This is useful for theorems such as transitivity rules,
where multiple premises must be simultaneously present for the rule to apply.
The following example demonstrates this feature using a transitivity axiom for a binary relation `R`:
```
opaque R : Int → Int → Prop
axiom Rtrans {x y z : Int} : R x y → R y z → R x z

grind_pattern Rtrans => R x y, R y z

example {a b c d} : R a b → R b c → R c d → R a d := by
  grind
```
By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to
instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context.
In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`,
and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`.

Instead of using `grind_pattern` to explicitly specify a pattern,
you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to
generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are:

- `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning).
  In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable
  (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not
  previously covered, it will add that subexpression as a pattern, until all arguments have been covered.
- `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning).
  This may fail if not all the arguments to the theorem appear in the conclusion.
- `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage,
  stopping when all arguments are covered.
- `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern.
  This may fail if not all of the arguments appear in the left-hand-side.

Here is the previous example again but using the attribute `[grind →]`
```
opaque R : Int → Int → Prop
@[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z

example {a b c d} : R a b → R b c → R c d → R a d := by
  grind
```

To control theorem instantiation and avoid generating an unbounded number of instances,
`grind` uses a generation counter. Terms in the original goal are assigned generation zero.
When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates
are assigned generation `n + 1`. This limits how far the tactic explores when applying
theorems and helps prevent an excessive number of instantiations.

#### Key options:
- `grind (ematch := <num>)` controls the number of E-matching rounds.
- `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching.
- `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`.
- `grind (gen := <num>)` sets the maximum generation.

### Linear integer arithmetic (`lia`)

`grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an
integrated decision procedure called **`lia`**.  It understands

* equalities   `p = 0`
* inequalities  `p ≤ 0`
* disequalities `p ≠ 0`
* divisibility  `d ∣ p`

The solver incrementally assigns integer values to variables; when a partial
assignment violates a constraint it adds a new, implied constraint and retries.
This *model-based* search is **complete for LIA**.

#### Key options:

* `grind -lia` disable the solver (useful for debugging)
* `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ)

#### Examples:

```
-- Even + even is never odd.
example {x y : Int} : 2 * x + 4 * y ≠ 5 := by
  grind

-- Mixing equalities and inequalities.
example {x y : Int} :
    2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by
  grind

-- Reasoning with divisibility.
example (a b : Int) :
    2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by
  grind

example (x y : Int) :
    27 ≤ 11*x + 13*y →
    11*x + 13*y ≤ 45 →
    -10 ≤ 7*x - 9*y →
    7*x - 9*y ≤ 4 → False := by
  grind

-- Types that implement the `ToInt` type-class.
example (a b c : UInt64)
    : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by
  grind
```

### Algebraic solver (`ring`)

`grind` ships with an algebraic solver nick-named **`ring`** for goals that can
be phrased as polynomial equations (or disequations) over commutative rings,
semirings, or fields.

*Works out of the box*
All core numeric types and relevant Mathlib types already provide the required
type-class instances, so the solver is ready to use in most developments.

What it can decide:

* equalities of the form `p = q`
* disequalities `p ≠ q`
* basic reasoning under field inverses (`a / b := a * b⁻¹`)
* goals that mix ring facts with other `grind` engines

#### Key options:

* `grind -ring` turn the solver off (useful when debugging)
* `grind (ringSteps := n)` cap the number of steps performed by this procedure.

#### Examples

```
open Lean Grind

example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by
  grind

-- Characteristic 256 means 16 * 16 = 0.
example [CommRing α] [IsCharP α 256] (x : α) :
    (x + 16) * (x - 16) = x^2 := by
  grind

-- Works on built-in rings such as `UInt8`.
example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by
  grind

example [CommRing α] (a b c : α) :
    a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    a^4 + b^4 = 9 - c^4 := by
  grind

example [Field α] [NoNatZeroDivisors α] (a : α) :
    1 / a + 1 / (2 * a) = 3 / (2 * a) := by
  grind
```

### Other options

- `grind (splits := <num>)` caps the *depth* of the search tree.  Once a branch performs `num` splits
  `grind` stops splitting further in that branch.
- `grind -splitIte` disables case splitting on if-then-else expressions.
- `grind -splitMatch` disables case splitting on `match` expressions.
- `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**.
- `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings.

### Additional Examples

```
example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by
  grind

example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by
  grind

example (as : Array α) (lo hi i j : Nat) :
    lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by
  grind
```
grind Configuration options for tactics. optConfig only? ([ The `!` modifier instructs `grind` to consider only minimal indexable subexpressions
when selecting patterns.
grindParam,* ] )? => grindSeq

Unlike grind, the sym tactic does not begin by introducing hypotheses or invoking proof by contradiction. The author of the script has full control over the context and goal.

syntaxSymbolic Simulation
tactic ::= ...
    | `sym` enters an interactive symbolic simulation mode built on `grind`.
Unlike `grind =>`, it does not eagerly introduce hypotheses or apply by-contradiction,
giving the user explicit control over `intro`, `apply`, and `internalize` steps.

Example:
```
example (x : Nat) : myP x → myQ x := by
  sym [myP_myQ] =>
    intro h
    finish
```
sym Configuration options for tactics. optConfig only? ([ The `!` modifier instructs `grind` to consider only minimal indexable subexpressions
when selecting patterns.
grindParam,* ] )? => grindSeq

Both tactics accept the same configuration options, the Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` only modifier, and the bracketed list of theorems and parameters that ordinary grind accepts. Certain features that are invoked automatically by grind and manually in sym have dedicated tactics that are described in the symbolic simulation mode section.

The grind? tactic takes the same arguments as grind but does not support entering interactive mode. It suggests an equivalent Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind only call, as well as an equivalent script. Within an interactive grind script, the finish? and cases? tactics similarly suggest scripts.

16.11.2. The grind Tactic Language🔗

The sequence of steps that follow Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind => is written in an indented block, with one step on each line; multiple steps may also be separated by ;. As an alternative to indentation, they may be enclosed in braces. Such a sequence is represented by the syntax category grindSeq.

Just as in an ordinary Lean tactic script, grind tactics manipulate a proof state that consists of a sequence of goals, each of which consists of a sequence of assumptions and a conclusion. When there are no further goals, the proof is complete. Most tactics operate on the main goal, and goal-management tactics redistribute goals across steps exactly as their ordinary tactic counterparts do. Additionally, grind tactics have access to the “whiteboard”.

16.11.2.1. Anchors🔗

Several tactics refer to entries on the whiteboard by an anchor, written # followed by a hexadecimal number, as in #1a. These anchors take the place of names: they identify a specific fact, equivalence class, or case-split candidate. Anchors appear in the output of the inspection tactics or in filtered states. The cases? and show_cases tactics report the anchors that cases can act on.

An anchor is a stable hash of the term that it refers to. It is computed from the term's structure while deliberately ignoring unstable implementation details such as internal names and instance arguments. This means that anchors are insensitive to many unrelated proof changes, but they may change if the structure of the referenced term changes. An anchor produced by cases? can therefore be saved in a script file as long as the referenced candidate remains structurally the same.

16.11.2.2. Filters🔗

Any grind tactic may be followed by | and a filter that selects some subset of the data in the grind state. The filtered state is then displayed both before and after the step runs, which is convenient for watching how a step changes the whiteboard. If the filter after | is empty, then the entire state matches.

grind filterGeneration Filters
grind_filter ::= ...
    | gen < num
grind_filter ::= ...
    | gen  num
grind_filter ::= ...
    | gen <= num
grind_filter ::= ...
    | gen = num
grind_filter ::= ...
    | gen != num
grind_filter ::= ...
    | gen  num
grind_filter ::= ...
    | gen >= num
grind_filter ::= ...
    | gen > num

These filters are satisfied when the generation of the entry satisfies the indicated comparison against the numeral. Lower generations correspond to facts discovered earlier in the search.

grind filterName Filters
grind_filter ::= ...
    | ident

This filter is satisfied when the identifier is the name of a local hypothesis or global constant and it occurs in the term.

grind filterFilter Expressions
grind_filter ::= ...
    | (grind_filter)
grind_filter ::= ...
    | grind_filter && grind_filter
grind_filter ::= ...
    | grind_filter || grind_filter
grind_filter ::= ...
    | !grind_filter

Filters may be grouped with parentheses, negated, and combined with conjunction and disjunction. Both conjunction and disjunction are left-associative and have the same precedence, so use explicit parentheses for grouping.

Filters
inductive Color where | red | green inductive Fruit where | cherry | lime example (peel pulp : Fruit Color) (h1 : peel .cherry = pulp .cherry) (h2 : f, pulp f = .red) : peel .cherry = .red := peel:Fruit Colorpulp:Fruit Colorh1:peel Fruit.cherry = pulp Fruit.cherryh2: (f : Fruit), pulp f = Color.redpeel Fruit.cherry = Color.red grind => All goals completed! 🐙 | pulp

Following a step with | and a filter shows the matching part of the grind state in the editor, both before and after the step runs. Filtering by | pulp keeps only the entries that mention pulp. Before the round, these are h2 and the equivalence class {peel .cherry, pulp .cherry} from h1:

[grind] Grind state
  [facts] Asserted facts
  [props] True propositions
    [_] ∀ (f : Fruit), pulp f = Color.red
  [eqc] Equivalence classes
    [eqc] {peel Fruit.cherry, pulp Fruit.cherry}

The false proposition peel .cherry = .red is omitted because it does not mention pulp. Instantiating h2 at .cherry asserts pulp .cherry = .red and closes the goal, so no grind state is shown after.

16.11.3. Goal Management and Sequencing🔗

These tactics don't invoke a solver or modify the grind state. They mirror the corresponding control structures and tactics in the primary tactic language.

🔗grind tactic
skip

skip does nothing.

🔗grind tactic
done

done succeeds iff there are no remaining goals.

A sequence may be grouped with parentheses, written ( steps ), which runs the enclosed steps on the current goals with no added effect. This grouping is used to delimit the scope of a control structure such as first or repeat.

🔗grind tactic
focus

focus tac focuses on the main goal, suppressing all other goals, and runs tac on it. Usually · tac, which enforces that the goal is closed by tac, should be preferred.

🔗grind tactic
next =>

next => tac focuses on the next goal and solves it using tac, or else fails. next x₁ ... xₙ => tac additionally renames the n most recent hypotheses with inaccessible names to the given names.

🔗grind tactic
·

· grindSeq focuses on the main grind goal and tries to solve it using the given sequence of grind tactics.

The bullet is shorthand for next. It focuses on the main goal, suppressing all others. It is an error if the enclosed tactic script does not result in a successful proof of the focused goal.

🔗grind tactic
all_goals

all_goals tac runs tac on each goal, concatenating the resulting goals. If the tactic fails on any goal, the entire all_goals tactic fails.

🔗grind tactic
any_goals

any_goals tac applies the tactic tac to every goal, concatenating the resulting goals for successful tactic applications. If the tactic fails on all of the goals, the entire any_goals tactic fails.

This tactic is like all_goals try tac except that it fails if none of the applications of tac succeeds.

🔗grind tactic
first

first (tac) ... runs each tac until one succeeds, or else fails.

🔗grind tactic
try

try tac runs tac and succeeds even if tac failed.

🔗grind tactic
repeat

repeat tac repeatedly applies tac so long as it succeeds. The tactic tac may be a tactic sequence, and if tac fails at any point in its execution, repeat will revert any partial changes that tac made to the tactic state. The tactic tac should eventually fail, otherwise repeat tac will run indefinitely.

🔗grind tactic
fail_if_success

fail_if_success t fails if the tactic t succeeds.

🔗grind tactic
fail

fail msg is a tactic that always fails, and produces an error using the given message.

🔗grind tactic
<;>

tac <;> tac' runs tac on the main goal and tac' on each produced goal, concatenating all goals produced by tac'.

If the tactic fails on any of the subgoals, then the whole <;> tactic fails.

16.11.4. Completing Proofs🔗

These tactics close goals, either by handing control back to grind's automatic search or by escaping into Lean's ordinary tactic language. Goals may also be closed by one of the theory solvers.

🔗grind tactic
finish

finish tries to close the current goal using grind's default strategy

🔗grind tactic
finish?

finish? tries to close the current goal using grind's default strategy and suggests a tactic script.

When finish? closes a goal, it offers a code action that replaces the call with the script it discovered, just as grind? suggests a Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind only call. This enables an interactive workflow where parts of the proof can be discovered by grind and then manually modified if needed.

Recording a grind Proof as an Interactive Script

WithSucc is closed under successor:

@[grind] inductive WithSucc (pred : Nat Prop): Nat Prop where | base : pred n WithSucc pred n | step : WithSucc pred n WithSucc pred (n + 1)

grind proves WithSucc pred 2 from a proof of WithSucc pred 0 by instantiating WithSucc.step twice:

example (h : WithSucc pred 0) : WithSucc pred 2 := pred:Nat Proph:WithSucc pred 0WithSucc pred 2 All goals completed! 🐙

Following the goal with => finish? instead closes it and reports a script that reproduces the proof:

example (h : WithSucc pred 0) : WithSucc pred 2 := pred:Nat Proph:WithSucc pred 0WithSucc pred 2 grind => Try these: [apply] instantiate only [WithSucc.step] instantiate only [WithSucc.step] [apply] finish only [WithSucc.step]All goals completed! 🐙

Applying the suggested script makes each instantiation explicit:

example (h : WithSucc pred 0) : WithSucc pred 2 := pred:Nat Proph:WithSucc pred 0WithSucc pred 2 grind => pred:Nat Proph:WithSucc pred 0h✝:¬WithSucc pred 2False All goals completed! 🐙
🔗grind tactic
sorry

The sorry tactic is a temporary placeholder for an incomplete tactic proof.

🔗grind tactic
admit

admit is a synonym for sorry.

🔗grind tactic
tactic =>

Executes the given tactic block to close the current goal.

🔗grind tactic
exact

exact e closes the main goal if its target type matches that of e.

16.11.5. Theory Solvers🔗

Each of these tactics runs one of grind's decision procedures on the current state.

🔗grind tactic
lia

lia linear integer arithmetic.

🔗grind tactic
linarith

linarith linear arithmetic.

🔗grind tactic
ring

ring (commutative) rings and fields.

🔗grind tactic
ac

ac associativity and commutativity procedure.

16.11.6. Theorem Instantiation🔗

These tactics add new facts to the grind state (that is, the “whiteboard”) by instantiating theorems.

🔗grind tactic
instantiate

Instantiates theorems using E-matching. The approx modifier is just a marker for users to easily identify automatically generated instantiate tactics that may have redundant arguments.

instantiate runs a single round of E-matching. By default, this round uses all of grind's currently active theorems; the bracketed list supplies further theorems to instantiate alongside them, and an anchor adds a specific local theorem from the state. Running show_local_thms displays the local theorems' anchors. The Lean.Parser.Tactic.Grind.instantiate : grindInstantiates theorems using E-matching. The `approx` modifier is just a marker for users to easily identify automatically generated `instantiate` tactics that may have redundant arguments. only modifier restricts the round to the listed theorems.

The Lean.Parser.Tactic.Grind.instantiate : grindInstantiates theorems using E-matching. The `approx` modifier is just a marker for users to easily identify automatically generated `instantiate` tactics that may have redundant arguments. approx modifier has no effect on how the step runs. It is a marker that appears in automatically generated scripts, such as those suggested by grind? and finish?, when grind cannot determine a precise list of theorems for the step. It signals that the listed theorems are an approximation that may need to be adjusted by hand.

Instantiating a Local Hypothesis

A hypothesis that is a quantified statement becomes a local theorem in the grind state. Because this hypothesis has no global name, it is instantiated by its anchor, which show_local_thms reports:

example (f : Nat Nat) (h : n, f n n * n) : f 3 9 := f:Nat Nath: (n : Nat), f n n * nf 3 9 grind =>
[thms] Local theorems
  • [thm] #7468 := (n : Nat), f n n * n
f:Nat Nath: (n : Nat), f n n * nh✝:10 f 3False
All goals completed! 🐙
[thms] Local theorems
  • [thm] #7468 := (n : Nat), f n n * n

Instantiating h at 3 adds the fact f 3 3 * 3 to the state, from which grind concludes that f 3 9.

🔗grind tactic
use

Shorthand for instantiate only

🔗grind tactic
mbtc

Adds new case-splits using model-based theory combination.

16.11.7. Case Analysis🔗

These tactics invoke grind's case splitting.

🔗grind tactic
cases

Performs a case-split on a logical connective, match-expression, if-then-else-expression, or inductive predicate. The argument is an anchor referencing one of the case-split candidates in the grind state. You can use cases? to select a specific candidate using a code action.

🔗grind tactic
cases?

A variant of cases that provides a code-action for selecting one of the candidate case-splits available in the grind state.

🔗grind tactic
cases_next

Performs the next case-split. The case-split is selected using the same heuristic used by finish.

16.11.8. State Inspection🔗

Many of the tactics in this section take an optional filter as a parameter. This filter should be provided without the | character, because that invokes the general state display mechanism instead of customizing the tactic's display.

Inspecting the Whiteboard

show_state prints the asserted facts, the true and false propositions, and the equivalence classes found so far:

inductive Color where | red | green inductive Fruit where | cherry | lime example (peel pulp : Fruit Color) (h1 : peel .cherry = pulp .cherry) (h2 : f, pulp f = .red) : peel .cherry = .red := peel:Fruit Colorpulp:Fruit Colorh1:peel Fruit.cherry = pulp Fruit.cherryh2: (f : Fruit), pulp f = Color.redpeel Fruit.cherry = Color.red grind =>
[grind] Grind state
peel:Fruit Colorpulp:Fruit Colorh1:peel Fruit.cherry = pulp Fruit.cherryh2: (f : Fruit), pulp f = Color.redh✝:¬peel Fruit.cherry = Color.redFalse
All goals completed! 🐙
[grind] Grind state

The class {peel .cherry, pulp .cherry} comes from h1. The universally quantified statement h2 is not yet instantiated, so the proof is not complete until finish hands control back to grind's search. The companion tactics show_asserted, show_true, show_false, and show_eqcs display individual sections of this state.

🔗grind tactic
show_state

Show grind state.

🔗grind tactic
show_asserted

Shows asserted facts.

🔗grind tactic
show_true

Shows propositions known to be True.

🔗grind tactic
show_false

Shows propositions known to be False.

🔗grind tactic
show_eqcs

Shows equivalence classes of terms.

🔗grind tactic
show_cases

Show case-split candidates.

🔗grind tactic
show_local_thms

Show active local theorems and their anchors for heuristic instantiation.

🔗grind tactic
show_goals

Shows the pending goals.

🔗grind tactic
show_term

show_term tac runs tac, then displays the generated proof in the InfoView.

16.11.9. Local Context and Naming🔗

These tactics add hypotheses to the local context and make inaccessible names available to later steps.

🔗grind tactic
have

The have tactic is for adding opaque definitions and hypotheses to the local context of the main goal. The definitions forget their associated value and cannot be unfolded.

  • have h : t := e adds the hypothesis h : t if e is a term of type t.

  • have h := e uses the type of e for t.

  • have : t := e and have := e use this for the name of the hypothesis.

A second form of Lean.Parser.Tactic.Grind.haveSilent : grindProves `<term>` using the current `grind` state and default search strategy. have, written have h : t with no value, proves t from the current grind state using the default search strategy and adds it as a hypothesis.

🔗grind tactic
have

Proves <term> using the current grind state and default search strategy.

🔗grind tactic
rename_i

rename_i x_1 ... x_n renames the last n inaccessible names using the given names.

🔗grind tactic
expose_names

expose_names renames all inaccessible variables with accessible names, making them available for reference in generated tactics. However, this renaming introduces machine-generated names that are not fully under user control. expose_names is primarily intended as a preamble for generated grind tactic scripts.

16.11.10. Configuration🔗

These tactics run a sequence of grind tactics with modified options or configuration.

🔗grind tactic
set_option

set_option opt val in tacs (the tactic) acts like set_option opt val at the command level, but it sets the option only within the tactics tacs.

🔗grind tactic
set_config

set_config configItem+ in tacs executes tacs with the updated configuration options configItem+

16.11.11. Symbolic Simulation Mode🔗

The sym tactic is a lower-level version of Lean.Parser.Tactic.grind : tactic`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**: every time grind discovers a new equality, inequality, or logical fact, it writes it on the board, groups together terms known to be equal, and lets each reasoning engine read from and contribute to the shared workspace. These engines work together to handle equality reasoning, apply known theorems, propagate new facts, perform case analysis, and run specialized solvers for domains like linear arithmetic and commutative rings. See [the reference manual's chapter on `grind`](https://lean-lang.org/doc/reference/4.31.0/find/?domain=Verso.Genre.Manual.section&name=grind-tactic) for more information. `grind` is *not* designed for goals whose search space explodes combinatorially, think large pigeonhole instances, graph‑coloring reductions, high‑order N‑queens boards, or a 200‑variable Sudoku encoded as Boolean constraints. Such encodings require thousands (or millions) of case‑splits that overwhelm `grind`’s branching search. For **bit‑level or combinatorial problems**, consider using **`bv_decide`**. `bv_decide` calls a state‑of‑the‑art SAT solver (CaDiCaL) and then returns a *compact, machine‑checkable certificate*. ### Equality reasoning `grind` uses **congruence closure** to track equalities between terms. When two terms are known to be equal, congruence closure automatically deduces equalities between more complex expressions built from them. For example, if `a = b`, then congruence closure will also conclude that `f a` = `f b` for any function `f`. This forms the foundation for efficient equality reasoning in `grind`. Here is an example: ``` example (f : Nat → Nat) (h : a = b) : f (f b) = f (f a) := by grind ``` ### Applying theorems using E-matching To apply existing theorems, `grind` uses a technique called **E-matching**, which finds matches for known theorem patterns while taking equalities into account. Combined with congruence closure, E-matching helps `grind` discover non-obvious consequences of theorems and equalities automatically. Consider the following functions and theorems: ``` def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := by simp [f, g] ``` The theorem `gf` asserts that `g (f x) = x` for all natural numbers `x`. The attribute `[grind =]` instructs `grind` to use the left-hand side of the equation, `g (f x)`, as a pattern for E-matching. Suppose we now have a goal involving: ``` example {a b} (h : f b = a) : g a = b := by grind ``` Although `g a` is not an instance of the pattern `g (f x)`, it becomes one modulo the equation `f b = a`. By substituting `a` with `f b` in `g a`, we obtain the term `g (f b)`, which matches the pattern `g (f x)` with the assignment `x := b`. Thus, the theorem `gf` is instantiated with `x := b`, and the new equality `g (f b) = b` is asserted. `grind` then uses congruence closure to derive the implied equality `g a = g (f b)` and completes the proof. The pattern used to instantiate theorems affects the effectiveness of `grind`. For example, the pattern `g (f x)` is too restrictive in the following case: the theorem `gf` will not be instantiated because the goal does not even contain the function symbol `g`. ``` example (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can use the command `grind_pattern` to manually select a pattern for a given theorem. In the following example, we instruct `grind` to use `f x` as the pattern, allowing it to solve the goal automatically: ``` grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := by grind ``` You can enable the option `trace.grind.ematch.instance` to make `grind` print a trace message for each theorem instance it generates. You can also specify a **multi-pattern** to control when `grind` should apply a theorem. A multi-pattern requires that all specified patterns are matched in the current context before the theorem is applied. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. The following example demonstrates this feature using a transitivity axiom for a binary relation `R`: ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z grind_pattern Rtrans => R x y, R y z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` By specifying the multi-pattern `R x y, R y z`, we instruct `grind` to instantiate `Rtrans` only when both `R x y` and `R y z` are available in the context. In the example, `grind` applies `Rtrans` to derive `R a c` from `R a b` and `R b c`, and can then repeat the same reasoning to deduce `R a d` from `R a c` and `R c d`. Instead of using `grind_pattern` to explicitly specify a pattern, you can use the `@[grind]` attribute or one of its variants, which will use a heuristic to generate a (multi-)pattern. The complete list is available in the reference manual. The main ones are: - `@[grind →]` will select a multi-pattern from the hypotheses of the theorem (i.e. it will use the theorem for forwards reasoning). In more detail, it will traverse the hypotheses of the theorem from left-to-right, and each time it encounters a minimal indexable (i.e. has a constant as its head) subexpression which "covers" (i.e. fixes the value of) an argument which was not previously covered, it will add that subexpression as a pattern, until all arguments have been covered. - `@[grind ←]` will select a multi-pattern from the conclusion of theorem (i.e. it will use the theorem for backwards reasoning). This may fail if not all the arguments to the theorem appear in the conclusion. - `@[grind]` will traverse the conclusion and then the hypotheses left-to-right, adding patterns as they increase the coverage, stopping when all arguments are covered. - `@[grind =]` checks that the conclusion of the theorem is an equality, and then uses the left-hand-side of the equality as a pattern. This may fail if not all of the arguments appear in the left-hand-side. Here is the previous example again but using the attribute `[grind →]` ``` opaque R : Int → Int → Prop @[grind →] axiom Rtrans {x y z : Int} : R x y → R y z → R x z example {a b c d} : R a b → R b c → R c d → R a d := by grind ``` To control theorem instantiation and avoid generating an unbounded number of instances, `grind` uses a generation counter. Terms in the original goal are assigned generation zero. When `grind` applies a theorem using terms of generation `≤ n`, any new terms it creates are assigned generation `n + 1`. This limits how far the tactic explores when applying theorems and helps prevent an excessive number of instantiations. #### Key options: - `grind (ematch := <num>)` controls the number of E-matching rounds. - `grind [<name>, ...]` instructs `grind` to use the declaration `name` during E-matching. - `grind only [<name>, ...]` is like `grind [<name>, ...]` but does not use theorems tagged with `@[grind]`. - `grind (gen := <num>)` sets the maximum generation. ### Linear integer arithmetic (`lia`) `grind` can solve goals that reduce to **linear integer arithmetic (LIA)** using an integrated decision procedure called **`lia`**. It understands * equalities   `p = 0` * inequalities  `p ≤ 0` * disequalities `p ≠ 0` * divisibility  `d ∣ p` The solver incrementally assigns integer values to variables; when a partial assignment violates a constraint it adds a new, implied constraint and retries. This *model-based* search is **complete for LIA**. #### Key options: * `grind -lia` disable the solver (useful for debugging) * `grind +qlia` accept rational models (shrinks the search space but is incomplete for ℤ) #### Examples: ``` -- Even + even is never odd. example {x y : Int} : 2 * x + 4 * y ≠ 5 := by grind -- Mixing equalities and inequalities. example {x y : Int} : 2 * x + 3 * y = 0 → 1 ≤ x → y < 1 := by grind -- Reasoning with divisibility. example (a b : Int) : 2 ∣ a + 1 → 2 ∣ b + a → ¬ 2 ∣ b + 2 * a := by grind example (x y : Int) : 27 ≤ 11*x + 13*y → 11*x + 13*y ≤ 45 → -10 ≤ 7*x - 9*y → 7*x - 9*y ≤ 4 → False := by grind -- Types that implement the `ToInt` type-class. example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by grind ``` ### Algebraic solver (`ring`) `grind` ships with an algebraic solver nick-named **`ring`** for goals that can be phrased as polynomial equations (or disequations) over commutative rings, semirings, or fields. *Works out of the box* All core numeric types and relevant Mathlib types already provide the required type-class instances, so the solver is ready to use in most developments. What it can decide: * equalities of the form `p = q` * disequalities `p ≠ q` * basic reasoning under field inverses (`a / b := a * b⁻¹`) * goals that mix ring facts with other `grind` engines #### Key options: * `grind -ring` turn the solver off (useful when debugging) * `grind (ringSteps := n)` cap the number of steps performed by this procedure. #### Examples ``` open Lean Grind example [CommRing α] (x : α) : (x + 1) * (x - 1) = x^2 - 1 := by grind -- Characteristic 256 means 16 * 16 = 0. example [CommRing α] [IsCharP α 256] (x : α) : (x + 16) * (x - 16) = x^2 := by grind -- Works on built-in rings such as `UInt8`. example (x : UInt8) : (x + 16) * (x - 16) = x^2 := by grind example [CommRing α] (a b c : α) : a + b + c = 3 → a^2 + b^2 + c^2 = 5 → a^3 + b^3 + c^3 = 7 → a^4 + b^4 = 9 - c^4 := by grind example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind ``` ### Other options - `grind (splits := <num>)` caps the *depth* of the search tree. Once a branch performs `num` splits `grind` stops splitting further in that branch. - `grind -splitIte` disables case splitting on if-then-else expressions. - `grind -splitMatch` disables case splitting on `match` expressions. - `grind +splitImp` instructs `grind` to split on any hypothesis `A → B` whose antecedent `A` is **propositional**. - `grind -linarith` disables the linear arithmetic solver for (ordered) modules and rings. ### Additional Examples ``` example {a b} {as bs : List α} : (as ++ bs ++ [b]).getLastD a = b := by grind example (x : BitVec (w+1)) : (BitVec.cons x.msb (x.setWidth w)) = x := by grind example (as : Array α) (lo hi i j : Nat) : lo ≤ i → i < j → j ≤ hi → j < as.size → min lo (as.size - 1) ≤ i := by grind ``` grind => that provides a higher degree of manual control over the state. When run, sym enters interactive mode without the eager introduction of hypotheses and use of proof by contradiction that grind performs.

When using sym, assumptions must be manually introduced, just as in the standard proof tactic language. Introducing an assumption moves it from the goal's conclusion into a new hypothesis. It is often also useful to add the assumption to the “whiteboard”, so that it participates in congruence closure and grind's other processes. Adding a hypothesis or term to the whiteboard is called internalization. Once internalized, a hypothesis participates in congruence reasoning, constraint propagation, and the theory solvers; until then, grind does not reason about it.

The following tactics are available only after sym; they are not available in plain interactive grind because it carries out their operations automatically when needed.

🔗grind tactic
intro

intro x₁ ... xₙ introduces binders and internalizes them into the E-graph. Only available in sym => mode. intro with no arguments introduces one binder with an inaccessible name. Use intro (internalize := false) or intro~ to skip internalization.

Introduction and Internalization

Introducing a hypothesis changes the goal and, by default, adds the hypothesis to the whiteboard:

(a b : Nat), a = b b = a

intro a b

When the current goal is an implication and the grind state is empty, running intro both adds the antecedent as an assumption and integrates it into the whiteboard. If the initial goal and grind state are:

a:Natb:Nata = b b = a
[grind] Grind state

then running

a:Natb:Nath:a = bb = a

results in a new assumption:

a:Natb:Nath:a = bb = a

This new assumption has also modified the grind state, which is now:

[grind] Grind state
  • [facts] Asserted facts
    • [_] a = b
  • [eqc] Equivalence classes
    • [eqc] {a, b}

(a b : Nat), a = b b = a

intro a b

With the same goal and empty state:

a:Natb:Nata = b b = a
[grind] Grind state

introducing without internalization:

a:Natb:Nath:a = bb = a

results in a modified goal but an empty state:

a:Natb:Nath:a = bb = a
[grind] Grind state

The shorthand intro~ introduces binders without internalizing them, equivalent to intro (internalize := false).

🔗grind tactic
intros

intros introduces all remaining binders and internalizes them. Only available in sym => mode. Use intros (internalize := false) or intros~ to skip internalization.

The shorthand intros~ introduces all remaining binders without internalizing them.

🔗grind tactic
apply

apply t applies theorem t as a backward rule. Only available in sym => mode. When used with repeat, the backward rule is cached for efficiency.

🔗grind tactic
internalize

internalize internalizes hypotheses into the grind E-graph. Only available in sym => mode.

  • internalize internalizes the next hypothesis.

  • internalize <num> internalizes the next <num> hypotheses.

🔗grind tactic
internalize_all

internalize_all internalizes all pending hypotheses into the grind E-graph. Only available in sym => mode.

🔗grind tactic
by_contra

by_contra applies proof by contradiction, negating the target and making it False. Only available in sym => mode.

The simp and dsimp tactics in sym scripts run grind's own simplifier, a separate implementation built for its internal representation of terms, with its own performance characteristics and feature set. This simplifier operates directly on that representation and avoids testing definitional equality. Instead of simp sets, it uses named variants: named bundles of simprocs (to be run before and after simplification) along with configuration overrides. Named variants are described in more detail in the next section.

🔗grind tactic
simp

simp applies the structural simplifier to the goal target. Only available in sym => mode.

  • simp — uses the default (identity) variant

  • simp myVariant — uses a named variant registered via register_sym_simp

  • simp [thm₁, thm₂, ...] — default variant with extra rewrite theorems appended to post

  • simp myVariant [thm₁, thm₂, ...] — named variant with extra theorems

🔗grind tactic
dsimp

dsimp applies the definitional simplifier to the goal target. Only available in sym => mode.

  • dsimp — uses the default (identity) variant

  • dsimp myVariant — uses a named variant registered via register_sym_dsimp

  • dsimp [id₁, id₂, ...] — default variant with extra declarations to unfold

  • dsimp myVariant [id₁, id₂, ...] — named variant with extra declarations

16.11.11.1. Registering Simplification Variants🔗

Named variants, used by the sym versions of simp and dsimp, are declared using the Lean.Parser.Command.registerSymSimp : commandRegister a named `Sym.simp` variant. ``` register_sym_simp myVariant where pre := telescope post := ground >> rewrite [thm1, thm2] with self maxSteps := 50000 ``` register_sym_simp and Lean.Parser.Command.registerSymDSimp : commandRegister a named `Sym.dsimp` variant. ``` register_sym_dsimp myVariant where pre := match post := ground >> zeta_delta maxSteps := 50000 ``` register_sym_dsimp commands. Once registered, a variant is then selected by following simp or dsimp with that name.

syntaxRegistering a Sym.simp Variant
command ::= ...
    | Register a named `Sym.simp` variant.

```
register_sym_simp myVariant where
  pre  := telescope
  post := ground >> rewrite [thm1, thm2] with self
  maxSteps := 50000
```
register_sym_simp ident where
        sym_simp_field*

Register a named Sym.simp variant.

register_sym_simp myVariant where
  pre  := telescope
  post := ground >> rewrite [thm1, thm2] with self
  maxSteps := 50000
syntaxRegistering a Sym.dsimp Variant
command ::= ...
    | Register a named `Sym.dsimp` variant.

```
register_sym_dsimp myVariant where
  pre  := match
  post := ground >> zeta_delta
  maxSteps := 50000
```
register_sym_dsimp ident where
        sym_dsimp_field*

Register a named Sym.dsimp variant.

register_sym_dsimp myVariant where
  pre  := match
  post := ground >> zeta_delta
  maxSteps := 50000

The fields in the where block are all optional:

pre and post

The simproc chains applied before and after simplification, respectively.

maxSteps

Bounds the number of simplification steps.

maxDischargeDepth

Bounds the depth of recursive side-goal discharge. Available only for Lean.Parser.Command.registerSymSimp : commandRegister a named `Sym.simp` variant. ``` register_sym_simp myVariant where pre := telescope post := ground >> rewrite [thm1, thm2] with self maxSteps := 50000 ``` register_sym_simp.