Zulip Chat Archive
Stream: lean4
Topic: Abysmal performance when doing large inductions
Jad Ghalayini (Apr 11 2022 at 15:23):
So when I'm trying to do a large induction with e.g. 30 cases; I get really really bad performance, especially if I have a large automated branch at the end handling say 25 of the cases: every time I modify one of the upper 5 cases, I need to wait for 2 minutes before the infoview updates and I can think about what to write next. Would it be possible to somehow cache work so this is not the case (the cases of the induction should be completely independent of each other no?)
Leonardo de Moura (Apr 11 2022 at 15:33):
We have a new checkpoint
tactic. It is an experimental stage right now, but it was added for this kind of scenario.
Could you try the following?
checkpoint induction n <;> try (<tactic that takes care of the 25 cases>)
case <remainin-case-1> => ...
...
case <remainin-case-5> => ...
Here is an example
constant f : Nat → Nat
@[simp] axiom f_eq_1 (n : Nat) : f (n+1) = f n
@[simp] axiom f_eq_2 : f 0 = 1
inductive Foo where
| c1 | c2 | c3 | c4 | c5 | c6 | c7
def h (n : Nat) := 1
def g (n : Foo) (x : Nat) : Nat :=
match n with
| .c7 => h x
| _ => f 1000
set_option maxRecDepth 100000
example : g n x = 1 := by
checkpoint induction n <;> try simp [g]
case c7 =>
simp [h]
Leonardo de Moura (Apr 11 2022 at 20:25):
Another trick is to temporarily use the stop
tactic to disable parts of your proof.
We could also write the example above as
example : g n x = 1 := by
induction n with
| c7 => /- we can now comfortably work on this branch without delay -/ done
| _ => stop simp [g] /- stop disabled the else branch. -/
Leonardo de Moura (Apr 11 2022 at 20:26):
@Mario Carneiro suggested other tricks in the following thread https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Partial.20evaluation.20of.20a.20file
Mario Carneiro (Apr 11 2022 at 20:27):
the stop
tactic is called sorry { ... }
in current lean 3
Leonardo de Moura (Apr 18 2022 at 17:03):
We have added the save
tactic yesterday. It is often more convenient to use than checkpoint
.
See https://github.com/leanprover/lean4/blob/master/RELEASES.md for additional details.
Leonardo de Moura (Apr 18 2022 at 17:05):
Example:
macro "expensive_tactic" : tactic => `(sleep 5000) -- sleeps for 5 seconds
example (h₁ : x = y) (h₂ : y = z) : z = x := by
expensive_tactic
save
-- We can comfortably make changes after the `save`
-- and get quick feedback from the editor.
have : y = x := h₁.symm
have : z = y := h₂.symm
trace "hello world"
apply this.trans
exact ‹y = x›
Jad Ghalayini (Apr 20 2022 at 23:28):
Would it be possible to automatically detect that branches of an induction are independent , rather than depending on a certain order? For example, the expensive tactic which solves the 25 cases is in the last branch.
Jad Ghalayini (Apr 20 2022 at 23:28):
Or are they not actually independent in the case of induction ... generalizing ... with | ...
Jad Ghalayini (Apr 20 2022 at 23:37):
On a related note, why is simp
on anything defined via a large match
expression so slow?
Jad Ghalayini (Apr 20 2022 at 23:38):
My proofs all become super slow whenever I do simp only [big_match]
for an expression like big_match (constructor_14 a) (constructor_3 b)
Jad Ghalayini (Apr 20 2022 at 23:38):
Even though as far as I can tell it should be fast as it's just a simple rewrite; and similar rewrites using theorems are very fast
Jad Ghalayini (Apr 20 2022 at 23:38):
Should I prove a theorem for each case of the match?
Jad Ghalayini (Apr 20 2022 at 23:39):
Seems hard to automate, and harder to put into simp
in the case of the automated cases
Jad Ghalayini (Apr 20 2022 at 23:39):
Is there a better way to do such "obvious" reductions?
Leonardo de Moura (Apr 22 2022 at 01:15):
Jad Ghalayini said:
On a related note, why is
simp
on anything defined via a largematch
expression so slow?
It is hard to tell without seeing the actual example, but I suspect the problem is not simp
, but the generation of equality theorems for the match
expression. I have seen some massive proofs when the match
expression has many overlapping patterns.
Lean 4 generates these theorems on demand when they are first needed.
Leonardo de Moura (Apr 22 2022 at 01:17):
Jad Ghalayini said:
My proofs all become super slow whenever I do
simp only [big_match]
for an expression likebig_match (constructor_14 a) (constructor_3 b)
If you copy and paste the theorem
as an example
after the theorem
is the example
also slow?
Leonardo de Moura (Apr 22 2022 at 01:18):
Jad Ghalayini said:
Should I prove a theorem for each case of the match?
You should not need to. Have you tried the new save
and stop
tactics?
Leonardo de Moura (Apr 22 2022 at 01:20):
Jad Ghalayini said:
Is there a better way to do such "obvious" reductions?
If the reductions are definitional, then we can add an option that skips the match equality theorem generation and relies only on definitional reduction for match
expressions.
Leonardo de Moura (Apr 22 2022 at 01:21):
Jad Ghalayini said:
Would it be possible to automatically detect that branches of an induction are independent , rather than depending on a certain order? For example, the expensive tactic which solves the 25 cases is in the last branch.
It is not clear to me what you are asking and/or proposing.
Jad Ghalayini (Apr 23 2022 at 17:46):
Leonardo de Moura said:
Jad Ghalayini said:
My proofs all become super slow whenever I do
simp only [big_match]
for an expression likebig_match (constructor_14 a) (constructor_3 b)
If you copy and paste the
theorem
as anexample
after thetheorem
is theexample
also slow?
No it's actually instant. I even made sure it wasn't parallelism by commenting it out, letting everything run, and then uncommenting it
Jad Ghalayini (Apr 23 2022 at 17:47):
Leonardo de Moura said:
Jad Ghalayini said:
Should I prove a theorem for each case of the match?
You should not need to. Have you tried the new
save
andstop
tactics?
These don't seem to be working for me; editing below a save
still causes the whole thing to start again for some reason and takes forever (even when there's nothing or very little under the save
, and when replacing other tactics with stop
does seem to speed things up)
Jad Ghalayini (Apr 23 2022 at 17:48):
Leonardo de Moura said:
Jad Ghalayini said:
Would it be possible to automatically detect that branches of an induction are independent , rather than depending on a certain order? For example, the expensive tactic which solves the 25 cases is in the last branch.
It is not clear to me what you are asking and/or proposing.
Well as far as I understand, say we do
induction H with
| label1 => ...
| label2 => ...
| label3 => ...
As far as I understand, those branches are totally independent, so why not save them individually as checkpoints so editing one branch does not affect the others?
Jad Ghalayini (Apr 23 2022 at 17:49):
Leonardo de Moura said:
Jad Ghalayini said:
On a related note, why is
simp
on anything defined via a largematch
expression so slow?It is hard to tell without seeing the actual example, but I suspect the problem is not
simp
, but the generation of equality theorems for thematch
expression. I have seen some massive proofs when thematch
expression has many overlapping patterns.
Lean 4 generates these theorems on demand when they are first needed.
This is weird because I have a lot of thoerems using simp
to handle the same match statemement, and they're all very very slow. Like it's not just one slow one. But, as I said, copying the exact same theorem was indeed fast.
Jad Ghalayini (Apr 23 2022 at 17:49):
Thanks so much for your help btw, I really appreciate the responsiveness!
Jad Ghalayini (Apr 23 2022 at 18:18):
Also btw I've managed to get by a little using the reduce
tactic before rewriting, but apparently that's for debugging purposes only. Much faster, though...
Jad Ghalayini (Apr 23 2022 at 18:18):
Since I am basically using simp
to do reduction; is there a better way to do this?
Henrik Böving (Apr 23 2022 at 18:21):
maybe dsimp
? It performs simp using definitional equality only
Leonardo de Moura (Apr 23 2022 at 18:29):
These don't seem to be working for me; editing below a save still causes the whole thing to start again for some reason and takes forever (even when there's nothing or very little under the save)
This is the expected behavior. There is no cache yet when you are typing save
, and the whole tactic will be executed from the beginning. Our plan is to add in the future an option set_option tactic.cache true
that will introduce save
automatically for us for potentially expensive tactics such as simp
. We also want to allow the user to register their own tactics as "expensive".
As far as I understand, those branches are totally independent, so why not save them individually as checkpoints so editing one branch does not affect the others?
It is not that simple. As you are typing in one of the branches, you are introducing syntax errors. The syntax errors temporarily disable branches. Try the following
inductive Foo where
| c1 | c2 | c3
example (x : Foo) : True := by
induction x with
| c1 => case -- You are tying here
| c2 => simp -- Note that `simp` is not highlighted because it was not even parsed, let alone elaborated
--^ parsing error here
| c3 => simp
EDIT: this is not the only obstacle, but it is the simplest to explain.
This is weird because I have a lot of thoerems using simp to handle the same match statemement, and they're all very very slow. Like it's not just one slow one. But, as I said, copying the exact same theorem was indeed fast.
We may have a bug in our cache, and may be recomputing things. A #mwe would help us a lot.
Also btw I've managed to get by a little using the reduce tactic before rewriting, but apparently that's for debugging purposes only. Much faster, though...
It makes sense. reduce
performs only definitional reductions and will never try to generate equation theorems for a match
expression.
Leonardo de Moura (Apr 23 2022 at 18:31):
Jad Ghalayini said:
Since I am basically using
simp
to do reduction; is there a better way to do this?
simp
is the way to go. We should work together to fix the performance problem and avoid unnecessary workarounds.
Jad Ghalayini (Apr 23 2022 at 18:39):
@Leonardo de Moura sounds good! I can get started on an MWE probably tonight; that said, do you know when a definitional-match-only simp could be available, as it's getting to the point where it's stalling all my progress as theorems just become unworkable?
Leonardo de Moura (Apr 23 2022 at 18:42):
Yes, we have support for definitional and non-definitional match
simplifications. simp
by default tries both. I am hopeful we will be able to fix the performance issue. If it cannot be fixed, we can add a flag to simp
to disable non-definitional match simplifications.
Jad Ghalayini (Apr 23 2022 at 18:57):
Would it be possible to add the flag now as a stopgap so I can get on with my proofs haha?
Jad Ghalayini (Apr 23 2022 at 18:57):
Or is there some kind of dsimp-like tactic I can use?
Leonardo de Moura (Apr 23 2022 at 19:17):
I will add a dsimp
-like tactic.
Leonardo de Moura (Apr 24 2022 at 13:26):
We have dsimp
and dsimp!
tactics in the nightly build https://github.com/leanprover/lean4-nightly/releases/tag/nightly-2022-04-24
Jad Ghalayini (Apr 26 2022 at 00:12):
Unfortunately, even using dsimp
, it's still about the same speed
Jad Ghalayini (Apr 26 2022 at 00:12):
The test program,
import LeanExperiments.Dependencies
set_option maxHeartbeats 1000000
theorem Term.stlc_wknth_true {t: Term} {Γ: Sparsity} {n: Nat}
: (t.wknth n).stlc (Γ.wknth n true) = (t.stlc Γ).wknth (Γ.ix n)
:= by {
revert Γ n;
induction t with
| var v =>
intro Γ n;
dsimp only [wknth, wk]
repeat rw [stlc_var]
dsimp only [Sparsity.stlc]
split
. have H: Γ.dep v = true := by
rw [<-Sparsity.wknth_dep]
assumption
rw [Sparsity.wknth_ix_true' H]
rw [if_pos H]
dsimp only [Stlc.wknth, Stlc.wk]
simp only [Wk.wknth_var]
. rw [if_neg]
rfl
simp only [Sparsity.wknth_dep] at *
assumption
| _ =>
intro Γ n;
dsimp only [wknth, wk]
repeat rw [Wk.lift_wknth_merge]
repeat rw [Wk.liftn_wknth_merge]
repeat rw [<-wknth_def]
try (rename TermKind _ => k; cases k);
try (rename AnnotSort => s; cases s);
all_goals try rfl;
all_goals (
dsimp only [stlc]
simp only [Sparsity.wknth_merge]
simp only [*]
repeat rw [Term.stlc_ty_wknth]
try rfl
)
}
still takes 29 seconds to typecheck; and even simply just writing dsimp [stlc]
takes a few seconds each time
Jad Ghalayini (Apr 26 2022 at 00:13):
Now this match isn't that huge, only about 20 branches, so I'm confused
Jad Ghalayini (Apr 26 2022 at 00:13):
I can probably work towards an MWE tomorrow, but any idea what gives?
Jad Ghalayini (Apr 26 2022 at 00:14):
This does seem to be quite simp
-specific since rw
and reduce
are both so fast I can't even tell I'm waiting for anything
Leonardo de Moura (Apr 26 2022 at 00:15):
The split
tactic will (indirectly) generate the equality theorems for the match
-expression.
Jad Ghalayini (Apr 26 2022 at 00:15):
On the other hand intuition tells me this kind of simple unfolding should be faster than reduce
, so why isn't it?
Jad Ghalayini (Apr 26 2022 at 00:15):
@Leonardo de Moura ah but even my other programs without split
are still slow
Jad Ghalayini (Apr 26 2022 at 00:15):
Lemme give another example
Leonardo de Moura (Apr 26 2022 at 00:15):
Jad Ghalayini said:
On the other hand intuition tells me this kind of simple unfolding should be faster than
reduce
, so why isn't it?
We need a #mwe to be able to answer this question.
Jad Ghalayini (Apr 26 2022 at 00:15):
Makes sense will get to it
Leonardo de Moura (Apr 26 2022 at 00:16):
Thanks.
Jad Ghalayini (Apr 26 2022 at 00:16):
Np, sorry for putting it off, just had a hectic week, barely time to even work on the proofs themselves haha!
Jad Ghalayini (Apr 26 2022 at 00:16):
Thanks for all the support, really appreciate it!
Jad Ghalayini (Apr 26 2022 at 00:37):
Actually decided to stop procrastinating and made an MWE
Jad Ghalayini (Apr 26 2022 at 00:38):
@Leonardo de Moura the program
inductive Level1: Type
| one
| two
| three
| four
| five
| six
| seven
| eight
| nine
| ten
inductive Level2: Type
| emp
| yi (l: Level1) (e: Level2)
| er (l: Level1) (e: Level2)
| san (l: Level1) (e: Level2)
| si (l: Level1) (e: Level2)
| wu (l: Level1) (e: Level2)
| liu (l: Level1) (e: Level2)
| qi (l: Level1) (e: Level2)
| ba (l: Level1) (e: Level2)
| jiu (l: Level1) (e: Level2)
| shi (l: Level1) (e: Level2)
open Level1
open Level2
def Level1.make_sentence: List Level1 -> Level2
| [] => emp
| one::xs => yi one (make_sentence xs)
| two::xs => er two (make_sentence xs)
| three::xs => san three (make_sentence xs)
| four::xs => si four (make_sentence xs)
| five::xs => wu five (make_sentence xs)
| six::xs => liu six (make_sentence xs)
| seven::xs => qi seven (make_sentence xs)
| eight::xs => ba eight (make_sentence xs)
| nine::xs => jiu nine (make_sentence xs)
| ten::xs => shi ten (make_sentence xs)
def Level2.is_translation: Level2 -> Bool
| yi one e => e.is_translation
| er two e => e.is_translation
| san three e => e.is_translation
| si four e => e.is_translation
| wu five e => e.is_translation
| liu six e => e.is_translation
| qi seven e => e.is_translation
| ba eight e => e.is_translation
| jiu nine e => e.is_translation
| shi ten e => e.is_translation
| emp => true
| _ => false
def Level1.sentence_is_translation' {l: List Level1}:
(make_sentence l).is_translation = true
:= by {
induction l with
| nil => rfl
| cons l xs I =>
cases l <;>
conv =>
lhs
reduce
rw [I]
}
def Level1.sentence_is_translation {l: List Level1}:
(make_sentence l).is_translation = true
:= by {
induction l with
| nil => rfl
| cons l xs I =>
cases l <;>
dsimp only [make_sentence, is_translation] <;>
exact I
}
Jad Ghalayini (Apr 26 2022 at 00:38):
Could probably be made slightly smaller but illustrates the point
Jad Ghalayini (Apr 26 2022 at 00:38):
sentence_is_translation' typechecks instantaneously
Jad Ghalayini (Apr 26 2022 at 00:38):
sentence_is_translation takes a couple of seconds
Leonardo de Moura (Apr 26 2022 at 00:45):
Thanks for creating the #mwe. I will work on it.
Leonardo de Moura (Apr 26 2022 at 01:14):
Pushed the following fix: https://github.com/leanprover/lean4/commit/3cf642688bcf12356292590822a8a372fbe04630
Level1.sentence_is_translation
should be very fast now.
The bottle neck is the equation theorem generation as we suspected. I will try to improve it tomorrow.
Jad Ghalayini (Apr 26 2022 at 16:18):
@Leonardo de Moura works great now, thanks!
Jad Ghalayini (Apr 26 2022 at 16:18):
Any chance we could get dsimp
support in conv
though?
Last updated: Dec 20 2023 at 11:08 UTC