Zulip Chat Archive
Stream: Is there code for X?
Topic: set of equivalences classes is equiv to quotient
Antoine Chambert-Loir (May 02 2023 at 22:05):
If c
is a partition of a type α
(setoid.is_partition c
), then the quotient by the equivalence relation whose classes are the elements of c
is equiv
to c
, as follows, but I couldn't find it in mathlib…
import data.setoid.partition
noncomputable example (α : Type*) (c : set (set α)) (hc : setoid.is_partition c) : quotient (setoid.mk_classes c hc.2) ≃ c :=
begin
let f : α → c := λ a, ⟨_, @setoid.eqv_class_mem _ c hc.2 a⟩,
refine equiv.of_bijective (@quotient.lift _ _ (setoid.mk_classes c hc.2) f _) _,
{ -- well defined
intros a b hab,
change (setoid.mk_classes c hc.2).rel a b at hab,
rw setoid.rel_iff_exists_classes at hab,
rw setoid.classes_mk_classes at hab,
obtain ⟨u, h, ha, hb⟩ := hab,
let hc2 := hc.2,
simp only [f, subtype.mk_eq_mk],
rw ← setoid.eq_eqv_class_of_mem hc.2 h ha,
rw ← setoid.eq_eqv_class_of_mem hc.2 h hb, },
split,
{ -- injective
intros x y,
obtain ⟨a, rfl⟩ := @quotient.exists_rep α (setoid.mk_classes c hc.2) x,
obtain ⟨b, rfl⟩ := @quotient.exists_rep α (setoid.mk_classes c hc.2) y,
simp only [quotient.lift_mk, f, subtype.mk_eq_mk],
intro hab,
apply quotient.sound,
change (setoid.mk_classes c hc.2).rel a b,
rw setoid.rel_iff_exists_classes,
use { x : α | (setoid.mk_classes c hc.2).rel x a},
split,
rw setoid.classes_mk_classes,
apply setoid.eqv_class_mem,
split,
rw set.mem_set_of, apply setoid.refl' _ a,
rw hab, rw set.mem_set_of, apply setoid.refl' _ b, },
{ -- surjective
rintro ⟨u, hu⟩,
have hu' : u.nonempty,
{ rw set.nonempty_iff_ne_empty,
intro hu', apply hc.1, rw ← hu', exact hu, },
obtain ⟨a, ha⟩ := hu',
use @quotient.mk _ (setoid.mk_classes c hc.2) a,
rw quotient.lift_mk,
simp only [f, subtype.mk_eq_mk],
rw setoid.eq_eqv_class_of_mem hc.2 hu ha, }
end
Eric Wieser (May 02 2023 at 22:47):
The forward direction golfs to
λ q, q.lift_on' (λ a, (⟨_, @setoid.eqv_class_mem _ c hc.2 a⟩ : c))
(λ a b hab, subtype.ext $ set.ext $ λ x,
⟨λ h, setoid.trans' _ h hab, λ h, setoid.trans' _ h (setoid.symm' _ hab)⟩),
Heather Macbeth (May 03 2023 at 01:09):
I have been thinking about this lately in the context of teaching, and I think the real answer is that "the set of equivalence classes" is not the fundamental concept in the Lean/mathlib "foundations of mathematics" that it is in conventional mathematics. The lemma docs#quotient.eq states that for an equivalence relation on , two points in go to the same point in the quotient if and only if they are related by . In practice one uses this lemma directly without mentioning equivalence classes.
Heather Macbeth (May 03 2023 at 01:10):
See also:
Kevin Buzzard said:
Another thing I found strange was that we don't ever seem to use the bijection between equivalence relations and partitions (which I got Amelia to make, convinced that it would be important). My model of mathematics was that this was used absolutely everywhere; more recently I have become slightly embarrassed to even teach it to the 1st years! Someone suggested to me that it was perhaps because Lean uses type theory and has inbuilt quotients.
Jireh Loreaux (May 03 2023 at 02:35):
I actually very much prefer to teach quotients via the universal property rather than as equivalence classes (as sets). I find that students often fail to completely grasp the concept of a function (out of a quotient) being well-defined, and reasonably so, because (in my opinion) it's such an absurd idea. You pretend to define a function from a quotient, but really if you look you are defining a function on the prequotient and implicitly claiming it factors through the quotient. Then you say, "wait, let's check this function is actually a function" (??) and you proceed to prove the implicit claim. By teaching via the universal property, you force students to think about functions out of quotients as first being functions defined on the prequotient, and I think this makes it easier for them not to tie themselves in knots.
My go to example for why we need to do this (i.e., define from the prequotient and check that the function respects the equivalence) is to "define" f : ℚ → ℤ
by f(p/q) = p + q
. I find that first-year students are abysmal at recognizing the problem here. I then point out the problem (e.g., what is f(1/2)
? f(2/4)
?), and then they understand it for that example, but they don't yet comprehend the wider issue. This then leads into an explanation that the function g : ℤ × (ℤ \ {0}) → ℤ
(or insert your favorite prequotient here) defined by g x y := x + y
is perfectly valid, it just doesn't respect the equivalence relation, thereby leading to the universal property.
I recently wrote up something detailing the link between Lean's quotient
and abstract equivalence classes ⟦x⟧
, and the concrete equivalence classes as sets. It starts basically from the ground up. It purposefully mirrors some existing definitions (like setoid.is_partition
), and even imports data.setoid.partition
. This is only for pedagogical purposes, and it's still very rough; I basically just wrote it start-to-finish and haven't edited much, but maybe it will be useful to someone or inspire them to improve it.
partition.lean
Antoine Chambert-Loir (May 03 2023 at 07:03):
I perfectly agree that it is not the correct point of view on the quotient. Taking the set of equivalence classes is just a (well thought) way of constructing the quotient.
Antoine Chambert-Loir (May 03 2023 at 07:04):
(However, I was led to proving exactly that in Lean!)
Does the following exist :
import data.setoid.partition
example (α : Type*) [fintype α] (c : set (set α)) (hc : setoid.is_partition c) :
finsum (λ (x : c), nat.card x) = nat.card α :=
sorry
Anatole Dedecker (May 03 2023 at 11:02):
I don’t think this lemma is true without finiteness assumptions. If you have an infinite type and take a partition with one singleton and its complement, then the rhs is zero but the lhs is one, right?
Anatole Dedecker (May 03 2023 at 11:13):
Regarding the more general issue, I think "use docs#quotient everywhere" is not the right approach either: the real solution is to make sure that all lemmas about docs#quotient have a version for a quotient (i.e docs#function.surjective). Then you should just prove that the function mapping an element to its class in a docs#setoid.is_partition is surjective, and suddenly you wouldn’t even need to get back to docs#quotient anymore
Anatole Dedecker (May 03 2023 at 11:15):
The real reason equivalence classes aren’t the right notion is not that docs#quotient is better. It’s that the construction that is used shouldn’t matter in the first place.
Anatole Dedecker (May 03 2023 at 11:22):
(Except if you care about the nice definitional equalities provided by docs#quot and docs#quotient)
Eric Wieser (May 03 2023 at 12:31):
Anatole Dedecker said:
the real solution is to make sure that all lemmas about docs#quotient have a version for a quotient (i.e docs#function.surjective).
Often an even better solution is to have a version for docs#function.right_inverse
Anatole Dedecker (May 03 2023 at 12:34):
Sure, but you can easily go from one to another in theory
Anatole Dedecker (May 03 2023 at 12:52):
Here's a proof of the two statements with a bunch of API that feels reasonable.
import data.setoid.partition
import algebra.big_operators.finprod
import set_theory.cardinal.finite
lemma setoid.classes_ker_of_surjective {α β : Type*} {f : α → β} (hf : function.surjective f) :
(setoid.ker f).classes = set.range (λ b : β, f ⁻¹' {b}) :=
begin
ext s,
rw [setoid.classes, set.mem_set_of, set.mem_range, hf.exists],
refine exists_congr (λ a, _),
rw [eq_comm],
refl
end
def setoid.is_partition.to {α : Type*} {c : set (set α)} (hc : setoid.is_partition c) : α → c :=
λ x, ⟨classical.some (hc.2 x).exists, (classical.some_spec (hc.2 x).exists).elim (λ h _ _, h)⟩
lemma setoid.is_partition.mem_to {α : Type*} {c : set (set α)} (hc : setoid.is_partition c)
(x : α) : x ∈ (hc.to x : set α) :=
(classical.some_spec (hc.2 x).exists).elim (λ _ h _, h)
lemma setoid.is_partition.eq_to_iff {α : Type*} {c : set (set α)} {s : set α}
(hc : setoid.is_partition c) (x : α) (hs : s ∈ c) :
s = hc.to x ↔ x ∈ s :=
begin
refine ⟨λ h, h.symm ▸ hc.mem_to x, λ h, (hc.2 x).unique _ _⟩;
rw exists_unique_prop,
{ exact ⟨hs, h⟩ },
{ exact ⟨subtype.coe_prop _, hc.mem_to x⟩ }
end
lemma setoid.is_partition.fiber_to_eq {α : Type*} {c : set (set α)}
(hc : setoid.is_partition c) (s : c) :
hc.to ⁻¹' {s} = s :=
begin
ext,
rw [set.mem_preimage, set.mem_singleton_iff, eq_comm, subtype.ext_iff],
exact hc.eq_to_iff x (subtype.coe_prop s)
end
lemma setoid.is_partition.surjective_to {α : Type*} {c : set (set α)}
(hc : setoid.is_partition c) : function.surjective hc.to :=
begin
rintro ⟨s, hs⟩,
set x := (setoid.nonempty_of_mem_partition hc hs).some,
use x,
ext : 1,
symmetry,
rw hc.eq_to_iff x (subtype.coe_prop _),
exact (setoid.nonempty_of_mem_partition hc hs).some_spec
end
lemma setoid.is_partition.ker_to {α : Type*} {c : set (set α)} (hc : setoid.is_partition c) :
setoid.ker hc.to = setoid.mk_classes c hc.2 :=
begin
refine setoid.classes_inj.mpr _,
have : (λ (s : ↥c), hc.to ⁻¹' {s}) = coe := by {ext s : 1, exact hc.fiber_to_eq s},
rw [setoid.classes_mk_classes, setoid.classes_ker_of_surjective hc.surjective_to, this,
subtype.range_coe]
end
noncomputable def foo {α : Type*} {c : set (set α)} (hc : setoid.is_partition c) :
quotient (setoid.mk_classes c hc.2) ≃ c :=
calc quotient (setoid.mk_classes c hc.2)
≃ quotient (setoid.ker hc.to) : quotient.congr_right (λ _ _, by rw hc.ker_to)
... ≃ c : setoid.quotient_ker_equiv_of_surjective hc.to hc.surjective_to
example {α : Type*} [finite α] {c : set (set α)} (hc : setoid.is_partition c) :
finsum (λ (x : c), nat.card x) = nat.card α :=
begin
classical,
letI := fintype.of_finite α,
rw [finsum_eq_sum_of_fintype, nat.card_eq_fintype_card,
fintype.card_congr (equiv.sigma_preimage_equiv hc.to).symm, fintype.card_sigma],
congr,
ext s,
rw [nat.card_eq_fintype_card, hc.fiber_to_eq],
refl
end
Anatole Dedecker (May 03 2023 at 12:52):
(golf welcome)
Antoine Chambert-Loir (May 03 2023 at 15:43):
Anatole Dedecker said:
I don’t think this lemma is true without finiteness assumptions. If you have an infinite type and take a partition with one singleton and its complement, then the rhs is zero but the lhs is one, right?
(Of course. I corrected the lemma.)
Yaël Dillies (May 03 2023 at 15:47):
I think your lemma can assume the parts are finite, rather than assuming that α
is finite.
Antoine Chambert-Loir (May 03 2023 at 16:12):
Yes. If the parts are finite but the set is infinite, there are infinitely many parts, nat.card
is zero, as is the finsum
. But mathematically, it has not the same meaning.
Antoine Chambert-Loir (May 03 2023 at 21:09):
I went on playing that game…
lemma setoid.nat_sum {α : Type*} [finite α] {c : set (set α)}
(hc : setoid.is_partition c) :
finsum (λ (x : c), nat.card x) = nat.card α :=
begin
classical,
letI := fintype.of_finite α,
simp only [finsum_eq_sum_of_fintype, nat.card_eq_fintype_card],
rw ← fintype.card_sigma,
refine fintype.card_congr (equiv.of_bijective (λ x, x.snd : (Σ (a : ↥c), ↥a) → α) _),
split,
-- injectivity
rintros ⟨⟨x, hx⟩,⟨a, ha : a ∈ x⟩⟩ ⟨⟨y, hy⟩, ⟨b, hb : b ∈ y⟩⟩ hab,
dsimp at hab,
rw hab at ha,
rw [sigma.subtype_ext_iff],
simp only [subtype.mk_eq_mk, subtype.coe_mk],
apply and.intro _ hab,
refine unique_of_exists_unique (hc.2 b) _ _,
simp only [exists_unique_iff_exists, exists_prop], exact ⟨hx, ha⟩,
simp only [exists_unique_iff_exists, exists_prop], exact ⟨hy, hb⟩,
-- surjectivity
intro a,
obtain ⟨x,⟨hx, ha : a ∈ x, ha'⟩, hx'⟩ := hc.2 a,
use ⟨⟨x, hx⟩, ⟨a, ha⟩⟩, refl,
end
Eric Wieser (May 03 2023 at 21:57):
That proof looks like it should be trivial once you extract the equivalence into its own def
(which likely already exists somewhere)
Antoine Chambert-Loir (May 03 2023 at 23:42):
The point is at the split
, where I need to prove the bijectivity of the function λ x, x.snd : (Σ (a : ↥c), ↥a) → α)
.
If I enter rw function.bijective_iff_exists_unique
, I am reduced to prove
∀ (b : α), ∃! (a : Σ (a : ↥c), ↥a), ↑(a.snd) = b
which is very close to the definition of set.is_partition c
, except that the latter is
∀ (a : α), ∃! (b : set α) (H : b ∈ c), a ∈ b
and I don't know how to change trivially one to another.
Antoine Chambert-Loir (May 05 2023 at 08:27):
Eric Wieser said:
That proof looks like it should be trivial once you extract the equivalence into its own
def
(which likely already exists somewhere)
I could not find the equivalence (Σ (s : ↥c), ↥s) ≃ α
(with hc : setoid.is_partition c
) in mathlib.
Once we have it, everything simplifies as you indicated:
def setoid.is_partition.equiv {c : set (set α)}
(hc : setoid.is_partition c) :
(Σ (s : ↥c), ↥s) ≃ α := {
to_fun := λ x, x.snd,
inv_fun := λ a, ⟨⟨(exists_unique.exists2 (hc.2 a)).some, (exists_unique.exists2 (hc.2 a)).some_spec.some⟩, ⟨a, (exists_unique.exists2 (hc.2 a)).some_spec.some_spec⟩⟩,
left_inv := λ x,
begin
rw sigma.subtype_ext_iff,
split,
{ rw ← subtype.coe_inj,
apply exists_unique.elim (hc.2 x.snd),
intros s hs hst,
simp only [exists_unique_iff_exists, exists_prop] at hs,
simp only [exists_unique_iff_exists, exists_prop, and_imp] at hst,
rw hst x.fst x.fst.prop x.snd.prop,
have := (exists_unique.exists2 (hc.2 (x.snd))).some_spec,
simp only [exists_prop] at this ⊢,
exact hst _ this.1 this.2, },
simp only [subtype.coe_mk],
end,
right_inv := λ a, by simp only [subtype.coe_mk] }
lemma setoid.nat_sum'{α : Type*} [finite α] {c : set (set α)}
(hc : setoid.is_partition c) :
finsum (λ (x : c), nat.card x) = nat.card α :=
begin
classical,
letI := fintype.of_finite α,
simp only [finsum_eq_sum_of_fintype, nat.card_eq_fintype_card],
rw ← fintype.card_sigma,
apply fintype.card_congr hc.equiv,
end
Last updated: Dec 20 2023 at 11:08 UTC