Zulip Chat Archive
Stream: mathlib4
Topic: advice for defining bundled hom and equiv structures
Edward van de Meent (Feb 27 2024 at 17:11):
i'd like to define bundled hom and equiv structures for Codes (which i have defined in my personal branch), and i'd like to know how i should go about extending existing structs, like Equiv
and such...
looking through how this was done for other structures, i have some specific questions i'd like to know the answer to, but general advice is also welcome.
-
Looking at
LinearEquiv
, it doesn't seem to explicitly requireσ
orσ'
to be a ring-isomorphism, rather requiring them to be of typeR →+* S
andS →+* R
respectively, and having parametersRingHomInvPair σ σ'
andRingHomInvPair σ' σ
, both of which contain fields stating that composing them either way givesid
on either ring. why is this defined like this? -
Looking at the same, i noticed that in the code, it extends
M ≃+ M₂
, while the snippet in the documentation doesn't... why is this? -
It extends both
LinearMap σ M M₂
andM ≃+ M₂
. The left inheritance reveals a structureAddHom
, and so does the right. there doesn't appear to be any field stating the values of these structures are the same... It is my understanding that if this were aclass
, this would lead to diamond issues, but because this is a structure, it doesn't. However, there is no field stating these fields are the same either. Does lean know these need to be the same by itself, or is this specified somewhere I overlooked? -
Similarly,
RingEquiv
extendsR ≃ S
,R ≃* S
andR ≃+ S
(but only the first is mentioned in the documentation)... If lean knows to unify theR ≃ S
field, why is it needed to explicitly state it as an extension in the code? Why doesn't it only extendR ≃* S
andR ≃+ S
? -
If lean knows to
unify
these fields, why can't the same be done with classes? Why doesn't lean solve diamond issues by itself?
Anne Baanen (Feb 28 2024 at 11:04):
- I believe this is so that turning a linear equivalence (not semilinear equivalence) into a semilinear map gives an actual linear map:
RingHom.id
instead of(RingEquiv.refl _).toRingHom
. We are having similar issues in #6057 where Lean expectsMonoidHom.id
but gets(RingHom.id _).toMonoidHom
. - You mean in the autogenerated declaration at docs#LinearEquiv it doesn't mention
extends AddEquiv
? I'm not sure why that happens, maybe the diamond inheritance that you mention? In most cases, it is much better to follow the actual code than the autogenerated documentation. - There is in fact diamond inheritance, but for both
structure
s andclass
es this is not an issue. What happens under the hood is that a structure likeLinearEquiv
will look like a fieldtoLinearMap
containing everything fromLinearMap
and then separately all the fields that are inAddEquiv
but not inLinearMap
:invFun
,left_inv
andright_inv
. When you want to applyLinearEquiv.toAddEquiv
, under the hood it unpacks the required fields fromtoLinearMap
and adds the remaining separate fields. - This is probably done to remain neutral between additive and multiplicative structure: under the hood a
RingEquiv
will have three fields:toEquiv
,map_add'
andmap_mul'
. If we wroteRingHom extends MulEquiv, AddEquiv
then we get two asymmetrical fields:toMulEquiv
andmap_add'
. It seems neater this way, although I don't have any technical reason that we need do this or something will break. - It does! To be technical, as long as your diamond inheritance is forgetful there will be no problems. When you use
extends
, Lean ensures this is the case. A diamond in the typeclass hierachy becomes a problem once you create data out of existing fields. The big example is scalar multiplication by a natural number: in any additive monoidA
, we could definen • a := a + a + ... + a
,n
times. In any multiplicative monoidM
, we could definea • b := a * b
. So what ifA = M = ℕ
? Then we have to arrange these multiplications to coincide, which we do in Mathlib by not fixing the definitionn • a = a + a + ... + a
but instead adding this as an extra axiom.
Notification Bot (Feb 28 2024 at 13:58):
Edward van de Meent has marked this topic as resolved.
Notification Bot (Mar 03 2024 at 17:43):
Edward van de Meent has marked this topic as unresolved.
Edward van de Meent (Mar 03 2024 at 17:45):
is there somewhere i could find a list of definitions/theorems i should implement for bundled Homs/ Equivs (and their respective classes)? i'm thinking of things like .id
, .rfl
, .trans
, etc?
Edward van de Meent (Mar 05 2024 at 10:13):
i am tempted to declare classes HomClass
and EquivClass
, because i think that is basically the pattern that we're implementing each time...
is it possibly the case that i want to use that objects and their Homs form a category?
Edward van de Meent (Mar 05 2024 at 10:14):
and something similar for Equiv types?
Edward van de Meent (Mar 05 2024 at 10:17):
(i tried declaring the classes and ran into universe errors which i have no experience with solving, which tipped me off to this being some kind of category theory concept)
Johan Commelin (Mar 05 2024 at 10:46):
Yup, those universe problems are known, and they can't really be solved.
Johan Commelin (Mar 05 2024 at 10:46):
One solution would be to have some meta-code that can generate all these id
, rfl
, trans
, etc... declarations.
Johan Commelin (Mar 05 2024 at 10:46):
But that's a substantial project
Edward van de Meent (Mar 05 2024 at 10:48):
a nice start would be to have a list of declarations and proofs, to at least make the convention documented somewhere
Edward van de Meent (Mar 06 2024 at 19:29):
i made a start with extracting the general API...
i have created a generic structure MyHom
which extends structures XHom
and YHom
, where YHom
has the same parameters as MyHom
, while XHom
has one less.
definitions and lemmas for XHom and YHom
definitions and lemmas for MyHom
Edward van de Meent (Mar 06 2024 at 19:33):
there is also MyHomClass
in there.
if anyone is missing some lemmas/definitions, please say so
Edward van de Meent (Mar 06 2024 at 19:34):
i ported all of these from Mathlib.Algebra.Group.Hom
.Defs
, if anyone wants to know
Edward van de Meent (Mar 06 2024 at 19:35):
i'll see if i can expand this to also include Equiv versions and the like
Edward van de Meent (Mar 06 2024 at 19:43):
some of these lemmas are proven using the fact that MyHom
only adds Prop-valued fields. other than that, there are still some other lemmas using sorry.
those are:
MyHom.id
, where you need to provide values for all extra fields ofMyHom
compared to its parents, such as proofs thatid
has the properties thatMyHom
describesMyHom.comp
, where you need to provide those values too, such as proofs that composition preserves the properties thatMyHom
describes
Edward van de Meent (Mar 06 2024 at 19:50):
if you are adding other non-prop fields, however, that will make it all a tad bit more complicated... for instance, the proof of cancel_left
depends on the FunLike.coe_injective'
field, while comp_assoc
is currently proven rfl
Edward van de Meent (Mar 06 2024 at 19:52):
although honestly i have yet to think of an example where that is relevant
Edward van de Meent (Mar 07 2024 at 18:03):
I'm implementing my own Isometry structure, and i ran into the following:
The docs in Mathlib.Data.FunLike.Equiv
say i should implement .copy
for my Equiv as follows:
protected def copy (f : MyIso A B) (f' : A → B) (f_inv : B → A) (h : f' = ⇑f) : MyIso A B :=
{ toFun := f',
invFun := f_inv,
left_inv := h.symm ▸ f.left_inv,
right_inv := h.symm ▸ f.right_inv,
map_op' := h.symm ▸ f.map_op' }
However, the example provided doesn't work, as you also need f_inv = f.invFun
, or leave f.invFun
the same between the instances...
I looked for examples in mathlib where this pattern is implemented, but a quick search didn't result in anything much.
Could someone tell me if this pattern is actually wanted, and if so, where i can find a working example of this API for any isometry-like structure?
Jireh Loreaux (Mar 07 2024 at 18:09):
You don't need them to be equal because they are provably equal.
Edward van de Meent (Mar 07 2024 at 18:09):
are they? f_inv
is a generic function B -> A
though?
Edward van de Meent (Mar 07 2024 at 18:10):
completely independent from f'
Jireh Loreaux (Mar 07 2024 at 18:12):
oh, sorry, you haven't proved copy
, I misunderstood. Yes, you need that hypothesis in order to prove left_inv
and right_inv
.
Jireh Loreaux (Mar 07 2024 at 18:13):
And you should definitely allow the user to replace the invFun
too, because the whole purpose is for providing better definitional properties.
Edward van de Meent (Mar 07 2024 at 18:15):
ok... is there some implementation in mathlib i could look at for inspiration?
Jireh Loreaux (Mar 07 2024 at 18:19):
A PR updating the text in Mathlib.Data.FunLike.Equiv
would be appreciated.
Jireh Loreaux (Mar 07 2024 at 18:19):
Without the morphism properties, it just looks like this:
variable {α β : Type*}
def Equiv.copy (e : α ≃ β) (f : α → β) (f_inv : β → α) (hf : f = ↑e) (hf_inv : f_inv = ⇑e.symm) :
α ≃ β where
toFun := f
invFun := f_inv
left_inv := hf_inv.symm ▸ hf.symm ▸ e.left_inv
right_inv := hf_inv.symm ▸ hf.symm ▸ e.right_inv
Jireh Loreaux (Mar 07 2024 at 18:20):
Does that answer your question? I'm not sure we have very many (any?) of these .copy
defs for EquivLike
types.
Jireh Loreaux (Mar 07 2024 at 18:20):
(Although it would be convenient if we did)
Edward van de Meent (Mar 07 2024 at 18:21):
yes, thanks! indeed, i haven't found any EquivLike types implementing this.
Edward van de Meent (Mar 07 2024 at 18:36):
it turns out it is a tad trickier when extending other copy lemmas, as apparently lean is unable to rewrite with hf
and hf_inv
at the inverse lemmas of e
. what does consistently work, however, is this:
variable {α β : Type*}
def Equiv.copy (e : α ≃ β) (f : α → β) (f_inv : β → α) (hf : f = ↑e) (hf_inv : f_inv = ⇑e.symm) :
α ≃ β where
toFun := f
invFun := f_inv
left_inv := by
simp_rw [hf_inv,hf]
exact f.left_inv
right_inv := by
simp_rw [hf_inv,hf]
exact f.right_inv
Edward van de Meent (Mar 11 2024 at 12:20):
i also ran into some trouble with the suggested way to define MyIsoClass
..
defining MyIso.instMyIsoClass
as suggested (see section A
in MWE) gives an error. my current solution to this is section B
, although i do think that's hacky and somewhat ugly...
#mwe :
import Mathlib.Data.FunLike.Equiv
import Mathlib.Logic.Equiv.Defs
variable {α β :Type*}
structure MyHom (α β :Type*) where
toFun : α → β
class MyHomClass (T:Type*) (α β: outParam Type*) [FunLike T α β]
namespace MyHom
instance instFunLike : FunLike (MyHom α β) α β where
coe := MyHom.toFun
coe_injective' := fun f g h => by cases f; cases g; congr
instance instMyHomClass : MyHomClass (MyHom α β) α β where
end MyHom
structure MyEquiv (α β:Type*) extends α ≃ β, MyHom α β where
class MyEquivClass (T:Type*) (α β: outParam Type*) extends EquivLike T α β, MyHomClass T α β
namespace MyEquiv
section A
-- /-
instance instMyEquivClass : MyEquivClass (MyEquiv α β) α β := {-- failed to synthesize instance FunLike (MyEquiv α β) α β
coe := fun f => f.toFun
inv := fun f => f.invFun
left_inv := fun f => f.left_inv
right_inv := fun f => f.right_inv
coe_injective' := fun f g h => by cases f; cases g; congr; simp_all
}
-- -/
end A
section B
/-
def instEquivLike : EquivLike (MyEquiv α β) α β := {
coe := fun f => f.toFun
inv := fun f => f.invFun
left_inv := fun f => f.left_inv
right_inv := fun f => f.right_inv
coe_injective' := fun f g h => by cases f; cases g; congr; simp_all
}
def instMyHomClass :
@MyHomClass (MyEquiv α β) α β (@EquivLike.toFunLike
(MyEquiv α β) α β MyEquiv.instEquivLike) :=
(@MyHomClass.mk (MyEquiv α β) α β (@EquivLike.toFunLike
(MyEquiv α β) α β MyEquiv.instEquivLike))
instance instMyEquivClass : MyEquivClass (MyEquiv α β) α β :=
@MyEquivClass.mk (MyEquiv α β) α β MyEquiv.instEquivLike MyEquiv.instMyHomClass
-/
end B
end MyEquiv
Edward van de Meent (Mar 11 2024 at 12:23):
basically, my current solution is using MyIsoClass.mk
with explicit arguments.
Edward van de Meent (Mar 11 2024 at 12:24):
i honestly expected lean to be able to handle version A
, but apparently not...
Edward van de Meent (Mar 11 2024 at 12:30):
i found a shorter solution, but i'm still not really happy...
instance instMyEquivClass : MyEquivClass (MyEquiv α β) α β := {({
coe := fun f => f.toFun
inv := fun f => f.invFun
left_inv := fun f => f.left_inv
right_inv := fun f => f.right_inv
coe_injective' := fun f g h => by cases f; cases g; congr; simp_all} : EquivLike (MyEquiv α β) α β) with
}
Last updated: May 02 2025 at 03:31 UTC