lean4-htt/tests/lean/run/3943.lean
Kyle Miller 7abc9106d7
feat: optimized simp routine for let telescopes (#8968)
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.
2025-06-27 02:13:20 +00:00

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