Zulip Chat Archive

Stream: new members

Topic: Finiteness of quotients


Christian Merten (Sep 29 2023 at 23:45):

I am trying to show that if the set of equivalence classes of an equivalence relation r : Setoid α (in the sense of Setoid.classes r) is finite, then the quotient is finite. For this I hoped to use some bijection or equivalence between the quotient and the set of equivalence classes, but I can't find anything in this direction.

Adam Topaz (Sep 29 2023 at 23:52):

I suppose you could look around docs#Setoid.classes

Adam Topaz (Sep 29 2023 at 23:53):

But having said that, if you’re using equivalence classes then it’s likely that you’re trying to do something in a suboptimal way. Can you un - #xy ?

Christian Merten (Sep 29 2023 at 23:59):

I want to show that in a compact topological group G, any open subgroup U has finite index. This is because the cosets of U form a disjoint open cover of G. Then by compactness, one obtains that the number of cosets is finite. But now I somehow have to say that the number of cosets, aka cardinality of Setoid.classes (QuotientGroup.leftRel U), is the cardinality of G / U.

Kevin Buzzard (Sep 30 2023 at 05:51):

What is your definition of index?

Every definition matters in lean. In maths we just say "the following five things are equal and they're all called the index". In lean it doesn't work like that, you have to say much more precisely what you mean. The best way to ask your question is not to post what you've written above, but to post something called an #mwe (this is a link explaining what I mean). Then it's much easier to help you. It's work writing a MWE but it's work well spent because it enables people to quickly answer your actual precise question as well as being able to comment on design decisions. You've chosen a very nice mathematical question to work on but now we need to see the details.

Christian Merten (Sep 30 2023 at 06:54):

I use the definition in Mathlib.GroupTheory.Index. The idea was to use Subgroup.index_ne_zero_of_finite, so I only need to show Finite (G / U). Here is a mwe:

import Mathlib.GroupTheory.Index
import Mathlib.Topology.Algebra.Group.Basic
import Mathlib.Data.Setoid.Partition

universe u

section Partition

variable {α : Type*} [TopologicalSpace α] [CompactSpace α]

lemma IsCompact.openPartition_finite (c : Set (Set α))
    (hp : Setoid.IsPartition c) (hU :  s  c, IsOpen s) : c.Finite :=
  -- I have a proof for this, deduced from IsCompact.elim_finite_subcover
  sorry

end Partition

variable {G : Type u} [Group G] [TopologicalSpace G] [TopologicalGroup G]
  [CompactSpace G]

example (U : Subgroup G) (U_open : IsOpen (U : Set G)) : U.index  0 := by
  have : Finite (G  U) := by
    let A : Set (Set G) := Setoid.classes (QuotientGroup.leftRel U)
    have A_partition : Setoid.IsPartition A := Setoid.isPartition_classes (QuotientGroup.leftRel U)
    have A_open :  s  A, IsOpen (s : Set G) := sorry
    have A_finite : A.Finite := IsCompact.openPartition_finite A A_partition A_open
    admit
  exact Subgroup.index_ne_zero_of_finite

Maybe one can try building an indexed partition and use docs#IndexedPartition.equivQuotient

Christian Merten (Sep 30 2023 at 07:47):

One can certainly construct an IndexedPartition P using the noncomputable docs#IndexedPartition.mk' and one then obtains an equivalence A ≃ IndexedPartition.Quotient P. But now I still need to have some equivalence from the actual quotient G / U and IndexedPartition.Quotient P.

Kevin Buzzard (Sep 30 2023 at 08:34):

Yeah it seems like there's not much API for Setoid.classes. That file was initially written by Amelia Livingston, who was at the time an undergraduate at Imperial (maybe 2019?), because I assured her that the relationship between equivalence classes and partitions was fundamental to mathematics (why else would I spend an entire hour proving this to 1st year undergraduates every year?). To my great surprise this file got very little use; even now, according to https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Setoid/Partition.html , it is only imported by one other file (in graph theory). So it looks like you found a hole in the API (which should be filled). In fact what is fundamental to mathematics is the universal property of quotients, it turns out.

Christian Merten (Sep 30 2023 at 08:37):

