Zulip Chat Archive

Stream: general

Topic: Typeclass search is affected by other subexpressions


Eric Wieser (Sep 08 2023 at 08:20):

Two very confusing typeclass failures:

import Mathlib.Analysis.NormedSpace.OperatorNorm

variable (π•œ : Type*) [NontriviallyNormedField π•œ]
variable (E : Type*) [SeminormedAddCommGroup E] [NormedSpace π•œ E]

abbrev Dual : Type _ := E β†’L[π•œ] π•œ

def inclusionInDoubleDual : E β†’L[π•œ] Dual π•œ (Dual π•œ E) := sorry

#check _ = β€–ContinuousLinearMap.id π•œ (Dual π•œ E)β€–

-- ok
example : (by exact β€–inclusionInDoubleDual π•œ Eβ€–) = β€–ContinuousLinearMap.id π•œ (Dual π•œ E)β€– := sorry

-- fail
example : β€–inclusionInDoubleDual π•œ Eβ€– = β€–ContinuousLinearMap.id π•œ (Dual π•œ E)β€– := sorry

--ok
example := @ContinuousLinearMap.norm_id_le π•œ (Dual π•œ E) _ _ _

-- fail
example : β€–inclusionInDoubleDual π•œ Eβ€– ≀ 1 := by
  have := @ContinuousLinearMap.norm_id_le π•œ (Dual π•œ E) _ _ _
  Β· sorry
  Β· sorry -- where did these goals come from?
  Β· sorry

Eric Wieser (Sep 08 2023 at 08:20):

In the first example, the β€–inclusionInDoubleDual π•œ Eβ€– on the LHS is breaking typeclass search on the RHS (unless we use by exact to isolate it)

Eric Wieser (Sep 08 2023 at 08:21):

In the second example, the type of the goal is somehow changing the elaboration of a have statement

Eric Wieser (Sep 08 2023 at 08:21):

(cc @Sebastian Ullrich, whom I discussed this with this morning)

Eric Wieser (Sep 08 2023 at 08:24):

Replacing Dual π•œ E with E β†’L[π•œ] π•œ in any of the broken examples seems to fix the issue, suggesting this is a bug in abbrev + TC search.

Eric Wieser (Sep 08 2023 at 08:39):

The problem seems to be a bad search cache: on the first example, I get

[Meta.synthInstance] ❌ Norm (Dual π•œ E β†’L[π•œ] Dual π•œ E) β–Ό
  [] new goal Norm (Dual π•œ E β†’L[π•œ] Dual π•œ E) β–Ό
    [instances] #[@SeminormedAddGroup.toNorm, @SeminormedGroup.toNorm, @NormedAddGroup.toNorm, @NormedGroup.toNorm, @SeminormedAddCommGroup.toNorm, @SeminormedCommGroup.toNorm, @NormedAddCommGroup.toNorm, @NormedCommGroup.toNorm, @NonUnitalSeminormedRing.toNorm, @SeminormedRing.toNorm, @NonUnitalNormedRing.toNorm, @NormedRing.toNorm, @NormedDivisionRing.toNorm, @NormedField.toNorm, @ContinuousLinearMap.hasOpNorm]
  [] ❌ apply @ContinuousLinearMap.hasOpNorm to Norm (Dual π•œ E β†’L[π•œ] Dual π•œ E) β–Ό
    [tryResolve] ❌ Norm (Dual π•œ E β†’L[π•œ] Dual π•œ E) β‰Ÿ Norm (?m.16472 β†’SL[?m.16480] ?m.16473) β–Ό
      [] βœ… NontriviallyNormedField π•œ β–Ό
        [] result inst✝² (cached)
      [] βœ… NontriviallyNormedField π•œ β–Ά
      [] ❌ SeminormedAddCommGroup (Dual π•œ E) β–Ό
        [] result <not-available> (cached)
<snip>

where on the last line, lean didn't even bother to look for the instance that it couldn't find and just falsely claimed it already knew it was missing

Eric Wieser (Sep 08 2023 at 08:52):

Another MWE, where a rw affects TC search withina have that follows it; even though the have does not ever see the main goal!

import Mathlib.Analysis.NormedSpace.OperatorNorm

variable (π•œ : Type) [NontriviallyNormedField π•œ]
variable (E : Type) [SeminormedAddCommGroup E] [NormedSpace π•œ E]

abbrev Dual : Type _ := E β†’L[π•œ] π•œ

def inclusionInDoubleDual : E β†’L[π•œ] Dual π•œ (Dual π•œ E) := sorry

example : β€–inclusionInDoubleDual π•œ Eβ€– = 0 := by
  -- this `rw` is needed to make the `have` below work
  -- rw [inclusionInDoubleDual]
  haveI : Norm (Dual π•œ E β†’L[π•œ] Dual π•œ E) := inferInstance
  sorry

Sebastian Ullrich (Sep 08 2023 at 10:36):

Underlying bug is lean4#2522. More data on impact and a non-Mathlib MWE would be greatly appreciated!

Eric Wieser (Sep 08 2023 at 11:47):

Thanks, your explanation is already helpful!

Kevin Buzzard (Sep 08 2023 at 12:48):

Making a non-Mathlib MWE will be hours of work, however it would not surprise me if someone could be persuaded to do that work if someone explained to them more details about why non-Mathlib MWEs are so important. All I know from experience is that they are very much appreciated by the lean dev community, but I don't really understand why. Is it because you don't know about lake exe cache get and think that you have to spend hours compiling mathlib? ;-) If the answer is "mathlib just makes traces much more cluttered in general" then my first reaction to this is "are you absolutely sure that mathlib is making traces much more cluttered in this particular case?". But maybe the answer is something else which I haven't internalised yet.

Eric Wieser (Sep 08 2023 at 12:49):

Is it because you don't know about lake exe cache get and think that you have to spend hours compiling mathlib?

If you make a fix to Lean, the cache is (probably?) invalidated and you have to rebuild it all again

Sebastian Ullrich (Sep 08 2023 at 12:54):

@Kevin Buzzard Also, we can't add code with dependencies to the Lean 4 test suite (whose tests' runtimes should be measured in seconds, not hours)

Sebastian Ullrich (Sep 08 2023 at 12:56):

But note that doing minimization the usual way starting from the full code really isn't the optimal way here I believe. Instead one should start at the traces I posted and recreate only the components mentioned there, which hopefully should be much faster.

Kevin Buzzard (Sep 08 2023 at 13:26):

I think that it's not unreasonable to suggest that a general tool which produces mathlib-free MWEs would be very useful for this sort of thing; of course I don't have the ability to make it. I've heard that there is one in Coq but I don't know how well it works in practice. Thanks for the responses Sebastian and Eric.

Kevin Buzzard (Sep 08 2023 at 13:28):

@Eric Wieser and @Patrick Massot I might well take on mathlib-free minimisation of an example this evening; just to let you know in case you've started on one as well. What is the best target? Is it this post?

Kevin Buzzard (Sep 08 2023 at 13:28):

Sebastian -- do you have a preference?

Eric Wieser (Sep 08 2023 at 13:29):

I was not planning to take this on in the immediate future, so please do so!

Eric Wieser (Sep 08 2023 at 13:29):

I would guess all three examples will reproduce the same way

Kevin Buzzard (Sep 08 2023 at 20:28):

Here is a preliminary reduction, although there is still a long way to go:

import Mathlib.Analysis.Normed.Field.Basic

variable (π•œ : Type) [NonUnitalSeminormedRing π•œ]
variable (E : Type) [SeminormedAddCommGroup E]

structure ContinuousLinearMap' (M : Type) [TopologicalSpace M] (Mβ‚‚ : Type) [TopologicalSpace Mβ‚‚] where

notation:25 M " β†’z " Mβ‚‚ => ContinuousLinearMap' M Mβ‚‚

abbrev Dual : Type := E β†’z π•œ

instance ContinuousLinearMap'.toSeminormedAddCommGroup {E : Type} {F : Type}
    [SeminormedAddCommGroup E] [SeminormedAddCommGroup F] :
  SeminormedAddCommGroup (E β†’z F) := sorry

