Gadget for representing generalization steps h : x = val
in patterns
This gadget is used to represent patterns in theorems that have been generalized to reduce the
number of casts introduced during E-matching based instantiation.
For example, consider the theorem
Option.pbind_some {α1 : Type u_1} {a : α1} {α2 : Type u_2}
{f : (a_1 : α1) → some a = some a_1 → Option α2}
: (some a).pbind f = f a rfl
Now, suppose we have a goal containing the term c.pbind g
and the equivalence class
{c, some b}
. The E-matching module generates the instance
(some b).pbind (cast ⋯ g)
The cast
is necessary because g
's type contains c
instead of some b. This
cast` problematic because we don't have a systematic way of pushing casts over functions
to its arguments. Moreover, heterogeneous equality is not effective because the following theorem
is not provable in DTT:
theorem hcongr (h₁ : f ≍ g) (h₂ : a ≍ b) : f a ≍ g b := ...
The standard solution is to generalize the theorem above and write it as
theorem Option.pbind_some'
{α1 : Type u_1} {a : α1} {α2 : Type u_2}
{x : Option α1}
{f : (a_1 : α1) → x = some a_1 → Option α2}
(h : x = some a)
: x.pbind f = f a h := by
subst h
apply Option.pbind_some
Internally, we use this gadget to mark the E-matching pattern as
(genPattern h x (some a)).pbind f
This pattern is matched in the same way we match (some a).pbind f
, but it saves the proof
for the actual term to the some
-application in f
, and the actual term in x
.
In the example above, c.pbind g
also matches the pattern (genPattern h x (some a)).pbind f
,
and stores c
in x
, b
in a
, and the proof that c = some b
in h
.
Equations
- Lean.Grind.genPattern _h x _val = x
Instances For
Similar to genPattern
but for the heterogeneous case
Equations
- Lean.Grind.genHEqPattern _h x _val = x
Instances For
Reset all grind
attributes. This command is intended for testing purposes only and should not be used in applications.
Equations
- Lean.Parser.resetGrindAttrs = Lean.ParserDescr.node `Lean.Parser.resetGrindAttrs 1024 (Lean.ParserDescr.symbol "reset_grind_attrs%")
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- Lean.Parser.Attr.grindUsr = Lean.ParserDescr.nodeWithAntiquot "grindUsr" `Lean.Parser.Attr.grindUsr (Lean.ParserDescr.nonReservedSymbol "usr" false)
Instances For
Equations
- Lean.Parser.Attr.grindCases = Lean.ParserDescr.nodeWithAntiquot "grindCases" `Lean.Parser.Attr.grindCases (Lean.ParserDescr.nonReservedSymbol "cases" false)
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- Lean.Parser.Attr.grindIntro = Lean.ParserDescr.nodeWithAntiquot "grindIntro" `Lean.Parser.Attr.grindIntro (Lean.ParserDescr.nonReservedSymbol "intro" false)
Instances For
Equations
- Lean.Parser.Attr.grindExt = Lean.ParserDescr.nodeWithAntiquot "grindExt" `Lean.Parser.Attr.grindExt (Lean.ParserDescr.nonReservedSymbol "ext" false)
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
The configuration for grind
.
Passed to grind
using, for example, the grind (config := { matchEqs := true })
syntax.
- trace : Bool
- splits : Nat
Maximum number of case-splits in a proof search branch. It does not include splits performed during normalization.
- ematch : Nat
Maximum number of E-matching (aka heuristic theorem instantiation) rounds before each case split.
- gen : Nat
Maximum term generation. The input goal terms have generation 0. When we instantiate a theorem using a term from generation
n
, the new terms have generationn+1
. Thus, this parameter limits the length of an instantiation chain. - instances : Nat
Maximum number of theorem instances generated using E-matching in a proof search tree branch.
- matchEqs : Bool
- splitMatch : Bool
If
splitMatch
istrue
,grind
performs case-splitting onmatch
-expressions during the search. - splitIte : Bool
- splitIndPred : Bool
If
splitIndPred
istrue
,grind
performs case-splitting on inductive predicates. Otherwise, it performs case-splitting only on types marked with[grind cases]
attribute. - splitImp : Bool
- canonHeartbeats : Nat
Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test.
- ext : Bool
- extAll : Bool
- etaStruct : Bool
- funext : Bool
- lookahead : Bool
TODO
- verbose : Bool
If
verbose
isfalse
, additional diagnostics information is not collected. - clean : Bool
- qlia : Bool
- mbtc : Bool
- zetaDelta : Bool
When set to
true
(default:true
), local definitions are unfolded during normalization and internalization. In other words, given a local context with an entryx : t := e
, the free variablex
is reduced toe
. Note that this behavior is also available insimp
, but there its default isfalse
becausesimp
is not always used as a terminal tactic, and it important to preserve the abstractions introduced by users. Additionally, ingrind
we observed thatzetaDelta
is particularly important when combined with function induction. In such scenarios, the same let-expressions can be introduced by function induction and also by unfolding the corresponding definition. We want to avoid a situation in whichzetaDelta
is not applied to let-declarations introduced by function induction whilezeta
unfolds the definition, causing a mismatch. Finally, note that congruence closure is less effective on terms containing many binders such aslambda
andlet
expressions. - zeta : Bool
When
true
(default:true
), performs zeta reduction of let expressions during normalization. That is,let x := v; e[x]
reduces toe[v]
. See alsozetaDelta
. - ring : Bool
When
true
(default:true
), uses procedure for handling equalities over commutative rings. - ringSteps : Nat
- ringNull : Bool
When
true
(default:false
), the commutative ring procedure ingrind
constructs stepwise proof terms, instead of a single-step Nullstellensatz certificate - linarith : Bool
When
true
(default:true
), uses procedure for handling linear arithmetic forIntModule
, andCommRing
. - cutsat : Bool
Instances For
Equations
- One or more equations did not get rendered due to their size.
Equations
grind
tactic and related tactics.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
grind
is a tactic inspired by modern SMT solvers.
Picture a virtual white‑board: every time grind
discovers a new equality, inequality,
or Boolean literal it writes that fact on the board, merges equivalent terms into buckets,
and invites each engine to read from, and add back to, the same workspace.
The cooperating engines are: congruence closure, constraint propagation, E‑matching,
guided case analysis, and a suite of satellite theory solvers (linear integer arithmetic,
commutative rings, ...).
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.
E-matching is a mechanism used by grind
to instantiate theorems efficiently.
It is especially effective when combined with congruence closure, enabling
grind
to discover non-obvious consequences of equalities and annotated theorems
automatically. The theorem instantiation process is interrupted using the generation
threshold.
Terms occurring in the input goal have generation
zero. When grind
instantiates
a theorem using terms with generation ≤ n
, the new generated terms have generation n+1
.
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 heuristic instantiation via 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.
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.
Main configuration options:
grind (splits := <num>)
caps the depth of the search tree. Once a branch performsnum
splitsgrind
stops splitting further in that branch.grind -splitIte
disables case splitting on if-then-else expressions.grind -splitMatch
disables case splitting onmatch
expressions.grind +splitImp
instructsgrind
to split on any hypothesisA → B
whose antecedentA
is propositional.grind (ematch := <num>)
controls the number of E-matching rounds.grind [<name>, ...]
instructsgrind
to use the declarationname
during E-matching.grind only [<name>, ...]
is likegrind [<name>, ...]
but does not use theorems tagged with@[grind]
.grind (gen := <num>)
sets the maximum generation.grind -ring
disables the ring solver based on Gröbner basis.grind (ringSteps := <num>)
limits the number of steps performed by ring solver.grind -cutsat
disables the linear integer arithmetic solver based on the cutsat procedure.grind -linarith
disables the linear arithmetic solver for (ordered) modules and rings.
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 (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by
grind
example [Field α] (a : α) : (2 : α) ≠ 0 → 1 / a + 1 / (2 * a) = 3 / (2 * a) := 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
example [CommRing α] [NoNatZeroDivisors α] (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 (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
Equations
- One or more equations did not get rendered due to their size.