Skip to content

Commit 51a3c0a

Browse files
committed
feat: quotient lift on a finite family (#5576)
In the future it may be used to prove that `ZFSet` is a model of ZF set theory without using `Classical.choice`. Specifically, it will be used to prove that all first-order formulas of `ZFSet` are definable. Comparing to `(finChoice q).liftOn f`, `finLiftOn q f` asks users to provide a proof of `(∀ i, a i ≈ b i) → f a = f b` instead of `a ≈ b → f a = f b`, which is more readable and easier to rewrite. It also makes it easier for users to discover and use relevant APIs. Co-authored-by: negiizhao <egresf@gmail.com>
1 parent 5f98fda commit 51a3c0a

File tree

2 files changed

+228
-68
lines changed

2 files changed

+228
-68
lines changed

Mathlib/Data/Fintype/Quotient.lean

Lines changed: 217 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,239 @@
11
/-
22
Copyright (c) 2018 Mario Carneiro. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
4-
Authors: Mario Carneiro
4+
Authors: Mario Carneiro, Yuyang Zhao
55
-/
6+
import Mathlib.Data.List.Pi
67
import Mathlib.Data.Fintype.Basic
78

89
/-!
910
# Quotients of families indexed by a finite type
1011
11-
This file provides `Quotient.finChoice`, a mechanism to go from a finite family of quotients
12-
to a quotient of finite families.
12+
This file proves some basic facts and defines lifting and recursion principle for quotients indexed
13+
by a finite type.
1314
1415
## Main definitions
1516
16-
* `Quotient.finChoice`
17+
* `Quotient.finChoice`: Given a function `f : Π i, Quotient (S i)` on a fintype `ι`, returns the
18+
class of functions `Π i, α i` sending each `i` to an element of the class `f i`.
19+
* `Quotient.finChoiceEquiv`: A finite family of quotients is equivalent to a quotient of
20+
finite families.
21+
* `Quotient.finLiftOn`: Given a fintype `ι`. A function on `Π i, α i` which respects
22+
setoid `S i` for each `i` can be lifted to a function on `Π i, Quotient (S i)`.
23+
* `Quotient.finRecOn`: Recursion principle for quotients indexed by a finite type. It is the
24+
dependent version of `Quotient.finLiftOn`.
1725
1826
-/
1927

2028

21-
/-- An auxiliary function for `Quotient.finChoice`. Given a
22-
collection of setoids indexed by a type `ι`, a (finite) list `l` of
23-
indices, and a function that for each `i ∈ l` gives a term of the
24-
corresponding quotient type, then there is a corresponding term in the
25-
quotient of the product of the setoids indexed by `l`. -/
26-
def Quotient.finChoiceAux {ι : Type*} [DecidableEq ι] {α : ι → Type*} [S : ∀ i, Setoid (α i)] :
27-
∀ l : List ι, (∀ i ∈ l, Quotient (S i)) → @Quotient (∀ i ∈ l, α i) (by infer_instance)
28-
| [], _ => ⟦fun _ h => nomatch List.not_mem_nil _ h⟧
29-
| i :: l, f => by
30-
refine Quotient.liftOn₂ (f i (List.mem_cons_self _ _))
31-
(Quotient.finChoiceAux l fun j h => f j (List.mem_cons_of_mem _ h)) ?_ ?_
32-
· exact fun a l => ⟦fun j h =>
33-
if e : j = i then by rw [e]; exact a else l _ ((List.mem_cons.1 h).resolve_left e)⟧
34-
refine fun a₁ l₁ a₂ l₂ h₁ h₂ => Quotient.sound fun j h => ?_
35-
by_cases e : j = i
36-
· simp [e]; subst j
37-
exact h₁
38-
· simpa [e] using h₂ _ _
39-
40-
theorem Quotient.finChoiceAux_eq {ι : Type*} [DecidableEq ι] {α : ι → Type*}
41-
[S : ∀ i, Setoid (α i)] :
42-
∀ (l : List ι) (f : ∀ i ∈ l, α i), (Quotient.finChoiceAux l fun i h => ⟦f i h⟧) = ⟦f⟧
43-
| [], _ => Quotient.sound fun _ h => nomatch List.not_mem_nil _ h
44-
| i :: l, f => by
45-
simp only [finChoiceAux, Quotient.finChoiceAux_eq l, eq_mpr_eq_cast, lift_mk]
46-
refine Quotient.sound fun j h => ?_
47-
by_cases e : j = i <;> simp [e] <;> try exact Setoid.refl _
48-
subst j; exact Setoid.refl _
49-
50-
/-- Given a collection of setoids indexed by a fintype `ι` and a
51-
function that for each `i : ι` gives a term of the corresponding
52-
quotient type, then there is corresponding term in the quotient of the
53-
product of the setoids. -/
54-
def Quotient.finChoice {ι : Type*} [DecidableEq ι] [Fintype ι] {α : ι → Type*}
55-
[S : ∀ i, Setoid (α i)] (f : ∀ i, Quotient (S i)) : @Quotient (∀ i, α i) (by infer_instance) :=
56-
Quotient.liftOn
57-
(@Quotient.recOn _ _ (fun l : Multiset ι => @Quotient (∀ i ∈ l, α i) (by infer_instance))
58-
Finset.univ.1 (fun l => Quotient.finChoiceAux l fun i _ => f i) (fun a b h => by
59-
have := fun a => Quotient.finChoiceAux_eq a fun i _ => Quotient.out (f i)
60-
simp? [Quotient.out_eq] at this says simp only [out_eq] at this
61-
simp only [Multiset.quot_mk_to_coe, this]
62-
let g := fun a : Multiset ι =>
63-
(⟦fun (i : ι) (_ : i ∈ a) => Quotient.out (f i)⟧ : Quotient (by infer_instance))
64-
apply eq_of_heq
65-
trans (g a)
66-
· exact eqRec_heq (φ := fun l : Multiset ι => @Quotient (∀ i ∈ l, α i) (by infer_instance))
67-
(Quotient.sound h) (g a)
68-
· change HEq (g a) (g b); congr 1; exact Quotient.sound h))
69-
(fun f => ⟦fun i => f i (Finset.mem_univ _)⟧) (fun _ _ h => Quotient.sound fun i => by apply h)
70-
71-
theorem Quotient.finChoice_eq {ι : Type*} [DecidableEq ι] [Fintype ι] {α : ι → Type*}
72-
[∀ i, Setoid (α i)] (f : ∀ i, α i) : (Quotient.finChoice fun i => ⟦f i⟧) = ⟦f⟧ := by
73-
dsimp only [Quotient.finChoice]
74-
conv_lhs =>
75-
enter [1]
76-
tactic =>
77-
change _ = ⟦fun i _ => f i⟧
78-
exact Quotient.inductionOn (@Finset.univ ι _).1 fun l => Quotient.finChoiceAux_eq _ _
29+
namespace Quotient
30+
31+
section List
32+
variable {ι : Type*} [DecidableEq ι] {α : ι → Sort*} {S : ∀ i, Setoid (α i)} {β : Sort*}
33+
34+
/-- Given a collection of setoids indexed by a type `ι`, a list `l` of indices, and a function that
35+
for each `i ∈ l` gives a term of the corresponding quotient type, then there is a corresponding
36+
term in the quotient of the product of the setoids indexed by `l`. -/
37+
def listChoice {l : List ι} (q : ∀ i ∈ l, Quotient (S i)) : @Quotient (∀ i ∈ l, α i) piSetoid :=
38+
match l with
39+
| [] => ⟦nofun⟧
40+
| i :: _ => Quotient.liftOn₂ (List.Pi.head (i := i) q)
41+
(listChoice (List.Pi.tail q))
42+
(⟦List.Pi.cons _ _ · ·⟧)
43+
(fun _ _ _ _ ha hl ↦ Quotient.sound (List.Pi.forall_rel_cons_ext ha hl))
44+
45+
theorem listChoice_mk {l : List ι} (a : ∀ i ∈ l, α i) : listChoice (S := S) (⟦a · ·⟧) = ⟦a⟧ :=
46+
match l with
47+
| [] => Quotient.sound nofun
48+
| i :: l => by
49+
unfold listChoice List.Pi.tail
50+
rw [listChoice_mk]
51+
exact congrArg (⟦·⟧) (List.Pi.cons_eta a)
52+
53+
/-- Choice-free induction principle for quotients indexed by a `List`. -/
54+
@[elab_as_elim]
55+
lemma list_ind {l : List ι} {C : (∀ i ∈ l, Quotient (S i)) → Prop}
56+
(f : ∀ a : ∀ i ∈ l, α i, C (⟦a · ·⟧)) (q : ∀ i ∈ l, Quotient (S i)) : C q :=
57+
match l with
58+
| [] => cast (congr_arg _ (funext₂ nofun)) (f nofun)
59+
| i :: l => by
60+
rw [← List.Pi.cons_eta q]
61+
induction' List.Pi.head q using Quotient.ind with a
62+
refine @list_ind _ (fun q ↦ C (List.Pi.cons _ _ ⟦a⟧ q)) ?_ (List.Pi.tail q)
63+
intro as
64+
rw [List.Pi.cons_map a as (fun i ↦ Quotient.mk (S i))]
65+
exact f _
66+
67+
end List
68+
69+
section Fintype
70+
variable {ι : Type*} [Fintype ι] [DecidableEq ι] {α : ι → Sort*} {S : ∀ i, Setoid (α i)} {β : Sort*}
71+
72+
/-- Choice-free induction principle for quotients indexed by a finite type.
73+
See `Quotient.induction_on_pi` for the general version assuming `Classical.choice`. -/
74+
@[elab_as_elim]
75+
lemma ind_fintype_pi {C : (∀ i, Quotient (S i)) → Prop}
76+
(f : ∀ a : ∀ i, α i, C (⟦a ·⟧)) (q : ∀ i, Quotient (S i)) : C q := by
77+
have {m : Multiset ι} (C : (∀ i ∈ m, Quotient (S i)) → Prop) :
78+
∀ (_ : ∀ a : ∀ i ∈ m, α i, C (⟦a · ·⟧)) (q : ∀ i ∈ m, Quotient (S i)), C q := by
79+
induction m using Quotient.ind
80+
exact list_ind
81+
exact this (fun q ↦ C (q · (Finset.mem_univ _))) (fun _ ↦ f _) (fun i _ ↦ q i)
82+
83+
/-- Choice-free induction principle for quotients indexed by a finite type.
84+
See `Quotient.induction_on_pi` for the general version assuming `Classical.choice`. -/
85+
@[elab_as_elim]
86+
lemma induction_on_fintype_pi {C : (∀ i, Quotient (S i)) → Prop}
87+
(q : ∀ i, Quotient (S i)) (f : ∀ a : ∀ i, α i, C (⟦a ·⟧)) : C q :=
88+
ind_fintype_pi f q
89+
90+
/-- Given a collection of setoids indexed by a fintype `ι` and a function that for each `i : ι`
91+
gives a term of the corresponding quotient type, then there is corresponding term in the quotient
92+
of the product of the setoids.
93+
See `Quotient.choice` for the noncomputable general version. -/
94+
def finChoice (q : ∀ i, Quotient (S i)) :
95+
@Quotient (∀ i, α i) piSetoid := by
96+
let e := Equiv.subtypeQuotientEquivQuotientSubtype (fun l : List ι ↦ ∀ i, i ∈ l)
97+
(fun s : Multiset ι ↦ ∀ i, i ∈ s) (fun i ↦ Iff.rfl) (fun _ _ ↦ Iff.rfl) ⟨_, Finset.mem_univ⟩
98+
refine e.liftOn
99+
(fun l ↦ (listChoice fun i _ ↦ q i).map (fun a i ↦ a i (l.2 i)) ?_) ?_
100+
· exact fun _ _ h i ↦ h i _
101+
intro _ _ _
102+
refine ind_fintype_pi (fun a ↦ ?_) q
103+
simp_rw [listChoice_mk, Quotient.map_mk]
104+
105+
theorem finChoice_eq (a : ∀ i, α i) :
106+
finChoice (S := S) (⟦a ·⟧) = ⟦a⟧ := by
107+
dsimp [finChoice]
108+
obtain ⟨l, hl⟩ := (Finset.univ.val : Multiset ι).exists_rep
109+
simp_rw [← hl, Equiv.subtypeQuotientEquivQuotientSubtype, listChoice_mk]
110+
rfl
111+
112+
lemma eval_finChoice (f : ∀ i, Quotient (S i)) :
113+
eval (finChoice f) = f :=
114+
induction_on_fintype_pi f (fun a ↦ by rw [finChoice_eq]; rfl)
115+
116+
/-- Lift a function on `∀ i, α i` to a function on `∀ i, Quotient (S i)`. -/
117+
def finLiftOn (q : ∀ i, Quotient (S i)) (f : (∀ i, α i) → β)
118+
(h : ∀ (a b : ∀ i, α i), (∀ i, a i ≈ b i) → f a = f b) : β :=
119+
(finChoice q).liftOn f h
120+
121+
@[simp]
122+
lemma finLiftOn_empty [e : IsEmpty ι] (q : ∀ i, Quotient (S i)) :
123+
finLiftOn (β := β) q = fun f _ ↦ f e.elim := by
124+
ext f h
125+
dsimp [finLiftOn]
126+
induction finChoice q using Quotient.ind
127+
exact h _ _ e.elim
128+
129+
@[simp]
130+
lemma finLiftOn_mk (a : ∀ i, α i) :
131+
finLiftOn (S := S) (β := β) (⟦a ·⟧) = fun f _ ↦ f a := by
132+
ext f h
133+
dsimp [finLiftOn]
134+
rw [finChoice_eq]
79135
rfl
80136

137+
/-- `Quotient.finChoice` as an equivalence. -/
138+
@[simps]
139+
def finChoiceEquiv :
140+
(∀ i, Quotient (S i)) ≃ @Quotient (∀ i, α i) piSetoid where
141+
toFun := finChoice
142+
invFun := eval
143+
left_inv q := by
144+
refine induction_on_fintype_pi q (fun a ↦ ?_)
145+
rw [finChoice_eq]
146+
rfl
147+
right_inv q := by
148+
induction q using Quotient.ind
149+
exact finChoice_eq _
150+
151+
/-- Recursion principle for quotients indexed by a finite type. -/
152+
@[elab_as_elim]
153+
def finHRecOn {C : (∀ i, Quotient (S i)) → Sort*}
154+
(q : ∀ i, Quotient (S i))
155+
(f : ∀ a : ∀ i, α i, C (⟦a ·⟧))
156+
(h : ∀ (a b : ∀ i, α i), (∀ i, a i ≈ b i) → HEq (f a) (f b)) :
157+
C q :=
158+
eval_finChoice q ▸ (finChoice q).hrecOn f h
159+
160+
/-- Recursion principle for quotients indexed by a finite type. -/
161+
@[elab_as_elim]
162+
def finRecOn {C : (∀ i, Quotient (S i)) → Sort*}
163+
(q : ∀ i, Quotient (S i))
164+
(f : ∀ a : ∀ i, α i, C (⟦a ·⟧))
165+
(h : ∀ (a b : ∀ i, α i) (h : ∀ i, a i ≈ b i),
166+
Eq.ndrec (f a) (funext fun i ↦ Quotient.sound (h i)) = f b) :
167+
C q :=
168+
finHRecOn q f (rec_heq_iff_heq.mp <| heq_of_eq <| h · · ·)
169+
170+
@[simp]
171+
lemma finHRecOn_mk {C : (∀ i, Quotient (S i)) → Sort*}
172+
(a : ∀ i, α i) :
173+
finHRecOn (C := C) (⟦a ·⟧) = fun f _ ↦ f a := by
174+
ext f h
175+
refine eq_of_heq ((eqRec_heq _ _).trans ?_)
176+
rw [finChoice_eq]
177+
rfl
178+
179+
@[simp]
180+
lemma finRecOn_mk {C : (∀ i, Quotient (S i)) → Sort*}
181+
(a : ∀ i, α i) :
182+
finRecOn (C := C) (⟦a ·⟧) = fun f _ ↦ f a := by
183+
unfold finRecOn
184+
simp
185+
186+
end Fintype
187+
188+
end Quotient
189+
190+
namespace Trunc
191+
variable {ι : Type*} [DecidableEq ι] [Fintype ι] {α : ι → Sort*} {β : Sort*}
192+
81193
/-- Given a function that for each `i : ι` gives a term of the corresponding
82194
truncation type, then there is corresponding term in the truncation of the product. -/
83-
def Trunc.finChoice {ι : Type*} [DecidableEq ι] [Fintype ι] {α : ι → Type*}
84-
(f : ∀ i, Trunc (α i)) : Trunc (∀ i, α i) :=
85-
Quotient.map' id (fun _ _ _ => trivial)
86-
(Quotient.finChoice f (S := fun _ => trueSetoid))
195+
def finChoice (q : ∀ i, Trunc (α i)) : Trunc (∀ i, α i) :=
196+
Quotient.map' id (fun _ _ _ => trivial) (Quotient.finChoice q)
87197

88-
theorem Trunc.finChoice_eq {ι : Type*} [DecidableEq ι] [Fintype ι] {α : ι → Type*}
89-
(f : ∀ i, α i) : (Trunc.finChoice fun i => Trunc.mk (f i)) = Trunc.mk f :=
198+
theorem finChoice_eq (f : ∀ i, α i) : (Trunc.finChoice fun i => Trunc.mk (f i)) = Trunc.mk f :=
90199
Subsingleton.elim _ _
200+
201+
/-- Lift a function on `∀ i, α i` to a function on `∀ i, Trunc (α i)`. -/
202+
def finLiftOn (q : ∀ i, Trunc (α i)) (f : (∀ i, α i) → β) (h : ∀ (a b : ∀ i, α i), f a = f b) : β :=
203+
Quotient.finLiftOn q f (fun _ _ _ ↦ h _ _)
204+
205+
@[simp]
206+
lemma finLiftOn_empty [e : IsEmpty ι] (q : ∀ i, Trunc (α i)) :
207+
finLiftOn (β := β) q = fun f _ ↦ f e.elim :=
208+
funext₂ fun _ _ ↦ congrFun₂ (Quotient.finLiftOn_empty q) _ _
209+
210+
@[simp]
211+
lemma finLiftOn_mk (a : ∀ i, α i) :
212+
finLiftOn (β := β) (⟦a ·⟧) = fun f _ ↦ f a :=
213+
funext₂ fun _ _ ↦ congrFun₂ (Quotient.finLiftOn_mk a) _ _
214+
215+
/-- `Trunc.finChoice` as an equivalence. -/
216+
@[simps]
217+
def finChoiceEquiv : (∀ i, Trunc (α i)) ≃ Trunc (∀ i, α i) where
218+
toFun := finChoice
219+
invFun q i := q.map (· i)
220+
left_inv _ := Subsingleton.elim _ _
221+
right_inv _ := Subsingleton.elim _ _
222+
223+
/-- Recursion principle for `Trunc`s indexed by a finite type. -/
224+
@[elab_as_elim]
225+
def finRecOn {C : (∀ i, Trunc (α i)) → Sort*}
226+
(q : ∀ i, Trunc (α i))
227+
(f : ∀ a : ∀ i, α i, C (mk <| a ·))
228+
(h : ∀ (a b : ∀ i, α i), (Eq.ndrec (f a) (funext fun _ ↦ Trunc.eq _ _)) = f b) :
229+
C q :=
230+
Quotient.finRecOn q (f ·) (fun _ _ _ ↦ h _ _)
231+
232+
@[simp]
233+
lemma finRecOn_mk {C : (∀ i, Trunc (α i)) → Sort*}
234+
(a : ∀ i, α i) :
235+
finRecOn (C := C) (⟦a ·⟧) = fun f _ ↦ f a := by
236+
unfold finRecOn
237+
simp
238+
239+
end Trunc

Mathlib/Data/Quot.lean

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,17 @@ instance piSetoid {ι : Sort*} {α : ι → Sort*} [∀ i, Setoid (α i)] : Seto
387387
fun h _ ↦ Setoid.symm (h _),
388388
fun h₁ h₂ _ ↦ Setoid.trans (h₁ _) (h₂ _)⟩
389389

390+
/-- Given a class of functions `q : @Quotient (∀ i, α i) _`, returns the class of `i`-th projection
391+
`Quotient (S i)`. -/
392+
def Quotient.eval {ι : Type*} {α : ι → Sort*} {S : ∀ i, Setoid (α i)}
393+
(q : @Quotient (∀ i, α i) (by infer_instance)) (i : ι) : Quotient (S i) :=
394+
q.map (· i) fun _ _ h ↦ by exact h i
395+
396+
@[simp]
397+
theorem Quotient.eval_mk {ι : Type*} {α : ι → Type*} {S : ∀ i, Setoid (α i)} (f : ∀ i, α i) :
398+
Quotient.eval (S := S) ⟦f⟧ = fun i ↦ ⟦f i⟧ :=
399+
rfl
400+
390401
/-- Given a function `f : Π i, Quotient (S i)`, returns the class of functions `Π i, α i` sending
391402
each `i` to an element of the class `f i`. -/
392403
noncomputable def Quotient.choice {ι : Type*} {α : ι → Type*} {S : ∀ i, Setoid (α i)}

0 commit comments

Comments
 (0)