Zulip Chat Archive
Stream: new members
Topic: Beginner formalizing group actions
Yiming Xu (Nov 08 2024 at 09:57):
Hi everyone. I am starting to get my hands dirty by doing a little exercise in Lean. I would like mature users to make comments and suggestions during my attempt to the proof. This post would be updated in the next few days, with very stupid and naive newbie questions. I appreciate everyone who may come by and say something.
I think the point that I mostly need help is on the "structures" and "instances". There seems to be a whole bunch of black technologies here, but at the beginning, I would appreciate if you might teach me in a rather elementary way.
My goal is to prove the category of group actions has exponential (it may already be in mathlib, but even if it is the case, I just would like to re-prove it to get the practice).
The category has objects set with a right action, and a map is an -equivarient map.
Given such -sets, the exponential consists of the following information:
The set of equivarient maps , with the action map defined by , where is the map defined by , where is the product taken in the monoid .
Background:
I have read some chapters of Hitchhiker's Guide:
https://github.com/lean-forward/logical_verification_2024/blob/main/hitchhikers_guide_2024_desktop.pdf
and some of "mathematics in Lean". They are my main text references. But I have not tried all the exercises. In particular, I think things start getting complicated after chapter 7. Hopefully, I can get more sense of it by practicing . My memory is not very good so sorry if I forget things.
If you intend to help me better using analogy, thanks for your attempt! Comparision with HOL4, Isabelle, and Standard ML would make sense to me, but I would not understand it if you were speaking about Coq/Haskell/C/java/Python.etc, and programming languages in general(unless you are willing to teach me the background story as well.).
Helpful suggestions include, but are far from being limited to:
Alternative presentation of definitions/theorems (not necessarily better maybe, but let me see some alternative approaches.)
Specific tactics/functions that may be helpful(or just eye-opening for a beginner).
Point to similar examples that I can look up. (Ideally in "mathematics in Lean", but not necessary).
In MIL Chapter8 https://leanprover-community.github.io/mathematics_in_lean/C08_Groups_and_Rings.html I see the example:
variable {G : Type*} [Group G]
lemma conjugate_one (H : Subgroup G) : conjugate 1 H = H := by
sorry
instance : MulAction G (Subgroup G) where
smul := conjugate
one_smul := by
sorry
mul_smul := by
sorry
end GroupActions
where "Subgroup" is a "structure", not a "class". I will need to construct the type of all equivariant maps of an action, should it be a structure or a class? What are the differences if any?
Yiming Xu (Nov 08 2024 at 10:00):
I quickly wrote a piece of (wrong!) instance, and have question about it. here it is:
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ one_smul := sorry,
mul_smul := sorry}
It is wrong, because I should not use the whole type M × X -> Y
, but the subtype consisting only the equivariant maps instead.
Yiming Xu (Nov 08 2024 at 10:01):
I am curious that why Lean still parses it, the above piece of wrong code does not report any error. But here I even have not specified the action yet! It should report an error and ask me for the field smul
. What is Lean guessing in this case?
Yiming Xu (Nov 08 2024 at 10:07):
If I ignore the wrongness of this instance, and want to specify the smul, I get confused again. I wrote the smul in {}
, it asks for a term:
X : Type u_2
Y : Type u_3
M : Type u_1
inst✝² : Monoid M
inst✝¹ : MulAction M X
inst✝ : MulAction M Y
⊢ M → (M × X → Y) → M × X → Y
and I wrote:
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ ⟨fun (g,x) ↦ e (M.smul k g,x)⟩,
one_smul := sorry,
mul_smul := sorry}
I see it reports an error, as in:
image.png
Yiming Xu (Nov 08 2024 at 10:08):
May I please ask why is the error? Am I wrong with the syntax?
Ruben Van de Velde (Nov 08 2024 at 10:17):
Please share a #mwe with all code, including imports, that we need to see that error in the web editor
Yiming Xu (Nov 08 2024 at 10:18):
import Mathlib.Algebra.Group.Action.Defs
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ ⟨fun (g,x) ↦ e (M.smul k g,x)⟩,
one_smul := sorry,
mul_smul := sorry}
Yiming Xu (Nov 08 2024 at 10:18):
Thanks! This parses in the web editor, with an error that I would like to know the reason and fix.
Yiming Xu (Nov 08 2024 at 10:24):
Yiming Xu said:
I quickly wrote a piece of (wrong!) instance, and have question about it. here it is:
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):= { one_smul := sorry, mul_smul := sorry}
It is wrong, because I should not use the whole type
M × X -> Y
, but the subtype consisting only the equivariant maps instead.
I tried
import Mathlib.Algebra.Group.Action.Defs
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ one_smul := sorry,
mul_smul := sorry}
reports the error of missing smul, and I expected, in the web editor, but not in VScode. What happens there?
Yiming Xu (Nov 08 2024 at 11:26):
This one does not work
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (M.mul k g, x),
one_smul := sorry,
mul_smul := sorry}
Yiming Xu (Nov 08 2024 at 11:26):
But that one works.
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (@Mul.mul M _ k g, x),
one_smul := sorry,
mul_smul := sorry}
Yiming Xu (Nov 08 2024 at 11:26):
Anyone know why?
Kevin Buzzard (Nov 08 2024 at 11:33):
What's the error?
Kevin Buzzard (Nov 08 2024 at 11:34):
Why not just use k * g
?
Yiming Xu (Nov 08 2024 at 12:14):
Kevin Buzzard said:
What's the error?
Weird! The web version does not give any error for this piece of code
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (M.mul k g, x),
one_smul := sorry,
mul_smul := sorry}
but my local lean reports on the same piece of code reports:
invalid field notation, type is not of the form (C ...) where C is a constant
M
has type
Type u_1
.
Yiming Xu (Nov 08 2024 at 12:15):
Kevin Buzzard said:
Why not just use
k * g
?
Thanks! It simply works.
Yiming Xu (Nov 09 2024 at 13:10):
Another question: If I delete the {M : Type*}
from
instance X2Y {M : Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (k * g, x),
one_smul := sorry,
mul_smul := sorry}
then it does not report any error, so does it make any practical difference if I write {M : Type*}
in the statement or not? I guess maybe it only makes some difference if the type M
is required to be in some universe level, say, the level has to be non-zero?
Kevin Buzzard (Nov 09 2024 at 13:59):
Probably you have set_option autoImplicit
set to true
. You can just compare the types of the declarations before and after the change by hovering over X2Y
to see if there is any difference. Having autoImplicit true is a bit of a footgun so beware.
Yiming Xu (Nov 09 2024 at 14:07):
Thanks! I checked according your instruction just now.
Before the change, I have:
X2Y.{u_1, u_2, u_3} {X : Type u_2} {Y : Type u_3} {M : Type u_1} [Monoid M] [MulAction M X] [MulAction M Y] :
MulAction M (M × X → Y)
and after the change, I see:
X2Y.{u_1, u_2, u_3} {M : Type u_1} {X : Type u_2} {Y : Type u_3} [Monoid M] [MulAction M X] [MulAction M Y] :
MulAction M (M × X → Y)
Seems no difference except for the order of arguments.
But if I write set_option autoImplicit false
, then Lean is unhappy until I wrote:
instance X2Y {M : Type*} {X : Type*} {Y:Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (k * g, x),
one_smul := sorry,
mul_smul := sorry}
.
Yiming Xu (Nov 09 2024 at 14:08):
Kevin Buzzard said:
Probably you have
set_option autoImplicit
set totrue
. You can just compare the types of the declarations before and after the change by hovering overX2Y
to see if there is any difference. Having autoImplicit true is a bit of a footgun so beware.
I conceive it as "I should have autoImplicit false" so I should write like:
instance X2Y {M : Type*} {X : Type*} {Y:Type*} [Monoid M] [MulAction M X] [MulAction M Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (k * g, x),
one_smul := sorry,
mul_smul := sorry}
instead. Thank you!
Yiming Xu (Nov 09 2024 at 14:10):
It seems like MulAction is designed for left action, is there already something done with right action as well? Existing examples in mathlib would be very helpful.(Since the proof I wrote uses right action. )
Eric Wieser (Nov 09 2024 at 14:24):
For right actions, you can use MulAction (MulOpposite _) _
Yiming Xu (Nov 09 2024 at 14:31):
Thanks for the swift reply! Let me look it up.
Yiming Xu (Nov 10 2024 at 11:53):
I tried
import Mathlib.Algebra.Group.Action.Defs
instance X2Y {M : Type*} {X : Type*} {Y:Type*} [Monoid M] [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction M (M × X -> Y):=
{ smul:= fun k e ↦ fun (g,x) ↦ e (k * g, x),
one_smul := sorry,
mul_smul := sorry}
and it reports error under [MulAction (MulOpposite M) X]
.
Yiming Xu (Nov 10 2024 at 11:56):
It is complaining about
failed to synthesize
Monoid Mᵐᵒᵖ
I think I should use Monoid.toOpposite
for that, as in https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/Group/Action/Opposite.html#Monoid.toOppositeMulAction.
Kevin Buzzard (Nov 10 2024 at 11:57):
Yes, you're not importing enough. The problem with the Defs
files is that they contain only definitions, no theorems (like the theorem you need).
Kevin Buzzard (Nov 10 2024 at 11:58):
When developing, I just import Mathlib
and then cut things down at the end. With import Mathlib
your code works fine.
Yiming Xu (Nov 10 2024 at 11:59):
Ah yes after importing the error under [MulAction (MulOpposite M) X]
goes away. Thanks for the swift reply!
Yiming Xu (Nov 10 2024 at 11:59):
Kevin Buzzard said:
When developing, I just
import Mathlib
and then cut things down at the end. Withimport Mathlib
your code works fine.
Just tried it locally with import Mathlib
and it works fine.
Yiming Xu (Nov 10 2024 at 12:06):
In this case, there is obviously only one instance for M
to be a monoid, but M
can have multiple monoid structures, so there might be multiple instances. In the case that I want to provide the instance by hand, what should I write in ???
for the instance that M^mop
is a monoid?
variable {M : Type*} [Monoid M] {X : Type*}
#check @Monoid.toOppositeMulAction M ???
Does the instance [Monoid M]
has a name?
#check @Monoid.toOppositeMulAction M [Monoid M]
reports error.
Kevin Buzzard (Nov 10 2024 at 12:16):
Mathlib is designed with the assumption that a type will only have one monoid structure. If it has more than one then x * y
is ambiguous for x y : M
. There are ways to work around this but you'll have to say more about your use case. If you have multiple instances then basically all but the last one will be ignored.
Kevin Buzzard (Nov 10 2024 at 12:17):
If you want to give the instance a name you can name it with [h : Monoid M]
but the design of mathlib is such that this should not really be necessary most of the time. You can look in the infoview to see what the system calls it when you don't give it a name yourself, it's probably called _inst_37
or something.
Yiming Xu (Nov 10 2024 at 12:19):
Thanks for the elaboration! Great! I checked that
variable {M : Type*} [h:Monoid M] {X : Type*}
#check @Monoid.toOppositeMulAction M h
works. And the fact that there is only one instance explains my confusion that why instances do not have a name.
Yiming Xu (Nov 10 2024 at 12:20):
By "multiple structures on one type" I basically mean, e.g. can be a mulgroup or/and an addgroup. But I guess in this case we would have one instance for mulgroup and another instance for addgroup, instead of having two instances for the structure : group.
Kevin Buzzard (Nov 10 2024 at 12:21):
Yes that's right, Z is both a multiplicative and an additive monoid and this is of course important for mathematics, which is why we have both Monoid
and AddMonoid
.
Here are some other tricks:
import Mathlib
variable {M : Type*} [Monoid M] {X : Type*}
#check @Monoid.toOppositeMulAction M ‹Monoid M›
#check @Monoid.toOppositeMulAction M inferInstance
Yiming Xu (Nov 10 2024 at 12:25):
I see. Instead of giving it a name, I can just use the term ‹Monoid M›
for the instance term. (Aha, that looks similar to the counterpart in Isabelle: we have the option of giving it a name, or quote the statement in triangular brackets).
Mario Carneiro (Nov 10 2024 at 21:32):
fun fact, this syntax was borrowed from isabelle
Yiming Xu (Nov 11 2024 at 11:34):
Mario Carneiro said:
fun fact, this syntax was borrowed from isabelle
Funny! Then maybe also the "from" syntax.
Yiming Xu (Nov 11 2024 at 13:50):
Had some hard time trying to get the "MulOpposite" correct.
Bugged a colleague and got a pieces of suggestion:
1.Helpful to prove lemmas about the operations like:
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e (k * g, x)
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : M) (e : M × X → Y) : k • e = fun (g,x) ↦ e (k * g, x) :=by rfl
and write smul:= Test.smul.smul
as the field, instead of expanding the explicit definition.
2.If I have variables and instances declared as
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
then I should omit them from my definition X2Y
, and have:
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction (MulOpposite M) (M × X -> Y)
instead.
I have trouble getting the MulOpposite work. Recall I want to act on maps as: for and , , where is taken in (instead of ), as a right action.
I want to write:
import Mathlib
set_option autoImplicit false
set_option diagnostics true
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e (k * g, x)
Lean complains and tells me:
failed to synthesize
HMul Mᵐᵒᵖ M ?m.546
Seems that it is asking for an instance for ?
But for the e (k * g, x)
, I do not want it to be taken in , but in instead. How should I tell Lean?
Yiming Xu (Nov 11 2024 at 13:52):
The broken attempt up to now:
import Mathlib
set_option autoImplicit false
set_option diagnostics true
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e (k * g, x)
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : M) (e : M × X → Y) : k • e = fun (g,x) ↦ e (k * g, x) :=by rfl
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction (MulOpposite M) (M × X -> Y):=
{ smul:= Test.smul.smul
one_smul := by
intro b
rw [Test.smul_def]
simp
mul_smul := by
intros k1 k2 e
simp_rw [Test.smul_def]
ext x
congr 2
ac_rfl}
Kevin Buzzard (Nov 11 2024 at 15:40):
op : α → αᵐᵒᵖ
and unop : αᵐᵒᵖ → α
are the maps you need to use (in the MulOpposite
namespace).
Kevin Buzzard (Nov 11 2024 at 15:42):
import Mathlib
set_option autoImplicit false
set_option diagnostics true
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
open MulOpposite -- make namespace available
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e (unop k * g, x)
Kevin Buzzard (Nov 11 2024 at 15:43):
You shouldn't think of Mᵐᵒᵖ
and M
as being equal. If they were, then a * b
would be ambiguous. You need to pass between them using functions. They encode the same information in Lean, but they are not equal.
Yiming Xu (Nov 11 2024 at 15:45):
Thanks a lot! I see. You have just told me that there can be only one instance for one underlying type. op
passes to another monoid structure, so we have to change the underlying type as well, otherwise, it does not even get type-checked.
Yiming Xu (Nov 11 2024 at 15:49):
But let me check if this is what I want...
I think so. Now k
is now from MulOpposite M
, and what we are taking is unop k * g
, it is sends k
back to M
and then multiply with g
in M.
Okay nice!
Yiming Xu (Nov 11 2024 at 15:50):
Then the lemma should be
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e : M × X → Y) : k • e = fun (g,x) ↦ e ( (unop k) * g, x) :=by rfl
Yiming Xu (Nov 11 2024 at 15:52):
And here is the instance:
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction (MulOpposite M) (M × X -> Y):=
{ smul:= Test.smul.smul
one_smul := by
intro b
rw [Test.smul_def]
simp
mul_smul := by
intros k1 k2 e
simp_rw [Test.smul_def]
ext x
congr 2
simp
ac_rfl }
Kevin Buzzard (Nov 11 2024 at 16:02):
Here is a golfed version where I get simp
to do all the work:
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] :
MulAction (MulOpposite M) (M × X -> Y) where
smul := Test.smul.smul
one_smul b := by
simp [Test.smul_def]
mul_smul k1 k2 e := by
ext ⟨m, x⟩
simp [Test.smul_def, mul_assoc]
Yiming Xu (Nov 11 2024 at 16:05):
Nice! From here I also see the {}
is not necessary, and I can give arguments for one_smul to get rid of the intros.
Kevin Buzzard (Nov 11 2024 at 16:49):
In fact I had to get rid of the {}
s to use the arguments trick :-(
Eric Wieser (Nov 11 2024 at 16:57):
Yiming Xu said:
You have just told me that there can be only one instance for one underlying type
Note that you unfortunately have a new problem here: mathlib already provide a solution to MulAction (MulOpposite M) (M × X -> Y)
, which you can find as Pi.mulAction Mᵐᵒᵖ
:
-- put this before X2Y
variable [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] in
#synth MulAction (MulOpposite M) (M × X -> Y)
Unfortunately Pi.mulAction Mᵐᵒᵖ ≠ X2Y
in general, so now you have the same issue you had with worrying about multiple monoid structures on M
Yiming Xu (Nov 11 2024 at 19:06):
Kevin Buzzard said:
In fact I had to get rid of the
{}
s to use the arguments trick :-(
I see. That is good to know.
Yiming Xu (Nov 11 2024 at 19:08):
Eric Wieser said:
Yiming Xu said:
You have just told me that there can be only one instance for one underlying type
Note that you unfortunately have a new problem here: mathlib already provide a solution to
MulAction (MulOpposite M) (M × X -> Y)
, which you can find asPi.mulAction Mᵐᵒᵖ
:-- put this before X2Y variable [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] in #synth MulAction (MulOpposite M) (M × X -> Y)
Unfortunately
Pi.mulAction Mᵐᵒᵖ ≠ X2Y
in general, so now you have the same issue you had with worrying about multiple monoid structures on M
Wow that seems fatal... So maybe I can try to, instead, create a type class of right action instead? Or is there any suggestions on how would I solve this clash?
Kevin Buzzard (Nov 11 2024 at 19:29):
The issue is not left/right action, the issue is that mathlib has taken the decision that if G acts on X, then G acts on functions from anything to X in the obvious way. If you want to make G act on functions in a different way, then you need to give your function space a new name. Giving it a new name will stop typeclass inference (the system which is finding all the actions) from finding the mathlib standard axiom.
Kevin Buzzard (Nov 11 2024 at 19:31):
import Mathlib
variable (x : ℤˣ) (f : ℕ → ℤ)
#check x • f -- action defined by obvious action on the target
def NewType := ℕ → ℤ -- `NewType` is *equal* to `ℕ → ℤ`
variable (g : NewType)
#check x • g -- error
Notification Bot (Nov 11 2024 at 19:33):
rzeta0 has marked this topic as resolved.
Kevin Buzzard (Nov 11 2024 at 19:33):
Equality is a very subtle concept in a theorem prover. NewType
and ℕ → ℤ
are equal, but they could be more equal! In particular typeclass inference doesn't notice that they are equal. So now you are free to define any action of ℤˣ
on NewType
that you like, without any problems.
Notification Bot (Nov 11 2024 at 19:33):
rzeta0 has marked this topic as unresolved.
Yiming Xu (Nov 11 2024 at 19:37):
Kevin Buzzard said:
Equality is a very subtle concept in a theorem prover.
NewType
andℕ → ℤ
are equal, but they could be more equal! In particular typeclass inference doesn't notice that they are equal. So now you are free to define any action ofℤˣ
onNewType
that you like, without any problems.
So it reports an error because it does not find any instance on NewType
, I see. But in which sense are they more equal
?
Yiming Xu (Nov 11 2024 at 19:39):
[I just recalled that I want my action not on the whole ! Only on the set of -equivariant maps, hopefully Lean do not have already an instance on this set... But that is important to know how to resolve the clash.]
Kevin Buzzard (Nov 11 2024 at 19:59):
Aah, but this is great: if you only care about M-equivariant maps then your problem is solved because that definitely won't be equal to the space of all maps.
To learn about the levels of equality being talked about here, you need to understand when Lean unfolds a definition. I can't remember the exact definitions, but I know that typeclass inference will not unfold a def
but will unfold an abbrev
.
import Mathlib
variable (x : ℤˣ) (f : ℕ → ℤ)
#check x • f -- action defined by obvious action on the target
abbrev NewType := ℕ → ℤ -- now they are a bit more equal
variable (g : NewType)
#check x • g -- no error
I think there are three levels of reducibility for definitions: reducible, semireducible and irreducible, and different parts of the system will check different levels of reducibility. This is some technicality about how the system works internally which I never learnt (or more likely, learnt and then forgot, and I think it might even have changed recently). But the thing to remember is that typeclass inference sees through abbrevs and not through defs, this is what I use in practice when writing Lean code, and it informs which of the two choices I want to make for a new definition.
Yiming Xu (Nov 11 2024 at 20:57):
That makes a lot of sense! In Isabelle, abbrev is purely notational, i.e. for pretty-printing only. The parser conceive them as the same, so, if we write abbrev A = B, then the parser really read A as B, and the kernel that manipulates the logic never know what A is, because it get access to B only, and never sees A. A can only be seen by the parser, so it is impossible to "unfold" A.
I guess maybe Lean does something similar, so abbrev is just notational. Unfortunately I cannot read C and hence the source code of Lean...
Eric Wieser (Nov 11 2024 at 21:07):
Kevin Buzzard said:
If you want to make G act on functions in a different way, then you need to give your function space a new name
Or give G
a new name. It would be tempting to spell your action MulAction (DomMulAct (Prod.FstAct M) (M × X -> Y)
instead, where you would then need to implement MulAction (Prod.FstAct M) (M × X)
which would give you the instance you want for free.
But this then runs into trouble when (Edit: nevermind, this can't actually happen in this specific case). There is a short section in my thesis about the troubles that arise with docs#DomMulAct and similar patterns, but none of the solutions are very appealing.X := M × M
, because this then conflicts with the instance saying that actions on products are induced by actions on both elements...
Yiming Xu (Nov 12 2024 at 17:35):
Eric Wieser said:
Kevin Buzzard said:
If you want to make G act on functions in a different way, then you need to give your function space a new name
Or give
G
a new name. It would be tempting to spell your actionMulAction (DomMulAct (Prod.FstAct M) (M × X -> Y)
instead, where you would then need to implementMulAction (Prod.FstAct M) (M × X)
which would give you the instance you want for free.
But this then runs into trouble when(Edit: nevermind, this can't actually happen in this specific case). There is a short section in my thesis about the troubles that arise with docs#DomMulAct and similar patterns, but none of the solutions are very appealing.X := M × M
, because this then conflicts with the instance saying that actions on products are induced by actions on both elements...
Do you mean like this?
import Mathlib
variable (x : ℤˣ) (f : ℕ → ℤ)
#check x • f -- action defined by obvious action on the target
def NewType := ℤˣ -- `NewType` is *equal* to `ℕ → ℤ`
variable (x : NewType)
#check x • f -- error
Yiming Xu (Nov 12 2024 at 17:36):
But what is the DomMulAct
and Prod.FstAct
stuff? I am afraid I do not get this part.
Yiming Xu (Nov 12 2024 at 17:49):
For another question I am confused: The following code does not have any error:
import Mathlib
set_option autoImplicit false
set_option diagnostics true
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
open MulOpposite -- make namespace available
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e (unop k * g, x)
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e : M × X → Y) : k • e = fun (g,x) ↦ e ( (unop k) * g, x) :=by rfl
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction (MulOpposite M) (M × X -> Y):=
{ smul:= fun k ↦ fun e↦ fun (g,x) ↦ e ( (unop k) * g, x)
one_smul := by
intro b
rw [Test.smul_def]
simp
mul_smul := by
intros k1 k2 e
simp_rw [Test.smul_def]
ext x
congr 2
simp
ac_rfl }
My questions are:
1.Note that the definition of smul
is written by hand here, why does the rw [Test.smul_def]
give the rw? I did not tell Lean that the dot here is the smul above. Is it again because it only accepts one structure, and just found the one above?
2.If I omit the definition of Test.smul and Test.smul_def, and want to just write something like (note that the tactic does not work anymore), what should I type after intro b
to unfold 1 • b = b
, where \cdot
is defined as fun k ↦ fun e↦ fun (g,x) ↦ e ( (unop k) * g, x)
. (Bugged a colleague in Lean, tried that simp[ (_ \cdot _)] does not work.)
import Mathlib
set_option autoImplicit false
set_option diagnostics true
variable {M : Type*} [Monoid M] {X : Type*} {Y:Type*}
open MulOpposite -- make namespace available
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] : MulAction (MulOpposite M) (M × X -> Y):=
{ smul:= fun k ↦ fun e↦ fun (g,x) ↦ e ( (unop k) * g, x)
one_smul := by
intro b
rw [Test.smul_def]
simp
mul_smul := by
intros k1 k2 e
simp_rw [Test.smul_def]
ext x
congr 2
simp
ac_rfl }
Yiming Xu (Nov 15 2024 at 09:59):
I think I have difficulty defining the action only on equivariant maps. I have tried asking around but have not found a satisfactory solution. Firstly, code up to now, for copying and pasting:
import Mathlib
set_option autoImplicit false
set_option diagnostics true
#check MulOpposite
variable (M : Type*) [Monoid M] {X : Type*} {Y:Type*}
--#check @Monoid.toOppositeMulAction M h
#check ‹Monoid M›
#check Mul (Mᵐᵒᵖ)
#check @Monoid.toOppositeMulAction M ‹Monoid M›
#check @Monoid.toOppositeMulAction M inferInstance
open MulOpposite
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e ((unop k) * g, x)
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e : M × X → Y) : k • e = fun (g,x) ↦ e ( (unop k) * g, x) :=by rfl
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] :
MulAction (MulOpposite M) (M × X -> Y) where
smul := (Test.smul M).smul
one_smul b := by
simp [Test.smul_def]
mul_smul k1 k2 e := by
ext ⟨m, x⟩
simp [Test.smul_def,mul_assoc]
/-
class MulAction (α : Type*) (β : Type*) [Monoid α] extends SMul α β where
/-- One is the neutral element for `•` -/
protected one_smul : ∀ b : β, (1 : α) • b = b
/-- Associativity of `•` and `*` -/
mul_smul : ∀ (x y : α) (b : β), (x * y) • b = x • y • b
-/
/-
1. is it fine to declare variable and use this to declare a class?
2.class vs structure, which one to choose?
-/
class Equivariant (E: Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E X Y] where
comm_square : ∀ (f: E) (x : X) (m : Mᵐᵒᵖ), m • f x = f (m • x)
@[ext]
structure Eqvariant_struct (X : Type*) (Y:Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] where
toFun: X → Y
comm_square : ∀ (x : X) (m : Mᵐᵒᵖ), m • (toFun x) = toFun (m • x)
variable (f:X → Y)
instance Test.smul1 [MulAction (MulOpposite M) X]: SMul (MulOpposite M) (M × X) where
smul a := fun ⟨m, x⟩ ↦ (a • m, a • x)
instance Msmul : SMul (MulOpposite M) M where
smul m0 m := m * (unop m0)
theorem Msmul_def (m0 : MulOpposite M) (m : M) : m0 • m = m * (unop m0) :=by rfl
instance : MulAction (MulOpposite M) M where
smul m0 m:= m * (unop m0)
one_smul := by simp[Msmul_def]
mul_smul m1 m2 m := by
simp[Msmul_def,mul_assoc]
theorem Test.smul1_def
[MulAction (MulOpposite M) X]
(a: MulOpposite M) (mx : M× X): (a • mx = (a • mx.1, a • mx.2)) := by rfl
instance foo [MulAction (MulOpposite M) X] : MulAction (MulOpposite M) (M × X) where
smul a:= (Test.smul1 M).smul a
one_smul b := by simp [Test.smul1_def]
mul_smul k1 k2 mx := by
simp [Test.smul1_def]
simp [mul_assoc, mul_smul k1 k2 mx.2]
/-action on equivariant function e: M × X → Y by
class Equivariant (E: Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E X Y] where
comm_square : ∀ (f: E) (x : X) (m : Mᵐᵒᵖ), m • f x = f (m • x)
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e ((unop k) * g, x)
always give an equivariant function
-/
def smul_eqv [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : (MulOpposite M))
(e: Eqvariant_struct M (M × X) Y)
: Eqvariant_struct M (M × X) Y where
toFun := k • e.toFun
comm_square mx m0 := by
cases mx with
| mk m x =>
--want m0 • ((k • e.toFun) (m, x)) = (k • e.toFun) (m0 • (m, x))
--the left m0 acts on the element in Y. unfold the definition of k on e first
simp[Test.smul_def]
-- reduce to m0 • e.toFun (unop k * m, x) = e.toFun (unop k * (m * unop m0), m0 • x)
-- e is equivariant says m0 • e.toFun (unop k * m, x) is equal to the thing
-- when we move the m0 to act on the pair first, invoke comm_square on this pair
simp [e.comm_square (unop k * m, x) m0]
--e.toFun (unop k * m * unop m0, m0 • x) = e.toFun (unop k * (m * unop m0), m0 • x)
--curiously, automatically unfold the action of m0 is pairewise
simp[mul_assoc]
/-
lemma Action_Equivariant (E: Type*)
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E (M × X) Y]
(f: E) :
-/
instance [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
: MulAction (MulOpposite M) (Eqvariant_struct M (M × X) Y) where
smul k e := smul_eqv M k e
one_smul b:= by
ext mx
cases' mx with m x --put cursor here to see my issue
simp[smul_eqv]
I am on my way to construct the group action on the structures of equivariant maps. I am stuck on the goal looks like:
M : Type u_1
inst✝² : Monoid M
X : Type u_2
Y : Type u_3
f : X → Y
inst✝¹ : MulAction Mᵐᵒᵖ X
inst✝ : MulAction Mᵐᵒᵖ Y
b : Eqvariant_struct M (M × X) Y
m : M
x : X
⊢ (1 • b).toFun (m, x) = b.toFun (m, x)
Here 1 \smul b
is not parsed as a function M \times X \to Y
, but as a structure, and I would like to tell Lean to unfold the definition of the action on maps, so the LHS is equal to b.toFun (1 * m, x)
. May I please ask how would I do it?
Yiming Xu (Nov 15 2024 at 12:33):
I believe I have solved it. But more help is needed for the exponential proof. I will ask in more detail in a separate post. (okay, maybe I am over-confident?)
Eric Wieser (Nov 15 2024 at 12:34):
You shouldn't need
instance Msmul : SMul (MulOpposite M) M where
smul m0 m := m * (unop m0)
this is builtin to mathlib
Yiming Xu (Nov 15 2024 at 12:35):
Let me try to delete it.
Yiming Xu (Nov 15 2024 at 12:35):
You are right. Thank you!
Yiming Xu (Nov 15 2024 at 12:37):
I will have to prove the construction makes equivariant maps an exponential.
Yiming Xu (Nov 15 2024 at 12:38):
Is Cartesian.lean the correct file to look at?
Eric Wieser (Nov 15 2024 at 12:38):
I think your Equivariant
is docs#MulActionHom with φ := id
Yiming Xu (Nov 15 2024 at 12:39):
import Mathlib
set_option autoImplicit false
set_option diagnostics true
#check MulOpposite
variable (M : Type*) [Monoid M] {X : Type*} {Y:Type*}
--#check @Monoid.toOppositeMulAction M h
#check ‹Monoid M›
#check Mul (Mᵐᵒᵖ)
#check @Monoid.toOppositeMulAction M ‹Monoid M›
#check @Monoid.toOppositeMulAction M inferInstance
open MulOpposite
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e ((unop k) * g, x)
theorem Test.smul_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e : M × X → Y) : k • e = fun (g,x) ↦ e ( (unop k) * g, x) :=by rfl
instance X2Y [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] :
MulAction (MulOpposite M) (M × X -> Y) where
smul := (Test.smul M).smul
one_smul b := by
simp [Test.smul_def]
mul_smul k1 k2 e := by
ext ⟨m, x⟩
simp [Test.smul_def,mul_assoc]
/-
class MulAction (α : Type*) (β : Type*) [Monoid α] extends SMul α β where
/-- One is the neutral element for `•` -/
protected one_smul : ∀ b : β, (1 : α) • b = b
/-- Associativity of `•` and `*` -/
mul_smul : ∀ (x y : α) (b : β), (x * y) • b = x • y • b
-/
/-
1. is it fine to declare variable and use this to declare a class?
2.class vs structure, which one to choose?
-/
class Equivariant (E: Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E X Y] where
comm_square : ∀ (f: E) (x : X) (m : Mᵐᵒᵖ), m • f x = f (m • x)
@[ext]
structure Eqvariant_struct (X : Type*) (Y:Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] where
toFun: X → Y
comm_square : ∀ (x : X) (m : Mᵐᵒᵖ), m • (toFun x) = toFun (m • x)
variable (f:X → Y)
instance Test.smul1 [MulAction (MulOpposite M) X]: SMul (MulOpposite M) (M × X) where
smul a := fun ⟨m, x⟩ ↦ (a • m, a • x)
theorem Test.smul1_def
[MulAction (MulOpposite M) X]
(a: MulOpposite M) (mx : M× X): (a • mx = (a • mx.1, a • mx.2)) := by rfl
instance foo [MulAction (MulOpposite M) X] : MulAction (MulOpposite M) (M × X) where
smul a:= (Test.smul1 M).smul a
one_smul b := by simp [Test.smul1_def]
mul_smul k1 k2 mx := by
simp [Test.smul1_def]
simp [mul_assoc, mul_smul k1 k2 mx.2]
/-action on equivariant function e: M × X → Y by
class Equivariant (E: Type*) [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E X Y] where
comm_square : ∀ (f: E) (x : X) (m : Mᵐᵒᵖ), m • f x = f (m • x)
instance Test.smul : SMul (MulOpposite M) (M × X → Y) where
smul k e := fun (g,x) ↦ e ((unop k) * g, x)
always give an equivariant function
-/
def smul_eqv [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : (MulOpposite M))
(e: Eqvariant_struct M (M × X) Y)
: Eqvariant_struct M (M × X) Y where
toFun := k • e.toFun
comm_square mx m0 := by
cases mx with
| mk m x =>
--want m0 • ((k • e.toFun) (m, x)) = (k • e.toFun) (m0 • (m, x))
--the left m0 acts on the element in Y. unfold the definition of k on e first
simp[Test.smul_def]
-- reduce to m0 • e.toFun (unop k * m, x) = e.toFun (unop k * (m * unop m0), m0 • x)
-- e is equivariant says m0 • e.toFun (unop k * m, x) is equal to the thing
-- when we move the m0 to act on the pair first, invoke comm_square on this pair
simp [e.comm_square (unop k * m, x) m0]
--e.toFun (unop k * m * unop m0, m0 • x) = e.toFun (unop k * (m * unop m0), m0 • x)
--curiously, automatically unfold the action of m0 is pairewise
simp[mul_assoc]
/-
lemma Action_Equivariant (E: Type*)
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] [FunLike E (M × X) Y]
(f: E) :
-/
theorem smul_eqv_def_toFun
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e: Eqvariant_struct M (M × X) Y) :
k • e.toFun = fun (g,x) ↦ e.toFun ( (unop k) * g, x) :=by rfl
instance [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] :
SMul (MulOpposite M) (Eqvariant_struct M (M × X) Y) where
smul k e := smul_eqv M k e
theorem smul_eqv_def
[MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
(k : MulOpposite M) (e: Eqvariant_struct M (M × X) Y) :
k • e = smul_eqv M k e :=by rfl
instance [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
: MulAction (MulOpposite M) (Eqvariant_struct M (M × X) Y) where
smul k e := smul_eqv M k e
one_smul b:= by
ext mx
cases' mx with m x
rw [smul_eqv_def, smul_eqv]
simp
mul_smul := by
intros m m' e
repeat rw [smul_eqv_def, smul_eqv]
ext ⟨m, x⟩
simp
repeat rw [Test.smul_def]
simp[mul_assoc]
Things up to now.
Yiming Xu (Nov 15 2024 at 12:41):
Eric Wieser said:
I think your
Equivariant
is docs#MulActionHom withφ := id
Thanks and let me take a look! Maybe I should try to use it to get myself familiarize with how to connect to mathlib before going further.
Yiming Xu (Nov 15 2024 at 12:48):
I agree with you on that. Thanks for the information!
Yiming Xu (Nov 15 2024 at 12:58):
I would like to make an abbreviation to for the maps I want. Why is:
abbrev Equivariant [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] :=
MulActionHom id X Y
not working?
Yiming Xu (Nov 15 2024 at 13:01):
I would like to make MulAction a category, how would we call the whole collection of MulActions?
Eric Wieser (Nov 15 2024 at 13:09):
Yiming Xu said:
I would like to make an abbreviation to for the maps I want. Why is:
abbrev Equivariant [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y] := MulActionHom id X Y
not working?
This doesn't work because you need to write (@id (MulOpposite M))
instead of id
; but mathlib already has notation for this, X →[MulOpposite M] Y
Yiming Xu (Nov 15 2024 at 13:17):
So given X and Y types and instances that X and Y has actions, then X →[MulOpposite M] Y
is the type for all equivariant maps between X and Y?
Yiming Xu (Nov 15 2024 at 13:19):
I see, I can check it by
variables [MulAction (MulOpposite M) X] [MulAction (MulOpposite M) Y]
#check (X →[MulOpposite M] Y)
Yiming Xu (Nov 15 2024 at 13:19):
Is there also a notion of "the category of actions" existing in mathlib already?
Eric Wieser (Nov 15 2024 at 14:25):
docs#Action might be what you want
Yiming Xu (Nov 15 2024 at 20:04):
Thank you! This is very helpful. I think what I should investigate is:
#check Action (Type u_1) (MonCat.of (MulOpposite M))
where the Type u_1
is often left out as _
.
Please correct me if I am wrong!
Yiming Xu (Nov 15 2024 at 20:10):
Why do I have a red underscore under Category
reporting error when I type:
#check Category (Action (Type u_1) (MonCat.of (MulOpposite M)))
Lean complains:
unknown identifier 'Category'
But I have already imported mathlib.
Yaël Dillies (Nov 15 2024 at 20:12):
Have you done open CategoryTheory
?
Yiming Xu (Nov 15 2024 at 20:13):
Yaël Dillies said:
Have you done
open CategoryTheory
?
No! Thank you! After opening CategoryTheory it works!
Yiming Xu (Nov 15 2024 at 20:14):
May I please ask if there exists any example proving some category has exponentials? The definition of exponential is very nested.
Yiming Xu (Nov 15 2024 at 20:26):
Confused again, I think I should start from:
instance HasFiniteProducts (Category (Action (Type u_1) (MonCat.of (MulOpposite M)))) := sorry
Lean refused to accept the statement. I wrote:
import Mathlib
open CategoryTheory
variables {M : Type*} [Monoid M]
instance HasFiniteProducts (Category (Action (Type u_1) (MonCat.of (MulOpposite M)))) := sorry
And it complains
unexpected token '('; expected ')'
Yiming Xu (Nov 15 2024 at 20:26):
I believe my parentheses match up...
Yaël Dillies (Nov 15 2024 at 20:30):
You should replace instance HasFiniteProducts ...
with instance : HasFiniteProducts ...
Yiming Xu (Nov 15 2024 at 20:32):
Thanks for the swift reply! And good point for mentioning the ":", this is not the first time I missed it...
Tried in web Lean. It is still complaining if I have:
import Mathlib
open CategoryTheory
variables {M : Type*} [Monoid M]
instance : HasFiniteProducts (Category (Action (Type u_1) (MonCat.of (MulOpposite M)) )) := sorry
Yiming Xu (Nov 15 2024 at 20:32):
Edward van de Meent (Nov 15 2024 at 20:34):
use set_option autoImplicit false
Yaël Dillies (Nov 15 2024 at 20:34):
You don't have to copy-paste the link to the playground :wink: If you hover over a code block on Zulip, you will see
image.png
Edward van de Meent (Nov 15 2024 at 20:36):
looking at docs#CategoryTheory.Limits.HasFiniteProducts , it seems you're missing an open Limits
Yiming Xu (Nov 15 2024 at 20:36):
Edward van de Meent said:
use
set_option autoImplicit false
Let me try.
Yiming Xu (Nov 15 2024 at 20:36):
Yaël Dillies said:
You don't have to copy-paste the link to the playground :wink: If you hover over a code block on Zulip, you will see
image.png
Wow that's good to know!
Edward van de Meent (Nov 15 2024 at 20:37):
additionally, you don't need the Category
inside the HasFiniteProducts
Yiming Xu (Nov 15 2024 at 20:37):
Edward van de Meent said:
looking at docs#CategoryTheory.Limits.HasFiniteProducts , it seems you're missing an
open Limits
Still complaining.
import Mathlib
open CategoryTheory
open Limits
set_option autoImplicit false
variables {M : Type*} [Monoid M]
instance : HasFiniteProducts (Category (Action (Type u_1) (MonCat.of (MulOpposite M)) )) := sorry
BTW Limits live in CategoryTheory, so I assume if I open CategoryTheory, then I open everything there. Is that wrong?
Edward van de Meent (Nov 15 2024 at 20:38):
that is indeed wrong. Limits
is a namespace inside the CategoryTheory
namespace
Yiming Xu (Nov 15 2024 at 20:38):
Edward van de Meent said:
looking at docs#CategoryTheory.Limits.HasFiniteProducts , it seems you're missing an
open Limits
Oh this takes me back to the world of love and peace. Silly me. Lean is happy now.
Yiming Xu (Nov 15 2024 at 20:39):
Edward van de Meent said:
that is indeed wrong.
Limits
is a namespace inside theCategoryTheory
namespace
So, once we have nested namespace, we need to open them layer by layer?
Yiming Xu (Nov 15 2024 at 20:40):
I tried. Discovered that I cannot delete any line of:
open CategoryTheory
open Limits
Edward van de Meent (Nov 15 2024 at 20:40):
you can smush them together tho: open CategoryTheory Limits
Yiming Xu (Nov 15 2024 at 20:41):
Tried! Thanks. And the order matters. Have to do it like opening a Matryoshka.
Last updated: May 02 2025 at 03:31 UTC