Lean 语言参考手册

16.6. E-匹配🔗

E-匹配是一种用基项高效实例化量化定理陈述的过程。 它被广泛用于 SMT 求解器中,而 grind 也利用它来高效实例化定理。 当它与 同余闭包 结合使用时尤其有效,能够让 grind 自动发现等式与已标注定理的非显然后果。

E-匹配会基于定理索引,把新的事实加入这个比喻意义上的白板。 当白板中出现与索引匹配的项时,E-匹配引擎就会实例化相应定理,而由此得到的项又能供后续的 同余闭包约束传播 与特定理论求解器继续使用。 每一个由 E-匹配加入白板的事实,都称为一个 实例。 为定理添加 E-匹配标注、从而把它们加入索引,是让 grind 有效利用库内容的关键。

除了用户指定的定理以外,grind 还会把为 Lean.Parser.Term.match : termPattern matching. `match e, ... with | p, ... => f | ...` matches each given term `e` against each pattern `p` of a match alternative. When all patterns of an alternative match, the `match` term evaluates to the value of the corresponding right-hand side `f` with the pattern variables bound to the respective matched values. If used as `match h : e, ... with | p, ... => f | ...`, `h : e = p` is available within `f`. When not constructing a proof, `match` does not automatically substitute variables matched on in dependent variables' types. Use `match (generalizing := true) ...` to enforce this. Syntax quotations can also be used in a pattern match. This matches a `Syntax` value against quotations, pattern variables, or `_`. Quoted identifiers only match identical identifiers - custom matching such as by the preresolved names only should be done explicitly. `Syntax.atom`s are ignored during matching by default except when part of a built-in literal. For users introducing new atoms, we recommend wrapping them in dedicated syntax kinds if they should participate in matching. For example, in ```lean syntax "c" ("foo" <|> "bar") ... ``` `foo` and `bar` are indistinguishable during matching, but in ```lean syntax foo := "foo" syntax "c" (foo <|> "bar") ... ``` they are not. match 表达式自动生成的等式当作 E-匹配定理使用。 在幕后,精译器会生成实现模式匹配的辅助函数,以及描述其行为的等式定理。 将这些等式与 E-匹配配合使用,就能让 grind 化简这些模式匹配实例。

16.6.1. 模式🔗

E-匹配索引是一张由模式组成的表。 当某个项与表中的某个模式匹配时,grind 就会尝试实例化并应用相应定理,从而产生更多事实与等式。 选择合适的模式,是有效使用 grind 的重要一环:如果模式过于严格,有用的定理就可能无法应用;如果模式过于宽泛,性能则可能下降。

E-匹配模式

考虑下面这些函数和定理:

def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := x:Natg (f x) = x All goals completed! 🐙

定理 gf 断言:对所有自然数 x,都有 g (f x) = x。 属性 grind = 告诉 grind 使用等式左边的 g (f x) 作为 E-匹配启发式实例化时的模式。

这个证明目标并不包含 g (f x) 的实例,但 grind 仍然能够将其解决:

example {a b} (h : f b = a) : g a = b := x:Nata✝:Natb✝:Nata:Natb:Nath:f b = ag a = b All goals completed! 🐙

虽然 g a 并不是模式 g (f x) 的一个实例,但在等式 f b = a 的意义下,它会变成一个实例。 把 g a 中的 a 替换成 f b 后,我们得到项 g (f b),它就与模式 g (f x) 匹配,对应赋值为 x := b。 因此,定理 gf 会以 x := b 进行实例化,并断言新的等式 g (f b) = b。 随后,grind 使用同余闭包推出蕴含的等式 g a = g (f b),从而完成证明。

Lean.Parser.Command.grind_patterngrind_pattern 命令可用于手动为定理选择 E-匹配模式。 开启选项 trace.grind.ematch.instance 后,grind 会为其生成的每个定理实例打印一条追踪消息,这在确定 E-匹配模式时会很有帮助。

语法E-匹配模式选择
command ::= ...
    | The `grind_pattern` command can be used to manually select a pattern for theorem instantiation.
Enabling the option `trace.grind.ematch.instance` causes `grind` to print a trace message for each
theorem instance it generates, which can be helpful when determining patterns.

When multiple patterns are specified together, all of them must match in the current context before
`grind` attempts to instantiate the theorem. This is referred to as a *multi-pattern*.
This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously
present for the rule to apply.

In the following example, `R` is a transitive binary relation over `Int`.
```
opaque R : Int → Int → Prop
axiom Rtrans {x y z : Int} : R x y → R y z → R x z
```
To use the fact that `R` is transitive, `grind` must already be able to satisfy both premises.
This is represented using a multi-pattern:
```
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
```
The multi-pattern `R x y`, `R y z` instructs `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`.

You can add constraints to restrict theorem instantiation. For example:
```
grind_pattern extract_extract => (as.extract i j).extract k l where
  as =/= #[]
```
The constraint instructs `grind` to instantiate the theorem only if `as` is **not** definitionally equal
to `#[]`.

## Constraints

- `x =/= term`: The term bound to `x` (one of the theorem parameters) is **not** definitionally equal to `term`.
  The term may contain holes (i.e., `_`).

- `x =?= term`: The term bound to `x` is definitionally equal to `term`.
  The term may contain holes (i.e., `_`).

- `size x < n`: The term bound to `x` has size less than `n`. Implicit arguments
and binder types are ignored when computing the size.

- `depth x < n`: The term bound to `x` has depth less than `n`.

- `is_ground x`: The term bound to `x` does not contain local variables or meta-variables.

- `is_value x`: The term bound to `x` is a value. That is, it is a constructor fully applied to value arguments,
a literal (`Nat`, `Int`, `String`, etc.), or a lambda `fun x => t`.

- `is_strict_value x`: Similar to `is_value`, but without lambdas.

- `not_value x`: The term bound to `x` is a **not** value (see `is_value`).

- `not_strict_value x`: Similar to `not_value`, but without lambdas.

- `gen < n`: The theorem instance has generation less than `n`. Recall that each term is assigned a
generation, and terms produced by theorem instantiation have a generation that is one greater than
the maximal generation of all the terms used to instantiate the theorem. This constraint complements
the `gen` option available in `grind`.

- `max_insts < n`: A new instance is generated only if less than `n` instances have been generated so far.

- `guard e`: The instantiation is delayed until `grind` learns that `e` is `true` in this state.

- `check e`: Similar to `guard e`, but `grind` checks whether `e` is implied by its current state by
assuming `¬ e` and trying to deduce an inconsistency.

## Example

Consider the following example where `f` is a monotonic function
```
opaque f : Nat → Nat
axiom fMono : x ≤ y → f x ≤ f y
```
and you want to instruct `grind` to instantiate `fMono` for every pair of terms `f x` and `f y` when
`x ≤ y` and `x` is **not** definitionally equal to `y`. You can use
```
grind_pattern fMono => f x, f y where
  guard x ≤ y
  x =/= y
```
Then, in the following example, only three instances are generated.
```
/--
trace: [grind.ematch.instance] fMono: a ≤ f a → f a ≤ f (f a)
[grind.ematch.instance] fMono: f a ≤ f (f a) → f (f a) ≤ f (f (f a))
[grind.ematch.instance] fMono: a ≤ f (f a) → f a ≤ f (f (f a))
-/
#guard_msgs in
example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by
  set_option trace.grind.ematch.instance true in
  grind
```
`attrKind` 匹配 `("scoped" <|> "local")?`,用于属性之前,例如 `@[local simp]`。grind_pattern ident => term,*

将一个定理与一个或多个模式关联起来。 如果在同一个 Lean.Parser.Command.grind_patterngrind_pattern 命令中给出了多个模式,那么必须全部匹配到项,grind 才会尝试实例化该定理。

command ::= ...
    | The `grind_pattern` command can be used to manually select a pattern for theorem instantiation.
Enabling the option `trace.grind.ematch.instance` causes `grind` to print a trace message for each
theorem instance it generates, which can be helpful when determining patterns.

When multiple patterns are specified together, all of them must match in the current context before
`grind` attempts to instantiate the theorem. This is referred to as a *multi-pattern*.
This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously
present for the rule to apply.

In the following example, `R` is a transitive binary relation over `Int`.
```
opaque R : Int → Int → Prop
axiom Rtrans {x y z : Int} : R x y → R y z → R x z
```
To use the fact that `R` is transitive, `grind` must already be able to satisfy both premises.
This is represented using a multi-pattern:
```
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
```
The multi-pattern `R x y`, `R y z` instructs `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`.

You can add constraints to restrict theorem instantiation. For example:
```
grind_pattern extract_extract => (as.extract i j).extract k l where
  as =/= #[]
```
The constraint instructs `grind` to instantiate the theorem only if `as` is **not** definitionally equal
to `#[]`.

## Constraints

- `x =/= term`: The term bound to `x` (one of the theorem parameters) is **not** definitionally equal to `term`.
  The term may contain holes (i.e., `_`).

- `x =?= term`: The term bound to `x` is definitionally equal to `term`.
  The term may contain holes (i.e., `_`).

- `size x < n`: The term bound to `x` has size less than `n`. Implicit arguments
and binder types are ignored when computing the size.

- `depth x < n`: The term bound to `x` has depth less than `n`.

- `is_ground x`: The term bound to `x` does not contain local variables or meta-variables.

