Skip to content

Conversation

@kmill
Copy link
Collaborator

@kmill kmill commented Jun 24, 2025

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 feat: proper let_fun support in simp #6220 did for letFun telescopes. Furthermore, simp converts letFuns into haves (nondependent lets), and we remove the feat: proper let_fun support in simp #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 lets, but the letToHave procedure can skip checking some subexpressions, and it modifies the lets in an entire expression at once rather than one at a time.
  • A +zetaHave configuration option, to turn off zeta reduction of haves specifically. The motivation is that dependent lets 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 haves 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.

@kmill kmill requested a review from leodemoura as a code owner June 24, 2025 06:59
@kmill kmill added the changelog-language Language features and metaprograms label Jun 24, 2025
@nomeata
Copy link
Collaborator

nomeata commented Jun 24, 2025

The motivation is that dependent lets can only be dsimped by let, so zeta reducing just nondependent lets is a reasonable way to make progress.

Did you mean ”so zeta reducing only dependent lets” in the last sentence, referring to the behavior with -zetaHave?

@kmill kmill force-pushed the kmill_simp_letToHave branch from f8d88ed to 8945bb6 Compare June 24, 2025 21:26
@github-actions github-actions bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Jun 24, 2025
@leanprover-community-bot
Copy link
Collaborator

leanprover-community-bot commented Jun 24, 2025

Mathlib CI status (docs):

  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase ddbba944d42a1479ec9e4f350b9a270b008c01f0 --onto db499e96aac8ad654c8ed5ab40c4e6885d38c9a1. You can force Mathlib CI using the force-mathlib-ci label. (2025-06-24 21:48:41)
  • ✅ Mathlib branch lean-pr-testing-8968 has successfully built against this PR. (2025-06-26 17:17:07) View Log
  • ✅ Mathlib branch lean-pr-testing-8968 has successfully built against this PR. (2025-06-27 02:00:35) View Log
  • ✅ Mathlib branch lean-pr-testing-8968 has successfully built against this PR. (2025-06-27 03:09:06) View Log

kmill added 4 commits June 26, 2025 08:59
This PR adds the following features to `simp`:
- A routine for simplifying `let` telescopes, like what leanprover#6220 did for `letFun` telescopes. Furthermore, simp converts `letFun`s into `have`s (nondependent lets), and we remove the leanprover#6220 routine.
- A `+letToHave` configuration option (enabled by default) that converts lets into haves when possible, when `-zeta` is set. Previosuly Lean would need to do a full typecheck the bodies of `let`s, but the `letToHave` procedure can be faster, and it modifies the `let`s in an entire expression at once.
- 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 nondependent lets is a reasonable way to make progress.
@kmill kmill force-pushed the kmill_simp_letToHave branch from 3c15843 to af87540 Compare June 26, 2025 16:06
leanprover-community-mathlib4-bot added a commit to leanprover-community/batteries that referenced this pull request Jun 26, 2025
leanprover-community-mathlib4-bot added a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Jun 26, 2025
@leanprover-community-bot leanprover-community-bot added the builds-mathlib CI has verified that Mathlib builds against this PR label Jun 26, 2025
and then after that we have the versions that `simpHaveTelescope` actually uses,
which avoid this issue.
-/
/-
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmill I will add this issue to my todo list for next quarter. Could you please send me examples that expose the quadratic behavior? Have you checked whether the external checker written in Rust also has this performance issue?

Copy link
Collaborator Author

@kmill kmill Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deep telescopes at the ends of simpHave.lean and simpLetFunIssue.lean illustrate the issue. They also have a separate issue arising from using simp to unfold a recursive function that introduces a have to the telescope. (This is what the id id hack is addressing.)

I'll make some more examples for you next quarter.

I haven't had a chance to compare this with external checkers yet. I would be surprised if they did not have a similar issue.


theorem have_body_congr' {α : Sort u} {β : Sort v} (a : α) {f f' : α → β}
(h : ∀ x, f x = f' x) : f a = f' a :=
h a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmill As far as I understood, the theorems above are a workaround for the performance issue, and are not stated as you wanted to state them. If I understood it correctly, could you please include in the comment the desired version you wanted to have?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desired ones are in the comment. (I added the desired ones in a previous PR, and in this PR I commented them out to make sure I didn't accidentally use them.)

I haven't confirmed it yet, but I think there's a similar performance issue still with these ∀ x, f x = f' x hypotheses causing beta reductions. We can talk about it next week.

deps.insert idx
else
deps
return { info with bodyDeps, bodyTypeDeps, body, bodyType, level }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming this new approach had a very positive impact on @hargoniX benchmarks. @hargoniX Have you tried this PR?

Copy link
Contributor

@hargoniX hargoniX Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't have time to run a full SMT-LIB run on this yet but testing some of the files that have long let chains and are solved only in rewriting I can already see significant improvements yes.

Baseline:

λ time lake env .lake/build/bin/leanwuzla --disableEmbeddedConstraintSubst --timeout=1200 --maxSteps=100000000 --disableKernel --maxRecDepth=1048576 --maxHeartbeats=20000000000 /home/henrik/smtlib/non-incremental/QF_BV/sage/app7/bench_4994.smt2                               <<<
Normalizing

unsat
lake env .lake/build/bin/leanwuzla --disableEmbeddedConstraintSubst        51.34s user 0.23s system 99% cpu 51.691 total

With this PR

λ time lake env .lake/build/bin/leanwuzla --disableEmbeddedConstraintSubst --timeout=1200 --maxSteps=100000000 --disableKernel --maxRecDepth=1048576 --maxHeartbeats=20000000000 /home/henrik/smtlib/non-incremental/QF_BV/sage/app7/bench_4994.smt2
Normalizing
unsat
lake env .lake/build/bin/leanwuzla --disableEmbeddedConstraintSubst        14.85s user 0.11s system 99% cpu 14.961 total

The fresh profile also looks quite sane to me (apart from the already known check assignment quick that's not as quick), none of the previous deep whnf calls during discrimination tree lookups and stuff like that is around anymore.

by detecting a `simpHaveTelescope` proofs and removing the type hint.
-/
def simpHaveTelescope (e : Expr) : SimpM Result := do
Prod.fst <$> withTraceNode `Debug.Meta.Tactic.simp (fun
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmill Have you observed a positive impact on performance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and in particular

  • for the letE encoding of have this is a huge win
  • it appears to be about as fast as (maybe marginally faster than) the letFun telescope code, while handling dependent types.

Though as we've discussed the kernel checking is slower (about 2x time), but that should be addressable eventually.

@kmill kmill enabled auto-merge June 27, 2025 01:00
@kmill kmill added this pull request to the merge queue Jun 27, 2025
leanprover-community-mathlib4-bot added a commit to leanprover-community/batteries that referenced this pull request Jun 27, 2025
leanprover-community-mathlib4-bot added a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Jun 27, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 27, 2025
@kmill kmill enabled auto-merge June 27, 2025 02:00
@kmill kmill added this pull request to the merge queue Jun 27, 2025
leanprover-community-mathlib4-bot added a commit to leanprover-community/batteries that referenced this pull request Jun 27, 2025
leanprover-community-mathlib4-bot added a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Jun 27, 2025
Merged via the queue into leanprover:master with commit 7abc910 Jun 27, 2025
16 checks passed
@nomeata nomeata mentioned this pull request Sep 23, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builds-mathlib CI has verified that Mathlib builds against this PR changelog-language Language features and metaprograms toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants