Zulip Chat Archive

Stream: mathlib4

Topic: defeq abuse in `WithLp`


Jireh Loreaux (Aug 13 2024 at 14:54):

A few months ago, I helped Jon Bannon clean up some defeq abuse related to EuclideanSpace 𝕜 n in files relating to Hermitian elements. This was causing tremendous pain.

Just this weekend, I realized there is a lot of defeq abuse that occurs in docs#WithLp, and moreover, it's intentional! For instance, given x : WithLp 2 (E × F), we write x.fst even though WithLp.fst doesn't exist. Instead, Lean sees through the type synonym and applies Prod.fst instead, which, in my opinion, is terrible type hygiene. We then have a series of lemmas which copy the usual Prod simplification lemmas from Prod to this defeq abused WithLp.

This then concludes with these simp lemmas which transform type-correct terms into the defeq abused ones (presumably so that the lemmas just stated previously will then apply).

I will grant that some care has been taken here, as it includes the comment:

/-! Note that the unapplied versions of these lemmas are deliberately omitted, as they break
the use of the type synonym. -/

However, to me, this just seems like design that is liable to cause headaches down the line. I'm asking because I'm creating a type synonym for docs#CStarModule, and I noticed these abuses when copying some of the code from docs#WithLp.

Jireh Loreaux (Aug 13 2024 at 14:54):

@Eric Wieser I would like your opinion specifically on the topic above.

Eric Wieser (Aug 13 2024 at 15:03):

This f.fst stuff for f : WithLp 2 (E × F) is indeed somewhat abusive, and was copied from the prior f 0 stuff for f : WithLp 2 (Fin 2 -> F)

Eric Wieser (Aug 13 2024 at 15:03):

I think there's arguably something special about defeq abuse of function application (we also use it for matrices, Injective, etc), and so we took .fst and .snd along for the ride for consistency

Jireh Loreaux (Aug 13 2024 at 15:06):

Hmmm.... It's exactly the function application defeq abuse that was causing the pain with EuclideanSpace.

Eric Wieser (Aug 13 2024 at 15:07):

Was it? I thought it was the function introduction defeq (via fun _ => _or ![_, _, ...]) that caused the pain

Jireh Loreaux (Aug 13 2024 at 15:07):

And we don't use it for docs#Function.Injective, right? The underlying object gets coerced to a function, so there's no abuse.

Eric Wieser (Aug 13 2024 at 15:08):

Sure we do, we write hinj hxy when hinj : Function.Injective f and hxy : x = y, and there's no coercion there

Yaël Dillies (Aug 13 2024 at 15:09):

I think Jireh means Injective ⇑f

Jireh Loreaux (Aug 13 2024 at 15:09):

oh, that's what you mean.

Eric Wieser (Aug 13 2024 at 15:10):

Eric Wieser said:

I think there's arguably something special about defeq abuse of function application (we also use it for matrices, Injective, etc), and so we took .fst and .snd along for the ride for consistency

I guess Injective is quite different to Matrix and WithLp, since the latter two are data and so defeq matters

Jireh Loreaux (Aug 13 2024 at 15:10):

Eric Wieser said:

Was it? I thought it was the function introduction defeq (via fun _ => _or ![_, _, ...]) that caused the pain

I think it was that we only had one direction of the Equiv around (the other deleted by defeq abuse), and so things wouldn't cancel properly.

Jireh Loreaux (Aug 13 2024 at 15:11):

So regardless of "which direction" goes away, both are problematic.

Eric Wieser (Aug 13 2024 at 15:14):

I don't fully remember the situation you're thinking of, but I think we fixed it by adding some missing simp lemmas? I think this defeq abuse is sufficiently convenient that we should have a concrete example of the issue it causes on master before spending lots of effort to change the defeq and make things less convenient.

Eric Wieser (Aug 13 2024 at 15:14):

The defeq abuse that matters with WithLp is that we never want to end up with @norm (WithLp p V) _ v where v : V

Jireh Loreaux (Aug 13 2024 at 15:16):

Eric Wieser said:

but I think we fixed it by adding some missing simp lemmas?

no, we fixed it by adding a CoeFun instance to PiLp and explicitly using where needed so that Lean didn't see through the type synonym automatically.

Jireh Loreaux (Aug 13 2024 at 15:17):

But it's possible it only mattered in situations where the "function" was unapplied.

Yaël Dillies (Aug 13 2024 at 15:20):

I guess the difference is that we have CoeToFun but not CoeToProd

Jireh Loreaux (Aug 13 2024 at 15:26):

Well, sure, you don't have a convenient way to state it, but that's sort of beside the point. You could still write the equiv in there manually.

Eric Wieser (Aug 13 2024 at 15:34):

Jireh Loreaux said:

no, we fixed it by adding a CoeFun instance to PiLp and explicitly using where needed so that Lean didn't see through the type synonym automatically.

Did we? Where's the instance?

Jireh Loreaux (Aug 13 2024 at 15:35):

docs#instCoeFunPiLpForall

Jireh Loreaux (Aug 13 2024 at 15:40):

I think probably the difference between the two situations is exactly what the comment in the WithLp file is getting at: when it's applied (i.e., with fst or snd, or an argument to the function), it's (maybe?) okay to commit the defeq abuse (because the type of the complete expression is the same with or without the abuse, but without it we would have to create new defs on the synonym in order for it to make sense), but when it's unapplied, then it would just be blatant defeq abuse (and the types of the resulting expression are different) and could lead to problems like you mentioned above with the norm.

Eric Wieser (Aug 13 2024 at 15:41):

Thanks, I had forgotten about #11943. I agree the situation is less compelling for the prod case then.

Jireh Loreaux (Aug 13 2024 at 15:41):

In the case of EuclideanSpace, we we definitely dealing with the unapplied situation a lot, because we were consider eigenvectors as terms in EuclideanSpace 𝕜 n, but then sometimes the defeq abuse was leading them to be considered as terms of type n → 𝕜.

Jireh Loreaux (Aug 13 2024 at 15:55):

Eric, we still have the corresponding lemmas for PiLp (i.e., the PiLp versions of the lemmas I mentioned in the original post).

Jireh Loreaux (Aug 13 2024 at 15:58):

I think I'm going to tentatively stick with the WithLp approach of mild defeq abuse here. It does seem to work okay.

Eric Wieser (Aug 13 2024 at 15:59):

Jireh Loreaux said:

Eric, we still have the corresponding lemmas for PiLp (i.e., the PiLp versions of the lemmas I mentioned in the original post).

WithLp.equiv_pi_apply, you mean? From the source code it looks to me like the CoeFun instance is useless and doesn't actually ever apply.

Jireh Loreaux (Aug 13 2024 at 16:00):

No, the lemmas directly above that.

Jireh Loreaux (Aug 13 2024 at 16:02):

Note, the CoeFun instance is useless for applied functions because of the lemma you mentioned. It's not useless so that you can write ⇑x to get from x : WithLp p (Π i, V i) to Π i, V i without writing WithLp.equiv p _ x (assuming Lean can infer the type correctly there).

Eric Wieser (Aug 13 2024 at 16:03):

I don't think it's useless because of those lemmas, but rather because x i elaborates without ever doing an instance search

Eric Wieser (Aug 13 2024 at 16:03):

But good point about the ⇑f coercion being handy

Jireh Loreaux (Aug 13 2024 at 16:06):

Eric Wieser said:

I don't think it's useless because of those lemmas, but rather because x i elaborates without ever doing an instance search

indeed, also that.

Yury G. Kudryashov (Aug 13 2024 at 16:19):

What do you think about making WithLp a one-field structure, or even split it back into WithLpPi and WithLpProd (both structures)?

Yury G. Kudryashov (Aug 13 2024 at 16:22):

My reason: the distance on WithLp 0 _ defines a topology that doesn't agree with the usual one.

Yury G. Kudryashov (Aug 13 2024 at 16:23):

Also, as we discussed some time ago, we may want to redefine the distance on WithLp p _, p < 1, so that it becomes an additive normed group which is a p-space.

Yury G. Kudryashov (Aug 13 2024 at 16:23):

This way we'll get a normed group for all p.

Yury G. Kudryashov (Aug 13 2024 at 16:23):

And a homeomorphism to the usual (indexed) product for p ≠ 0.

Tomas Skrivan (Aug 13 2024 at 16:24):

From my experience, dealing with type synonyms is super hard when writing tactics. Things just break randomly and it is super hard to fix. For the purpose of automatic differentiation I gave up on using WithLp and Id monad. I would strongly prefer those to be one element structures.

Jireh Loreaux (Aug 13 2024 at 16:33):

