Zulip Chat Archive

Stream: mathlib4

Topic: instances that match unconditionally


Matthew Ballard (Mar 11 2025 at 15:00):

Using the following

code

gives the following list https://gist.github.com/mattrobball/8690f3a7bbb5ccb4c8e6cb632a495a34

These are instances that Lean will always attempt for the corresponding synthesis problem. Their current priority is listed

Matthew Ballard (Mar 11 2025 at 15:07):

Library note:

Therefore, if we create an instance that always applies, we set the priority of these instances to 100 (or something similar, which is below the default value of 1000).

If you filter the list of 2600+, 1504 of them have default priority. (None above thankfully)

Edward van de Meent (Mar 11 2025 at 15:08):

at first glance, a bunch of these look like class extends instances?

Matthew Ballard (Mar 11 2025 at 15:09):

Yes many are

Edward van de Meent (Mar 11 2025 at 15:14):

btw, what is the tag of the library note you're referring to?

Matthew Ballard (Mar 11 2025 at 15:15):

Matthew Ballard said:

Yes many are

792 to be exact

Matthew Ballard (Mar 11 2025 at 15:16):

Edward van de Meent said:

btw, what is the tag of the library note you're referring to?

"lower instance priority"

Edward van de Meent (Mar 11 2025 at 15:21):

"lower cancel priority" seems to be a related (duplicate/specialized) library note?

We lower the priority of inheriting from cancellative structures.
This attempts to avoid expensive checks involving bundling and unbundling with the IsDomain class.
since IsDomain already depends on Semiring, we can synthesize that one first.
#general > Why is `simpNF` complaining here?

Matthew Ballard (Mar 11 2025 at 15:33):

There are two issues:

  1. slowing down instance synthesis that succeeds
  2. slowing down instance synthesis that fails

Priority doesn't really matter for the latter as everything get tried. For that, the only real tool we have is scoping to a namespace.

I would argue that some are pretty crazy from a human perspective. For example docs#IsAddKleinFour.instFinite. If I was handed a random type and asked to check it is Finite, asking "Is it a Klein 4-group?" would not be anywhere on my list of questions. Unless, of course, I was already working with such things. But it is globally available and has default priority.

Matthew Ballard (Mar 11 2025 at 15:35):

Of the two issues, the latter seems to do the most damage to people's workflow.

Edward van de Meent (Mar 11 2025 at 15:53):

Matthew Ballard said:

For example docs#IsAddKleinFour.instFinite. If I was handed a random type and asked to check it is Finite, asking "Is it a Klein 4-group?" would not be anywhere on my list of questions.

looking into this, since among the fields of IsAddKleinFour is Nat.card T = 4, this indeed is a very useless instance :rolling_on_the_floor_laughing:

Matthew Ballard (Mar 11 2025 at 16:01):

Updated the gist to reflect that classes with more than one parameter exist.

Kevin Buzzard (Mar 11 2025 at 16:48):

Cool that we have a universal property for being the Klein four group though.

Matthew Ballard (Mar 11 2025 at 17:25):

I agree :)

Matthew Ballard (Mar 11 2025 at 17:26):

Matthew Ballard said:

Updated the gist to reflect that classes with more than one parameter exist.

Updated the code also now

Matthew Ballard (Mar 11 2025 at 18:48):

naughty_instances.csv
Here is a spreadsheet with the instances for easier human inspection

Eric Wieser (Mar 11 2025 at 19:00):