Do you think it is a good idea to try to establish an equivalence between Quotient s and Setoid.classes s?

Eric Wieser (Sep 30 2023 at 08:38):

I'm pretty sure that exists, because I remember Kevin PRing a docstring for it!

Christian Merten (Sep 30 2023 at 08:40):

@loogle Quotient, Setoid.classes

Kevin Buzzard (Sep 30 2023 at 08:41):

I can't find it. Modulo that, you're done:

section maybe_missing_api

def Setoid.classes_equiv_quotient {α : Type*} (r : Setoid α) : Setoid.classes r  Quotient r := sorry

end maybe_missing_api


variable {G : Type u} [Group G] [TopologicalSpace G] [TopologicalGroup G]
  [CompactSpace G]

example (U : Subgroup G) (U_open : IsOpen (U : Set G)) : U.index  0 := by
  have : Finite (G  U) := by
    let A : Set (Set G) := Setoid.classes (QuotientGroup.leftRel U)
    have A_partition : Setoid.IsPartition A := Setoid.isPartition_classes (QuotientGroup.leftRel U)
    have A_open :  s  A, IsOpen (s : Set G) := sorry
    have A_finite : A.Finite := IsCompact.openPartition_finite A A_partition A_open
    rwa [ Set.finite_coe_iff,
         Equiv.finite_iff (QuotientGroup.leftRel U).classes_equiv_quotient] at A_finite
  exact Subgroup.index_ne_zero_of_finite

Christian Merten (Sep 30 2023 at 08:42):

Yes, thats what I was aiming for.

Kevin Buzzard (Sep 30 2023 at 08:43):

I don't know why loogle isn't responding here but the web version is reporting 0 matches

Eric Wieser (Sep 30 2023 at 08:46):

Maybe we lost it in the port?

Kevin Buzzard (Sep 30 2023 at 08:47):

Ask Jeremy to look under the benches at the Hoskinson

Kevin Buzzard (Sep 30 2023 at 08:49):

I PR'ed this docstring:

/-- The order-preserving bijection between equivalence relations on a type `α`, and
  partitions of `α` into subsets. -/
protected def Partition.orderIso : Setoid α o { C : Set (Set α) // IsPartition C }
...

Kyle Miller (Sep 30 2023 at 08:50):

@Kevin Buzzard In graph theory, the way Setoid.classes pops up is in going from a graph coloring to a graph partition (docs#SimpleGraph.Coloring.toPartition). The difference between a coloring and a partition is that a coloring is a labeled partition, or conversely a partition is a coloring modulo the symmetry group for the colors. Maybe it's telling that Mathlib.Combinatorics.SimpleGraph.Partition isn't imported by anything yet... In any case, this gives a nice construction to remove unused colors via an adjunction between colorings and partitions (you re-color the graph using the color classes themselves as colors).

Christian Merten (Sep 30 2023 at 08:52):

So I suppose it does not exist yet, so I try to write a version myself.

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

It will be fiddly :-/ Get the maps right and hope aesop proves the diagrams commute.

Christian Merten (Sep 30 2023 at 10:00):

I wrote something down using the universal property of the quotient, it is still quite unpolished, but I am stuck at one seemingly trivial step:

import Mathlib.Data.Setoid.Partition

variable {α : Type*} (s : Setoid α)

noncomputable example : Quotient s  Setoid.classes s := by
  let cls : Set (Set α) := Setoid.classes s
  have is_part : Setoid.IsPartition cls := Setoid.isPartition_classes s
  have f (a : α) : cls := ⟨{ x | Setoid.r x a }, Setoid.mem_classes s a
  have f_val (a : α) : (f a).val = {x | Setoid.r x a} := sorry
  have f_respects_relation :  (a b : α), Setoid.r a b  f a = f b := by
    intro a b a_rel_b
    have : b  {x | Setoid.r x a} := Setoid.symm a_rel_b
    have eq_cls : {x | Setoid.r x a} = {x | Setoid.r x b} := by
      apply Setoid.eq_of_mem_classes
      exact Setoid.mem_classes s a
      exact Setoid.symm a_rel_b
      exact Setoid.mem_classes s b
      exact Setoid.refl b
    let cl_a : cls := ⟨{x | Setoid.r x a}, Setoid.mem_classes s a
    let cl_b : cls := ⟨{x | Setoid.r x b}, Setoid.mem_classes s b
    have : cl_a = cl_b := by simp only [Subtype.mk.injEq]; exact eq_cls
    rw [Subtype.mk.injEq, f_val a, f_val b]
    exact eq_cls
  --have h : Quotient s → cls := Quot.lift f f_respects_relation
  have h_bij : Function.Bijective (Quot.lift f f_respects_relation) := by
    constructor
    . intro (q_a : Quotient s) (q_b : Quotient s)
      obtain a, ha := Quotient.exists_rep q_a
      obtain b, hb := Quotient.exists_rep q_b
      intro h_eq
      rw [ha, hb] at *
      simp only [Quotient.lift_mk] at h_eq
      have : (f a).val = (f b).val := congrArg Subtype.val h_eq
      have cls_eq : { x | Setoid.r x a} = { x | Setoid.r x b } := by
        rw [f_val a, f_val b]
        assumption
      have : Setoid.r a b := by
        have : a  { x | Setoid.r x a } := Setoid.refl a
        rw [cls_eq] at this
        exact this
      exact Quotient.sound this
    . apply (Quot.surjective_lift f_respects_relation).mpr
      intro c, c_in_cls
      have : c.Nonempty := Setoid.nonempty_of_mem_partition is_part c_in_cls
      obtain a, a_in_c := this
      let cl_a := {x | Setoid.r x a}
      have cl_a_eq_c : cl_a = c := by
        apply Setoid.eq_of_mem_classes
        exact Setoid.mem_classes s a
        exact Setoid.refl a
        exact c_in_cls
        exact a_in_c
      exists a
      rw [Subtype.mk.injEq, cl_a_eq_c]
      exact f_val a
  exact Equiv.ofBijective (Quot.lift f f_respects_relation) h_bij

I can't resolve the sorry. Neither rfl, simp, aesop, exact? proof this, although I feel like this should hold by the very definition of f.

Christian Merten (Sep 30 2023 at 10:07):

I fixed it by replacing have with let. Now I dont even need the f_val. Somehow I thought have and let are the same thing.

Kevin Buzzard (Sep 30 2023 at 10:09):

have can't be used for data because it forgets the answer. have a : Nat := 37 only ends up with Lean knowing a is a Nat so you can't prove it's 37. Whereas let a : Nat := 37 means that a = 37 and the proof is rfl.

Christian Merten (Sep 30 2023 at 10:09):

I see, makes sense. Thank you.

Kevin Buzzard (Sep 30 2023 at 10:10):

This is Ok with proofs becase if I have h : P then who cares what the proof was, all we need to know is that h has type P and is thus a proof of P. Indeed any two proofs of P are equal and the proof is rfl.

Kevin Buzzard (Sep 30 2023 at 10:10):

Well done for getting that out by the way!

Christian Merten (Sep 30 2023 at 10:36):

Should I PR a polished version of this? If yes, where should it go? I suppose in Setoid.Data.Partition?

Kevin Buzzard (Sep 30 2023 at 10:47):

This is what I came up with but it's still pretty horrible:

section maybe_missing_api

theorem Setoid.class_eq_class_of_equiv {α : Type*} (r : Setoid α) (a b : α) (h : a  b) :
    {x | Rel r x a} = {x | Rel r x b} := by
  ext c
  exact fun h'  trans' r h' h, fun h'  symm' r (trans' r h (symm' r h'))⟩

theorem Setoid.equiv_of_class_eq_class {α : Type*} (r : Setoid α) (a b : α)
    (h : {x | Rel r x a} = {x | Rel r x b}) : a  b := by
  rw [Set.ext_iff] at h
  exact (h a).1 <| refl' r a

noncomputable def Setoid.classes_equiv_quotient {α : Type*} (r : Setoid α) :
    Setoid.classes r  Quotient r where
  toFun := fun x  x.2.choose
  invFun := fun y  Quotient.lift (fun y : α  ⟨{ x | r.Rel x y }, y, rfl⟩)
    (fun a b h  by simp [r.class_eq_class_of_equiv a b h]) y
  left_inv := by
    rintro s, a, rfl
    simp only [Quotient.lift_mk, Subtype.mk.injEq]
    exact (Exists.choose_spec (Subtype.property
      (⟨{x | Rel r x a}, a, rfl : classes r) : {x | Rel r x a}  classes r)).symm
  right_inv := by
    rintro x
    refine Quotient.inductionOn x (fun a  Quotient.sound <| ?_)
    apply r.equiv_of_class_eq_class
    exact (Exists.choose_spec (Subtype.property
      (Quotient.lift (fun y  (⟨{x | Rel r x y}, y, rfl : classes r))
      (fun a b h  by simp [r.class_eq_class_of_equiv a b h])
      (Quotient.mk r a)) : (Quotient.lift
        (fun y  (⟨{x | Rel r x y}, y, rfl : (classes r)))
        _ (Quotient.mk r a))  classes r)).symm

end maybe_missing_api

Christian Merten (Sep 30 2023 at 10:51):

I don't have the permission to open branches on mathlib4 yet. Could you grant me this? My github handle is @chrisflav

Christian Merten (Sep 30 2023 at 10:53):

Kevin Buzzard said:

This is what I came up with but it's still pretty horrible:

section maybe_missing_api

theorem Setoid.class_eq_class_of_equiv {α : Type*} (r : Setoid α) (a b : α) (h : a  b) :
    {x | Rel r x a} = {x | Rel r x b} := by
  ext c
  exact fun h'  trans' r h' h, fun h'  symm' r (trans' r h (symm' r h'))⟩