- `is_value x`: The term bound to `x` is a value. That is, it is a constructor fully applied to value arguments,
a literal (`Nat`, `Int`, `String`, etc.), or a lambda `fun x => t`.

- `is_strict_value x`: Similar to `is_value`, but without lambdas.

- `not_value x`: The term bound to `x` is a **not** value (see `is_value`).

- `not_strict_value x`: Similar to `not_value`, but without lambdas.

- `gen < n`: The theorem instance has generation less than `n`. Recall that each term is assigned a
generation, and terms produced by theorem instantiation have a generation that is one greater than
the maximal generation of all the terms used to instantiate the theorem. This constraint complements
the `gen` option available in `grind`.

- `max_insts < n`: A new instance is generated only if less than `n` instances have been generated so far.

- `guard e`: The instantiation is delayed until `grind` learns that `e` is `true` in this state.

- `check e`: Similar to `guard e`, but `grind` checks whether `e` is implied by its current state by
assuming `¬ e` and trying to deduce an inconsistency.

## Example

Consider the following example where `f` is a monotonic function
```
opaque f : Nat → Nat
axiom fMono : x ≤ y → f x ≤ f y
```
and you want to instruct `grind` to instantiate `fMono` for every pair of terms `f x` and `f y` when
`x ≤ y` and `x` is **not** definitionally equal to `y`. You can use
```
grind_pattern fMono => f x, f y where
  guard x ≤ y
  x =/= y
```
Then, in the following example, only three instances are generated.
```
/--
trace: [grind.ematch.instance] fMono: a ≤ f a → f a ≤ f (f a)
[grind.ematch.instance] fMono: f a ≤ f (f a) → f (f a) ≤ f (f (f a))
[grind.ematch.instance] fMono: a ≤ f (f a) → f a ≤ f (f (f a))
-/
#guard_msgs in
example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by
  set_option trace.grind.ematch.instance true in
  grind
```
`attrKind` 匹配 `("scoped" <|> "local")?`,用于属性之前,例如 `@[local simp]`。grind_pattern ident => term,* where (isValue
       | isStrictValue
       | notValue
       | notStrictValue
       | isGround
       | sizeLt
       | depthLt
       | genLt
       | maxInsts
       | guard
       | check
       | notDefEq
       | defEq)

可选的 Lean.Parser.Command.grind_patternwhere 子句给出了一组约束;只有满足这些约束时,grind 才会尝试实例化该定理。 每个约束都形如 variable =/= value,用于阻止在模式变量会被赋成指定值时发生实例化。 这对于避免某些问题项导致的无界或过度实例化很有用。

选择模式

grind = 属性会把等式左边用作 gf 的 E-匹配模式:

def f (a : Nat) : Nat := a + 1 def g (a : Nat) : Nat := a - 1 @[grind =] theorem gf (x : Nat) : g (f x) = x := x:Natg (f x) = x All goals completed! 🐙

例如,在下面这种情况下,模式 g (f x) 就过于严格: 定理 gf 不会被实例化,因为目标里甚至根本不包含函数符号 g

在这个例子中,grind 会失败,因为模式太严格:目标不包含函数符号 g

example (h₁ : f b = a) (h₂ : f c = a) : b = c := b:Nata:Natc:Nath₁:f b = ah₂:f c = ab = c `grind` failed b a c:Nath₁:f b = ah₂:f c = ah:¬b = cFalse
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] f b = a
    • [prop] f c = a
    • [prop] ¬b = c
  • [eqc] False propositions
    • [prop] b = c
  • [eqc] Equivalence classes
    • [eqc] {a, f b, f c}
All goals completed! 🐙
`grind` failed
b a c:Nath₁:f b = ah₂:f c = ah:¬b = cFalse
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] f b = a
    • [prop] f c = a
    • [prop] ¬b = c
  • [eqc] False propositions
    • [prop] b = c
  • [eqc] Equivalence classes
    • [eqc] {a, f b, f c}

只用 f x 作为模式,就足以让 grind 自动解决该目标:

grind_pattern gf => f x example {a b c} (h₁ : f b = a) (h₂ : f c = a) : b = c := a:Natb:Natc:Nath₁:f b = ah₂:f c = ab = c All goals completed! 🐙

开启 trace.grind.ematch.instance 后,就可以看到 E-匹配找到的等式:

example (h₁ : f b = a) (h₂ : f c = a) : b = c := b:Nata:Natc:Nath₁:f b = ah₂:f c = ab = c set_option trace.grind.ematch.instance true in [grind.ematch.instance] gf: g (f c) = c[grind.ematch.instance] gf: g (f b) = bAll goals completed! 🐙
[grind.ematch.instance] gf: g (f c) = c[grind.ematch.instance] gf: g (f b) = b

在 E-匹配之后,证明之所以成功,是因为同余闭包会把 g (f c)g (f b) 判定为相等;这是由于 f bf c 都等于 a。 因此,bc 必须处于同一个等价类中。

当多个模式被一起指定时,只有它们全部在当前上下文中匹配成功,grind 才会尝试实例化该定理。 这称为 多模式。 对于传递性规则这类引理,它尤其有用,因为规则适用时往往要求多个前提同时在场。 通过多次调用 Lean.Parser.Command.grind_patterngrind_pattern,或者使用 @[grind _=_] 属性,一个定理也可以关联到多个彼此独立的模式。 只要这些独立模式中有任意一个匹配成功,该定理就会被实例化。

多模式

RInt 上的一个传递二元关系:

opaque R : Int Int Prop axiom Rtrans {x y z : Int} : R x y R y z R x z

要利用 R 的传递性,grind 必须已经能够同时满足两个前提。 这可以通过一个 多模式 来表示:

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 := a:Intb:Intc:Intd:IntR a b R b c R c d R a d All goals completed! 🐙

多模式 R x y, R y z 告诉 grind:只有当上下文中同时存在 R x yR y z 时,才实例化 Rtrans。 在这个例子里,grind 先由 R a bR b c 应用 Rtrans 推出 R a c,然后再次重复同样的推理,由 R a cR c d 推出 R a d

模式约束

某些定理组合可能导致无界实例化,也就是 E-匹配反复生成越来越长的项。 考虑与 List.flatMapList.reverse 有关的定理。 如果 List.flatMap_defList.flatMap_reverseList.reverse_flatMap 都被加上 @[grind =] 标注,那么一旦 List.flatMap_reverse 被实例化,就会发生下面这一连串实例化,不断构造出带有更多 List.reverse 组合的函数。 这一点可以用 #grind_lint 命令观察到:

attribute [local grind =] List.reverse_flatMap

set_option trace.grind.ematch.instance true in
#grind_lint inspect List.flatMap_reverse

追踪输出展示了这种无界实例化:

[grind.ematch.instance] List.flatMap_def: List.flatMap (List.reverse ∘ f) l = (List.map (List.reverse ∘ f) l).flatten
[grind.ematch.instance] List.flatMap_def: List.flatMap f l.reverse = (List.map f l.reverse).flatten
[grind.ematch.instance] List.flatMap_reverse: List.flatMap f l.reverse = (List.flatMap (List.reverse ∘ f) l).reverse
[grind.ematch.instance] List.reverse_flatMap: (List.flatMap (List.reverse ∘ f) l).reverse =
  List.flatMap (List.reverse ∘ List.reverse ∘ f) l.reverse
[grind.ematch.instance] List.flatMap_def: List.flatMap (List.reverse ∘ List.reverse ∘ f) l.reverse =
  (List.map (List.reverse ∘ List.reverse ∘ f) l.reverse).flatten

这种模式会无限继续下去,每次迭代都会在组合中再添一个 List.reverseLean.Parser.Command.grind_patternwhere 子句可以通过排除有问题的实例化来阻止这种情况:

grind_pattern reverse_flatMap => (l.flatMap f).reverse where
  f =/= List.reverse ∘ _

这会指示 grind 使用模式 (l.flatMap f).reverse,但只在 f 不是与 List.reverse 的复合时才使用,从而阻止那条无界实例化链。

你可以用 #grind_lint check 查找有问题的模式,也可以用 #grind_lint check in List#grind_lint check in module Std.Data 在特定命名空间或模块中检查。