At some point there was an RFC to lean 4 to lower the priority of instances generated by extends (edit: lean4#2325), but I think it was rejected because it didn't actually affect performance.

Matthew Ballard (Mar 11 2025 at 19:13):

Yeah, I tried that about a year ago with different values for both the parent and non-parents. It didn't seem to make a big difference.

I think the main issue are instances that expand the search space too much. Eg you are searching for Unique and then you hit a Group -> Unique and you are crawling the whole algebraic hierarchy when it has little to do with your current setting

Yury G. Kudryashov (Mar 11 2025 at 23:29):

Probably, you mean Group -> Nonempty. You can't prove Group -> Unique.

Eric Wieser (Mar 11 2025 at 23:34):

I'm not really convinced that these unconditional instances are a bad thing, so long as none of them loop. Worst case scenario should be that we visit every typeclass once though these, right?

Aaron Liu (Mar 11 2025 at 23:35):

Eric Wieser said:

I'm not really convinced that these unconditional instances are a bad thing, so long as none of them loop. Worst case scenario should be that we visit every typeclass once though these, right?

No, you can repeat typeclasses with different parameters.

Eric Wieser (Mar 11 2025 at 23:44):

The examples I can think of all have structurally "smaller" parameters in the assumptions, if you're describing what I think you are

Floris van Doorn (Mar 12 2025 at 10:41):

I think instances that have the form MyClass (MyReducibleDefinition ..) are a footgun, since they always apply, but don't look like they do.

Floris van Doorn (Mar 12 2025 at 10:43):

Eric Wieser said:

I'm not really convinced that these unconditional instances are a bad thing, so long as none of them loop. Worst case scenario should be that we visit every typeclass once though these, right?

Is simp still checking whether a type is a subsingleton for all terms it traverses? You might be doing this check frequently, for many different types.

Floris van Doorn (Mar 12 2025 at 11:21):

But I'm very much in favor of scoping instances from a very domain-specific area to a general area with weak keys.

For example, the following failure spends 90% of the time trying to show that Fin 2 is a sifted category because of docs#CategoryTheory.IsSifted.nonempty (before #22780 it would also spend a bunch of time in the algebraic hierarchy).

import Mathlib

set_option trace.Meta.synthInstance true
-- set_option trace.profiler true
set_option profiler true
set_option profiler.threshold 1

-- attribute [-instance]
--   CategoryTheory.IsSifted.nonempty

example (A : Type*) : Subsingleton (Fin 2  ) := by
  try infer_instance
  sorry

Matthew Ballard (Mar 12 2025 at 12:07):

Yury G. Kudryashov said:

Probably, you mean Group -> Nonempty. You can't prove Group -> Unique.

Right. I was trying to say that Group + Stuff -> Unique. Depending on the ordering, you end up searching the algebraic hierarchy.

Matthew Ballard (Mar 12 2025 at 12:08):

Floris van Doorn said:

But I'm very much in favor of scoping instances from a very domain-specific area to a general area with weak keys.

If there is scoped notation, then there is no UX impact with scoping instances

Matthew Ballard (Mar 12 2025 at 12:09):

Eric Wieser said:

I'm not really convinced that these unconditional instances are a bad thing, so long as none of them loop. Worst case scenario should be that we visit every typeclass once though these, right?

Does caching use the context? I should look

Matthew Ballard (Mar 12 2025 at 12:10):

I can try to rerun my experiment setting all projection instances to 100

Matthew Ballard (Mar 12 2025 at 12:13):

Floris van Doorn said:

I think instances that have the form MyClass (MyReducibleDefinition ..) are a footgun, since they always apply, but don't look like they do.

Should we have a “redundant instance” linter? It wouldn’t ban them per se but the user would have to opt in in making it and give a doc string

Floris van Doorn (Mar 12 2025 at 12:26):

Maybe. We can also port the instance priority linter from Lean 3 and make sure it looks at the key, so that reducible definitions are also caught.
But before we do that, we probably should test that setting instance priorities actually speeds up (succeeding) instance synthesis.

Eric Wieser (Mar 12 2025 at 15:22):

docs#CategoryTheory.IsSifted.isSifted_of_hasBinaryCoproducts_and_nonempty forms a loop though, with IsSifted.nonempty

Yury G. Kudryashov (Mar 13 2025 at 03:52):

Why Nonempty (Fin 2) helps to prove Subsingleton (Fin 2 → _)?

Kevin Buzzard (Mar 13 2025 at 07:55):

Yes you would think that this is a step backwards!

Yury G. Kudryashov (Mar 14 2025 at 02:36):

Can you follow the path that leads to this search?

Floris van Doorn (Mar 15 2025 at 00:07):

docs#IsEmpty.instSubsingleton + docs#instIsEmptyForallOfNonempty
This indeed leads to the unfortunate consequence that you're solving useless side-conditions. Not sure how we can solve this... The simple search that type-class inference might just be too simple to figure out that these are "stupid" sequences of instances.

Jovan Gerbscheid (Mar 15 2025 at 12:27):

I made #22953 to reorder the arguments of docs#instIsEmptyForallOfNonempty, so it becomes

instance {p : α  Sort*} [ x, IsEmpty (p x)] [h : Nonempty α] : IsEmpty ( x, p x)

So then the useless side condition Nonempty α is only attempted after the main condition. This should fix the above slow synthesis.

Jovan Gerbscheid (Mar 15 2025 at 12:28):

Arguably, the same swap should be made in instCountableForallOfFinite, so it becomes

instance [ a, Countable (π a)] [Finite α] : Countable ( a, π a)

where Finite α plays the role of useless side condition.

Michael Rothgang (Mar 15 2025 at 14:45):

:working_on_it: the KleinFour instance

Michael Rothgang (Mar 15 2025 at 14:51):

#22958 scopes the KleinFour.instFinite instance

Jovan Gerbscheid (Mar 15 2025 at 16:21):

I was going to say that CategoryTheory.IsSifted.nonempty (with IsSifted.isSifted_of_hasBinaryCoproducts_and_nonempty) is a bad instance.

But even worse is that following traverses the algebraic hierarchy three times, once for Limits.WalkingPair, once for (J : Type _) and once for (J : Type):

import Mathlib

open CategoryTheory
set_option trace.Meta.synthInstance true
example (C : Type*) [Category C] : Limits.HasBinaryCoproducts C := by
  infer_instance

Michael Rothgang (Mar 18 2025 at 14:42):

#23052 scopes the IsSifted.nonempty instance.

Michael Rothgang (Mar 18 2025 at 15:52):

Results of the above: -0,5% on linting, neutral on instructions (which is probably not surprising, given that these files are close to leaf files).

Michael Rothgang (Mar 18 2025 at 20:09):

Jovan Gerbscheid said:

Arguably, the same swap should be made in instCountableForallOfFinite, so it becomes

instance [ a, Countable (π a)] [Finite α] : Countable ( a, π a)

where Finite α plays the role of useless side condition.

#23073 tries this (still running CI)

Yury G. Kudryashov (Mar 19 2025 at 00:32):

Re: #23073: No benchmark entry differed by at least +1.0⬝10⁹ instructions.

Matthew Ballard (Mar 19 2025 at 13:19):

Matthew Ballard said:

I can try to rerun my experiment setting all projection instances to 100

branch#mrb/reduce_prios will (should!) have a working toolchain in ~ 1 hr. If people want to hack on it to get it working, please do.

Michael Rothgang (Mar 19 2025 at 13:50):

CI in that run failed: from the log, in the "print lean and lake versions" step

info: downloading component 'lean'
error: binary package was not provided for 'linux'
Error: Process completed with exit code 1.

Matthew Ballard (Mar 19 2025 at 15:37):

CI didn't wait an hour :) The toolchain should be there now

Matthew Ballard (Mar 19 2025 at 21:03):

I've pushed it along but have to step away for the day. Please feel free to continue


Last updated: May 02 2025 at 03:31 UTC