Zulip Chat Archive
Stream: mathlib4
Topic: Timeout in Submodule (π K) (π K)
Xavier Roblot (Dec 08 2023 at 19:30):
Lean cannot synthesize Module (π K) (π K)
without an increase of heartbeats.
import Mathlib.NumberTheory.NumberField.Basic
open NumberField
variable (K : Type*) [Field K] [NumberField K]
variable (I : Ideal (π K)) -- OK
variable (M : Submodule (π K) (π K)) -- Timeout
Is it something to worry about?
Andrew Yang (Dec 08 2023 at 20:34):
My experience in FLT regular is that π K
is usually very slow to work with, and I use [IsIntegralClosure B A K]
whenever possible. And we also had
attribute [local instance 2000] inst_ringOfIntegersAlgebra Algebra.toSMul Algebra.toModule
which would for example make Submodule (π K) (π K)
instant.
Andrew Yang (Dec 08 2023 at 20:37):
Looking at the trace of the output without these hacks, I guess things would also improve if the ordered-algebraic classes or topological-algebraic classes were scoped. But this might be controversial.
YaΓ«l Dillies (Dec 08 2023 at 20:40):
What kind of classes is it looking for? If it's CovariantClass
/ContravariantClass
, I might have a speedup to offer.
Andrew Yang (Dec 08 2023 at 20:43):
I'm referring to these
image.png
Though I'm not sure these are the culprit yet.
YaΓ«l Dillies (Dec 08 2023 at 20:47):
Oh yeah no. Those are fast and sturdy and very very useful everywhere.
Andrew Yang (Dec 08 2023 at 20:58):
In
import Mathlib.NumberTheory.NumberField.Basic
open NumberField
variable (K : Type*) [Field K] [NumberField K]
set_option profiler true
set_option trace.Meta.synthInstance.tryResolve true
set_option trace.Meta.isDefEq true
set_option synthInstance.maxHeartbeats 1000000
set_option maxHeartbeats 10000000000
variable (M : Submodule (π K) (π K)) -- Timeout
There is a [12.650751s] β
IsDomain β₯(π K) β IsDomain β₯(π K)
, which looks like lean is having a trouble unifying
Subalgebra.toCommRing
with SubringClass.toRing
.
Maybe @Anne Baanen knows what's going on?
Anne Baanen (Dec 08 2023 at 22:28):
Oh that's a big issue indeed, worth looking into. Don't know from the top of my head what's going wrong here, the defeq cache is a lot more robust nowadays so I assume it's not the same equality being tested exponentially often, right?
Adam Topaz (Dec 08 2023 at 22:37):
Should we make docs#NumberField.ringOfIntegers irreducible?
Matthew Ballard (Dec 12 2023 at 21:39):
You can get away with just set_option synthInstance.maxHeartbeats 50000
if using lean4#2478
Xavier Roblot (Dec 13 2023 at 10:27):
Adam Topaz said:
Should we make docs#NumberField.ringOfIntegers irreducible?
If that just means replacing def
by irreducible_def
in the definition of ringOfIntegers
, then it does not help much... It still get a timeout when I try #synth Module (π K) (π K)
Kevin Buzzard (Jan 11 2024 at 13:35):
Andrew just reminded me of this issue in their FLT-regular talk at Lean Together 2024. I think the relevant part of the instance trace is this:
[tryResolve] [9.838419s] β
IsDomain β₯(π K) β IsDomain β₯(π K) βΌ
[isDefEq] [4.865673s] β
IsDomain β₯(π K) =?= IsDomain β₯?m.9046 βΌ
[] [4.865389s] β
CommSemiring.toSemiring =?= Ring.toSemiring βΌ
[] [4.865353s] β
Ring.toSemiring =?= Ring.toSemiring βΌ
[delta] [4.865339s] β
Ring.toSemiring =?= Ring.toSemiring βΌ
[] [4.865321s] β
CommRing.toRing =?= SubringClass.toRing (π K) βΌ
[] [4.865221s] β
CommRing.toRing =?= Function.Injective.ring Subtype.val (_ : Function.Injective fun a => βa) (_ : β0 = β0)
(_ : β1 = β1) (_ : β (x x_1 : β₯(π K)), β(x + x_1) = β(x + x_1))
(_ : β (x x_1 : β₯(π K)), β(x * x_1) = β(x * x_1)) (_ : β (x : β₯(π K)), β(-x) = β(-x))
(_ : β (x x_1 : β₯(π K)), β(x - x_1) = β(x - x_1))
(_ : β (x : β₯(π K)) (x_1 : β), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β₯(π K)) (x_1 : β€), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β₯(π K)) (x_1 : β), β(x ^ x_1) = β(x ^ x_1)) (_ : β (x : β), ββx = ββx)
(_ : β (x : β€), ββx = ββx) βΆ
[isDefEq] [4.941348s] β
?m.8348 =?= SubringClass.instIsDomainSubtypeMemInstMembershipToSemiringToRing (π K) βΆ
The thing taking all of the time is checking IsDomain β₯(π K) β IsDomain β₯(π K)
, something which looks obvious but the problem is that one of the ring structures is coming from CommSemiring.toSemiring
and the other from Ring.toSemiring
, and typeclass inference has a habit of sometimes exploding with these kinds of question. The 4.5 seconds is just breaking down into many many tiny goals which then keep exploding again, e.g.
[delta] [0.566770s] β
AddGroupWithOne.zsmul =?= AddGroupWithOne.zsmul βΌ
[] [0.566496s] β
Function.Injective.addGroupWithOne Subtype.val (_ : Function.Injective fun a => βa) (_ : β0 = β0)
(_ : β1 = β1)
(_ : β (x x_1 : β₯(Subalgebra.toSubring (π K))), β(x + x_1) = β(x + x_1))
(_ : β (x : β₯(Subalgebra.toSubring (π K))), β(-x) = β(-x))
(_ : β (x x_1 : β₯(Subalgebra.toSubring (π K))), β(x - x_1) = β(x - x_1))
(_ : β (x : β₯(Subalgebra.toSubring (π K))) (x_1 : β), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β₯(Subalgebra.toSubring (π K))) (x_1 : β€), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β), ββx = ββx)
(_ :
β (x : β€),
ββx =
ββx) =?= Function.Injective.addGroupWithOne Subtype.val
(_ : Function.Injective fun a => βa) (_ : β0 = β0) (_ : β1 = β1)
(_ : β (x x_1 : β₯(π K)), β(x + x_1) = β(x + x_1))
(_ : β (x : β₯(π K)), β(-x) = β(-x))
(_ : β (x x_1 : β₯(π K)), β(x - x_1) = β(x - x_1))
(_ : β (x : β₯(π K)) (x_1 : β), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β₯(π K)) (x_1 : β€), β(x_1 β’ x) = β(x_1 β’ x))
(_ : β (x : β), ββx = ββx) (_ : β (x : β€), ββx = ββx) βΆ
@Matthew Ballard do you understand how to fix these kinds of problems nowadays?
Kevin Buzzard (Jan 11 2024 at 13:38):
This seems to be a minimal hack which "fixes" the issue:
import Mathlib.Algebra.Module.Submodule.Basic
import Mathlib.NumberTheory.NumberField.Basic
variable (K : Type*) [Field K] [NumberField K]
open NumberField
attribute [local instance 1001] Algebra.toModule -- makes it quick; 1000 not good enough
set_option synthInstance.maxHeartbeats 0 in
set_option maxHeartbeats 0 in
set_option trace.profiler true in
variable (M : Submodule (π K) (π K))
Andrew Yang (Jan 11 2024 at 13:53):
Matthew Ballard said:
You can get away with just
set_option synthInstance.maxHeartbeats 50000
if using lean4#2478
This is the structure flattening I was talking about. This alone does not solve the problem though.
Kevin Buzzard (Jan 11 2024 at 14:34):
Not the main issue, but I was surprised to run into this in the trace:
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
[Meta.synthInstance] β Field β€
[Meta.synthInstance] result <not-available> (cached)
Matthew Ballard (Jan 11 2024 at 14:37):
See also #mathlib4 > example (p : P) : Q := p
takes 0.25s to fail!
Matthew Ballard (Jan 11 2024 at 14:38):
Matthew Ballard said:
Getting flashbacks
Kevin Buzzard (Jan 11 2024 at 14:44):
Here's the fix which demotes the bad instance rather than promotes the good one:
import Mathlib.Algebra.Module.Submodule.Basic
import Mathlib.NumberTheory.NumberField.Basic
variable (K : Type*) [Field K] [NumberField K]
open NumberField
attribute [local instance 909] Subalgebra.module' -- 910 no good
variable (M : Submodule (π K) (π K))
Matthew Ballard (Jan 11 2024 at 14:48):
Is that searching over all A
?
Matthew Ballard (Jan 11 2024 at 15:52):
It is committing to R = β€
in
instance module' [Semiring R'] [SMul R' R] [Module R' A] [IsScalarTower R' R A] : Module R' S :=
S.toSubmodule.module'
and then trying everything to get SMul K β€
Anne Baanen (Jan 11 2024 at 16:00):
As a quick fix it sounds like a decent idea to lower the priority of docs#Subalgebra.module', since the "default" instance is docs#Subalgebra.module
Anne Baanen (Jan 11 2024 at 16:03):
I find it weird though that there is this free parameter that becomes β€
: in module'
every parameter is specified by the expected type.
Matthew Ballard (Jan 11 2024 at 16:04):
In the process of now of priority := low
Anne Baanen (Jan 11 2024 at 16:06):
Anne Baanen said:
I find it weird though that there is this free parameter that becomesβ€
: inmodule'
every parameter is specified by the expected type.
Nevermind, we get R = β€
because π K : Subalgebra β€ K
.
Matthew Ballard (Jan 11 2024 at 16:06):
Should this even be an instance?
Anne Baanen (Jan 11 2024 at 16:08):
I believe there are a few uses where we have a tower of ring extensions and want to make a subalgebra to put something in between the top. So I think yes, it should be an instance.
Matthew Ballard (Jan 11 2024 at 16:09):
I've seen other places where this pattern is a performance hit
Anne Baanen (Jan 11 2024 at 16:09):
I'll try de-instancing it on my machine and seeing how much breaks.
Matthew Ballard (Jan 11 2024 at 16:09):
Yeah, I took the easy way out :)
Matthew Ballard (Jan 11 2024 at 16:19):
Well, success for the example. I'll bench next.
Anne Baanen (Jan 11 2024 at 16:35):
At least the following files rely on the instance:
- file#Mathlib/Algebra/Module/Submodule/Localization
- file#Mathlib/RingTheory/Ideal/Operations
- file#Mathlib/LinearAlgebra/Basis/VectorSpace
And the build stopped at 2741/4118 so about 1/3 of Mathlib depends transitively on these files.
Matthew Ballard (Jan 11 2024 at 16:36):
#9655 is on the benchmark queue so maybe that will be enough
Matthew Ballard (Jan 11 2024 at 17:15):
No change with performance overall - up for review
Antoine Chambert-Loir (Jan 19 2024 at 12:56):
@Anne Baanen , what happens if those 3 files are added a local instance?
Anne Baanen (Jan 22 2024 at 18:36):
Actually it turns out I disabled the wrong instance: Submodule.module'
instead of Subalgebra.module'
. According to branch#uninstance-Subalgebra-module-tower on my machine, it is totally fine to remove this instance altogether!
Matthew Ballard (Jan 22 2024 at 18:37):
I think this is prudent since the integers will end up satisfying the constraints often
Kevin Buzzard (Apr 22 2024 at 23:51):
I occasionally come back to this question. Here is a summary of the issue (or at least, one of the issues):
import Mathlib.Tactic
variable (K : Type) [Field K] [NumberField K]
open NumberField
-- #synth SMul (π K) (π K) -- times out
set_option synthInstance.maxHeartbeats 70000 in -- fails at 60000
set_option trace.Meta.synthInstance true in
set_option trace.profiler true in
#synth SMul (π K) (π K) -- fails without instance maxHeartbeats bump
This kind of failure makes working with integers of number fields very difficult. There are other variants, e.g. if is also a number field and a -algebra, then SMul (π K) (π L)
also takes forever, even though Algebra (π K) (π L)
is fast.
What I've discovered whilst sitting jetlagged in a hotel in Singapore at 5am is that the problem is the following: Because π K
is actually a term of type Subalgebra \Z K
, the goal is really SMul β₯(π K) β₯(π K)
. Typeclass inference can find 15 or so instances which apply, but among them are the deadly instances Submonoid.smul
, Subsemiring.smul
, and the poorly-named Subalgebra.instSMulSubtypeMemSubalgebraInstMembershipInstSetLikeSubalgebra
which should really be called Subalgebra.smul
. The type of Submonoid.smul
is basically [SMul M' Ξ±] (S : Submonoid M') : SMul (β₯S) Ξ±
and you can now guess what the others are. These do look good syntactically for a goal of SMul β₯(π K) β₯(π K)
, but they are deadly because they leave a goal of the form SMul K (π K)
and this takes 2.5 seconds to fail on my machine.
So now I realise that this is the same problem as #synth SMul β β
that we saw in another thread, although this is even worse because it takes even longer to give up.
In this situation it is extremely difficult to steer typeclass inference away from ever running into the goal SMul K (π K)
, so the question is whether it is possible to make it fail quickly.
import Mathlib.Tactic
variable (K : Type) [Field K] [NumberField K]
open NumberField
set_option synthInstance.maxHeartbeats 70000 in
set_option trace.Meta.synthInstance true in
set_option trace.profiler true in
#synth SMul K (π K) -- fails after thousands of lines of nonsense
Riccardo Brasca (Apr 23 2024 at 03:53):
Thanks for looking at this. If you want a concrete example to work on, in #11792 there is a discussion about a rather slow file.
Riccardo Brasca (Apr 23 2024 at 03:54):
BTW, is there a simple way of profiling a file? I mean, set_option profiler true
gives information about any single declaration, I would like to have the total.
Michael Stoll (Apr 23 2024 at 09:20):
If you do lake build <Module>
in the terminal (on a file containing set_option profiler true
near the beginning), it also prints a summary at the end.
Michael Stoll (Apr 23 2024 at 09:24):
Kevin Buzzard said:
In this situation it is extremely difficult to steer typeclass inference away from ever running into the goal
SMul K (π K)
, so the question is whether it is possible to make it fail quickly.
You could try to remove the instance
attribute locally from the instance leading to the problematic SMul K (π K)
search (or reduce its priority). You could possibly also try to increase the priority of the instance you want to trigger. Or you could make (local) shortcut instances for SMul (π K) (π K)
and SMul (π K) (π L)
.
I've been working on one of the very slow Mathlib files to see if I can speed it up, and I'll report on this soon somewhere here. Stay tuned!
YaΓ«l Dillies (Apr 23 2024 at 10:11):
I believe there's a better solution: Replace
def ringOfIntegers : Subalgebra β€ K := integralClosure β€ K
by
def ringOfIntegers : Type _ := integralClosure β€ K
YaΓ«l Dillies (Apr 23 2024 at 10:12):
Then your TC goals will be Submodule (π K) (π K)
rather than Submodule β₯(π K) β₯(π K)
and will match many less instances.
YaΓ«l Dillies (Apr 23 2024 at 10:13):
Of course, this comes at the cost of π K
not being a Subalgebra
anymore, but the coercion to Type _
of one.
Riccardo Brasca (Apr 23 2024 at 10:22):
I was also thinking about this... I will try to see how annoying it is.
Anne Baanen (Apr 23 2024 at 12:07):
Another approach that we could try is to give the β₯
coercion a different name for each setlike, instead of docs#SetLike.instCoeSortType.
YaΓ«l Dillies (Apr 23 2024 at 12:12):
Wouldn't Lean still try to unfold them?
Anne Baanen (Apr 23 2024 at 12:12):
I mean like we already do for coe
s:
def coeSort (s : MySubobject X) : Type* :=
{ x : X // x β s }
instance : CoeSort (MySubobject X) Type* :=
β¨coeSortβ©
Anne Baanen (Apr 23 2024 at 12:13):
(Presumably using a @[coeSort]
attribute to do delaboration.)
Anne Baanen (Apr 23 2024 at 12:13):
Then the discrimination tree can index on MySubobject.coeSort
and we don't have the billion candidate instances.
YaΓ«l Dillies (Apr 23 2024 at 12:16):
Oh sorry, I misunderstood. Yes, that sounds reasonable.
Anne Baanen (Apr 23 2024 at 12:31):
One drawback of my idea is that all the instances on Subtype
have to be copied over manually: LE
, Coe
, Mul
, ...
Anne Baanen (Apr 23 2024 at 12:46):
Yeah, I don't see this working out. For example docs#MulMemClass.subtype must use the generic CoeSort
instance, and docs#Subsemigroup.topEquiv_toMulHom must use the specific. We're definitely going to get type errors this way.
Anne Baanen (Apr 23 2024 at 12:48):
I think I'll try out YaΓ«l's plan instead. Defining RingOfIntegers
as a Type
instead of a Subalgebra
is probably the least destructive alternative.
Kevin Buzzard (Apr 23 2024 at 14:02):
Michael Stoll said:
You could try to remove the
instance
attribute locally from the instance leading to the problematicSMul K (π K)
search (or reduce its priority).
There are multiple problematic instances (three).
You could possibly also try to increase the priority of the instance you want to trigger.
This all seems so brittle.
Or you could make (local) shortcut instances for
SMul (π K) (π K)
andSMul (π K) (π L)
.
Yes, probably this is what I'd do if I actually ran into this in practice. Basically the problem is that there are many misleading instances of the from SMul \u A \uB
.
Anne Baanen (Apr 23 2024 at 16:33):
Anne Baanen said:
I think I'll try out YaΓ«l's plan instead. Defining
RingOfIntegers
as aType
instead of aSubalgebra
is probably the least destructive alternative.
Seems to work fine! I didn't get totally to the end of fixing, feel free to take over branch#RingOfInteger_Type.
Riccardo Brasca (Apr 23 2024 at 16:34):
Great! I will have a look tomorrow morning (morning in Singapore)
Anne Baanen (Apr 23 2024 at 16:34):
Note that quite a bit of the difficulty comes from deciding to replace the coercion (π K) β K
with algebraMap (π K) K
. Since both are in use at different places this seemed like a good opportuinity to standardize.
YaΓ«l Dillies (Apr 23 2024 at 16:55):
Fixed some of the style
YaΓ«l Dillies (Apr 23 2024 at 16:56):
I won't work on it more soon
Matthew Ballard (Apr 24 2024 at 00:28):
It builds now and the example above is much, much, much better.
Riccardo Brasca (Apr 24 2024 at 00:37):
Wow, thanks!!
Matthew Ballard (Apr 24 2024 at 00:37):
Most of the work was done already :)
Riccardo Brasca (Apr 24 2024 at 00:41):
I've created #12386 just to test
Matthew Ballard (Apr 24 2024 at 00:45):
It doesn't lint yet
Riccardo Brasca (Apr 24 2024 at 00:46):
Yep, I fixed the errors
Riccardo Brasca (Apr 24 2024 at 01:57):
The bench results are very good!
Riccardo Brasca (Apr 24 2024 at 02:45):
I've marked #12386 "awaiting review". I hope I have added everybody as coauthor, but I am not sure I did it in the right way, so don't hesitate to make any modification.
Riccardo Brasca (Apr 24 2024 at 02:48):
(@Anne Baanen I don't how to make you the main author, if you want to open another PR this is if course perfectly fine)
Jireh Loreaux (Apr 24 2024 at 02:55):
Hmmm... I'm wondering how often we should do this. Maybe it's a design pattern we should use elsewhere too.
Jireh Loreaux (Apr 24 2024 at 02:56):
I'm thinking of, for example, docs#elementalStarAlgebra
Riccardo Brasca (Apr 24 2024 at 03:02):
I don't know, but in practice everybody working with ring of integers noticed it is sometimes a pain.
YaΓ«l Dillies (Apr 24 2024 at 07:19):
Woohoo, that's seemingly the first time I got a good idea wrt performance :)
YaΓ«l Dillies (Apr 24 2024 at 07:20):
I would like to do the same to docs#circle not because of performance issues but because we never use it as a submonoid (and, like, it's mathematically a subgroup)
Anne Baanen (Apr 24 2024 at 09:01):
Riccardo Brasca said:
(Anne Baanen I don't how to make you the main author, if you want to open another PR this is if course perfectly fine)
Don't worry about attribution too much: being listed as a co-author is useful for finding the changes later, otherwise it's YaΓ«l's idea with Matt's and Riccardo's fixes so there is no one author.
Matthew Ballard (Apr 24 2024 at 11:32):
Jireh Loreaux said:
Hmmm... I'm wondering how often we should do this. Maybe it's a design pattern we should use elsewhere too.
I think part of the reason this is so bad is that Lean is led repeatedly to find some instance of a class on β€
to make progress. But then β€
matches almost everything.
Kevin Buzzard (Apr 24 2024 at 20:16):
Yes, Algebra K (O K) can lead quickly to Algebra K Z
YaΓ«l Dillies (Jul 25 2024 at 08:02):
I have written up the lessons learned in a wiki page: https://github.com/leanprover-community/mathlib4/wiki/Tradeoffs-of-concrete-types-defined-as-subobjects. I would happily turn it into a blog post if you think it would be valuable.
Kim Morrison (Jul 25 2024 at 10:28):
Yes please, a blog post would be great. We'd like to move away from having the wiki at all.
YaΓ«l Dillies (Jul 25 2024 at 11:45):
YaΓ«l Dillies (Jul 25 2024 at 11:46):
Please in particular check the last paragraph. My understanding of the implications of Proj : Scheme
is shaky
Andrew Yang (Jul 25 2024 at 17:05):
I think the underlying space of Proj A
is not the same as the underlying space of Proj as a manifold anyways so I'm not sure if the example is right.
YaΓ«l Dillies (Jul 25 2024 at 18:39):
Hmm, okay, do people know of a better example?
Eric Wieser (Jul 27 2024 at 08:18):
I don't fully believe the head symbol claim; lean4 uses discrtrees nor head symbols for almost all indexing
YaΓ«l Dillies (Jul 27 2024 at 08:19):
How do you explain the performance boost in #12386 then?
Kevin Buzzard (Jul 27 2024 at 09:18):
IIRC this was caused by Module (π K) (π K)
triggering a search for Module K (π K)
which took a long time to fail
Kevin Buzzard (Jul 27 2024 at 09:18):
The obvious solution Module.id
or whatever it's called was being tried last because it is the first instance of module defined in mathlib and hence at the very back of the queue
Anne Baanen (Jul 29 2024 at 08:35):
Don't have the trace in front of me, but I believe it's effectively indexed on head symbols here anyway because in the instance declarations, the only arguments to the classes are free variables or other instances.
Yakov Pechersky (Aug 01 2024 at 20:55):
I'm hitting a related timeout currently working with residue fields. Is there a good way of resolving?
import Mathlib
open Valued
variable (K : Type*) [Field K] {Ξβ : outParam Type*}
[LinearOrderedCommGroupWithZero Ξβ] [vK : Valued K Ξβ]
example (n : β) : Module πͺ[K] (π[K] ^ n : Ideal πͺ[K]) := Submodule.module _
example (n : β) : Module πͺ[K] (π[K] ^ n : Ideal πͺ[K]) := by infer_instance -- timeout
Yakov Pechersky (Aug 01 2024 at 20:59):
Might have to do with Valued.ResidueField
being a def (even reducible) and not an abbrev?
Matthew Ballard (Aug 01 2024 at 23:07):
The keys for the goal are #[Module, Subtype, _uniq.26983, βΎ, Subtype, Subtype, _uniq.26983, βΎ, βΎ, *, *]
but we have a lot of instances like docs#LieSubmodule.module' with weak keys like #[Module, *, Subtype, *, βΎ, *, *]
.
Of course, we have no Lie algebra structure in play but it because the keys match Lean then tries to synthesize the instance parameters and unify against a LieSubmodule
. The signature is
{R : Type u} β
{L : Type v} β
{M : Type w} β
[inst : CommRing R] β
[inst_1 : LieRing L] β
[inst_2 : AddCommGroup M] β
[inst_3 : _root_.Module R M] β
[inst_4 : LieRingModule L M] β
(N : LieSubmodule R L M) β
{S : Type u_1} β
[inst_5 : Semiring S] β
[inst_6 : SMul S R] β
[inst_7 : _root_.Module S M] β [inst_8 : IsScalarTower S R M] β _root_.Module S β₯βN
so this takes some time.
There are quite few of these instances of Module
on subtypes with weak keys so it adds. Especially because both the arguments to Module
are coerced from subtypes.
Kevin Buzzard (Aug 02 2024 at 07:48):
Two quick workarounds: (1) set_option synthInstance.maxHeartbeats 40000
and it works (2) don't import Lie algebras and other things making typeclass inference go off the rails,
import Mathlib.Topology.Algebra.Valued.ValuedField
open Valued
variable (K : Type*) [Field K] {Ξβ : outParam Type*}
[LinearOrderedCommGroupWithZero Ξβ] [vK : Valued K Ξβ]
#synth Semiring πͺ[K]
example (n : β) : Module πͺ[K] (π[K] ^ n : Ideal πͺ[K]) := Submodule.module _
-- set_option synthInstance.maxHeartbeats 40000
-- set_option trace.Meta.synthInstance true
-- set_option trace.profiler true
count_heartbeats in -- 15457
example (n : β) : Module πͺ[K] (π[K] ^ n : Ideal πͺ[K]) := by infer_instance
variable (n : β) in
#synth Module πͺ[K] (π[K] ^ n : Ideal πͺ[K]) -- modern way to do the last line
takes under 1/2 as long.
In my opinion the right long-term fix for this is to redefine πͺ[K]
as a Type not a Subring
. I might be saying the same thing as Matt but my understanding of the problem is that because the goal is actually Module β₯πͺ[K] β₯(π[K] ^ n)
, Lean is constantly being sidetracked with ideas of the form "ooh we need to prove ClassName β₯X Y
and (X : Subring X')
so we could perhaps make progress by finding a term of type ClassName X' Y
." This idea is almost always a disaster in the sense that it might take some time to fail because Mathlib is now big, and there are lots of classes.
Rereading Matt's response I think it says the same thing as mine but in a more sophisticated way.
See here for how I designed finite adele rings not as subalgebras but as types, to avoid this kind of problem.
Matthew Ballard (Aug 02 2024 at 14:51):
I haven't finished getting a working version of mathlib (or even a test-passing toolchain) but I suspect that swapping the argument order of Membership.mem
as advised by Mario is going to help a good deal here and elsewhere
Matthew Ballard (Aug 03 2024 at 10:33):
With those changes @Yakov Pechersky 's example goes down to under 13000 heartbeats for instance synthesis and 19000 over all.
YaΓ«l Dillies (Aug 03 2024 at 10:35):
Matthew, may I nudge you into contributing some paragraph in blog#86 :pray:
Matthew Ballard (Aug 03 2024 at 10:47):
Mario Carneiro said:
One thing I just noticed is that docs#SetLike.instCoeSortType directly uses a lambda and membership:
instance (priority := 100) : CoeSort A (Type _) := β¨fun p => { x : B // x β p }β©
Since this is a coercion, it is unfolded and therefore loads of mathlib terms contain these explicit lambdas. (This also needs a dedicated delaborator, written just below it.) Has anyone tried replacing this with a call to a
setToType
function? The specific reason I have to believe that this will improve performance across the library is that I can see failed instance searches caused by the fact that the indexing is not able to handle the lambdas: if I have definitionsfoo, bar : Subgroup G
thenCommGroup βfoo
also attempts to apply an instance forCommGroup βbar
. Normally this would not happen because the discrimination tree can tell them apart, but after the aforementioned elaboration the result isCommGroup {x // x β foo}
vsCommGroup {x // x β bar}
and lean gives up indexing at the lambda.
Does linking to Mario's original message count?
Notification Bot (Aug 03 2024 at 10:47):
A message was moved here from #mathlib4 > Ways to speed up Mathlib by Matthew Ballard.
YaΓ«l Dillies (Aug 03 2024 at 10:50):
Will make do
Matthew Ballard (Aug 03 2024 at 11:07):
My understanding: we have the following values for the keys
inductive Key where
| const : Name β Nat β Key
| fvar : FVarId β Nat β Key
| lit : Literal β Key
| star : Key
| other : Key
| arrow : Key
| proj : Name β Nat β Nat β Key
Currently docs#Lean.Meta.DiscrTree.mkPath sends e.getAppFn
to .other
for the cases
(Expr.mdata (KVMap.mk _) _)
(Expr.letE _ _ _ _ _)
(Expr.lam _ _ _ _)
(Expr.app _ _)
(Expr.sort _)
(Expr.bvar _)
In particular, anything hiding inside of a .lam
is just crunched down to .other
. As Mario explained above, with the current ordered of Membership.mem
we have a lambda at the head of any coercion of a subtype to sort.
Reversing the order, the head becomes Membership.mem
, which being a constant, gets placed into the keys itself and indexing can go deeper to generate more refined keys.
YaΓ«l Dillies (Aug 03 2024 at 11:10):
That non-keying of lambda I had understood, but I had not thought of looking up SetLike.instCoeSortType
Matthew Ballard (Aug 03 2024 at 11:14):
It is a fairly subtle change when just looking at the expression pretty printed. After all, you only see two things get swapped. But if you are better than me at reading off the Expr
it is perhaps more obvious.
Matthew Ballard (Aug 03 2024 at 11:24):
In "fixing" mathlib, the worst offender is just the standard docs#Set.CoeSortType. Interestingly if you look at the porting note for it, you see that docs#Set.Elem is introduced to solve a similar indexing problem but for norm_cast
. But since it is a essentially an abbrev
the indexing for keys just unfolds and marches on.
Matthew Ballard (Aug 04 2024 at 20:59):
Actually, I spent some time on cleaning up my understanding here and I am more confused.
At some point there is a reduction step for the expression where we eta reduce. And it seems like the deBruijn variables themselves are quite important in the eta reduction.
We have a lambda whose body is the application of .bvar 0
after the change. Before itβs more complicated application. But the bound variable doesnβt come from the lambda itself but from the initial .forallE
earlier.
Matthew Ballard (Aug 04 2024 at 21:01):
This is the example I still testing on
β {G : Type*} [Group G] {H : Subgroup G}, SMul (β₯H.op) G
Before
#[SMul, Subtype, MulOpposite, *, βΎ, *]
and after
#[SMul, Subtype, MulOpposite, *, Membership.mem, MulOpposite, *, Subgroup, MulOpposite, *, *, *, Subgroup.op, *, *, *, *]
Last updated: May 02 2025 at 03:31 UTC