def foo : E β†’z Dual π•œ (Dual π•œ E) := 0

example : β€–foo π•œ Eβ€– = 0 := by
  -- this `rw` is needed to make the `have` below work
  --rw [foo]
  have : SeminormedAddCommGroup (Dual π•œ E) := inferInstance
  sorry

But now I need to properly understand what is going on, because further attempts by me to minimise just result in the problem going away. I have two questions about the summary at lean4#2522 :

1) what is a nested TC problem?

2) If I add set_option trace.Meta.synthInstance true then I can see the failure

[Meta.synthInstance] ❌ SeminormedAddCommGroup (Dual π•œ E) β–Ό
  [] result <not-available> (cached)

at the end of the example above. My understanding is that the current belief is that the cache has been tricked to return this because of some earlier failure (when things were "nested"). But I can't find that earlier failure in the trace and am not entirely sure where I should be looking.

Kevin Buzzard (Sep 08 2023 at 20:52):

Aah, I've solved (2), it's deep within [Meta.synthInstance] βœ… Norm (E β†’z Dual π•œ (Dual π•œ E))

Kevin Buzzard (Sep 08 2023 at 20:59):

Aah and in my search I suspect I've also figured out what a nested typeclass search is; indeed the reason it was hard to find was because it was...erm...a typeclass search within a typeclass search.

Kevin Buzzard (Sep 08 2023 at 22:10):

class Norm' (E : Type) where
  norm : E β†’ Nat

export Norm' (norm)

class TopologicalSpace' (Ξ± : Type) where
  random : Ξ± -- need something here for some reason

class SeminormedAddCommGroup' (Ξ± : Type) extends Norm' Ξ±, TopologicalSpace' Ξ± where

-- need to extend Norm' again for some reason; note that this doesn't seem to cause a diamond
class NonUnitalSeminormedRing' (Ξ± : Type) extends Norm' Ξ±, SeminormedAddCommGroup' Ξ± where

variable (π•œ : Type) [NonUnitalSeminormedRing' π•œ]
variable (E : Type) [SeminormedAddCommGroup' E]

structure ContinuousLinearMap' (M : Type) [TopologicalSpace' M] (Mβ‚‚ : Type) [TopologicalSpace' Mβ‚‚] where

notation:25 M " β†’z " Mβ‚‚ => ContinuousLinearMap' M Mβ‚‚

abbrev Dual : Type := E β†’z π•œ

instance ContinuousLinearMap'.toSeminormedAddCommGroup' {E : Type} {F : Type}
    [SeminormedAddCommGroup' E] [SeminormedAddCommGroup' F] :
  SeminormedAddCommGroup' (E β†’z F) := sorry

def foo : E β†’z Dual π•œ (Dual π•œ E) := sorry

#synth SeminormedAddCommGroup' (Dual π•œ E) -- works fine

-- set_option trace.Meta.synthInstance true
example : norm (foo π•œ E) = 0 := by
  -- this `rw` is needed to make the `have` below work
  --rw [foo]
  have : SeminormedAddCommGroup' (Dual π•œ E) := inferInstance -- now fails!
  sorry

/-

Relevant traces in the example:

1) On `norm (foo π•œ E)`:

[Meta.synthInstance] βœ… Norm' (E β†’z Dual π•œ (Dual π•œ E)) β–Ό
  [] new goal Norm' (E β†’z Dual π•œ (Dual π•œ E)) β–Ά
  [] βœ… apply @NonUnitalSeminormedRing'.toNorm' to Norm' (E β†’z Dual π•œ (Dual π•œ E)) β–Ά
  [] ❌ apply inst✝¹ to NonUnitalSeminormedRing' (E β†’z Dual π•œ (Dual π•œ E)) β–Ά
  [] βœ… apply @SeminormedAddCommGroup'.toNorm' to Norm' (E β†’z Dual π•œ (Dual π•œ E)) β–Ά
  [] ❌ apply inst✝ to SeminormedAddCommGroup' (E β†’z Dual π•œ (Dual π•œ E)) β–Ά
  [] βœ… apply @ContinuousLinearMap'.toSeminormedAddCommGroup' to SeminormedAddCommGroup' (E β†’z Dual π•œ (Dual π•œ E)) β–Ό
    [tryResolve] βœ… SeminormedAddCommGroup' (E β†’z Dual π•œ (Dual π•œ E)) β‰Ÿ SeminormedAddCommGroup' (E β†’z Dual π•œ (Dual π•œ E)) β–Ό
      [] βœ… SeminormedAddCommGroup' E β–Ά
      [] βœ… SeminormedAddCommGroup' (Dual π•œ (Dual π•œ E)) β–Ό
        [] new goal SeminormedAddCommGroup' (Dual π•œ (Dual π•œ E)) β–Ά
        [] ❌ apply inst✝ to SeminormedAddCommGroup' (Dual π•œ (Dual π•œ E)) β–Ά
        [] βœ… apply @ContinuousLinearMap'.toSeminormedAddCommGroup' to SeminormedAddCommGroup' (Dual π•œ (Dual π•œ E)) β–Ό
          [tryResolve] βœ… SeminormedAddCommGroup' (Dual π•œ (Dual π•œ E)) β‰Ÿ SeminormedAddCommGroup' (Dual π•œ E β†’z π•œ) β–Ό
            [] ❌ SeminormedAddCommGroup' (Dual π•œ E) β–Ό
              [] new goal SeminormedAddCommGroup' (Dual π•œ E) β–Ά
              [] ❌ apply inst✝ to SeminormedAddCommGroup' (Dual π•œ E) β–Ά
              [] ❌ apply @ContinuousLinearMap'.toSeminormedAddCommGroup' to SeminormedAddCommGroup' (Dual π•œ E) β–Ό
                [tryResolve] ❌ SeminormedAddCommGroup' (Dual π•œ E) β‰Ÿ SeminormedAddCommGroup' (?m.8308 β†’z ?m.8309)

2) Later, on `inferInstance`

