/
Coherence.lean
377 lines (306 loc) · 15.6 KB
/
Coherence.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/-
Copyright (c) 2021 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Mathlib.CategoryTheory.Monoidal.Free.Basic
import Mathlib.CategoryTheory.Groupoid
import Mathlib.CategoryTheory.DiscreteCategory
#align_import category_theory.monoidal.free.coherence from "leanprover-community/mathlib"@"f187f1074fa1857c94589cc653c786cadc4c35ff"
/-!
# The monoidal coherence theorem
In this file, we prove the monoidal coherence theorem, stated in the following form: the free
monoidal category over any type `C` is thin.
We follow a proof described by Ilya Beylin and Peter Dybjer, which has been previously formalized
in the proof assistant ALF. The idea is to declare a normal form (with regard to association and
adding units) on objects of the free monoidal category and consider the discrete subcategory of
objects that are in normal form. A normalization procedure is then just a functor
`fullNormalize : FreeMonoidalCategory C ⥤ Discrete (NormalMonoidalObject C)`, where
functoriality says that two objects which are related by associators and unitors have the
same normal form. Another desirable property of a normalization procedure is that an object is
isomorphic (i.e., related via associators and unitors) to its normal form. In the case of the
specific normalization procedure we use we not only get these isomorphisms, but also that they
assemble into a natural isomorphism `𝟭 (FreeMonoidalCategory C) ≅ fullNormalize ⋙ inclusion`.
But this means that any two parallel morphisms in the free monoidal category factor through a
discrete category in the same way, so they must be equal, and hence the free monoidal category
is thin.
## References
* [Ilya Beylin and Peter Dybjer, Extracting a proof of coherence for monoidal categories from a
proof of normalization for monoids][beylin1996]
-/
universe u
namespace CategoryTheory
open MonoidalCategory
namespace FreeMonoidalCategory
variable {C : Type u}
section
variable (C)
/-- We say an object in the free monoidal category is in normal form if it is of the form
`(((𝟙_ C) ⊗ X₁) ⊗ X₂) ⊗ ⋯`. -/
-- porting note (#10927): removed @[nolint has_nonempty_instance]
inductive NormalMonoidalObject : Type u
| unit : NormalMonoidalObject
| tensor : NormalMonoidalObject → C → NormalMonoidalObject
#align category_theory.free_monoidal_category.normal_monoidal_object CategoryTheory.FreeMonoidalCategory.NormalMonoidalObject
end
local notation "F" => FreeMonoidalCategory
local notation "N" => Discrete ∘ NormalMonoidalObject
local infixr:10 " ⟶ᵐ " => Hom
-- Porting note: this was automatic in mathlib 3
instance (x y : N C) : Subsingleton (x ⟶ y) := Discrete.instSubsingletonDiscreteHom _ _
/-- Auxiliary definition for `inclusion`. -/
@[simp]
def inclusionObj : NormalMonoidalObject C → F C
| NormalMonoidalObject.unit => unit
| NormalMonoidalObject.tensor n a => tensor (inclusionObj n) (of a)
#align category_theory.free_monoidal_category.inclusion_obj CategoryTheory.FreeMonoidalCategory.inclusionObj
/-- The discrete subcategory of objects in normal form includes into the free monoidal category. -/
def inclusion : N C ⥤ F C :=
Discrete.functor inclusionObj
#align category_theory.free_monoidal_category.inclusion CategoryTheory.FreeMonoidalCategory.inclusion
@[simp]
theorem inclusion_obj (X : N C) :
inclusion.obj X = inclusionObj X.as :=
rfl
@[simp]
theorem inclusion_map {X Y : N C} (f : X ⟶ Y) :
inclusion.map f = eqToHom (congr_arg _ (Discrete.ext _ _ (Discrete.eq_of_hom f))) := by
rcases f with ⟨⟨⟩⟩
cases Discrete.ext _ _ (by assumption)
apply inclusion.map_id
/-- Auxiliary definition for `normalize`. -/
def normalizeObj : F C → NormalMonoidalObject C → NormalMonoidalObject C
| unit, n => n
| of X, n => NormalMonoidalObject.tensor n X
| tensor X Y, n => normalizeObj Y (normalizeObj X n)
#align category_theory.free_monoidal_category.normalize_obj CategoryTheory.FreeMonoidalCategory.normalizeObj
@[simp]
theorem normalizeObj_unitor (n : NormalMonoidalObject C) : normalizeObj (𝟙_ (F C)) n = n :=
rfl
#align category_theory.free_monoidal_category.normalize_obj_unitor CategoryTheory.FreeMonoidalCategory.normalizeObj_unitor
@[simp]
theorem normalizeObj_tensor (X Y : F C) (n : NormalMonoidalObject C) :
normalizeObj (X ⊗ Y) n = normalizeObj Y (normalizeObj X n) :=
rfl
#align category_theory.free_monoidal_category.normalize_obj_tensor CategoryTheory.FreeMonoidalCategory.normalizeObj_tensor
/-- Auxiliary definition for `normalize`. -/
def normalizeObj' (X : F C) : N C ⥤ N C := Discrete.functor fun n ↦ ⟨normalizeObj X n⟩
section
open Hom
/-- Auxiliary definition for `normalize`. Here we prove that objects that are related by
associators and unitors map to the same normal form. -/
@[simp]
def normalizeMapAux : ∀ {X Y : F C}, (X ⟶ᵐ Y) → (normalizeObj' X ⟶ normalizeObj' Y)
| _, _, Hom.id _ => 𝟙 _
| _, _, α_hom X Y Z => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, α_inv _ _ _ => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, l_hom _ => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, l_inv _ => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, ρ_hom _ => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, ρ_inv _ => by dsimp; exact Discrete.natTrans (fun _ => 𝟙 _)
| _, _, (@comp _ _ _ _ f g) => normalizeMapAux f ≫ normalizeMapAux g
| _, _, (@Hom.tensor _ T _ _ W f g) =>
Discrete.natTrans <| fun ⟨X⟩ => (normalizeMapAux g).app ⟨normalizeObj T X⟩ ≫
(normalizeObj' W).map ((normalizeMapAux f).app ⟨X⟩)
| _, _, (@Hom.whiskerLeft _ T _ W f) =>
Discrete.natTrans <| fun ⟨X⟩ => (normalizeMapAux f).app ⟨normalizeObj T X⟩
| _, _, (@Hom.whiskerRight _ T _ f W) =>
Discrete.natTrans <| fun X => (normalizeObj' W).map <| (normalizeMapAux f).app X
#align category_theory.free_monoidal_category.normalize_map_aux CategoryTheory.FreeMonoidalCategory.normalizeMapAux
end
section
variable (C)
/-- Our normalization procedure works by first defining a functor `F C ⥤ (N C ⥤ N C)` (which turns
out to be very easy), and then obtain a functor `F C ⥤ N C` by plugging in the normal object
`𝟙_ C`. -/
@[simp]
def normalize : F C ⥤ N C ⥤ N C where
obj X := normalizeObj' X
map {X Y} := Quotient.lift normalizeMapAux (by aesop_cat)
#align category_theory.free_monoidal_category.normalize CategoryTheory.FreeMonoidalCategory.normalize
/-- A variant of the normalization functor where we consider the result as an object in the free
monoidal category (rather than an object of the discrete subcategory of objects in normal
form). -/
@[simp]
def normalize' : F C ⥤ N C ⥤ F C :=
normalize C ⋙ (whiskeringRight _ _ _).obj inclusion
#align category_theory.free_monoidal_category.normalize' CategoryTheory.FreeMonoidalCategory.normalize'
/-- The normalization functor for the free monoidal category over `C`. -/
def fullNormalize : F C ⥤ N C where
obj X := ((normalize C).obj X).obj ⟨NormalMonoidalObject.unit⟩
map f := ((normalize C).map f).app ⟨NormalMonoidalObject.unit⟩
#align category_theory.free_monoidal_category.full_normalize CategoryTheory.FreeMonoidalCategory.fullNormalize
/-- Given an object `X` of the free monoidal category and an object `n` in normal form, taking
the tensor product `n ⊗ X` in the free monoidal category is functorial in both `X` and `n`. -/
@[simp]
def tensorFunc : F C ⥤ N C ⥤ F C where
obj X := Discrete.functor fun n => inclusion.obj ⟨n⟩ ⊗ X
map f := Discrete.natTrans (fun n => _ ◁ f)
#align category_theory.free_monoidal_category.tensor_func CategoryTheory.FreeMonoidalCategory.tensorFunc
theorem tensorFunc_map_app {X Y : F C} (f : X ⟶ Y) (n) : ((tensorFunc C).map f).app n = _ ◁ f :=
rfl
#align category_theory.free_monoidal_category.tensor_func_map_app CategoryTheory.FreeMonoidalCategory.tensorFunc_map_app
theorem tensorFunc_obj_map (Z : F C) {n n' : N C} (f : n ⟶ n') :
((tensorFunc C).obj Z).map f = inclusion.map f ▷ Z := by
cases n
cases n'
rcases f with ⟨⟨h⟩⟩
dsimp at h
subst h
simp
#align category_theory.free_monoidal_category.tensor_func_obj_map CategoryTheory.FreeMonoidalCategory.tensorFunc_obj_map
/-- Auxiliary definition for `normalizeIso`. Here we construct the isomorphism between
`n ⊗ X` and `normalize X n`. -/
@[simp]
def normalizeIsoApp :
∀ (X : F C) (n : N C), ((tensorFunc C).obj X).obj n ≅ ((normalize' C).obj X).obj n
| of _, _ => Iso.refl _
| unit, _ => ρ_ _
| tensor X a, n =>
(α_ _ _ _).symm ≪≫ whiskerRightIso (normalizeIsoApp X n) a ≪≫ normalizeIsoApp _ _
#align category_theory.free_monoidal_category.normalize_iso_app CategoryTheory.FreeMonoidalCategory.normalizeIsoApp
/-- Almost non-definitionally equall to `normalizeIsoApp`, but has a better definitional property
in the proof of `normalize_naturality`. -/
@[simp]
def normalizeIsoApp' :
∀ (X : F C) (n : NormalMonoidalObject C), inclusionObj n ⊗ X ≅ inclusionObj (normalizeObj X n)
| of _, _ => Iso.refl _
| unit, _ => ρ_ _
| tensor X Y, n =>
(α_ _ _ _).symm ≪≫ whiskerRightIso (normalizeIsoApp' X n) Y ≪≫ normalizeIsoApp' _ _
theorem normalizeIsoApp_eq :
∀ (X : F C) (n : N C), normalizeIsoApp C X n = normalizeIsoApp' C X n.as
| of X, _ => rfl
| unit, _ => rfl
| tensor X Y, n => by
rw [normalizeIsoApp, normalizeIsoApp']
rw [normalizeIsoApp_eq X n]
rw [normalizeIsoApp_eq Y ⟨normalizeObj X n.as⟩]
rfl
@[simp]
theorem normalizeIsoApp_tensor (X Y : F C) (n : N C) :
normalizeIsoApp C (X ⊗ Y) n =
(α_ _ _ _).symm ≪≫ whiskerRightIso (normalizeIsoApp C X n) Y ≪≫ normalizeIsoApp _ _ _ :=
rfl
#align category_theory.free_monoidal_category.normalize_iso_app_tensor CategoryTheory.FreeMonoidalCategory.normalizeIsoApp_tensor
@[simp]
theorem normalizeIsoApp_unitor (n : N C) : normalizeIsoApp C (𝟙_ (F C)) n = ρ_ _ :=
rfl
#align category_theory.free_monoidal_category.normalize_iso_app_unitor CategoryTheory.FreeMonoidalCategory.normalizeIsoApp_unitor
/-- Auxiliary definition for `normalizeIso`. -/
@[simp]
def normalizeIsoAux (X : F C) : (tensorFunc C).obj X ≅ (normalize' C).obj X :=
NatIso.ofComponents (normalizeIsoApp C X)
(by
rintro ⟨X⟩ ⟨Y⟩ ⟨⟨f⟩⟩
dsimp at f
subst f
dsimp
simp)
#align category_theory.free_monoidal_category.normalize_iso_aux CategoryTheory.FreeMonoidalCategory.normalizeIsoAux
section
variable {D : Type u} [Category.{u} D] {I : Type u} (f : I → D) (X : Discrete I)
-- TODO: move to discrete_category.lean, decide whether this should be a global simp lemma
@[simp]
theorem discrete_functor_obj_eq_as : (Discrete.functor f).obj X = f X.as :=
rfl
#align category_theory.free_monoidal_category.discrete_functor_obj_eq_as CategoryTheory.FreeMonoidalCategory.discrete_functor_obj_eq_as
-- TODO: move to discrete_category.lean, decide whether this should be a global simp lemma
@[simp 1100]
theorem discrete_functor_map_eq_id (g : X ⟶ X) : (Discrete.functor f).map g = 𝟙 _ := rfl
#align category_theory.free_monoidal_category.discrete_functor_map_eq_id CategoryTheory.FreeMonoidalCategory.discrete_functor_map_eq_id
end
section
variable {C}
theorem normalizeObj_congr (n : NormalMonoidalObject C) {X Y : F C} (f : X ⟶ Y) :
normalizeObj X n = normalizeObj Y n := by
rcases f with ⟨f'⟩
apply @congr_fun _ _ fun n => normalizeObj X n
clear n f
induction f' with
| comp _ _ _ _ => apply Eq.trans <;> assumption
| whiskerLeft _ _ ih => funext; apply congr_fun ih
| whiskerRight _ _ ih => funext; apply congr_arg₂ _ rfl (congr_fun ih _)
| @tensor W X Y Z _ _ ih₁ ih₂ =>
funext n
simp [congr_fun ih₁ n, congr_fun ih₂ (normalizeObj Y n)]
| _ => funext; rfl
theorem normalize_naturality (n : NormalMonoidalObject C) {X Y : F C} (f : X ⟶ Y) :
inclusionObj n ◁ f ≫ (normalizeIsoApp' C Y n).hom =
(normalizeIsoApp' C X n).hom ≫
inclusion.map (eqToHom (Discrete.ext _ _ (normalizeObj_congr n f))) := by
revert n
induction f using Hom.inductionOn
case comp f g ihf ihg => simp [ihg, reassoc_of% (ihf _)]
case whiskerLeft X' X Y f ih =>
intro n
dsimp only [normalizeObj_tensor, normalizeIsoApp', tensor_eq_tensor, Iso.trans_hom,
Iso.symm_hom, whiskerRightIso_hom, Function.comp_apply, inclusion_obj]
rw [associator_inv_naturality_right_assoc, whisker_exchange_assoc, ih]
simp
case whiskerRight X Y h η' ih =>
intro n
dsimp only [normalizeObj_tensor, normalizeIsoApp', tensor_eq_tensor, Iso.trans_hom,
Iso.symm_hom, whiskerRightIso_hom, Function.comp_apply, inclusion_obj]
rw [associator_inv_naturality_middle_assoc, ← comp_whiskerRight_assoc, ih]
have := dcongr_arg (fun x => (normalizeIsoApp' C η' x).hom) (normalizeObj_congr n h)
simp [this]
all_goals simp
end
set_option tactic.skipAssignedInstances false in
/-- The isomorphism between `n ⊗ X` and `normalize X n` is natural (in both `X` and `n`, but
naturality in `n` is trivial and was "proved" in `normalizeIsoAux`). This is the real heart
of our proof of the coherence theorem. -/
def normalizeIso : tensorFunc C ≅ normalize' C :=
NatIso.ofComponents (normalizeIsoAux C) <| by
intro X Y f
ext ⟨n⟩
convert normalize_naturality n f using 1
any_goals dsimp [NatIso.ofComponents]; congr; apply normalizeIsoApp_eq
#align category_theory.free_monoidal_category.normalize_iso CategoryTheory.FreeMonoidalCategory.normalizeIso
/-- The isomorphism between an object and its normal form is natural. -/
def fullNormalizeIso : 𝟭 (F C) ≅ fullNormalize C ⋙ inclusion :=
NatIso.ofComponents
(fun X => (λ_ X).symm ≪≫ ((normalizeIso C).app X).app ⟨NormalMonoidalObject.unit⟩)
(by
intro X Y f
dsimp
rw [leftUnitor_inv_naturality_assoc, Category.assoc, Iso.cancel_iso_inv_left]
exact
congr_arg (fun f => NatTrans.app f (Discrete.mk NormalMonoidalObject.unit))
((normalizeIso.{u} C).hom.naturality f))
#align category_theory.free_monoidal_category.full_normalize_iso CategoryTheory.FreeMonoidalCategory.fullNormalizeIso
end
/-- The monoidal coherence theorem. -/
instance subsingleton_hom : Quiver.IsThin (F C) := fun X Y =>
⟨fun f g => by
have hfg : (fullNormalize C).map f = (fullNormalize C).map g := Subsingleton.elim _ _
have hf := NatIso.naturality_2 (fullNormalizeIso.{u} C) f
have hg := NatIso.naturality_2 (fullNormalizeIso.{u} C) g
exact hf.symm.trans (Eq.trans (by simp only [Functor.comp_map, hfg]) hg)⟩
#align category_theory.free_monoidal_category.subsingleton_hom CategoryTheory.FreeMonoidalCategory.subsingleton_hom
section Groupoid
section
open Hom
/-- Auxiliary construction for showing that the free monoidal category is a groupoid. Do not use
this, use `IsIso.inv` instead. -/
def inverseAux : ∀ {X Y : F C}, (X ⟶ᵐ Y) → (Y ⟶ᵐ X)
| _, _, Hom.id X => id X
| _, _, α_hom _ _ _ => α_inv _ _ _
| _, _, α_inv _ _ _ => α_hom _ _ _
| _, _, ρ_hom _ => ρ_inv _
| _, _, ρ_inv _ => ρ_hom _
| _, _, l_hom _ => l_inv _
| _, _, l_inv _ => l_hom _
| _, _, comp f g => (inverseAux g).comp (inverseAux f)
| _, _, Hom.whiskerLeft X f => (inverseAux f).whiskerLeft X
| _, _, Hom.whiskerRight f X => (inverseAux f).whiskerRight X
| _, _, Hom.tensor f g => (inverseAux f).tensor (inverseAux g)
#align category_theory.free_monoidal_category.inverse_aux CategoryTheory.FreeMonoidalCategory.inverseAux
end
instance : Groupoid.{u} (F C) :=
{ (inferInstance : Category (F C)) with
inv := Quotient.lift (fun f => ⟦inverseAux f⟧) (by aesop_cat) }
end Groupoid
end FreeMonoidalCategory
end CategoryTheory