Zulip Chat Archive

Stream: mathlib4

Topic: type-class inference slow/timing out


Kenny Lau (Jul 22 2025 at 16:03):

import Mathlib

#synth AddCommMonoid (  Ideal.span ({37} : Set ))

Kenny Lau (Jul 22 2025 at 16:03):

I think this is due to diamond instance coming from quotient and from addgroup -> Zmod?

Eric Wieser (Jul 22 2025 at 16:10):

This works:

import Mathlib

set_option synthInstance.maxHeartbeats 32197

#synth AddCommMonoid (  Ideal.span ({37} : Set ))

Eric Wieser (Jul 22 2025 at 16:13):

attribute [-instance] NormedAddCommGroup.toENormedAddCommMonoid also fixes it (docs#NormedAddCommGroup.toENormedAddCommMonoid)

Floris van Doorn (Jul 23 2025 at 10:33):

Some suspicious instances I found by looking through the trace, which maybe should be scoped:

Removing the 4 instances mentioned above reduced the number of heartbeats by 70x, from 20000 to 300.

Kenny Lau (Jul 23 2025 at 10:34):

(cc @ZhiKai Pong)

Floris van Doorn (Jul 23 2025 at 10:43):

I think we should make much more classes non-abbrevs: docs#IsArtinian, docs#WellFoundedLT (is docs#isWellOrder_lt now completely useless, since WellFoundedLT is currently an abbrev?), etc.

Floris van Doorn (Jul 23 2025 at 10:45):

(cc @Sébastien Gouëzel for the bundle instance)

Junyan Xu (Jul 24 2025 at 13:02):

So we should try to replace abbrev X := Y by class X extends Y, right? I try to do this in #27427 for semisimple modules and I expect I'll need to add a few instances, since previously e.g. IsSimpleModule->IsSemisimpleModule comes from IsSimpleOrder->ComplementedLattice.

Junyan Xu (Jul 24 2025 at 13:04):

The loss of defeq is annoying, see errors at https://github.com/leanprover-community/mathlib4/actions/runs/16497574869/job/46647016790?pr=27427

Junyan Xu (Jul 24 2025 at 13:33):

With class X extends Y we still get an instance from X to Y. Is this bad?

Floris van Doorn (Jul 24 2025 at 14:52):

Junyan Xu said:

The loss of defeq is annoying, see errors at https://github.com/leanprover-community/mathlib4/actions/runs/16497574869/job/46647016790?pr=27427

you can replace t of type ComplementedLattice ... by { t with } and that should work fine (if the type is known(?)), so that is barely any overhead, IMO.

Floris van Doorn (Jul 24 2025 at 14:56):

Junyan Xu said:

With class X extends Y we still get an instance from X to Y. Is this bad?

That direction is fine. In a semi-simple module it's fine if you use that Submodule is a completed lattice. We block the other direction, because it's rare that you are looking for a semi-simple module instance, and you want to find a generic complemented lattice instance. It is much more likely that you only want to try other semi-simple module instances. (Whenever you want to apply a generic complemented lattice instance to semi-simple modules, then that has to now be added as a separate instance.)

Floris van Doorn (Jul 24 2025 at 16:32):

I also diagnosed the type-class problem from #lean4 > grind and ordered ring (cc @Heather Macbeth), and identified a few more problematic instances, although the results here are not as clear as they were in the example above.

I do think we should strive for some better instances hygiene in Mathlib.

import Mathlib

set_option trace.Meta.synthInstance true
set_option trace.profiler true

variable {K : Type*} [Field K] {x y z : K}

/- instances that are not great, since they assume something ordered and conclude
something algebraic. It might be annoying to scope them, though... -/
attribute [-instance]
  IsStrictOrderedRing.toCharZero
  instIsAddTorsionFree
  IsStrictOrderedRing.noZeroDivisors
/- probably fine... -/
attribute [-instance]
  Module.instNoZeroSMulDivisorsOfIsDomain
/- concluding something algebraic from something finite(-dimensional).  -/
attribute [-instance]
  Module.instIsReflexiveOfFiniteOfProjective
  Module.free_of_finite_type_torsion_free'
/- these instances all conclude something algebraic from finite/subsingleton,
and should all be scoped, IMO -/
attribute [-instance]
  Subsingleton.to_noZeroDivisors
  Module.Free.of_subsingleton'
  Subsingleton.instComplementedLattice
  isArtinian_of_finite
  AddMonoid.fg_of_finite
  Finite.to_wellQuasiOrderedLE
  Finite.to_wellFoundedLT
  isNoetherian_of_finite
  isNoetherian_of_subsingleton
  Module.Finite.of_finite
  Subsingleton.to_isAddTorsionFree
  Module.FinitePresentation.of_subsingleton
  Module.Free.of_subsingleton
/- not very suspicious, but it's relatively slow by itself,
probably when unifying instances -/
attribute [-instance]
  IsFractionRing.instFaithfulSMul
/- not great together with `Module.IsNoetherian.finite`. -/
attribute [-instance]
  isNoetherian_of_isNoetherianRing_of_finite

#count_heartbeats in
#synth Lean.Grind.NoNatZeroDivisors K

Junyan Xu (Jul 24 2025 at 19:26):

Do we need some class IsTrivial extends Subsingleton as an algebraic version of Subsingleton?

Junyan Xu (Jul 24 2025 at 19:27):

The bench result of #27427 is out, overall it speeds things up but there are also some files that get a lot slower.

Robin Carlier (Jul 24 2025 at 19:39):

Do we have a documentation entry/library note/docstring that explains and document clearly why such instances are "bad/not great"?

Aaron Liu (Jul 24 2025 at 22:03):

-- see note [lower instance priority] maybe

Eric Wieser (Jul 25 2025 at 01:15):

Junyan Xu said:

The bench result of #27427 is out, overall it speeds things up but there are also some files that get a lot slower.

I'm not convinced that any benchmarks are trustworthy at the moment; many changes are in files that are earlier than your diff

Robin Carlier (Jul 25 2025 at 06:40):

Aaron Liu said:

-- see note [lower instance priority] maybe

The note is related but I feel like it's not the full story here.

I think if we're removing or scoping these instances this should come along such a documentation entry so that newcomers/less aware people don't think it's good to turn them back to instances or to make them global.

Sébastien Gouëzel (Jul 25 2025 at 11:06):

Floris van Doorn said:

docs#Bundle.instNormedAddCommGroupOfRiemannianBundle seems really bad: it has a variable as its head symbol, and can therefore unify with any function application. In particular, when trying to find NormedAddCommGroup (ℤ ⧸ Ideal.span {37}) it will use E := fun A => ℤ ⧸ A. This should definitely be scoped.

Scoped in #27462

Kim Morrison (Jul 28 2025 at 02:59):

/- these instances all conclude something algebraic from finite/subsingleton,
and should all be scoped, IMO -/

@Floris van Doorn, do you have ideas about where any of these should be scoped to? Subsingleton? Finite?

Floris van Doorn (Jul 28 2025 at 09:20):

That's how I would scope them, yes. It might be a bit annoying, but I expect that these instances are not used all that frequently(?)

Eric Wieser (Jul 28 2025 at 09:28):

I think we need them all to be available to the nontriviality tactic

Kim Morrison (Jul 28 2025 at 11:29):

#27592 looks at

  Subsingleton.to_noZeroDivisors
  Module.Free.of_subsingleton'
  Subsingleton.instComplementedLattice
  isArtinian_of_finite
  AddMonoid.fg_of_finite
  Finite.to_wellQuasiOrderedLE
  Finite.to_wellFoundedLT

either removing them as an instance, or scoping the instance.

Kim Morrison (Jul 28 2025 at 11:46):

Sadly this only minimally reduces the heartbeats required for variable (K : Type) [Field K] in #synth Lean.Grind.NoNatZeroDivisors K from 18808 down to 18643.

Kim Morrison (Jul 28 2025 at 11:54):

Eric Wieser said:

I think we need them all to be available to the nontriviality tactic

How does one open a namespace in TacticM?

Andrew Yang (Jul 28 2025 at 14:36):

Can we have a scope that contains all things that could be instances but cannot due to loops or bad performances or even diamonds and have something like infer_instance! that opens it locally and suggests a scope to open when it is found?

Heather Macbeth (Jul 28 2025 at 17:51):

It's surprising to me that Grind.NoNatZeroDivisors fails so slowly when IsAddTorsionFree fails so quickly:

import Mathlib

variable {K : Type*} [Field K]

#time -- 944 ms to fail
#synth Lean.Grind.NoNatZeroDivisors K

#time -- 973 ms to fail
#synth NoZeroSMulDivisors  K

#time -- 73 ms to fail
#synth IsAddTorsionFree K

Heather Macbeth (Jul 28 2025 at 17:52):

They're equivalent to each other under very mild conditions, right?

import Mathlib

example {K : Type*} [AddCommMonoid K] : Lean.Grind.NoNatZeroDivisors K  IsAddTorsionFree K := by
  constructor
  · rintro h
    constructor
    intro k hk a b
    exact h k a b hk
  · rintro h
    constructor
    intro k a b hk habk
    exact h hk habk

Heather Macbeth (Jul 28 2025 at 17:53):

Would it work simply to have only one instance of Lean.Grind.NoNatZeroDivisors, namely from IsAddTorsionFree?

Jireh Loreaux (Jul 28 2025 at 18:02):

It's worth also considering that, according to the module documentation here: https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/NoZeroSMulDivisors/Defs.html#NoZeroSMulDivisors, our goal will be to remove NoZeroSMulDivisors in favor of something mathematically correct for semimodules.

Heather Macbeth (Jul 28 2025 at 18:02):

Does that mean that IsAddTorsionFree K is the "good way"?

Jireh Loreaux (Jul 28 2025 at 18:03):

I would argue "yes".

Heather Macbeth (Jul 28 2025 at 18:04):

So maybe the really problematic instance here is docs#instNoNatZeroDivisorsOfNoZeroSMulDivisorsNat :grinning:

Yaël Dillies (Jul 29 2025 at 06:47):

Jireh Loreaux said:

It's worth also considering that, according to the module documentation here: https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/NoZeroSMulDivisors/Defs.html#NoZeroSMulDivisors, our goal will be to remove NoZeroSMulDivisors in favor of something mathematically correct for semimodules.

Ahah, I added this docstring three days ago. I am glad it turns out useful already

Kim Morrison (Jul 29 2025 at 08:18):

Unfortunately replacing docs#instNoNatZeroDivisorsOfNoZeroSMulDivisorsNat with an instance from IsAddTorsionFree K causes a grind failure in LinearAlgebra/RootSystem/Finite/Lemmas

Yaël Dillies (Jul 29 2025 at 08:18):

Where can I see that failure?

Kim Morrison (Jul 29 2025 at 08:19):

Let me push a branch.

Kim Morrison (Jul 29 2025 at 08:20):

#27627

Kim Morrison (Jul 29 2025 at 08:23):

I think the problem is that CommRing + CharZero doesn't give IsAddTorsionFree.

Kim Morrison (Jul 29 2025 at 08:23):

perhaps providing that instance will destroy the gains Heather observed above :-(

Scott Carnahan (Jul 29 2025 at 16:59):

That is strange. You ought to get IsAddTorsionFree from the combination of CharZero and IsDomain.

Kim Morrison (Jul 31 2025 at 11:31):

Yaël Dillies said:

Where can I see that failure?

@Yaël Dillies, would you be interested in fixing this?

Kim Morrison (Aug 06 2025 at 09:56):

I added the instance saying a characteristic zero domain has IsAddTorsionFree in #28031. It has no effect on the timings above.

Kim Morrison (Aug 06 2025 at 10:34):

and #28033 changes the instances for NoNatZeroDivisors to use this. It drastically speeds up the failing synthesis problem

import Mathlib

variable {K : Type*} [Field K] {x : K}
#synth Lean.Grind.NoNatZeroDivisors K

as well as @Heather Macbeth's originally reported problem

example : x ^ 3 * x ^ 42 = x ^ 45 := by grind

Yaël Dillies (Aug 06 2025 at 14:05):

Kim Morrison said:

Yaël Dillies said:

Where can I see that failure?

Yaël Dillies, would you be interested in fixing this?

Sorry, I am very behind on everything (although things will get better now that I live in one place). If you want me to do something, please somehow land a notification in my inbox (eg by creating an issue and assigning me to it)


Last updated: Dec 20 2025 at 21:32 UTC