[Meta.synthInstance] ❌ SeminormedAddCommGroup' (Dual π•œ E) β–Ό
  [] result <not-available> (cached)

-/

Mathlib-free! Should I post this on the github issue or are the names distasteful and I should replace them with proper computer science name like foo and bar?

Patrick Massot (Sep 08 2023 at 22:12):

I think those names are allowed.

Kevin Buzzard (Sep 08 2023 at 22:15):

The example is extremely fragile (this was one reason that minimisation was hard). If you tinker with random things then all of a sudden the failing [tryResolve] ❌ SeminormedAddCommGroup' (Dual π•œ E) β‰Ÿ SeminormedAddCommGroup' (?m.8308 β†’z ?m.8309) magically turns into a succeeding SeminormedAddCommGroup' (Dual π•œ E) β‰Ÿ SeminormedAddCommGroup' (E β†’z π•œ). I have no understanding of what is going on here. Similarly the topological space structure has to have a field or else the problem goes away, and you have to extend Norm' twice or the problem goes away.

Kevin Buzzard (Sep 08 2023 at 22:20):

class Norm' (E : Type) where
  norm : E β†’ Nat

export Norm' (norm)

class TopologicalSpace' (Ξ± : Type) where
  random : Ξ± -- need something here for some reason

class SeminormedAddCommGroup' (Ξ± : Type) extends Norm' Ξ±, TopologicalSpace' Ξ± where

-- need to extend Norm' again for some reason; note that this doesn't seem to cause a diamond
class NonUnitalSeminormedRing' (Ξ± : Type) extends Norm' Ξ±, SeminormedAddCommGroup' Ξ± where

variable (π•œ : Type) [NonUnitalSeminormedRing' π•œ]

-- no diamond
example : (NonUnitalSeminormedRing'.toNorm' : Norm' π•œ) =
    @SeminormedAddCommGroup'.toNorm' π•œ (@NonUnitalSeminormedRing'.toSeminormedAddCommGroup' π•œ _) := rfl

Last updated: Dec 20 2023 at 11:08 UTC