grind 属性会用启发式方法自动生成 E-匹配模式或多模式,而不必用 Lean.Parser.Command.grindPattern : commandThe `grind_pattern` command can be used to manually select a pattern for theorem instantiation. Enabling the option `trace.grind.ematch.instance` causes `grind` to print a trace message for each theorem instance it generates, which can be helpful when determining patterns. When multiple patterns are specified together, all of them must match in the current context before `grind` attempts to instantiate the theorem. This is referred to as a *multi-pattern*. This is useful for theorems such as transitivity rules, where multiple premises must be simultaneously present for the rule to apply. In the following example, `R` is a transitive binary relation over `Int`. ``` opaque R : Int → Int → Prop axiom Rtrans {x y z : Int} : R x y → R y z → R x z ``` To use the fact that `R` is transitive, `grind` must already be able to satisfy both premises. This is represented using a multi-pattern: ``` 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 ``` The multi-pattern `R x y`, `R y z` instructs `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`. You can add constraints to restrict theorem instantiation. For example: ``` grind_pattern extract_extract => (as.extract i j).extract k l where as =/= #[] ``` The constraint instructs `grind` to instantiate the theorem only if `as` is **not** definitionally equal to `#[]`. ## Constraints - `x =/= term`: The term bound to `x` (one of the theorem parameters) is **not** definitionally equal to `term`. The term may contain holes (i.e., `_`). - `x =?= term`: The term bound to `x` is definitionally equal to `term`. The term may contain holes (i.e., `_`). - `size x < n`: The term bound to `x` has size less than `n`. Implicit arguments and binder types are ignored when computing the size. - `depth x < n`: The term bound to `x` has depth less than `n`. - `is_ground x`: The term bound to `x` does not contain local variables or meta-variables. - `is_value x`: The term bound to `x` is a value. That is, it is a constructor fully applied to value arguments, a literal (`Nat`, `Int`, `String`, etc.), or a lambda `fun x => t`. - `is_strict_value x`: Similar to `is_value`, but without lambdas. - `not_value x`: The term bound to `x` is a **not** value (see `is_value`). - `not_strict_value x`: Similar to `not_value`, but without lambdas. - `gen < n`: The theorem instance has generation less than `n`. Recall that each term is assigned a generation, and terms produced by theorem instantiation have a generation that is one greater than the maximal generation of all the terms used to instantiate the theorem. This constraint complements the `gen` option available in `grind`. - `max_insts < n`: A new instance is generated only if less than `n` instances have been generated so far. - `guard e`: The instantiation is delayed until `grind` learns that `e` is `true` in this state. - `check e`: Similar to `guard e`, but `grind` checks whether `e` is implied by its current state by assuming `¬ e` and trying to deduce an inconsistency. ## Example Consider the following example where `f` is a monotonic function ``` opaque f : Nat → Nat axiom fMono : x ≤ y → f x ≤ f y ``` and you want to instruct `grind` to instantiate `fMono` for every pair of terms `f x` and `f y` when `x ≤ y` and `x` is **not** definitionally equal to `y`. You can use ``` grind_pattern fMono => f x, f y where guard x ≤ y x =/= y ``` Then, in the following example, only three instances are generated. ``` /-- trace: [grind.ematch.instance] fMono: a ≤ f a → f a ≤ f (f a) [grind.ematch.instance] fMono: f a ≤ f (f a) → f (f a) ≤ f (f (f a)) [grind.ematch.instance] fMono: a ≤ f (f a) → f a ≤ f (f (f a)) -/ #guard_msgs in example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by set_option trace.grind.ematch.instance true in grind ``` grind_pattern 显式指定模式。 它包含若干变体,用来选择不同的启发式。 grind? 属性会显示一条信息消息,指出所选模式——这对调试非常有帮助!

模式是定理陈述的子表达式。 如果某个子表达式的头部是可索引常量,那么它就是 可索引的;如果它能固定定理某个参数的取值,就称它 覆盖 了该参数。 可索引常量指除 EqHEqIffAndOrNot 之外的所有常量。 一个模式或多模式所覆盖参数的集合,称为它的 覆盖度。 有些常量的优先级低于其他常量;特别是算术运算符 HAdd.hAddHSub.hSubHMul.hMulDvd.dvdHDiv.hDivHMod.hMod 的优先级都较低。 如果不存在一个更小的可索引子表达式,并且它的头常量优先级至少同样高,那么该可索引子表达式就是 极小的

属性Grind 模式

当把 grind 属性加到某个定义上时,每当 grind 遇到该定义,就会把它展开为其主体。 在使用模块系统时,如果该定义的主体不可见(例如没有通过 @[expose] 暴露),那么 grind 属性会被忽略。

attr ::= ...
    | Marks a theorem or definition for use by the `grind` tactic.

An optional modifier (e.g. `=`, `→`, `←`, `cases`, `intro`, `ext`, `inj`, etc.)
controls how `grind` uses the declaration:
* whether it is applied forwards, backwards, or both,
* whether equalities are used on the left, right, or both sides,
* whether case-splits, constructors, extensionality, or injectivity are applied,
* or whether custom instantiation patterns are used.

See the individual modifier docstrings for details.
grind grindMod?

grind 属性会根据给定修饰符所决定的策略,自动为定理生成 E-匹配模式。 如果没有提供修饰符,那么 grind 会建议合适的修饰符,并显示相应生成的模式。

attr ::= ...
    | Like `@[grind]`, but enforces the **minimal indexable subexpression condition**:
when several subterms cover the same free variables, `grind!` chooses the smallest one.

This influences E-matching pattern selection.

### Example
```lean
theorem fg_eq (h : x > 0) : f (g x) = x

@[grind <-] theorem fg_eq (h : x > 0) : f (g x) = x
-- Pattern selected: `f (g x)`

-- With minimal subexpression:
@[grind! <-] theorem fg_eq (h : x > 0) : f (g x) = x
-- Pattern selected: `g x`
```
grind! grindMod?

grind! 属性会根据给定修饰符所决定的策略,自动为定理生成 E-匹配模式。 此外,它还强制要求所选模式必须是极小的可索引子表达式。

attr ::= ...
    | Like `@[grind]`, but also prints the pattern(s) selected by `grind`
as info messages. Useful for debugging annotations and modifiers.
grind? grindMod?

grind? 会显示所生成的模式。

attr ::= ...
    | Like `@[grind!]`, but also prints the pattern(s) selected by `grind`
as info messages. Combines minimal subexpression selection with debugging output.
grind!? grindMod?

grind!? 属性等价于 grind!,不同之处在于它会显示生成结果,便于检查。

在没有任何修饰符时,@[grind] 会先遍历结论,再从左到右遍历各个假设;每当某个模式能扩大覆盖度时,就将其加入,并在所有参数都被覆盖时停止。 这一默认策略也可以通过 Lean.Parser.Attr.grindDefThe `.` modifier instructs `grind` to select a multi-pattern by traversing the conclusion of the theorem, and then the hypotheses from left to right. We say this is the default modifier. Each time it encounters a subexpression which covers an argument which was not previously covered, it adds that subexpression as a pattern, until all arguments have been covered. If `grind!` is used, then only minimal indexable subexpressions are considered. . 修饰符显式请求。 除了使用默认策略之外,该属性还会检查哪些其他策略也适用,并显示所有由此得到的模式。

语法默认模式
grindMod ::= ...
    | The `.` modifier instructs `grind` to select a multi-pattern by traversing the conclusion of the
theorem, and then the hypotheses from left to right. We say this is the default modifier.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.
.
grindMod ::= ...
    | The `.` modifier instructs `grind` to select a multi-pattern by traversing the conclusion of the
theorem, and then the hypotheses from left to right. We say this is the default modifier.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.
·

. 修饰符指示 grind 先遍历定理的结论,再从左到右遍历各项假设,以选择一个多模式;这称为默认修饰符。每当遇到覆盖尚未覆盖参数的子表达式时,就将其加入模式,直到所有参数均被覆盖。使用 grind! 时,只考虑最小的可索引子表达式。

语法等式重写
grindMod ::= ...
    | The `=` modifier instructs `grind` to check 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.
=

= 修饰符指示 grind 检查定理结论是否为等式,然后将等式左侧用作模式。若左侧没有出现所有参数,此操作可能失败。

语法反向等式重写
grindMod ::= ...
    | The `=_` modifier instructs `grind` to check that the conclusion of the theorem is an equality,
and then uses the right-hand side of the equality as a pattern. This may fail if not all of the arguments appear
in the right-hand side.
=_

=_ 修饰符指示 grind 检查定理结论是否为等式,然后将等式右侧用作模式。若右侧没有出现所有参数,此操作可能失败。

语法双向等式重写
grindMod ::= ...
    | The `_=_` modifier acts like a macro which expands to `=` and `=_`.  It adds two patterns,
allowing the equality theorem to trigger in either direction.
_=_

_=_ 修饰符类似一个展开为 ==_ 的宏。它添加两个模式,使等式定理可由任一方向触发。

语法前向推理
grindMod ::= ...
    | The `→` modifier instructs `grind` to select a multi-pattern from the hypotheses of the theorem.
In other words, `grind` will use the theorem for forwards reasoning.
To generate a pattern, it traverses the hypotheses of the theorem from left to right.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.

修饰符指示 grind 从定理的假设中选择一个多模式,即使用该定理进行前向推理。它从左到右遍历各项假设,每遇到覆盖尚未覆盖参数的子表达式,就将其加入模式,直到所有参数均被覆盖。使用 grind! 时,只考虑最小的可索引子表达式。

语法后向推理
grindMod ::= ...
    | The `←` modifier instructs `grind` to select a multi-pattern from the conclusion of theorem.
In other words, `grind` will use the theorem for backwards reasoning.
This may fail if not all of the arguments to the theorem appear in the conclusion.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.

修饰符指示 grind 从定理结论中选择一个多模式,即使用该定理进行后向推理。若结论中未出现定理的所有参数,此操作可能失败。每遇到覆盖尚未覆盖参数的子表达式,就将其加入模式,直到所有参数均被覆盖。使用 grind! 时,只考虑最小的可索引子表达式。

检查 @[grind] 属性生成的模式非常重要,以确保它们匹配到的是引理中正确的部分。 如果模式过于严格,那么在它本应相关的情形下,引理也不会被应用,从而降低自动化程度。 如果模式过于宽泛,那么引理会在许多无助于证明的场景中被尝试,性能因此受损。

另外,还有三个较少使用的引理修饰符:

语法从左到右遍历
grindMod ::= ...
    | The `⇒` modifier instructs `grind` to select a multi-pattern by traversing all the hypotheses from
