Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit ca7fee8

Browse files
committed
feat(category_theory/limits): Results about pullbacks (#9984)
- Provided the explicit isomorphism `X ×[Z] Y ≅ Y ×[Z] X`. - Provided the pullback of f g when either one is iso or when both are mono. Co-authored-by: erdOne <36414270+erdOne@users.noreply.github.com>
1 parent be0a4af commit ca7fee8

File tree

1 file changed

+351
-0
lines changed

1 file changed

+351
-0
lines changed

src/category_theory/limits/shapes/pullbacks.lean

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,27 @@ begin
426426
{ rwa (is_colimit.desc' t _ _ _).2.2 },
427427
end
428428

429+
/--
430+
The pushout cocone `(𝟙 X, 𝟙 X)` for the pair `(f, f)` is a colimit if `f` is an epi. The converse is
431+
shown in `epi_of_is_colimit_mk_id_id`.
432+
-/
433+
def is_colimit_mk_id_id (f : X ⟶ Y) [epi f] :
434+
is_colimit (mk (𝟙 Y) (𝟙 Y) rfl : pushout_cocone f f) :=
435+
is_colimit.mk _
436+
(λ s, s.inl)
437+
(λ s, category.id_comp _)
438+
(λ s, by rw [←cancel_epi f, category.id_comp, s.condition])
439+
(λ s m m₁ m₂, by simpa using m₁)
440+
441+
/--
442+
`f` is an epi if the pushout cocone `(𝟙 X, 𝟙 X)` is a colimit for the pair `(f, f)`.
443+
The converse is given in `pushout_cocone.is_colimit_mk_id_id`.
444+
-/
445+
lemma epi_of_is_colimit_mk_id_id (f : X ⟶ Y)
446+
(t : is_colimit (mk (𝟙 Y) (𝟙 Y) rfl : pushout_cocone f f)) :
447+
epi f :=
448+
⟨λ Z g h eq, by { rcases pushout_cocone.is_colimit.desc' t _ _ eq with ⟨_, rfl, rfl⟩, refl }⟩
449+
429450
/-- Suppose `f` and `g` are two morphisms with a common domain and `s` is a colimit cocone over the
430451
diagram formed by `f` and `g`. Suppose `f` and `g` both factor through an epimorphism `h` via
431452
`x` and `y`, respectively. Then `s` is also a colimit cocone over the diagram formed by `x` and
@@ -614,6 +635,12 @@ pullback_cone.mono_snd_of_is_pullback_of_mono (limit.is_limit _)
614635
(h₁ : pushout.inr ≫ k = pushout.inr ≫ l) : k = l :=
615636
colimit.hom_ext $ pushout_cocone.coequalizer_ext _ h₀ h₁
616637

638+
/-- The pushout cocone built from the pushout coprojections is a pushout. -/
639+
def pushout_is_pushout {X Y Z : C} (f : X ⟶ Y) (g : X ⟶ Z) [has_pushout f g] :
640+
is_colimit (pushout_cocone.mk (pushout.inl : _ ⟶ pushout f g) pushout.inr pushout.condition) :=
641+
pushout_cocone.is_colimit.mk _ (λ s, pushout.desc s.inl s.inr s.condition)
642+
(by simp) (by simp) (by tidy)
643+
617644
/-- The pushout of an epimorphism is an epimorphism -/
618645
instance pushout.inl_of_epi {X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [has_pushout f g] [epi g] :
619646
epi (pushout.inl : Y ⟶ pushout f g) :=
@@ -661,6 +688,330 @@ by { ext; simp [← G.map_comp] }
661688

662689
end
663690

691+
section pullback_symmetry
692+
693+
open walking_cospan
694+
695+
variables (f : X ⟶ Z) (g : Y ⟶ Z)
696+
697+
/-- Making this a global instance would make the typeclass seach go in an infinite loop. -/
698+
lemma has_pullback_symmetry [has_pullback f g] : has_pullback g f :=
699+
⟨⟨⟨pullback_cone.mk _ _ pullback.condition.symm,
700+
pullback_cone.flip_is_limit (pullback_is_pullback _ _)⟩⟩⟩
701+
702+
local attribute [instance] has_pullback_symmetry
703+
704+
/-- The isomorphism `X ×[Z] Y ≅ Y ×[Z] X`. -/
705+
def pullback_symmetry [has_pullback f g] :
706+
pullback f g ≅ pullback g f :=
707+
is_limit.cone_point_unique_up_to_iso
708+
(pullback_cone.flip_is_limit (pullback_is_pullback f g) :
709+
is_limit (pullback_cone.mk _ _ pullback.condition.symm))
710+
(limit.is_limit _)
711+
712+
@[simp, reassoc] lemma pullback_symmetry_hom_comp_fst [has_pullback f g] :
713+
(pullback_symmetry f g).hom ≫ pullback.fst = pullback.snd := by simp [pullback_symmetry]
714+
715+
@[simp, reassoc] lemma pullback_symmetry_hom_comp_snd [has_pullback f g] :
716+
(pullback_symmetry f g).hom ≫ pullback.snd = pullback.fst := by simp [pullback_symmetry]
717+
718+
@[simp, reassoc] lemma pullback_symmetry_inv_comp_fst [has_pullback f g] :
719+
(pullback_symmetry f g).inv ≫ pullback.fst = pullback.snd := by simp [iso.inv_comp_eq]
720+
721+
@[simp, reassoc] lemma pullback_symmetry_inv_comp_snd [has_pullback f g] :
722+
(pullback_symmetry f g).inv ≫ pullback.snd = pullback.fst := by simp [iso.inv_comp_eq]
723+
724+
end pullback_symmetry
725+
726+
section pushout_symmetry
727+
728+
open walking_cospan
729+
730+
variables (f : X ⟶ Y) (g : X ⟶ Z)
731+
732+
/-- Making this a global instance would make the typeclass seach go in an infinite loop. -/
733+
lemma has_pushout_symmetry [has_pushout f g] : has_pushout g f :=
734+
⟨⟨⟨pushout_cocone.mk _ _ pushout.condition.symm,
735+
pushout_cocone.flip_is_colimit (pushout_is_pushout _ _)⟩⟩⟩
736+
737+
local attribute [instance] has_pushout_symmetry
738+
739+
/-- The isomorphism `Y ⨿[X] Z ≅ Z ⨿[X] Y`. -/
740+
def pushout_symmetry [has_pushout f g] :
741+
pushout f g ≅ pushout g f :=
742+
is_colimit.cocone_point_unique_up_to_iso
743+
(pushout_cocone.flip_is_colimit (pushout_is_pushout f g) :
744+
is_colimit (pushout_cocone.mk _ _ pushout.condition.symm))
745+
(colimit.is_colimit _)
746+
747+
@[simp, reassoc] lemma inl_comp_pushout_symmetry_hom [has_pushout f g] :
748+
pushout.inl ≫ (pushout_symmetry f g).hom = pushout.inr :=
749+
(colimit.is_colimit (span f g)).comp_cocone_point_unique_up_to_iso_hom
750+
(pushout_cocone.flip_is_colimit (pushout_is_pushout g f)) _
751+
752+
@[simp, reassoc] lemma inr_comp_pushout_symmetry_hom [has_pushout f g] :
753+
pushout.inr ≫ (pushout_symmetry f g).hom = pushout.inl :=
754+
(colimit.is_colimit (span f g)).comp_cocone_point_unique_up_to_iso_hom
755+
(pushout_cocone.flip_is_colimit (pushout_is_pushout g f)) _
756+
757+
@[simp, reassoc] lemma inl_comp_pushout_symmetry_inv [has_pushout f g] :
758+
pushout.inl ≫ (pushout_symmetry f g).inv = pushout.inr := by simp [iso.comp_inv_eq]
759+
760+
@[simp, reassoc] lemma inr_comp_pushout_symmetry_inv [has_pushout f g] :
761+
pushout.inr ≫ (pushout_symmetry f g).inv = pushout.inl := by simp [iso.comp_inv_eq]
762+
763+
end pushout_symmetry
764+
765+
section pullback_left_iso
766+
767+
open walking_cospan
768+
769+
variables (f : X ⟶ Z) (g : Y ⟶ Z) [is_iso f]
770+
771+
/-- If `f : X ⟶ Z` is iso, then `X ×[Z] Y ≅ Y`. This is the explicit limit cone. -/
772+
def pullback_cone_of_left_iso : pullback_cone f g :=
773+
pullback_cone.mk (g ≫ inv f) (𝟙 _) $ by simp
774+
775+
@[simp] lemma pullback_cone_of_left_iso_X :
776+
(pullback_cone_of_left_iso f g).X = Y := rfl
777+
778+
@[simp] lemma pullback_cone_of_left_iso_fst :
779+
(pullback_cone_of_left_iso f g).fst = g ≫ inv f := rfl
780+
781+
@[simp] lemma pullback_cone_of_left_iso_snd :
782+
(pullback_cone_of_left_iso f g).snd = 𝟙 _ := rfl
783+
784+
@[simp] lemma pullback_cone_of_left_iso_π_app_none :
785+
(pullback_cone_of_left_iso f g).π.app none = g := by { delta pullback_cone_of_left_iso, simp }
786+
787+
@[simp] lemma pullback_cone_of_left_iso_π_app_left :
788+
(pullback_cone_of_left_iso f g).π.app left = g ≫ inv f := rfl
789+
790+
@[simp] lemma pullback_cone_of_left_iso_π_app_right :
791+
(pullback_cone_of_left_iso f g).π.app right = 𝟙 _ := rfl
792+
793+
/-- Verify that the constructed limit cone is indeed a limit. -/
794+
def pullback_cone_of_left_iso_is_limit :
795+
is_limit (pullback_cone_of_left_iso f g) :=
796+
pullback_cone.is_limit_aux' _ (λ s, ⟨s.snd, by simp [← s.condition_assoc]⟩)
797+
798+
lemma has_pullback_of_left_iso : has_pullback f g :=
799+
⟨⟨⟨_, pullback_cone_of_left_iso_is_limit f g⟩⟩⟩
800+
801+
local attribute [instance] has_pullback_of_left_iso
802+
803+
instance pullback_snd_iso_of_left_iso : is_iso (pullback.snd : pullback f g ⟶ _) :=
804+
begin
805+
refine ⟨⟨pullback.lift (g ≫ inv f) (𝟙 _) (by simp), _, by simp⟩⟩,
806+
ext,
807+
{ simp [← pullback.condition_assoc] },
808+
{ simp [pullback.condition_assoc] },
809+
end
810+
811+
end pullback_left_iso
812+
813+
section pullback_right_iso
814+
815+
open walking_cospan
816+
817+
variables (f : X ⟶ Z) (g : Y ⟶ Z) [is_iso g]
818+
819+
/-- If `g : Y ⟶ Z` is iso, then `X ×[Z] Y ≅ X`. This is the explicit limit cone. -/
820+
def pullback_cone_of_right_iso : pullback_cone f g :=
821+
pullback_cone.mk (𝟙 _) (f ≫ inv g) $ by simp
822+
823+
@[simp] lemma pullback_cone_of_right_iso_X :
824+
(pullback_cone_of_right_iso f g).X = X := rfl
825+
826+
@[simp] lemma pullback_cone_of_right_iso_fst :
827+
(pullback_cone_of_right_iso f g).fst = 𝟙 _ := rfl
828+
829+
@[simp] lemma pullback_cone_of_right_iso_snd :
830+
(pullback_cone_of_right_iso f g).snd = f ≫ inv g := rfl
831+
832+
@[simp] lemma pullback_cone_of_right_iso_π_app_none :
833+
(pullback_cone_of_right_iso f g).π.app none = f := category.id_comp _
834+
835+
@[simp] lemma pullback_cone_of_right_iso_π_app_left :
836+
(pullback_cone_of_right_iso f g).π.app left = 𝟙 _ := rfl
837+
838+
@[simp] lemma pullback_cone_of_right_iso_π_app_right :
839+
(pullback_cone_of_right_iso f g).π.app right = f ≫ inv g := rfl
840+
841+
/-- Verify that the constructed limit cone is indeed a limit. -/
842+
def pullback_cone_of_right_iso_is_limit :
843+
is_limit (pullback_cone_of_right_iso f g) :=
844+
pullback_cone.is_limit_aux' _ (λ s, ⟨s.fst, by simp [s.condition_assoc]⟩)
845+
846+
lemma has_pullback_of_right_iso : has_pullback f g :=
847+
⟨⟨⟨_, pullback_cone_of_right_iso_is_limit f g⟩⟩⟩
848+
849+
local attribute [instance] has_pullback_of_right_iso
850+
851+
instance pullback_snd_iso_of_right_iso : is_iso (pullback.fst : pullback f g ⟶ _) :=
852+
begin
853+
refine ⟨⟨pullback.lift (𝟙 _) (f ≫ inv g) (by simp), _, by simp⟩⟩,
854+
ext,
855+
{ simp },
856+
{ simp [pullback.condition_assoc] },
857+
end
858+
859+
end pullback_right_iso
860+
861+
section pushout_left_iso
862+
863+
open walking_span
864+
865+
variables (f : X ⟶ Y) (g : X ⟶ Z) [is_iso f]
866+
867+
/-- If `f : X ⟶ Y` is iso, then `Y ⨿[X] Z ≅ Z`. This is the explicit colimit cocone. -/
868+
def pushout_cocone_of_left_iso : pushout_cocone f g :=
869+
pushout_cocone.mk (inv f ≫ g) (𝟙 _) $ by simp
870+
871+
@[simp] lemma pushout_cocone_of_left_iso_X :
872+
(pushout_cocone_of_left_iso f g).X = Z := rfl
873+
874+
@[simp] lemma pushout_cocone_of_left_iso_inl :
875+
(pushout_cocone_of_left_iso f g).inl = inv f ≫ g := rfl
876+
877+
@[simp] lemma pushout_cocone_of_left_iso_inr :
878+
(pushout_cocone_of_left_iso f g).inr = 𝟙 _ := rfl
879+
880+
@[simp] lemma pushout_cocone_of_left_iso_ι_app_none :
881+
(pushout_cocone_of_left_iso f g).ι.app none = g := by { delta pushout_cocone_of_left_iso, simp }
882+
883+
@[simp] lemma pushout_cocone_of_left_iso_ι_app_left :
884+
(pushout_cocone_of_left_iso f g).ι.app left = inv f ≫ g := rfl
885+
886+
@[simp] lemma pushout_cocone_of_left_iso_ι_app_right :
887+
(pushout_cocone_of_left_iso f g).ι.app right = 𝟙 _ := rfl
888+
889+
/-- Verify that the constructed cocone is indeed a colimit. -/
890+
def pushout_cocone_of_left_iso_is_limit :
891+
is_colimit (pushout_cocone_of_left_iso f g) :=
892+
pushout_cocone.is_colimit_aux' _ (λ s, ⟨s.inr, by simp [← s.condition]⟩)
893+
894+
lemma has_pushout_of_left_iso : has_pushout f g :=
895+
⟨⟨⟨_, pushout_cocone_of_left_iso_is_limit f g⟩⟩⟩
896+
897+
local attribute [instance] has_pushout_of_left_iso
898+
899+
instance pushout_inr_iso_of_left_iso : is_iso (pushout.inr : _ ⟶ pushout f g) :=
900+
begin
901+
refine ⟨⟨pushout.desc (inv f ≫ g) (𝟙 _) (by simp), (by simp), _⟩⟩,
902+
ext,
903+
{ simp [← pushout.condition] },
904+
{ simp [pushout.condition_assoc] },
905+
end
906+
907+
end pushout_left_iso
908+
909+
section pushout_right_iso
910+
911+
open walking_span
912+
913+
variables (f : X ⟶ Y) (g : X ⟶ Z) [is_iso g]
914+
915+
/-- If `f : X ⟶ Z` is iso, then `Y ⨿[X] Z ≅ Y`. This is the explicit colimit cocone. -/
916+
def pushout_cocone_of_right_iso : pushout_cocone f g :=
917+
pushout_cocone.mk (𝟙 _) (inv g ≫ f) $ by simp
918+
919+
@[simp] lemma pushout_cocone_of_right_iso_X :
920+
(pushout_cocone_of_right_iso f g).X = Y := rfl
921+
922+
@[simp] lemma pushout_cocone_of_right_iso_inl :
923+
(pushout_cocone_of_right_iso f g).inl = 𝟙 _ := rfl
924+
925+
@[simp] lemma pushout_cocone_of_right_iso_inr :
926+
(pushout_cocone_of_right_iso f g).inr = inv g ≫ f := rfl
927+
928+
@[simp] lemma pushout_cocone_of_right_iso_ι_app_none :
929+
(pushout_cocone_of_right_iso f g).ι.app none = f := by { delta pushout_cocone_of_right_iso, simp }
930+
931+
@[simp] lemma pushout_cocone_of_right_iso_ι_app_left :
932+
(pushout_cocone_of_right_iso f g).ι.app left = 𝟙 _ := rfl
933+
934+
@[simp] lemma pushout_cocone_of_right_iso_ι_app_right :
935+
(pushout_cocone_of_right_iso f g).ι.app right = inv g ≫ f := rfl
936+
937+
/-- Verify that the constructed cocone is indeed a colimit. -/
938+
def pushout_cocone_of_right_iso_is_limit :
939+
is_colimit (pushout_cocone_of_right_iso f g) :=
940+
pushout_cocone.is_colimit_aux' _ (λ s, ⟨s.inl, by simp [←s.condition]⟩)
941+
942+
lemma has_pushout_of_right_iso : has_pushout f g :=
943+
⟨⟨⟨_, pushout_cocone_of_right_iso_is_limit f g⟩⟩⟩
944+
945+
local attribute [instance] has_pushout_of_right_iso
946+
947+
instance pushout_inl_iso_of_right_iso : is_iso (pushout.inl : _ ⟶ pushout f g) :=
948+
begin
949+
refine ⟨⟨pushout.desc (𝟙 _) (inv g ≫ f) (by simp), (by simp), _⟩⟩,
950+
ext,
951+
{ simp [←pushout.condition] },
952+
{ simp [pushout.condition] },
953+
end
954+
955+
end pushout_right_iso
956+
957+
section
958+
959+
open walking_cospan
960+
961+
variable (f : X ⟶ Y)
962+
963+
instance has_kernel_pair_of_mono [mono f] : has_pullback f f :=
964+
⟨⟨⟨_, pullback_cone.is_limit_mk_id_id f⟩⟩⟩
965+
966+
lemma fst_eq_snd_of_mono_eq [mono f] : (pullback.fst : pullback f f ⟶ _) = pullback.snd :=
967+
((pullback_cone.is_limit_mk_id_id f).fac (get_limit_cone (cospan f f)).cone left).symm.trans
968+
((pullback_cone.is_limit_mk_id_id f).fac (get_limit_cone (cospan f f)).cone right : _)
969+
970+
@[simp] lemma pullback_symmetry_hom_of_mono_eq [mono f] :
971+
(pullback_symmetry f f).hom = 𝟙 _ := by ext; simp [fst_eq_snd_of_mono_eq]
972+
973+
instance fst_iso_of_mono_eq [mono f] : is_iso (pullback.fst : pullback f f ⟶ _) :=
974+
begin
975+
refine ⟨⟨pullback.lift (𝟙 _) (𝟙 _) (by simp), _, by simp⟩⟩,
976+
ext,
977+
{ simp },
978+
{ simp [fst_eq_snd_of_mono_eq] }
979+
end
980+
981+
instance snd_iso_of_mono_eq [mono f] : is_iso (pullback.snd : pullback f f ⟶ _) :=
982+
by { rw ← fst_eq_snd_of_mono_eq, apply_instance }
983+
984+
end
985+
986+
section
987+
988+
open walking_span
989+
990+
variable (f : X ⟶ Y)
991+
992+
instance has_cokernel_pair_of_epi [epi f] : has_pushout f f :=
993+
⟨⟨⟨_, pushout_cocone.is_colimit_mk_id_id f⟩⟩⟩
994+
995+
lemma inl_eq_inr_of_epi_eq [epi f] : (pushout.inl : _ ⟶ pushout f f) = pushout.inr :=
996+
((pushout_cocone.is_colimit_mk_id_id f).fac (get_colimit_cocone (span f f)).cocone left).symm.trans
997+
((pushout_cocone.is_colimit_mk_id_id f).fac (get_colimit_cocone (span f f)).cocone right : _)
998+
999+
@[simp] lemma pullback_symmetry_hom_of_epi_eq [epi f] :
1000+
(pushout_symmetry f f).hom = 𝟙 _ := by ext; simp [inl_eq_inr_of_epi_eq]
1001+
1002+
instance inl_iso_of_epi_eq [epi f] : is_iso (pushout.inl : _ ⟶ pushout f f) :=
1003+
begin
1004+
refine ⟨⟨pushout.desc (𝟙 _) (𝟙 _) (by simp), by simp, _⟩⟩,
1005+
ext,
1006+
{ simp },
1007+
{ simp [inl_eq_inr_of_epi_eq] }
1008+
end
1009+
1010+
instance inr_iso_of_epi_eq [epi f] : is_iso (pushout.inr : _ ⟶ pushout f f) :=
1011+
by { rw ← inl_eq_inr_of_epi_eq, apply_instance }
1012+
1013+
end
1014+
6641015
variables (C)
6651016

6661017
/--

0 commit comments

Comments
 (0)