theorem Setoid.equiv_of_class_eq_class {α : Type*} (r : Setoid α) (a b : α)
    (h : {x | Rel r x a} = {x | Rel r x b}) : a  b := by
  rw [Set.ext_iff] at h
  exact (h a).1 <| refl' r a

noncomputable def Setoid.classes_equiv_quotient {α : Type*} (r : Setoid α) :
    Setoid.classes r  Quotient r where
  toFun := fun x  x.2.choose
  invFun := fun y  Quotient.lift (fun y : α  ⟨{ x | r.Rel x y }, y, rfl⟩)
    (fun a b h  by simp [r.class_eq_class_of_equiv a b h]) y
  left_inv := by
    rintro s, a, rfl
    simp only [Quotient.lift_mk, Subtype.mk.injEq]
    exact (Exists.choose_spec (@Subtype.property (Set α) (fun x  x  classes r)
      { val := {x | Rel r x a},
        property := Exists.intro a (Eq.refl {x | Rel r x a}) } : {x | Rel r x a}  classes r)).symm
  right_inv := by
    rintro x
    refine Quotient.inductionOn x (fun a  ?_)
    apply Quotient.sound
    apply r.equiv_of_class_eq_class
    exact (Exists.choose_spec (@Subtype.property (Set α) (fun x  x  classes r)
      (Quotient.lift (fun y  { val := {x | Rel r x y}, property := y, rfl })
      (fun a b h  by simp [r.class_eq_class_of_equiv a b h])
      (Quotient.mk r a)) : (Quotient.lift
        (fun y  ({ val := {x | Rel r x y}, property := y, rfl } : (classes r)))
        _ (Quotient.mk r a))  classes r)).symm

end maybe_missing_api

Battling myself with Classical.choose was exactly what I tried to avoid :D

Kevin Buzzard (Sep 30 2023 at 11:23):

Christian Merten said:

I don't have the permission to open branches on mathlib4 yet. Could you grant me this? My github handle is @chrisflav

@maintainers

Kevin Buzzard (Sep 30 2023 at 11:24):

yeah, when I was a beginner I would avoid it but I'm more confident now. It's still a bit annoying I have to cut and paste the precise existence statement into Exists.choose_spec.

Kevin Buzzard (Sep 30 2023 at 11:27):

