This PR adds the following features to `simp`: - A routine for simplifying `have` telescopes in a way that avoids quadratic complexity arising from locally nameless expression representations, like what #6220 did for `letFun` telescopes. Furthermore, simp converts `letFun`s into `have`s (nondependent lets), and we remove the #6220 routine since we are moving away from `letFun` encodings of nondependent lets. - A `+letToHave` configuration option (enabled by default) that converts lets into haves when possible, when `-zeta` is set. Previously Lean would need to do a full typecheck of the bodies of `let`s, but the `letToHave` procedure can skip checking some subexpressions, and it modifies the `let`s in an entire expression at once rather than one at a time. - A `+zetaHave` configuration option, to turn off zeta reduction of `have`s specifically. The motivation is that dependent `let`s can only be dsimped by let, so zeta reducing just the dependent lets is a reasonable way to make progress. The `+zetaHave` option is also added to the meta configuration. - When `simp` is zeta reducing, it now uses an algorithm that avoids complexity quadratic in the depth of the let telescope. - Additionally, the zeta reduction routines in `simp`, `whnf`, and `isDefEq` now all are consistent with how they apply the `zeta`, `zetaHave`, and `zetaUnused` configurations. The `letToFun` option is addressing a TODO in `getSimpLetCase` ("handle a block of nested let decls in a single pass if this becomes a performance problem"). Performance should be compared to before #8804, which temporarily disabled the #6220 optimizations for `letFun` telescopes. Good kernel performance depends on carefully handling the `have` encoding. Due to the way the kernel instantiates bvars (it does *not* beta reduce when instantiating), we cannot use congruence theorems of the form `(have x := v; f x) = (have x ;= v'; f' x)`, since the bodies of the `have`s will not be syntactically equal, which triggers zeta reduction in the kernel in `is_def_eq`. Instead, we work with `f v = f' v'`, where `f` and `f'` are lambda expressions. There is still zeta reduction, but only when converting between these two forms at the outset of the generated proof.
51 lines
2.1 KiB
Text
51 lines
2.1 KiB
Text
example (f : Nat → Nat) : (if f x = 0 then f x else f x + 1) + f x = y := by
|
|
simp (config := { contextual := true })
|
|
guard_target =ₛ (if f x = 0 then 0 else f x + 1) + f x = y
|
|
sorry
|
|
|
|
example (f : Nat → Nat) : f x = 0 → f x + 1 = y := by
|
|
simp (config := { contextual := true })
|
|
guard_target =ₛ f x = 0 → 1 = y
|
|
sorry
|
|
|
|
example (f : Nat → Nat) : have _ : f x = 0 := sorryAx _ false; f x + 1 = y := by
|
|
simp (config := { contextual := true, zeta := false, zetaUnused := false })
|
|
guard_target =ₛ have _ : f x = 0 := sorryAx _ false; 1 = y
|
|
sorry
|
|
|
|
def overlap : Nat → Nat
|
|
| 0 => 0
|
|
| 1 => 1
|
|
| n+1 => overlap n
|
|
|
|
example : (if (n = 0 → False) then overlap (n+1) else overlap (n+1)) = overlap n := by
|
|
simp (config := { contextual := true }) only [overlap]
|
|
guard_target =ₛ (if (n = 0 → False) then overlap n else overlap (n+1)) = overlap n
|
|
sorry
|
|
|
|
example : (if (n = 0 → False) then overlap (n+1) else overlap (n+1)) = overlap n := by
|
|
-- The following tactic should because the default discharger only uses assumptions available
|
|
-- when `simp` was invoked unless `contextual := true`
|
|
fail_if_success simp only [overlap]
|
|
guard_target =ₛ (if (n = 0 → False) then overlap (n+1) else overlap (n+1)) = overlap n
|
|
sorry
|
|
|
|
example : (if (n = 0 → False) then overlap (n+1) else overlap (n+1)) = overlap n := by
|
|
-- assumption is not a well-behaved discharger, and the following should still work as expected
|
|
simp (discharger := assumption) only [overlap]
|
|
guard_target =ₛ (if (n = 0 → False) then overlap n else overlap (n+1)) = overlap n
|
|
sorry
|
|
|
|
opaque p : Nat → Bool
|
|
opaque g : Nat → Nat
|
|
@[simp] theorem g_eq (h : p x) : g x = x := sorry
|
|
|
|
example : (if p x then g x else g x + 1) + g x = y := by
|
|
simp (discharger := assumption)
|
|
guard_target =ₛ (if p x then x else g x + 1) + g x = y
|
|
sorry
|
|
|
|
example : (have _ : p x := sorryAx _ false; g x + 1 = y) ↔ g x = y := by
|
|
simp (config := { zeta := false, zetaUnused := false }) (discharger := assumption)
|
|
guard_target =ₛ (have _ : p x := sorryAx _ false; x + 1 = y) ↔ g x = y
|
|
sorry
|