Zulip Chat Archive
Stream: general
Topic: kernel slow to accept refl proof
Kevin Buzzard (May 11 2022 at 23:28):
Yael isolated a nice simple version of a phenomenon I've seen a couple of times now, where tactic mode is quick to accept a theorem as true, but the kernel then spends a long time convincing itself. This is some kind of "heavy refl" I think but of a very specific nature. Consider the following code:
import group_theory.free_abelian_group
example (α : Type) [monoid α] (x : free_abelian_group α) :
x * 0 = 0 :=
begin
refl -- works super-quickly
end
example (α : Type) [monoid α] (x y : free_abelian_group α) :
x * y * 0 = x * (y * 0) :=
begin
refl, -- works instantly
sorry, -- tactic failed, there are no goals to be solved
end
It seems that in this set-up, x * 0
is defeq to 0
and the first example compiles with no problem. The second example, which of course should also be refl
, also seems to compile without problem, you get the goals accomplished message, and an error on the sorry
complaining that there are no goals left. Commenting out the sorry
though makes the proof compilation time shoot up to about 5 seconds on my relatively new laptop, and although I only have a poor understanding of what's going on what I think is happening is that tactic mode accepted the refl
fine but the kernel seems to be having second thoughts about it for quite some time.
Switching on the profiler doesn't seem to help at all; it just reports everything as happening quickly, it's perhaps not profiling that last step where the kernel has to check it believes the proof term which has been constructed. I've seen situations like this before but this is by far the simplest one. Is it worth trying to demathlibify this or are examples which use only core Lean already known?
Note that
example (α : Type) [monoid α] (x y : free_abelian_group α) :
x * y * 0 = x * (y * 0) :=
begin
simp,
end
works fine and is also very quick. Is there more than one kind of "heavy refl"? The kind which takes a long time to work in tactic mode, and then a different kind like this one which is quick in tactic mode but then takes a long time in the kernel? Or are they all like this? Sorry if I'm talking nonsense about kernels, I don't really understand how all this works really.
Kevin Buzzard (May 11 2022 at 23:37):
import group_theory.free_abelian_group
example (α : Type) [monoid α] (x y : free_abelian_group α) :
x * (y * 0) = 0 :=
begin
refl, -- quick
end
example (α : Type) [monoid α] (x y : free_abelian_group α) :
(x * y) * 0 = 0 :=
begin
refl, -- quick
end
example (α : Type) [monoid α] (x y : free_abelian_group α) :
(x * y) * 0 = x * (y * 0) :=
begin
refl, -- very slow (but quick if you add `sorry` after)
end
Kevin Buzzard (May 11 2022 at 23:43):
This is different to the non-transitivity of rfl
at the end of section 3.7 of the reference manual; there the refl
just fails quickly, and adding a sorry
afterwards doesn't change anything.
Kevin Buzzard (May 12 2022 at 00:02):
Completely coincidentally Bhavik just showed me another very short example of apparently a totally different nature:
import analysis.special_functions.pow
lemma timeout : (0 : ℝ) < 128 ^ (500 : ℝ) :=
begin
norm_num1,
sorry, -- no goals to be solved
end
Bhavik Mehta (May 12 2022 at 00:02):
It gets even shorter!
import analysis.special_functions.pow
lemma timeout : (0 : ℝ) < 1 ^ (20 : ℝ) :=
begin
norm_num1,
end
Bhavik Mehta (May 12 2022 at 00:02):
My guess is that this is a bug in how norm_num deals with real powers?
Bhavik Mehta (May 12 2022 at 00:03):
(0 : ℝ) < 1 ^ (20 : ℕ)
is fine, and (0 : ℝ) < 1 ^ (19 : ℝ)
is fine
Bhavik Mehta (May 12 2022 at 00:04):
(in my case it's easily worked-around though, with rpow_pos_of_pos
)
Kevin Buzzard (May 12 2022 at 00:04):
@Gabriel Ebner @Mario Carneiro is this issue well-understood?
Kevin Buzzard (May 12 2022 at 00:07):
I'm interested because I feel like it's the only issue I ever run into with Lean 3 where I feel at a loss about how to solve it. Many other weird timeouts are fixed by making universes explicit, or using noncomputable!
, but this one is nasty and I have no understanding of it.
Mario Carneiro (May 12 2022 at 08:20):
When there is a surprising performance cliff in norm_num
kernel check time, the usual diagnosis is a bug in the proof construction. The reason it isn't always just a failure is because lean has all sorts of definitional equalities, so when you apply things which are of the wrong type lean just tries to unfold things until it works, leading to a huge cost. In this case, the bug was that it was expecting (20:ℝ) = ↑(20:ℕ)
and I gave it ↑(20:ℕ) = (20:ℝ)
. This works even though it's wrong, because ↑(20:ℕ)
and (20:ℝ)
are defeq (at great expense)
Kevin Buzzard (May 12 2022 at 14:38):
Aah so this looks like it's totally unrelated to the x*y*0
slowness.
Reid Barton (May 12 2022 at 14:41):
In the original x*y*0
issue, I would guess that the elaborator has chosen some good sequence of unfoldings but the kernel chose a bad one; unfortunately there doesn't seem to be any sort of logging in the kernel is_def_eq
check so I'm not sure how to debug it effectively
Mario Carneiro (May 12 2022 at 14:45):
Kyle Miller (May 12 2022 at 14:58):
I can't figure out how rfl : x*y*0=x*(y*0)
works at all. I thought it was defined in terms of list concatenation (with multiple levels of quotients), but this identity doesn't work for lists concatenation directly:
example (α : Type) (x y : list α) :
x ++ y ++ [] = x ++ (y ++ []) :=
begin
refl, -- fails
end
Unless somehow the concatenation is reversed?
example (α : Type) (x y : list α) :
([] ++ x) ++ y = [] ++ (x ++ y) :=
begin
refl, -- ok
end
Reid Barton (May 12 2022 at 14:59):
This is multiplication in the (additive) free abelian group on a monoid
Reid Barton (May 12 2022 at 15:03):
docs#free_abelian_group.semigroup
Mario Carneiro (May 12 2022 at 15:23):
Here's a simpler version:
import group_theory.free_abelian_group
namespace free_abelian_group
variables (α : Type) [monoid α] (x y : free_abelian_group α)
-- true but hard
example {β} [add_comm_group β] (f g : α → β) :
lift f (abelianization.of $ free_group.mk []) =
lift g (abelianization.of $ free_group.mk []) :=
rfl
-- true but harder, times out
example {β} [add_comm_group β] (f g : α → β) :
lift (lift f) (abelianization.of $ free_group.mk []) =
lift (lift g) (abelianization.of $ free_group.mk []) :=
rfl
Mario Carneiro (May 12 2022 at 15:24):
I think lift
is not stated in a very defeq-friendly way
Mario Carneiro (May 12 2022 at 15:28):
well, really this kind of thing is never easy, not sure that we need to do anything about it
Mario Carneiro (May 12 2022 at 15:29):
the basic issue is that the heuristic f x =?= g y <- f =?= g, x =?= y
is easy to mislead but also really important in practice
Mario Carneiro (May 12 2022 at 15:32):
In certain cases like this one, we want the iota reduction to take precedence (i.e. lift f (mk x) ~> f x
) rather than going off on a wild goose chase on lift f (mk x) =?= lift g (mk x) <- f =?= g
when we know f x
and g x
are obviously equal, but after they've been wrapped in this many layers of definition it is really hard to tell that this is still an iota rule
Bhavik Mehta (May 12 2022 at 18:21):
Mario Carneiro said:
Thanks for this!
Last updated: Dec 20 2023 at 11:08 UTC