@Tomas Skrivan that sounds (to me, though I'm not particularly experienced) that you're just dealing with things at the wrong level of transparency. If you're dealing with everything at reducible transparency, as I think there has been some consensus that automated tactics should do, this wouldn't be a problem.

Jireh Loreaux (Aug 13 2024 at 16:35):

Yury G. Kudryashov said:

What do you think about making WithLp a one-field structure, or even split it back into WithLpPi and WithLpProd (both structures)?

A one-field structure would be okay with me, but I really don't think we should split it.

Eric Wieser (Aug 13 2024 at 16:36):

I think we should develop some more metaprogramming infrastructure for one-field structures before investing the effort of making WithLp (and Matrix?) such a structure

Jireh Loreaux (Aug 13 2024 at 16:36):

Yury G. Kudryashov said:

My reason: the distance on WithLp 0 _ defines a topology that doesn't agree with the usual one.

I don't see what that has to do with making it a one-field structure.

Yury G. Kudryashov said:

Also, as we discussed some time ago, we may want to redefine the distance on WithLp p _, p < 1, so that it becomes an additive normed group which is a p-space.

This is fine, but we need to define quasi-metrics and quasi-norms first. And I also don't see what it has to do with making WithLp a structure.

Jireh Loreaux (Aug 13 2024 at 16:38):

Eric Wieser said:

I think we should develop some more metaprogramming infrastructure for one-field structures before investing the effort of making WithLp (and Matrix?) such a structure

Don't we have a transfer tactic for things like this?

Eric Wieser (Aug 13 2024 at 16:57):

I'm thinking more machinery to generate all the rfl lemmas about the canonical equivalence

Eric Wieser (Aug 13 2024 at 16:57):

transfer is pretty useless, I think.

Yury G. Kudryashov (Aug 13 2024 at 18:01):

Jireh Loreaux said:

Yury G. Kudryashov said:

My reason: the distance on WithLp 0 _ defines a topology that doesn't agree with the usual one.

I don't see what that has to do with making it a one-field structure.

Currently, we abuse the definitional equality of TopologicalSpace instances here and there (at least, we used to do it in the manifold library in Mathlib 3). Using one field structures will force us to avoid it, and it will print readable error messages instead of "these things that look the same aren't equal".

Jireh Loreaux (Aug 13 2024 at 18:01):

(deleted)

Yury G. Kudryashov (Aug 13 2024 at 18:06):

About p < 1: why do we need quasimetric? If we redefine the norm to be ∑ i, ‖x i‖^p for 0 < p < 1, then we get a usual normed group which is a p-space instead of a NormedSpace.

Jireh Loreaux (Aug 13 2024 at 18:09):

no we don't? It doesn't satisfy the triangle inequality if 0 < p < 1.

Jireh Loreaux (Aug 13 2024 at 18:10):

oh sorry, I misread / assumed I knew what you wrote.

Jireh Loreaux (Aug 13 2024 at 18:13):

If you want to refactor the definition of the distance and norm on WithLp to do a case split on p < 1 vs. 1 ≤ p, I'm okay with that if we get some buy-in from other maintainers.

Tomas Skrivan (Apr 21 2025 at 19:20):

Recently, @ZhiKai Pong is trying to prove some basic theorems about vector calculus is it very easy to get yourself into a corner by abusing defeq between EuclideanSpace ℝ (Fin 3) and Fin 3 → ℝ.

For example it took quite some time before we figured out how to prove

theorem EuclideanSpace.contDiff_apply {ι} [Fintype ι] (i : ι) :
    ContDiff  n (fun f : EuclideanSpace 𝕜 ι => f i) := ...

(discussed in How to do differentiation on concrete multivariable function)

Or in this example it is all too easy to apply fderiv_pi and then be baffled why simp can't apply ContinuousLinearMap.pi_apply.

import Mathlib

local notation "ℝ³" => EuclideanSpace  (Fin 3)
open InnerProductSpace
noncomputable def curl (f : ³  ³) :
    ³  ³ := fun x =>
  -- get i-th component of `f`
  let fi i x := f x, EuclideanSpace.single i (1:)⟫_
  -- derivative of i-th component in j-th coordinate
  -- ∂fᵢ/∂xⱼ
  let df i j x := fderiv  (fi i) x (EuclideanSpace.single j (1:))
  fun i =>
    match i with
    | 0 => df 1 2 x - df 2 1 x
    | 1 => df 2 0 x - df 0 2 x
    | 2 => df 0 1 x - df 1 0 x

variable {ft :   ³  ³}

example (x : ³) : curl (fderiv  (fun t => (ft t)) t 1) x = fderiv  (fun t => curl (ft t) x) t 1 := by
  ext i
  rw [fderiv_pi (ι:=Fin 3) (h:=by sorry)]
  simp only [ContinuousLinearMap.pi_apply]
  sorry

(discussed in simplify a match pattern nested within another function)

Also one should write (WithLp.equiv 2 (Fin 3 → ℝ)).symm fun i => in the definition of curl but it does not save you from anything as rw [fderiv_pi] will be applied anyway.

Not sure what the solution or lesson is here. Is it a problem of missing API? Is the problem the defeq abuse? I'm just leaving it here as an evidence that people hit this problem and it should be dealt with one way or another.

Matthew Ballard (Apr 21 2025 at 19:21):

I am very skeptical of docs#EuclideanSpace being an abbrev...

Tomas Skrivan (Apr 21 2025 at 19:27):

Yeah EuclideanSpace is abbrev of PiLp which is abbrev of WithLp which I think is fine. However, I personally think that WithLp should be one field structure and we should have proper tooling to deal with such structures. In the process, we will realize that writing (WithLp.equiv p (Fin n → ℝ)).symm fun i => ... instead of fun i => ... is really painful and we will be forced to come up with a solution that combines the best of both worlds. (The same for product type)

Yaël Dillies (Apr 21 2025 at 20:08):

For a start, I do not know why WithLp.equiv takes explicit arguments. Compare to eg docs#OrderDual.toDual whose arguments are all implicit

Yaël Dillies (Apr 21 2025 at 20:10):

Secondly, the interesting equiv is usually the forward one (from the original space to the type synonym), because it is overall more common to have an element of the original generic type, and turn it into an element of the type synonym. So turn WithLp.equiv around.

Jireh Loreaux (Apr 21 2025 at 20:10):

I've had this feeling before too. Let me find the thread where we discussed it.

Yaël Dillies (Apr 21 2025 at 20:11):

Thirdly, the name of the equivalence should be toSynonym/ofSynonym, or a shortening thereof. Here, WithLp.toLp would be a great name which is as short as four characters once you open WithLp.

Jireh Loreaux (Apr 21 2025 at 20:12):

oh wait, that's this thread. :face_palm:

Yaël Dillies (Apr 21 2025 at 20:12):

Finally, it's sometimes useful to have the reverse equivalence too. I am not sure we want it badly here, but in case we do it could be WithLp.ofLp

Paul Lezeau (Apr 21 2025 at 20:31):

Yaël Dillies said:

For a start, I do not know why WithLp.equiv takes explicit arguments. Compare to eg docs#OrderDual.toDual whose arguments are all implicit

I’d be happy to have a go at fixing that! (edit: started here: #24261)

Eric Wieser (Apr 21 2025 at 20:40):

If we're going to rename anyway it would be less annoying for downstream code if we leave things explicit in the old name

Paul Lezeau (Apr 21 2025 at 20:54):

Eric Wieser said:

If we're going to rename anyway it would be less annoying for downstream code if we leave things explicit in the old name

Is your suggestion to add a WithLp.toLp, and keep WithLp.equiv (possibly with a deprecation warning) ?

Yury G. Kudryashov (Apr 21 2025 at 21:10):

I think, that's reasonable. Also, I like the idea of making WithLp a 1-field structure.

Eric Wieser (Apr 21 2025 at 21:12):

I think we should do the structure refactor separately, rather than at the same time

Yury G. Kudryashov (Apr 21 2025 at 21:24):

I agree, separating refactors increase chances of both of them going through.

Jireh Loreaux (Apr 21 2025 at 21:53):

Do we have this problem with docs#Lex too? I'm wondering if all parametric type synonyms which use Π i, X i should actually be 1-field structures?

Eric Wieser (Apr 21 2025 at 22:15):

Matrix too

Filippo A. E. Nuccio (Apr 24 2025 at 14:00):

What's the advantage of turning it into a 1-field structure?

Eric Wieser (Apr 24 2025 at 14:00):

It makes it impossible to defeq abuse

Eric Wieser (Apr 24 2025 at 14:01):

We normally catch defeq abuse in review, but this doesn't stop it tripping up new users

Aaron Liu (Apr 24 2025 at 14:01):

For functions that eat a function, you get to use a funlike coercion instead of defeq abuse by default

Tomas Skrivan (Apr 24 2025 at 14:08):

Aaron Liu said:

For functions that eat a function, you get to use a funlike coercion instead of defeq abuse by default

Also having explicit coercion like this makes it easier to write tactics.


Last updated: May 02 2025 at 03:31 UTC