left to right, followed by the conclusion.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.
=>
grindMod ::= ...
    | The `⇒` modifier instructs `grind` to select a multi-pattern by traversing all the hypotheses from
left to right, followed by the conclusion.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.

修饰符指示 grind 先从左到右遍历全部假设,再遍历结论,以选择一个多模式。每遇到覆盖尚未覆盖参数的子表达式,就将其加入模式,直到所有参数均被覆盖。使用 grind! 时,只考虑最小的可索引子表达式。

语法从右到左遍历
grindMod ::= ...
    | The `⇐` modifier instructs `grind` to select a multi-pattern by traversing the conclusion, and then
all the hypotheses from right to left.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.
<=
grindMod ::= ...
    | The `⇐` modifier instructs `grind` to select a multi-pattern by traversing the conclusion, and then
all the hypotheses from right to left.
Each time it encounters a subexpression which covers an argument which was not
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
If `grind!` is used, then only minimal indexable subexpressions are considered.

修饰符指示 grind 先遍历结论,再从右到左遍历全部假设,以选择一个多模式。每遇到覆盖尚未覆盖参数的子表达式,就将其加入模式,直到所有参数均被覆盖。使用 grind! 时,只考虑最小的可索引子表达式。

语法等式上的后向推理
grindMod ::= ...
    | The `←=` modifier is unlike the other `grind` modifiers, and it used specifically for
backwards reasoning on equality. When a theorem's conclusion is an equality proposition and it
is annotated with `@[grind ←=]`, grind `will` instantiate it whenever the corresponding disequality
is assumed—this is a consequence of the fact that grind performs all proofs by contradiction.
Ordinarily, the grind attribute does not consider the `=` symbol when generating patterns.
=

= 修饰符专用于对等式进行后向推理,与其他 grind 修饰符不同。当定理结论是等式命题且以 @[grind =] 标注时,只要假设了对应的不等关系,grind 就会实例化该定理;这是因为 grind 的所有证明均采用反证法。通常,grind 属性生成模式时不会考虑 = 符号。

@[grind ←=] 属性

当尝试证明 a⁻¹ = b 时,由于存在 @[grind ←=] 标注,grind 会使用 inv_eq

@[grind =] theorem declaration uses `sorry`inv_eq [One α] [Mul α] [Inv α] {a b : α} (w : a * b = 1) : a⁻¹ = b := sorry
语法函数值的同余闭包
grindMod ::= ...
    | The `funCC` modifier marks global functions that support **function-valued congruence closure**.
Given an application `f a₁ a₂ … aₙ`, when `funCC := true`,
`grind` generates and tracks equalities for all partial applications:
- `f a₁`
- `f a₁ a₂`
- `…`
- `f a₁ a₂ … aₙ`
funCC

funCC 修饰符标记支持函数值同余闭包的全局函数。对于应用 f a₁ a₂ … aₙ,启用 funCC 后,grind 会为所有部分应用生成并跟踪等式:f a₁f a₁ a₂、……、f a₁ a₂ … aₙ

还有一些额外修饰符可用于把其他类型的引理加入索引。 这包括外延性定理、函数的单射性定理,以及一个将归纳定义谓词的所有构造子快捷加入索引的方式。

语法外延性
grindMod ::= ...
    | The `ext` modifier marks extensionality theorems for use by `grind`.
For example, the standard library marks `funext` with this attribute.

Whenever `grind` encounters a disequality `a ≠ b`, it attempts to apply any
available extensionality theorems whose matches the type of `a` and `b`.
ext

ext 修饰符标记供 grind 使用的外延性定理。例如,标准库用此属性标记 funext。每当 grind 遇到不等关系 a b 时,它会尝试应用类型与 ab 相匹配的外延性定理。

此外,给某个结构体加上 @[grind ext] 还会注册它的外延性定理。

@[grind ext] 属性

Point 是一个带有两个字段的结构体:

structure Point where x : Int y : Int

默认情况下,grind 可以解决下面这样的目标,因为定义相等对积类型包含 η-等价

example (p : Point) : p = p.x, p.y := p:Pointp = { x := p.x, y := p.y } All goals completed! 🐙

不过,它无法解决下面这种需要诉诸命题相等的目标:

example (p : Point) (a : Int) : a = p.x p = a, p.y := p:Pointa:Inta = p.x p = { x := a, y := p.y } `grind` failed p:Pointa:Inth:a = p.xh_1:¬p = { x := a, y := p.y }False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] a = p.x
    • [prop] ¬p = { x := a, y := p.y }
  • [eqc] False propositions
    • [prop] p = { x := a, y := p.y }
  • [eqc] Equivalence classes
    • [eqc] {a, p.x}
All goals completed! 🐙
`grind` failed
p:Pointa:Inth:a = p.xh_1:¬p = { x := a, y := p.y }False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] a = p.x
    • [prop] ¬p = { x := a, y := p.y }
  • [eqc] False propositions
    • [prop] p = { x := a, y := p.y }
  • [eqc] Equivalence classes
    • [eqc] {a, p.x}

在证明诸如“把点的字段交换两次等于恒等”的定理时,就可能遇到这种目标:

def Point.swap (p : Point) : Point := p.y, p.x theorem swap_swap_eq_id : Point.swap Point.swap = id := Point.swap Point.swap = id ((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id `grind` failed h:¬((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = idw:Pointh_1:¬{ x := w.x, y := w.y } = id wFalse
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] ¬((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id
    • [prop] x, ¬{ x := x.x, y := x.y } = id x
    • [prop] ¬{ x := w.x, y := w.y } = id w
    • [prop] id w = w
  • [eqc] True propositions
  • [eqc] False propositions
    • [prop] ((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id
    • [prop] { x := w.x, y := w.y } = id w
  • [eqc] Equivalence classes
    • [eqc] {w, id w}
  • [cases] Case analyses
    • [cases] [1/1]: x, ¬{ x := x.x, y := x.y } = id x
      • [cases] source: Extensionality `funext`
  • [ematch] E-matching patterns
    • [thm] id.eq_1: [@id #1 #0]
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] id.eq_11
All goals completed! 🐙
`grind` failed
h:¬((fun p => { x := p.y, y := p.x })  fun p => { x := p.y, y := p.x }) = idw:Pointh_1:¬{ x := w.x, y := w.y } = id wFalse
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] ¬((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id
    • [prop] x, ¬{ x := x.x, y := x.y } = id x
    • [prop] ¬{ x := w.x, y := w.y } = id w
    • [prop] id w = w
  • [eqc] True propositions
  • [eqc] False propositions
    • [prop] ((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id
    • [prop] { x := w.x, y := w.y } = id w
  • [eqc] Equivalence classes
    • [eqc] {w, id w}
  • [cases] Case analyses
    • [cases] [1/1]: x, ¬{ x := x.x, y := x.y } = id x
      • [cases] source: Extensionality `funext`
  • [ematch] E-matching patterns
    • [thm] id.eq_1: [@id #1 #0]
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] id.eq_11

Point 添加 @[grind ext] 属性后,grind 既能解决最初的例子,也能证明下面这个定理:

attribute [grind ext] Point example (p : Point) (a : Int) : a = p.x p = a, p.y := p:Pointa:Inta = p.x p = { x := a, y := p.y } All goals completed! 🐙 theorem swap_swap_eq_id' : Point.swap Point.swap = id := Point.swap Point.swap = id ((fun p => { x := p.y, y := p.x }) fun p => { x := p.y, y := p.x }) = id All goals completed! 🐙
语法单射性
grindMod ::= ...
    | The `inj` modifier marks injectivity theorems for use by `grind`.
The conclusion of the theorem must be of the form `Function.Injective f`
where the term `f` contains at least one constant symbol.
inj

inj 修饰符标记供 grind 使用的单射性定理。定理结论必须形如 Function.Injective f,且项 f 至少包含一个常量符号。

单射性模式

函数 double 会把它的参数翻倍:

def double (x : Nat) : Nat := x + x

默认情况下,grind 无法证明下面这个定理:

theorem A {n k : Nat} : double (n + 5) = double (k - 3) n + 8 = k := n:Natk:Natdouble (n + 5) = double (k - 3) n + 8 = k `grind` failed n k:Nath:double (n + 5) = double (k - 3)h_1:¬n + 8 = kh_2:-1 * k + 3 0False
[grind] Goal diagnostics
  • [facts] Asserted facts
  • [eqc] True propositions
  • [eqc] False propositions
    • [prop] n + 8 = k
  • [eqc] Equivalence classes
  • [cases] Case analyses
    • [cases] [1/2]: if -1 * k + 3 0 then k + -3 else 0
      • [cases] source: Initial goal
  • [cutsat] Assignment satisfying linear constraints
All goals completed! 🐙

不过,double 是单射的,而这一事实可以用 grind inj 属性为 grind 注册:

@[grind inj] theorem double_inj : Function.Injective double := Function.Injective double a₁ a₂ : Nat⦄, a₁ + a₁ = a₂ + a₂ a₁ = a₂ All goals completed! 🐙

这个单射性引理就足以证明该定理:

theorem B {n k : Nat} : double (n + 5) = double (k - 3) n + 8 = k := n:Natk:Natdouble (n + 5) = double (k - 3) n + 8 = k All goals completed! 🐙
语法构造子模式
grindMod ::= ...
    | The `intro` modifier instructs `grind` to use the constructors (introduction rules)
of an inductive predicate as E-matching theorems.Example:
```
inductive Even : Nat → Prop where
| zero : Even 0
| add2 : Even x → Even (x + 2)

attribute [grind intro] Even
example (h : Even x) : Even (x + 6) := by grind
example : Even 0 := by grind
```
Here `attribute [grind intro] Even` acts like a macro that expands to
`attribute [grind] Even.zero` and `attribute [grind] Even.add2`.
This is especially convenient for inductive predicates with many constructors.
intro

intro 修饰符指示 grind 将归纳谓词的构造器(引入规则)用作 E-matching 定理。例如:

inductive Even : Nat Prop where | zero : Even 0 | add2 : Even x Even (x + 2) attribute [grind intro] Even example (h : Even x) : Even (x + 6) := x:Nath:Even xEven (x + 6) All goals completed! 🐙 example : Even 0 := Even 0 All goals completed! 🐙

这里,attribute [grind intro] Even 的作用类似于一个宏,会展开为 attribute [grind] Even.zeroattribute [grind] Even.add2。 这对构造器较多的归纳谓词尤其方便。

构造子的模式

谓词 Decreasing 表示一个整数列表中的每个值都小于它前面的那个值,而函数 decreasing 会检查这一性质,并返回一个 Bool

inductive Decreasing : List Int Prop | nil : Decreasing [] | singleton : Decreasing [x] | cons : Decreasing (x :: xs) y > x Decreasing (y :: x :: xs) def decreasing : List Int Bool | [] | [_] => true | y :: x :: xs => y > x && decreasing (x :: xs)

如果且仅如果 Decreasing 对其参数成立时该函数返回 true,那么这个函数就是正确的。 尝试用 fun_inductiongrind 的组合来证明这一点,会立刻失败,三个分支一个也证不出来:

def decreasingCorrect : decreasing xs = Decreasing xs := xs:List Int(decreasing xs = true) = Decreasing xs (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) `grind` failed y x:Intxs:List Intih1:(decreasing (x :: xs) = true) = Decreasing (x :: xs)h:(-1 * y + x + 1 0 decreasing (x :: xs) = true) = ¬Decreasing (y :: x :: xs)left:-1 * y + x + 1 0left_1:decreasing (x :: xs) = trueright_1:¬Decreasing (y :: x :: xs)False
[grind] Goal diagnostics
`grind` failed h:True = ¬Decreasing []False
[grind] Goal diagnostics
`grind` failed head:Inth:True = ¬Decreasing [head]False
[grind] Goal diagnostics
All goals completed! 🐙
`grind` failed
h:True = ¬Decreasing []False
[grind] Goal diagnostics
`grind` failed
head:Inth:True = ¬Decreasing [head]False
[grind] Goal diagnostics
`grind` failed
y x:Intxs:List Intih1:(decreasing (x :: xs) = true) = Decreasing (x :: xs)h:(-1 * y + x + 1  0  decreasing (x :: xs) = true) = ¬Decreasing (y :: x :: xs)left:-1 * y + x + 1  0left_1:decreasing (x :: xs) = trueright_1:¬Decreasing (y :: x :: xs)False
[grind] Goal diagnostics

Decreasing 添加 grind intro 属性后,会为它的三个构造子分别加入 E-匹配模式。这样一来,grind 就能证明前两个目标,而最后一个目标只需再对某个假设做一次分类讨论即可:

attribute [grind intro] Decreasing Definition `decreasingCorrect'` is a proposition; use `theorem` instead of `def` Note: This linter can be disabled with `set_option linter.defProp false`def decreasingCorrect' : decreasing xs = Decreasing xs := xs:List Int(decreasing xs = true) = Decreasing xs (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) try All goals completed! 🐙 case case3 y x xs ih y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)(decide (y > x) && decreasing (x :: xs)) = true Decreasing (y :: x :: xs) y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)(decide (y > x) && decreasing (x :: xs)) = true Decreasing (y :: x :: xs)y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)Decreasing (y :: x :: xs) (decide (y > x) && decreasing (x :: xs)) = true y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)(decide (y > x) && decreasing (x :: xs)) = true Decreasing (y :: x :: xs) All goals completed! 🐙 y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)Decreasing (y :: x :: xs) (decide (y > x) && decreasing (x :: xs)) = true intro y:Intx:Intxs:List Intih:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)x✝:Decreasing (y :: x :: xs)hDec:Decreasing (x :: xs)hLt:y > x(decide (y > x) && decreasing (x :: xs)) = true All goals completed! 🐙

Decreasing 添加 grind cases 后,这个分类讨论也会自动完成,从而得到一个完全自动化的证明:

attribute [grind cases] Decreasing Definition `decreasingCorrect''` is a proposition; use `theorem` instead of `def` Note: This linter can be disabled with `set_option linter.defProp false`def decreasingCorrect'' : decreasing xs = Decreasing xs := xs:List Int(decreasing xs = true) = Decreasing xs (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) (true = true) = Decreasing []head✝:Int(true = true) = Decreasing [head✝]y✝:Intx✝:Intxs✝:List Intih1✝:(decreasing (x✝ :: xs✝) = true) = Decreasing (x✝ :: xs✝)((decide (y✝ > x✝) && decreasing (x✝ :: xs✝)) = true) = Decreasing (y✝ :: x✝ :: xs✝) All goals completed! 🐙
语法预处理时展开
grindMod ::= ...
    | The `unfold` modifier instructs `grind` to unfold the given definition during the preprocessing step.
Example:
```
@[grind unfold] def h (x : Nat) := 2 * x
example : 6 ∣ 3*h x := by grind
```
unfold

unfold 修饰符指示 grind 在预处理阶段展开给定定义。例如:

@[grind unfold] def h (x : Nat) := 2 * x example : 6 3*h x := x:Nat6 3 * h x All goals completed! 🐙
语法规范化规则
grindMod ::= ...
    | The `norm` modifier instructs `grind` to use a theorem as a normalization rule. That is,
the theorem is applied during the preprocessing step.
This feature is meant for advanced users who understand how the preprocessor and `grind`'s search
procedure interact with each other.
New users can still benefit from this feature by restricting its use to theorems that completely
eliminate a symbol from the goal. Example:
```
theorem max_def : max n m = if n ≤ m then m else n
```
For a negative example, consider:
```
opaque f : Int → Int → Int → Int
theorem fax1 : f x 0 1 = 1 := sorry
theorem fax2 : f 1 x 1 = 1 := sorry
attribute [grind norm] fax1
attribute [grind =] fax2

example (h : c = 1) : f c 0 c = 1 := by
  grind -- fails
```
In this example, `fax1` is a normalization rule, but it is not applicable to the input goal since
`f c 0 c` is not an instance of `f x 0 1`. However, `f c 0 c` matches the pattern `f 1 x 1` modulo
the equality `c = 1`. Thus, `grind` instantiates `fax2` with `x := 0`, producing the equality
`f 1 0 1 = 1`, which the normalizer simplifies to `True`. As a result, nothing useful is learned.
In the future, we plan to include linters to automatically detect issues like these.
Example:
```
opaque f : Nat → Nat
opaque g : Nat → Nat

@[grind norm] axiom fax : f x = x + 2
@[grind norm ←] axiom fg : f x = g x

example : f x ≥ 2 := by grind
example : f x ≥ g x := by grind
example : f x + g x ≥ 4 := by grind
```
norm

norm 修饰符指示 grind 将定理用作规范化规则,即在预处理阶段应用该定理。 这一功能面向了解预处理器与 grind 搜索过程如何交互的高级用户。 新用户仍可将其限用于能从目标中彻底消除某个符号的定理,例如:

theorem max_def : max n m = if n ≤ m then m else n

以下是一个反例:

opaque f : Int → Int → Int → Int
theorem fax1 : f x 0 1 = 1 := sorry
theorem fax2 : f 1 x 1 = 1 := sorry
attribute [grind norm] fax1
attribute [grind =] fax2

example (h : c = 1) : f c 0 c = 1 := by
  grind -- 失败

在此例中,fax1 是规范化规则,但它无法应用于输入目标,因为 f c 0 c 不是 f x 0 1 的实例。 不过,模等式 c = 1 而言,f c 0 c 匹配模式 f 1 x 1。 因此,grindx := 0 实例化 fax2,得到等式 f 1 0 1 = 1,随后规范化器将其化简为 True,结果没有获得任何有用信息。 未来计划加入检查器,以自动检测这类问题。

示例:

opaque f : Nat Nat opaque g : Nat Nat @[grind norm] axiom fax : f x = x + 2 @[grind norm ] axiom fg : f x = g x example : f x 2 := x:Natf x 2 All goals completed! 🐙 example : f x g x := x:Natf x g x All goals completed! 🐙 example : f x + g x 4 := x:Natf x + g x 4 All goals completed! 🐙

grind 策略可以处理某些求解基础设施并不丰富的源代数(例如位向量),做法是把它“嵌入”到另一个求解基础设施更丰富的代数中(例如自然数或整数)。 同态规则描述了这种从源到目标的嵌入,以及该嵌入如何与其他运算交换(例如在位向量情形下的加法或乘法)。 同态谓词则给出了关于该嵌入的更多事实,供 grind 使用(例如长度为 n 的位向量对应于一个小于 2^n 的自然数)。

语法同态规则
grindMod ::= ...
    | The `hom` modifier marks a theorem as a homomorphism rule for `grind`.

Homomorphism rules translate terms from a source domain into a target domain that has a
dedicated solver. A collection of homomorphism rules encodes an algebra homomorphism
`h : A → B`: each rule states how `h` commutes with a source-domain operation, as in
`h (f x y) = g (h x) (h y)`. Example: injecting bitvector operations into integer
arithmetic using `BitVec.toNat`:
```
@[grind hom] theorem toNat_add (x y : BitVec w) :
    (x + y).toNat = (x.toNat + y.toNat) % 2^w
```
The rules must be unconditional equations (or `Iff`s). They are applied to fixpoint
outside the E-graph, and only the final result is internalized.
hom

hom 修饰符将定理标记为 grind 的同态规则。

同态规则把项从源代数转换到拥有专用求解器的目标代数。一组同态规则可编码代数同态 h : A B:每条规则说明 h 如何与某项源域运算交换,例如 h (f x y) = g (h x) (h y)。 以下示例使用 BitVec.toNat,把位向量运算注入整数算术:

@[grind hom] theorem toNat_add (x y : BitVec w) :
    (x + y).toNat = (x.toNat + y.toNat) % 2^w

规则必须是无条件等式(或 Iff)。它们会在 E-图外反复应用至不动点,只有最终结果会被内部化。

语法同态谓词
grindMod ::= ...
    | The `hom_pred` modifier marks a theorem as a homomorphism predicate for `grind`.

Homomorphism predicates are facts that `grind` instantiates eagerly for the terms it
internalizes. The conclusion of the theorem must contain an application `f a₁ … aₙ`
whose trailing arguments are exactly the theorem's explicit parameters; the head
symbol `f` becomes the trigger. Whenever `grind` internalizes a term with head `f`,
the theorem is instantiated with the term's trailing arguments, and the resulting
fact is asserted. Typical uses are range facts for injection functions, and
translations of relations into a target domain. Examples:
```
@[grind hom_pred] theorem BitVec.toNat_range (x : BitVec w) : x.toNat < 2^w
@[grind hom_pred] theorem UInt8.le_iff (a b : UInt8) : a ≤ b ↔ a.toBitVec ≤ b.toBitVec
```
The first theorem is triggered by terms of the form `BitVec.toNat x`, and the second
one by `a ≤ b` applications. `grind` uses the types of `a` and `b` to discard
irrelevant instantiations.
hom_pred

hom_pred 修饰符将定理标记为 grind 的同态谓词。

同态谓词是 grind 会针对所内部化的项立即实例化的事实。 定理结论必须含有应用 f a₁ … aₙ,其末尾参数恰为定理的显式参数;头符号 f 将成为触发器。 典型用途包括注入函数的值域事实,以及把关系转换到目标域。例如:

@[grind hom_pred] theorem BitVec.toNat_range (x : BitVec w) : x.toNat < 2^w
@[grind hom_pred] theorem UInt8.le_iff (a b : UInt8) : a ≤ b ↔ a.toBitVec ≤ b.toBitVec

第一条定理由形如 BitVec.toNat x 的项触发,第二条则由 a b 应用触发。 grind 会利用 ab 的类型排除无关的实例化。

16.6.2. 检查模式🔗

grind? 属性是 grind 属性的一个变体,它还会额外显示所生成的模式或 多模式。 模式与多模式都会显示为子表达式列表,其中每个子表达式都是一个模式;普通模式则显示为单元素列表。 在这些显示出来的模式里,已定义常量的名字会原样打印。 当定理的参数出现在模式中时,它们会用数字而不是名字来显示。 具体来说,这些参数按从右到左的顺序编号,从 0 开始;这种表示法称为 de Bruijn 索引

模式检查示例

要想让 grind 使用下面这个“整除具有传递性”的证明,就需要为它提供 E-匹配模式:

theorem div_trans {n k j : Nat} : n k k j n j := n:Natk:Natj:Natn k k j n j n:Natk:Natj:Natd₁:Natp₁:k = n * d₁d₂:Natp₂:j = k * d₂n j exact d₁ * d₂, n:Natk:Natj:Natd₁:Natp₁:k = n * d₁d₂:Natp₂:j = k * d₂j = n * (d₁ * d₂) All goals completed! 🐙

正确的属性是 @[grind →],因为每个前提都应该对应一个模式。 使用 @[grind? →] 可以看到实际生成了哪些模式:

attribute [div_trans: [@Dvd.dvd `[Nat] `[Nat.instDvd] #4 #3, @Dvd.dvd `[Nat] `[Nat.instDvd] #3 #2]grind? ] div_trans

一共有两个:

div_trans: [@Dvd.dvd `[Nat] `[Nat.instDvd] #4 #3, @Dvd.dvd `[Nat] `[Nat.instDvd] #3 #2]

参数按从右到左编号,因此 #0 是假设 k ∣ j,而 #4n。 因此,这两个模式分别对应项 n ∣ kk ∣ j

从假设和结论的子表达式中选择模式的规则相当微妙。

前向模式生成
axiom p : Nat Nat axiom q : Nat Nat @[h₁: [q #1]grind!? ] theorem declaration uses `sorry`h₁ (w : p (q x) = 7) : p (x + 1) = q x := sorry
h₁: [q #1]

模式是 q x。 从右往左数,参数 #0 是前提 w,参数 #1 是隐式参数 x

为什么 @[grind! →] 会选择 q #1 呢? 属性 @[grind! →] 会通过从左到右遍历各个假设(也就是类型为命题的参数)来寻找模式。 在这里,只有一个假设:p (q x) = 7。 前面描述的启发式规则是:grind! 会寻找一个极小的 可索引 子表达式,它能够 覆盖 某个此前尚未覆盖的参数。 这里只有一个尚未覆盖的参数,也就是 x。 整个假设 p (q x) = 7 不能用,因为 grind 不会对等式建立索引。 右边的 7 也没有帮助,因为它并不能确定 x 的值。 p (q x) 也不合适,因为它并不极小:其中包含 q x,而 q x 本身就是可索引的(其头部是常量 q),并且它也能够确定 x 的值。 表达式 q x 本身则是极小的,因为 x 并不可索引。 因此,q x 被选为了模式。

后向模式生成

在这个例子中,Lean.Parser.Attr.grindMod 修饰符表示应当在结论中寻找模式:

set_option trace.grind.debug.ematch.pattern true in @[[grind.debug.ematch.pattern] place: p (x + 1) = q x[grind.debug.ematch.pattern] collect: p (x + 1) = q x[grind.debug.ematch.pattern] arg: Nat, support: true[grind.debug.ematch.pattern] arg: p (x + 1), support: false[grind.debug.ematch.pattern] collect: p (x + 1)[grind.debug.ematch.pattern] candidate: p (x + 1)[grind.debug.ematch.pattern] found pattern: p (#1 + 1)[grind.debug.ematch.pattern] found full coverage[grind.debug.ematch.pattern] arg: q x, support: falseh₂: [p (#1 + 1)]grind? ] theorem declaration uses `sorry`h₂ (w : 7 = p (q x)) : p (x + 1) = q x := sorry

这里使用的是等式左边,因为 Eq 不可索引,而 HAdd.hAdd 的优先级又低于 p

h₂: [p (#1 + 1)]
双向等式模式生成

在这个例子中,会从等式结论中生成两个彼此独立的 E-匹配模式。 其中一个匹配左边,另一个匹配右边。

@[h₃: [q #1]h₃: [p (#1 + 1)]grind? _=_] theorem declaration uses `sorry`h₃ (w : 7 = p (q x)) : p (x + 1) = q x := sorry
h₃: [q #1]

这里使用的是整个等式左边,而不是仅仅使用 x + 1,因为 HAdd.hAdd 的优先级低于 p

h₃: [p (#1 + 1)]
来自结论与假设的模式

在不加任何修饰符时,@[grind] 会先检查结论,再检查前提,从而生成一个多模式:

@[h₄: [p (#2 + 2), q #1]grind? .] theorem declaration uses `sorry`h₄ (w : p x = q y) : p (x + 2) = 7 := sorry

这里,参数 x#2y#1,而 w#0。 生成得到的多模式包含等式左边,因为它是结论中唯一一个既 极小可索引,并且能够覆盖某个参数(即 x)的子表达式。 它还包含 q y,因为这是前提 w 中唯一一个能够覆盖额外参数(即 y)的极小可索引子表达式。

h₄: [p (#2 + 2), q #1]
失败的后向模式生成

在这个例子中,模式生成会失败,因为定理的结论没有提到参数 y

@[`@[grind ←] theorem h₅` failed to find patterns in the theorem's conclusion, consider using different options or the `grind_pattern` commandgrind? ] theorem declaration uses `sorry`h₅ (w : p x = q y) : p (x + 2) = 7 := sorry
`@[grind ←] theorem h₅` failed to find patterns in the theorem's conclusion, consider using different options or the `grind_pattern` command
从左到右生成

在这个例子中,模式是通过先从左到右遍历前提、再遍历结论而生成的:

@[h₆: [q (#3 + 2), p (#2 + 2)]grind? =>] theorem declaration uses `sorry`h₆ (_ : q (y + 2) = q y) (_ : q (y + 1) = q y) : p (x + 2) = 7 := sorry

在这些模式里,y 是参数 #3x 是参数 #2,因为 自动隐式参数 是按从左到右的顺序插入的,而在定理陈述中 y 出现在 x 之前。 两个前提分别是参数 #1#0。 在生成的多模式中,y 由第一个前提的某个子表达式覆盖,而 x 由结论中的某个子表达式覆盖:

h₆: [q (#3 + 2), p (#2 + 2)]

16.6.3. E-匹配的资源限制🔗

E-匹配可能生成无界数量的定理 实例。 出于效率和终止性的双重考虑,grind 通过两种机制限制 E-匹配的运行次数:

生成层级

每个项都会被赋予一个 生成层级,而由 E-匹配生成的项,其生成层级会比所有用于实例化该定理的项中的最大生成层级大 1。 E-匹配只会考虑生成层级低于某个可配置阈值的项。 grindgen 选项控制这个生成层级阈值。

轮数限制

每次调用 E-匹配引擎都称为一 。 E-匹配只会执行有限轮。 grindematch 选项控制这个轮数上限。

实例过多

E-匹配可能生成过多的定理 实例。 有些模式甚至会生成无界数量的实例。

在这个例子中,s_eq 以模式 s x 被加入索引:

def s (Variable name `x` is not explicitly referenced. Hint: The binding can be removed (if unused) or named `_` (if used implicitly). Alternatively, prefix the name with `_` to silence this warning: [apply] _x Note: This linter can be disabled with `set_option linter.unusedVariables false`x : Nat) := 0 @[s_eq: [s #0]grind? =] theorem s_eq (x : Nat) : s x = s (x + 1) := rfl
s_eq: [s #0]

尝试使用这个定理会生成许多把 s 应用于具体值的事实。 特别地,在这五轮中的每一轮里,s_eq 都会用一个新的 Nat 来实例化。 首先,grindx := 0 实例化 s_eq,从而生成项 s 1。 这个项又会匹配模式 s x,于是进一步以 x := 1 实例化 s_eq,生成项 s 2, 如此继续,直到达到轮数上限。

example : s 0 > 0 := s 0 > 0 `grind` failed h:s 0 = 0False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] s 0 = 0
    • [prop] s 0 = s 1
    • [prop] s 1 = s 2
    • [prop] s 2 = s 3
    • [prop] s 3 = s 4
    • [prop] s 4 = s 5
  • [eqc] Equivalence classes
    • [eqc] {s 0, 0, s 1, s 2, s 3, s 4, s 5}
  • [ematch] E-matching patterns
  • [cutsat] Assignment satisfying linear constraints
    • [assign] s 0 := 0
    • [assign] s 1 := 0
    • [assign] s 2 := 0
    • [assign] s 3 := 0
    • [assign] s 4 := 0
    • [assign] s 5 := 0
  • [limits] Thresholds reached
    • [limit] maximum number of E-matching rounds has been reached, threshold: `(ematch := 5)`
[grind] Diagnostics
  • [thm] E-Matching instances
All goals completed! 🐙
`grind` failed
h:s 0 = 0False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] s 0 = 0
    • [prop] s 0 = s 1
    • [prop] s 1 = s 2
    • [prop] s 2 = s 3
    • [prop] s 3 = s 4
    • [prop] s 4 = s 5
  • [eqc] Equivalence classes
    • [eqc] {s 0, 0, s 1, s 2, s 3, s 4, s 5}
  • [ematch] E-matching patterns
  • [cutsat] Assignment satisfying linear constraints
    • [assign] s 0 := 0
    • [assign] s 1 := 0
    • [assign] s 2 := 0
    • [assign] s 3 := 0
    • [assign] s 4 := 0
    • [assign] s 5 := 0
  • [limits] Thresholds reached
    • [limit] maximum number of E-matching rounds has been reached, threshold: `(ematch := 5)`
[grind] Diagnostics
  • [thm] E-Matching instances

把轮数上限提高到 20 后,E-匹配会因为默认的生成层级上限 8 而终止:

example : s 0 > 0 := s 0 > 0 `grind` failed h:s 0 = 0False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] s 0 = 0
    • [prop] s 0 = s 1
    • [prop] s 1 = s 2
    • [prop] s 2 = s 3
    • [prop] s 3 = s 4
    • [prop] s 4 = s 5
    • [prop] s 5 = s 6
    • [prop] s 6 = s 7
    • [prop] s 7 = s 8
  • [eqc] Equivalence classes
    • [eqc] {s 0, 0, s 1, s 2, s 3, s 4, s 5, s 6, s 7, s 8}
  • [ematch] E-matching patterns
  • [cutsat] Assignment satisfying linear constraints
    • [assign] s 0 := 0
    • [assign] s 1 := 0
    • [assign] s 2 := 0
    • [assign] s 3 := 0
    • [assign] s 4 := 0
    • [assign] s 5 := 0
    • [assign] s 6 := 0
    • [assign] s 7 := 0
    • [assign] s 8 := 0
  • [limits] Thresholds reached
    • [limit] maximum term generation has been reached, threshold: `(gen := 8)`
[grind] Diagnostics
  • [thm] E-Matching instances
All goals completed! 🐙
`grind` failed
h:s 0 = 0False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] s 0 = 0
    • [prop] s 0 = s 1
    • [prop] s 1 = s 2
    • [prop] s 2 = s 3
    • [prop] s 3 = s 4
    • [prop] s 4 = s 5
    • [prop] s 5 = s 6
    • [prop] s 6 = s 7
    • [prop] s 7 = s 8
  • [eqc] Equivalence classes
    • [eqc] {s 0, 0, s 1, s 2, s 3, s 4, s 5, s 6, s 7, s 8}
  • [ematch] E-matching patterns
  • [cutsat] Assignment satisfying linear constraints
    • [assign] s 0 := 0
    • [assign] s 1 := 0
    • [assign] s 2 := 0
    • [assign] s 3 := 0
    • [assign] s 4 := 0
    • [assign] s 5 := 0
    • [assign] s 6 := 0
    • [assign] s 7 := 0
    • [assign] s 8 := 0
  • [limits] Thresholds reached
    • [limit] maximum term generation has been reached, threshold: `(gen := 8)`
[grind] Diagnostics
  • [thm] E-Matching instances
提高 E-匹配限制

iota 会返回所有严格小于其参数的数字所构成的列表,而定理 iota_succ 描述了它在 Nat.succ 上的行为:

def iota : Nat List Nat | 0 => [] | n + 1 => n :: iota n @[grind =] theorem iota_succ : iota (n + 1) = n :: iota n := rfl

事实 (iota 20).length > 10 可以通过反复实例化 iota_succList.length_cons 来证明。 然而,grind 默认并不会成功:

example : (iota 20).length > 10 := (iota 20).length > 10 `grind` failed h:(iota 20).length 10False
[grind] Goal diagnostics
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] iota_succ5
    • [thm] List.length_cons4
All goals completed! 🐙
`grind` failed
h:(iota 20).length  10False
[grind] Goal diagnostics
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] iota_succ5
    • [thm] List.length_cons4

由于 E-匹配轮数受限,这条实例化链没有走完。 提高这些限制后,grind 就可以成功:

example : (iota 20).length > 10 := (iota 20).length > 10 All goals completed! 🐙

当选项 diagnostics 设为 true 时,grind 会显示它为每个定理生成了多少实例。 这有助于找出那些由于模式设计而触发过多实例的定理。 在这里,诊断信息显示 iota_succ 被实例化了 12 次:

set_option diagnostics true in set_option diagnostics.threshold 10 in
[diag] Diagnostics
  • [type_class] used instances (max: 17, num: 2):
    • [type_class] instOfNatNat17
    • [type_class] instOfNat15
  • [kernel] unfolded declarations (max: 387, num: 80):
    • [kernel] Int.Internal.Linear.Poly.rec387
    • [kernel] Bool.rec324
    • [kernel] Int.Internal.Linear.Expr.rec197
    • [kernel] Int.rec192
    • [kernel] Nat.rec128
    • [kernel] Lean.RArray.rec118
    • [kernel] Int.casesOn116
    • [kernel] OfNat.ofNat110
    • [kernel] Int.Internal.Linear.Expr.casesOn104
    • [kernel] Internal.Bool.and'94
    • [kernel] List.rec85
    • [kernel] Int.Internal.Linear.Poly.casesOn85
    • [kernel] Int.Internal.Linear.Poly.denote.match_181
    • [kernel] NatCast.natCast77
    • [kernel] Add.add73
    • [kernel] HAdd.hAdd73
    • [kernel] Bool.casesOn68
    • [kernel] Nat.casesOn64
    • [kernel] Int.Internal.Linear.Expr.toPoly'.go._f58
    • [kernel] Int.Internal.Linear.Expr.toPoly'.go.match_158
    • [kernel] Int.Internal.Linear.Poly.brecOn51
    • [kernel] List.casesOn50
    • [kernel] cond49
    • [kernel] cond.match_149
    • [kernel] Int.add.match_148
    • [kernel] Int.Internal.Linear.Expr.denote._f46
    • [kernel] Int.Internal.Linear.Expr.denote.match_146
    • [kernel] Int.beq'44
    • [kernel] Int.Internal.Linear.Poly.brecOn.go41
    • [kernel] Int.negOfNat.match_140
    • [kernel] Int.Internal.Linear.Var37
    • [kernel] Int.Internal.Linear.Expr.brecOn36
    • [kernel] Int.Internal.Linear.Expr.brecOn.go35
    • [kernel] Int.Internal.Linear.Poly.norm._f35
    • [kernel] Int.Internal.Linear.Poly.insert._f34
    • [kernel] Int.negOfNat33
    • [kernel] Lean.RArray.get27
    • [kernel] Int.mul26
    • [kernel] Int.Internal.Linear.Poly.beq'26
    • [kernel] instOfNatNat25
    • [kernel] HMul.hMul25
    • [kernel] Mul.mul25
    • [kernel] Int.Internal.Linear.Var.denote25
    • [kernel] Function.comp24
    • [kernel] Int.Internal.Linear.Expr.denote24
    • [kernel] Int.Internal.Linear.Poly.insert23
    • [kernel] Nat.Internal.Linear.Expr.rec23
    • [kernel] instDecidableEqList.match_122
    • [kernel] List.length._f22
    • [kernel] iota._f21
    • [kernel] 30 more entries...
      • [kernel] iota.match_121
      • [kernel] Int.add20
      • [kernel] Int.neg.match_120
      • [kernel] Int.neg19
      • [kernel] Neg.neg19
      • [kernel] BEq.beq17
      • [kernel] Nat.blt16
      • [kernel] Decidable.casesOn15
      • [kernel] Decidable.rec15
      • [kernel] instOfNat14
      • [kernel] decide13
      • [kernel] Prod.casesOn13
      • [kernel] Prod.rec13
      • [kernel] Int.Internal.Linear.Poly.combine_mul_k13
      • [kernel] Int.Internal.Linear.Poly.combine_mul_k'13
      • [kernel] LE.le12
      • [kernel] List.brecOn12
      • [kernel] Int.Internal.Linear.norm_eq_cert12
      • [kernel] Int.Internal.Linear.Expr.norm12
      • [kernel] Int.Internal.Linear.Expr.toPoly'12
      • [kernel] Int.Internal.Linear.Poly.addConst12
      • [kernel] Int.Internal.Linear.Poly.norm12
      • [kernel] Nat.Internal.Linear.Expr.casesOn12
      • [kernel] Int.Internal.Linear.Expr.toPoly'.go12
      • [kernel] Int.Internal.Linear.Poly.addConst._f12
      • [kernel] instDecidableEqNat11
      • [kernel] Nat.decEq11
      • [kernel] List.brecOn.go11
      • [kernel] Nat.decEq.match_111
      • [kernel] Int.Internal.Linear.eq_eq_subst'_cert11
  • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
example : (iota 20).length > 10 := (iota 20).length > 10
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] iota_succ12
    • [thm] List.length_cons11
  • [app] Applications
  • [grind] Simplifier
    • [simp] used theorems (max: 15, num: 2):
      • [simp] Lean.Meta.Grind.Arith.normNatOfNatInst15
      • [simp] Nat.reduceAdd12
    • [simp] tried theorems (max: 46, num: 1):
      • [simp] eq_self46 ❌️
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
All goals completed! 🐙
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] iota_succ12
    • [thm] List.length_cons11
  • [app] Applications
  • [grind] Simplifier
    • [simp] used theorems (max: 15, num: 2):
      • [simp] Lean.Meta.Grind.Arith.normNatOfNatInst15
      • [simp] Nat.reduceAdd12
    • [simp] tried theorems (max: 46, num: 1):
      • [simp] eq_self46 ❌️
    • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters

默认情况下,grind 会把为 Lean.Parser.Term.match : termPattern matching. `match e, ... with | p, ... => f | ...` matches each given term `e` against each pattern `p` of a match alternative. When all patterns of an alternative match, the `match` term evaluates to the value of the corresponding right-hand side `f` with the pattern variables bound to the respective matched values. If used as `match h : e, ... with | p, ... => f | ...`, `h : e = p` is available within `f`. When not constructing a proof, `match` does not automatically substitute variables matched on in dependent variables' types. Use `match (generalizing := true) ...` to enforce this. Syntax quotations can also be used in a pattern match. This matches a `Syntax` value against quotations, pattern variables, or `_`. Quoted identifiers only match identical identifiers - custom matching such as by the preresolved names only should be done explicitly. `Syntax.atom`s are ignored during matching by default except when part of a built-in literal. For users introducing new atoms, we recommend wrapping them in dedicated syntax kinds if they should participate in matching. For example, in ```lean syntax "c" ("foo" <|> "bar") ... ``` `foo` and `bar` are indistinguishable during matching, but in ```lean syntax foo := "foo" syntax "c" (foo <|> "bar") ... ``` they are not. match 表达式自动生成的等式当作 E-匹配定理使用。 这可以通过把 matchEqs 标志设为 false 来禁用。

E-匹配与模式匹配

打开诊断信息后可以看到,grind 在 E-匹配期间使用了辅助匹配函数的某一条等式:

theorem gt1 (x y : Nat) : x = y + 1 0 < match x with | 0 => 0 | _ + 1 => 1 := x:Naty:Natx = y + 1 0 < match x with | 0 => 0 | n.succ => 1
[diag] Diagnostics
  • [reduction] unfolded reducible declarations (max: 36, num: 1):
    • [reduction] Nat.casesOn36
  • [kernel] unfolded declarations (max: 40, num: 6):
    • [kernel] List.rec40
    • [kernel] Bool.rec28
    • [kernel] OfNat.ofNat28
    • [kernel] List.casesOn25
    • [kernel] Nat.Internal.Linear.Expr.rec23
    • [kernel] Bool.casesOn22
  • use `set_option diagnostics.threshold <num>` to control threshold for reporting counters
set_option diagnostics true in
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] gt1.match_1.congr_eq_21
  • [app] Applications
All goals completed! 🐙
[grind] Diagnostics
  • [thm] E-Matching instances
    • [thm] gt1.match_1.congr_eq_21
  • [app] Applications

这个定理的类型如下:

gt1.match_1.congr_eq_2.{u_1} (motive : Nat Sort u_1) (x✝ : Nat) (h_1 : Unit motive 0) (h_2 : (n : Nat) motive n.succ) (n✝ : Nat) (heq_1 : x✝ = n✝.succ) : (match x✝ with | 0 => h_1 () | n.succ => h_2 n) h_2 n✝#check gt1.match_1.congr_eq_2
gt1.match_1.congr_eq_2.{u_1} (motive : Nat  Sort u_1) (x✝ : Nat) (h_1 : Unit  motive 0)
  (h_2 : (n : Nat)  motive n.succ) (n✝ : Nat) (heq_1 : x✝ = n✝.succ) :
  (match x✝ with
    | 0 => h_1 ()
    | n.succ => h_2 n) 
    h_2 n✝

禁用匹配器函数等式后,证明就会失败:

example (x y : Nat) : x = y + 1 0 < match x with | 0 => 0 | _+1 => 1 := x:Naty:Natx = y + 1 0 < match x with | 0 => 0 | n.succ => 1 `grind` failed x y:Nath:x = y + 1h_1:(match x with | 0 => 0 | n.succ => 1) = 0n:Nath_2:x = n + 1False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] x = y + 1
    • [prop] (match x with | 0 => 0 | n.succ => 1) = 0
    • [prop] x = n + 1
  • [eqc] Equivalence classes
    • [eqc] {x, y + 1, n + 1}
    • [eqc] {y, n}
    • [eqc] others
      • [eqc] {y, n}
      • [eqc] {y, n}
      • [eqc] {(y + 1), (n + 1)}
      • [eqc] {0, match x with | 0 => 0 | n.succ => 1}
  • [cases] Case analyses
    • [cases] [2/2]: match x with | 0 => 0 | n.succ => 1
      • [cases] source: Initial goal
  • [cutsat] Assignment satisfying linear constraints
    • [assign] x := 1
    • [assign] y := 0
    • [assign] match x with | 0 => 0 | n.succ => 1 := 0
    • [assign] n := 0
  • [ring] Rings
    • [ring] Ring `Lean.Grind.Ring.OfSemiring.Q Nat`
      • [basis] Basis
        • [_] n + -1 * y = 0
    • [ring] Ring `Int`
[grind] Diagnostics
  • [cases] Cases instances
All goals completed! 🐙
`grind` failed
x y:Nath:x = y + 1h_1:(match x with
  | 0 => 0
  | n.succ => 1) =
  0n:Nath_2:x = n + 1False
[grind] Goal diagnostics
  • [facts] Asserted facts
    • [prop] x = y + 1
    • [prop] (match x with | 0 => 0 | n.succ => 1) = 0
    • [prop] x = n + 1
  • [eqc] Equivalence classes
    • [eqc] {x, y + 1, n + 1}
    • [eqc] {y, n}
    • [eqc] others
      • [eqc] {y, n}
      • [eqc] {y, n}
      • [eqc] {(y + 1), (n + 1)}
      • [eqc] {0, match x with | 0 => 0 | n.succ => 1}
  • [cases] Case analyses
    • [cases] [2/2]: match x with | 0 => 0 | n.succ => 1
      • [cases] source: Initial goal
  • [cutsat] Assignment satisfying linear constraints
    • [assign] x := 1
    • [assign] y := 0
    • [assign] match x with | 0 => 0 | n.succ => 1 := 0
    • [assign] n := 0
  • [ring] Rings
    • [ring] Ring `Lean.Grind.Ring.OfSemiring.Q Nat`
      • [basis] Basis
        • [_] n + -1 * y = 0
    • [ring] Ring `Int`
[grind] Diagnostics
  • [cases] Cases instances
🔗选项
trace.grind.ematch.instance

默认值:false

启用后,grind 会为其生成的每个 E-matching 定理实例输出一条跟踪消息;这有助于检查和调试实例化模式。