Skip to content

Commit

Permalink
feat(category_theory/limits): add characteristic predicate for zero o…
Browse files Browse the repository at this point in the history
…bjects (#13511)
  • Loading branch information
jcommelin committed Apr 19, 2022
1 parent 5dc8c1c commit 634eab1
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 82 deletions.
2 changes: 1 addition & 1 deletion src/algebra/category/Group/zero.lean
Expand Up @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.category.Group.basic
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms

/-!
# The category of (commutative) (additive) groups has a zero object.
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/normed/group/SemiNormedGroup.lean
Expand Up @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Riccardo Brasca
-/
import analysis.normed.group.hom
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms
import category_theory.concrete_category.bundled_hom

/-!
Expand Down
2 changes: 1 addition & 1 deletion src/category_theory/closed/zero.lean
Expand Up @@ -5,7 +5,7 @@ Authors: Bhavik Mehta
-/

import category_theory.closed.cartesian
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms
import category_theory.punit
import category_theory.conj

Expand Down
2 changes: 1 addition & 1 deletion src/category_theory/limits/preserves/shapes/zero.lean
Expand Up @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import category_theory.limits.preserves.shapes.terminal
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms

/-!
# Preservation of zero objects and zero morphisms
Expand Down
2 changes: 1 addition & 1 deletion src/category_theory/limits/shapes/default.lean
Expand Up @@ -5,7 +5,7 @@ import category_theory.limits.shapes.finite_products
import category_theory.limits.shapes.finite_limits
import category_theory.limits.shapes.biproducts
import category_theory.limits.shapes.images
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms
import category_theory.limits.shapes.kernels
import category_theory.limits.shapes.equalizers
import category_theory.limits.shapes.wide_pullbacks
Expand Down
Expand Up @@ -6,6 +6,7 @@ Authors: Scott Morrison
import category_theory.limits.shapes.products
import category_theory.limits.shapes.images
import category_theory.isomorphism_classes
import category_theory.limits.shapes.zero_objects

/-!
# Zero morphisms and zero objects
Expand Down Expand Up @@ -131,51 +132,24 @@ instance : has_zero_morphisms (C ⥤ D) :=

end

variables (C)

/-- A category "has a zero object" if it has an object which is both initial and terminal. -/
class has_zero_object :=
(zero : C)
(unique_to : Π X : C, unique (zero ⟶ X))
(unique_from : Π X : C, unique (X ⟶ zero))

instance has_zero_object_punit : has_zero_object (discrete punit) :=
{ zero := punit.star,
unique_to := by tidy,
unique_from := by tidy, }
/-- A category with a zero object has zero morphisms.
variables {C}
It is rarely a good idea to use this. Many categories that have a zero object have zero
morphisms for some other reason, for example from additivity. Library code that uses
`zero_morphisms_of_zero_object` will then be incompatible with these categories because
the `has_zero_morphisms` instances will not be definitionally equal. For this reason library
code should generally ask for an instance of `has_zero_morphisms` separately, even if it already
asks for an instance of `has_zero_objects`. -/
def is_zero.has_zero_morphisms {O : C} (hO : is_zero O) : has_zero_morphisms C :=
{ has_zero := λ X Y,
{ zero := hO.from X ≫ hO.to Y },
zero_comp' := λ X Y Z f, by { rw category.assoc, congr, apply hO.eq_of_src, },
comp_zero' := λ X Y Z f, by { rw ←category.assoc, congr, apply hO.eq_of_tgt, }}

namespace has_zero_object

variables [has_zero_object C]

/--
Construct a `has_zero C` for a category with a zero object.
This can not be a global instance as it will trigger for every `has_zero C` typeclass search.
-/
protected def has_zero : has_zero C :=
{ zero := has_zero_object.zero }

localized "attribute [instance] category_theory.limits.has_zero_object.has_zero" in zero_object
localized "attribute [instance] category_theory.limits.has_zero_object.unique_to" in zero_object
localized "attribute [instance] category_theory.limits.has_zero_object.unique_from" in zero_object

@[ext]
lemma to_zero_ext {X : C} (f g : X ⟶ 0) : f = g :=
by rw [(has_zero_object.unique_from X).uniq f, (has_zero_object.unique_from X).uniq g]

@[ext]
lemma from_zero_ext {X : C} (f g : 0 ⟶ X) : f = g :=
by rw [(has_zero_object.unique_to X).uniq f, (has_zero_object.unique_to X).uniq g]

instance (X : C) : subsingleton (X ≅ 0) := by tidy

instance {X : C} (f : 0 ⟶ X) : mono f :=
{ right_cancellation := λ Z g h w, by ext, }

instance {X : C} (f : X ⟶ 0) : epi f :=
{ left_cancellation := λ Z g h w, by ext, }
open_locale zero_object

/-- A category with a zero object has zero morphisms.
Expand All @@ -191,38 +165,6 @@ def zero_morphisms_of_zero_object : has_zero_morphisms C :=
zero_comp' := λ X Y Z f, by { dunfold has_zero.zero, rw category.assoc, congr, },
comp_zero' := λ X Y Z f, by { dunfold has_zero.zero, rw ←category.assoc, congr, }}

/-- A zero object is in particular initial. -/
def zero_is_initial : is_initial (0 : C) :=
is_initial.of_unique 0
/-- A zero object is in particular terminal. -/
def zero_is_terminal : is_terminal (0 : C) :=
is_terminal.of_unique 0

/-- A zero object is in particular initial. -/
@[priority 10]
instance has_initial : has_initial C :=
has_initial_of_unique 0
/-- A zero object is in particular terminal. -/
@[priority 10]
instance has_terminal : has_terminal C :=
has_terminal_of_unique 0

/-- The (unique) isomorphism between any initial object and the zero object. -/
def zero_iso_is_initial {X : C} (t : is_initial X) : 0 ≅ X :=
zero_is_initial.unique_up_to_iso t

/-- The (unique) isomorphism between any terminal object and the zero object. -/
def zero_iso_is_terminal {X : C} (t : is_terminal X) : 0 ≅ X :=
zero_is_terminal.unique_up_to_iso t

/-- The (unique) isomorphism between the chosen initial object and the chosen zero object. -/
def zero_iso_initial [has_initial C] : 0 ≅ ⊥_ C :=
zero_is_initial.unique_up_to_iso initial_is_initial

/-- The (unique) isomorphism between the chosen terminal object and the chosen zero object. -/
def zero_iso_terminal [has_terminal C] : 0 ≅ ⊤_ C :=
zero_is_terminal.unique_up_to_iso terminal_is_terminal

section has_zero_morphisms
variables [has_zero_morphisms C]

Expand Down Expand Up @@ -256,10 +198,6 @@ by ext

end has_zero_morphisms

@[priority 100]
instance has_strict_initial : initial_mono_class C :=
initial_mono_class.of_is_initial zero_is_initial (λ X, category_theory.mono _)

open_locale zero_object

instance {B : Type*} [category B] [has_zero_morphisms C] : has_zero_object (B ⥤ C) :=
Expand Down
205 changes: 205 additions & 0 deletions src/category_theory/limits/shapes/zero_objects.lean
@@ -0,0 +1,205 @@
/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Johan Commelin
-/
import category_theory.limits.shapes.products
import category_theory.limits.shapes.images
import category_theory.isomorphism_classes

/-!
# Zero objects
A category "has a zero object" if it has an object which is both initial and terminal. Having a
zero object provides zero morphisms, as the unique morphisms factoring through the zero object;
see `category_theory.limits.shapes.zero_morphisms`.
## References
* [F. Borceux, *Handbook of Categorical Algebra 2*][borceux-vol2]
-/

noncomputable theory

universes v u v' u'

open category_theory
open category_theory.category

namespace category_theory.limits

variables {C : Type u} [category.{v} C]
variables {D : Type u'} [category.{v'} D]

/-- An object `X` in a category is a *zero object* if for every object `Y`
there is a unique morphism `to : X → Y` and a unique morphism `from : Y → X`.
This is a characteristic predicate for `has_zero_object`. -/
structure is_zero (X : C) : Prop :=
(unique_to : ∀ Y, nonempty (unique (X ⟶ Y)))
(unique_from : ∀ Y, nonempty (unique (Y ⟶ X)))

namespace is_zero

variables {X Y : C}

/-- If `h : is_zero X`, then `h.to Y` is a choice of unique morphism `X → Y`. -/
protected def «to» (h : is_zero X) (Y : C) : X ⟶ Y :=
@default (X ⟶ Y) $ @unique.inhabited _ $ (h.unique_to Y).some

lemma eq_to (h : is_zero X) (f : X ⟶ Y) : f = h.to Y :=
@unique.eq_default _ (id _) _

lemma to_eq (h : is_zero X) (f : X ⟶ Y) : h.to Y = f :=
(h.eq_to f).symm

/-- If `h : is_zero X`, then `h.from Y` is a choice of unique morphism `Y → X`. -/
protected def «from» (h : is_zero X) (Y : C) : Y ⟶ X :=
@default (Y ⟶ X) $ @unique.inhabited _ $ (h.unique_from Y).some

lemma eq_from (h : is_zero X) (f : Y ⟶ X) : f = h.from Y :=
@unique.eq_default _ (id _) _

lemma from_eq (h : is_zero X) (f : Y ⟶ X) : h.from Y = f :=
(h.eq_from f).symm

lemma eq_of_src (hX : is_zero X) (f g : X ⟶ Y) : f = g :=
(hX.eq_to f).trans (hX.eq_to g).symm

lemma eq_of_tgt (hX : is_zero X) (f g : Y ⟶ X) : f = g :=
(hX.eq_from f).trans (hX.eq_from g).symm

/-- Any two zero objects are isomorphic. -/
def iso (hX : is_zero X) (hY : is_zero Y) : X ≅ Y :=
{ hom := hX.to Y,
inv := hX.from Y,
hom_inv_id' := hX.eq_of_src _ _,
inv_hom_id' := hY.eq_of_src _ _, }

/-- A zero object is in particular initial. -/
protected def is_initial (hX : is_zero X) : is_initial X :=
@is_initial.of_unique _ _ X $ λ Y, (hX.unique_to Y).some

/-- A zero object is in particular terminal. -/
protected def is_terminal (hX : is_zero X) : is_terminal X :=
@is_terminal.of_unique _ _ X $ λ Y, (hX.unique_from Y).some

/-- The (unique) isomorphism between any initial object and the zero object. -/
def iso_is_initial (hX : is_zero X) (hY : is_initial Y) : X ≅ Y :=
hX.is_initial.unique_up_to_iso hY

/-- The (unique) isomorphism between any terminal object and the zero object. -/
def iso_is_terminal (hX : is_zero X) (hY : is_terminal Y) : X ≅ Y :=
hX.is_terminal.unique_up_to_iso hY

lemma of_iso (hY : is_zero Y) (e : X ≅ Y) : is_zero X :=
begin
refine ⟨λ Z, ⟨⟨⟨e.hom ≫ hY.to Z⟩, λ f, _⟩⟩, λ Z, ⟨⟨⟨hY.from Z ≫ e.inv⟩, λ f, _⟩⟩⟩,
{ rw ← cancel_epi e.inv, apply hY.eq_of_src, },
{ rw ← cancel_mono e.hom, apply hY.eq_of_tgt, },
end

end is_zero

lemma iso.is_zero_iff {X Y : C} (e : X ≅ Y) :
is_zero X ↔ is_zero Y :=
⟨λ h, h.of_iso e.symm, λ h, h.of_iso e⟩

variables (C)

/-- A category "has a zero object" if it has an object which is both initial and terminal. -/
class has_zero_object :=
(zero : C)
(unique_to : Π X : C, unique (zero ⟶ X))
(unique_from : Π X : C, unique (X ⟶ zero))

instance has_zero_object_punit : has_zero_object (discrete punit) :=
{ zero := punit.star,
unique_to := by tidy,
unique_from := by tidy, }


namespace has_zero_object

variables [has_zero_object C]

/--
Construct a `has_zero C` for a category with a zero object.
This can not be a global instance as it will trigger for every `has_zero C` typeclass search.
-/
protected def has_zero : has_zero C :=
{ zero := has_zero_object.zero }

localized "attribute [instance] category_theory.limits.has_zero_object.has_zero" in zero_object
localized "attribute [instance] category_theory.limits.has_zero_object.unique_to" in zero_object
localized "attribute [instance] category_theory.limits.has_zero_object.unique_from" in zero_object

lemma is_zero_zero : is_zero (0 : C) :=
{ unique_to := λ Y, ⟨has_zero_object.unique_to Y⟩,
unique_from := λ Y, ⟨has_zero_object.unique_from Y⟩ }

/-- Every zero object is isomorphic to *the* zero object. -/
def is_zero.iso_zero {X : C} (hX : is_zero X) : X ≅ 0 :=
hX.iso (is_zero_zero C)

variables {C}

@[ext]
lemma to_zero_ext {X : C} (f g : X ⟶ 0) : f = g :=
(is_zero_zero C).eq_of_tgt _ _

@[ext]
lemma from_zero_ext {X : C} (f g : 0 ⟶ X) : f = g :=
(is_zero_zero C).eq_of_src _ _

instance (X : C) : subsingleton (X ≅ 0) := by tidy

instance {X : C} (f : 0 ⟶ X) : mono f :=
{ right_cancellation := λ Z g h w, by ext, }

instance {X : C} (f : X ⟶ 0) : epi f :=
{ left_cancellation := λ Z g h w, by ext, }

/-- A zero object is in particular initial. -/
def zero_is_initial : is_initial (0 : C) :=
is_initial.of_unique 0

/-- A zero object is in particular terminal. -/
def zero_is_terminal : is_terminal (0 : C) :=
is_terminal.of_unique 0

/-- A zero object is in particular initial. -/
@[priority 10]
instance has_initial : has_initial C :=
has_initial_of_unique 0

/-- A zero object is in particular terminal. -/
@[priority 10]
instance has_terminal : has_terminal C :=
has_terminal_of_unique 0

/-- The (unique) isomorphism between any initial object and the zero object. -/
def zero_iso_is_initial {X : C} (t : is_initial X) : 0 ≅ X :=
zero_is_initial.unique_up_to_iso t

/-- The (unique) isomorphism between any terminal object and the zero object. -/
def zero_iso_is_terminal {X : C} (t : is_terminal X) : 0 ≅ X :=
zero_is_terminal.unique_up_to_iso t

/-- The (unique) isomorphism between the chosen initial object and the chosen zero object. -/
def zero_iso_initial [has_initial C] : 0 ≅ ⊥_ C :=
zero_is_initial.unique_up_to_iso initial_is_initial

/-- The (unique) isomorphism between the chosen terminal object and the chosen zero object. -/
def zero_iso_terminal [has_terminal C] : 0 ≅ ⊤_ C :=
zero_is_terminal.unique_up_to_iso terminal_is_terminal

@[priority 100]
instance has_strict_initial : initial_mono_class C :=
initial_mono_class.of_is_initial zero_is_initial (λ X, category_theory.mono _)

open_locale zero_object

end has_zero_object

end category_theory.limits
2 changes: 1 addition & 1 deletion src/category_theory/simple.lean
Expand Up @@ -3,7 +3,7 @@ Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Scott Morrison
-/
import category_theory.limits.shapes.zero
import category_theory.limits.shapes.zero_morphisms
import category_theory.limits.shapes.kernels
import category_theory.abelian.basic

Expand Down

0 comments on commit 634eab1

Please sign in to comment.