The advantage of my approach is that both toFun and invFun are concrete functions by definition. Your approach via Equiv.ofBijective has the disadvantage that you don't know what the invFun is, all you know is that it's the inverse of the toFun. So for example if someone wanted to prove that the map you're implicitly defining from Setoid.classes to Quotient is fun x ↦ ⟦x.2.choose⟧ they'd have to do what I'm doing in my approach.

Kevin Buzzard (Sep 30 2023 at 11:29):

In constructive mathematics, a function + proof that it's a bijection is a slightly weaker amount of data than a function plus an inverse function plus the two proofs. Even the inverse of a computable function might not be computable, as I discovered to my surprise when I was trying to get on top of this stuff (see https://xenaproject.wordpress.com/2019/06/11/the-inverse-of-a-bijection/ )

Anatole Dedecker (Sep 30 2023 at 11:29):

Invitation sent!

Christian Merten (Sep 30 2023 at 12:49):

https://github.com/leanprover-community/mathlib4/pull/7444

Christian Merten (Sep 30 2023 at 12:53):

Kevin Buzzard said:

The advantage of my approach is that both toFun and invFun are concrete functions by definition. Your approach via Equiv.ofBijective has the disadvantage that you don't know what the invFun is, all you know is that it's the inverse of the toFun. So for example if someone wanted to prove that the map you're implicitly defining from Setoid.classes to Quotient is fun x ↦ ⟦x.2.choose⟧ they'd have to do what I'm doing in my approach.

I see, do you think we should put your version instead then?

Adam Topaz (Sep 30 2023 at 13:00):

If you’re going to use choose for one direction of the equiv, you may as well just use docs#Equiv.ofBijective IMO

Patrick Massot (Sep 30 2023 at 14:36):

The reason why we never use Setoid.classes is there is always something more efficient to do. In the case at hand the answer is pretty clear. If your actual goal is

example {G : Type u} [Group G] [TopologicalSpace G] [TopologicalGroup G]
  [CompactSpace G]  (U : Subgroup G) (U_open : IsOpen (U : Set G)) :   Finite (G  U)

then the nice proof is to use docs#finite_of_compact_of_discrete. The quotient is compact because it is the image of a compact space under a continuous map, and it it discrete because every singleton is open (because its preimage is a copy of the open set U).

Christian Merten (Sep 30 2023 at 14:44):

That is very nice and way more elegant than the way around Setoid.classes. Thanks!

Patrick Massot (Sep 30 2023 at 15:08):

I thought it would be a fun exercise to implement this approach. It seems I found a hole in the subgroup quotient API, but maybe I didn't search correctly. In any case, the whole thing doesn't have to be longer than

import Mathlib

open Function Set

lemma QuotientGroup.preimage_mk_singleton_mk {G : Type*} [Group G] (H : Subgroup G) (g : G) :
    mk (s := H) ⁻¹' {mk g} = (g * ·) '' H := by
  ext g'
  simp only [mem_preimage, mem_singleton_iff, QuotientGroup.eq, image_mul_left, SetLike.mem_coe]
  rw [ H.inv_mem_iff]
  simp

variable {G : Type*} [Group G] [TopologicalSpace G] [TopologicalGroup G] (U : Subgroup G)

lemma Subgroup.discreteTopology  (U_open : IsOpen (U : Set G)) : DiscreteTopology (G  U) := by
  apply singletons_open_iff_discrete.mp
  rintro g
  erw [isOpen_mk, QuotientGroup.preimage_mk_singleton_mk]
  exact Homeomorph.mulLeft g |>.isOpen_image|>.mpr U_open

lemma Subgroup.finite_quotient [CompactSpace G] (U_open : IsOpen (U : Set G)) : Finite (G  U) :=
  have : CompactSpace (G  U) := Quotient.compactSpace
  have : DiscreteTopology (G  U) := U.discreteTopology U_open
  finite_of_compact_of_discrete

Christian Merten (Sep 30 2023 at 15:29):

I was just trying it out myself and found it quite annoying to show the equality you call QuotientGroup.preimage_mk_singleton_mk. Your solution is quite elegant.

Patrick Massot (Sep 30 2023 at 16:16):

In case this isn't clear, the big simp only in the middle comes from simp? and I didn't try to optimize it.


Last updated: Dec 20 2023 at 11:08 UTC