Other Agda modules in this section (8)
Chapter 1, rebuild2, checked 14 October 2026 · source revision b982f5a.
# Products of categories over a base The product of `(C,p)` and `(D,q)` over `S` is the actual chosen pullback, with structural map `p ∘ pullback₁`. An arbitrary categorical family into this product is constructed from two relative families. Its pullback beta and its subsequent relative-family beta retain their full matching squares. For the categorical universal property, native functor categories preserve the actual pullback. Pullback pasting identifies its relative fiber with the product of the two relative fibers. All arrow changes use their specified identifications; in particular the right leg of a relative fiber is not replaced definitionally by the terminal functor. ```agda {-# OPTIONS --without-K #-} module SCT.VolumeI.Chapter01.Section09.RelativeProducts where open import SCT.VolumeI.Chapter01.Section01.Vocabulary hiding (_=_) open Operations open import SCT.VolumeI.Chapter01.Section01.Terminal open Constructions open import SCT.VolumeI.Chapter01.Section02.Coherence using (id₌; comp-assoc; module Composition) open Composition open import SCT.VolumeI.Chapter01.Section03.RelativeFunctors open import SCT.VolumeI.Chapter01.Section03.Equivalences using (equiv-compose) open import SCT.VolumeI.Chapter01.Section03.Identifications.Triangles using (restrict-triangle) open import SCT.VolumeI.Chapter01.Section06.Cones open import SCT.VolumeI.Chapter01.Section06.ConeIdentifications open import SCT.VolumeI.Chapter01.Section06.ConeTransport open import SCT.VolumeI.Chapter01.Section06.ConeUniversality using (isPullback-change-left; module SwappedCone) open import SCT.VolumeI.Chapter01.Section06.Pullbacks open import SCT.VolumeI.Chapter01.Section06.PullbackSquares open import SCT.VolumeI.Chapter01.Section06.PullbackConstructions using (pullback-cone-isPullback; pullback-swap; pullback-swap-isEquiv; module TerminalBase) open import SCT.VolumeI.Chapter01.Section06.NestedPullbacks using (module Nested) open import SCT.VolumeI.Chapter01.Section06.ConePasting using (postcompose-cone-left; postcompose-cone-left-identification; postcompose-cone-left-restrict) open import SCT.VolumeI.Chapter01.Section07.FunctorCategories using (Fun) open import SCT.VolumeI.Chapter01.Section07.Functoriality using (funPost; funPost-comp) open import SCT.VolumeI.Chapter01.Section07.FunctorPullbacks using (mappedCone; fun-preserves-pullback; mappedCone-evaluation) open import SCT.VolumeI.Chapter01.Section09.RelativeFunctorCategories open import SCT.VolumeI.Chapter01.Section09.RelativeFamilyComparisons module RelativeProduct {Γ : Context} {S : CAT Γ} (A B : CategoryOverBase S) where private C = CategoryOverBase.category A D = CategoryOverBase.category B p = CategoryOverBase.map A q = CategoryOverBase.map B category : CAT Γ category = Pullback p q cone : Cone p q category cone = pullback-cone p q over-base : CategoryOverBase S over-base = category-over category (p ∘ Cone.left cone) left : FunctorOverBase over-base A left = functor-over (Cone.left cone) (id₌ _) right : FunctorOverBase over-base B right = functor-over (Cone.right cone) ((Cone.match cone) ⁻¹) module Families (X : CategoryOverBase S) where module ToC = RelativeFunctorCategory X A module ToD = RelativeFunctorCategory X B module ToProduct = RelativeFunctorCategory X over-base module ProductFamilies = RelativeFamilies X over-base module Pair {K : CAT Γ} (F : ToC.Family K) (G : ToD.Family K) where supplied : Cone p q (K × CategoryOverBase.category X) supplied = record { left = FunctorOverBase.functor F ; right = FunctorOverBase.functor G ; match = (FunctorOverBase.cell G) ⁻¹ ∙ FunctorOverBase.cell F } underlying : MAP (K × CategoryOverBase.category X) category underlying = pullback-factor supplied pullback-computation : ConeIdentification₁ (cone-restrict underlying cone) supplied pullback-computation = pullback-factor-β supplied family : ToProduct.Family K family = functor-over underlying (FunctorOverBase.cell F ∙ ((p ◁ ConeIdentification₁.left pullback-computation) ∙ comp-assoc underlying (Cone.left cone) p)) curried : MAP K ToProduct.category curried = ToProduct.ChosenFamily.factor family relative-computation : ConeIdentification₁ (ProductFamilies.family-cone (ToProduct.evaluated-family curried)) (ProductFamilies.family-cone family) relative-computation = ProductFamilies.FamilyComputation.comparison family ``` ## The categorical product comparison The two middle pastings use the same mapped pullback cone. Changing its left composite uses native postcomposition coherence. Changing the next composite uses precisely the matching of the first relative fiber. The last pasting uses the swapped second relative fiber, with its actual right leg. ```agda module UniversalProperty (X : CategoryOverBase S) where private T = CategoryOverBase.category X module FC = RelativeFunctorCategory X A module FD = RelativeFunctorCategory X B module FP = RelativeFunctorCategory X over-base ordinary-cone : Cone FC.postcomposition FD.postcomposition (Fun T category) ordinary-cone = mappedCone T cone ordinary-isPullback : IsPullback ordinary-cone ordinary-isPullback = fun-preserves-pullback T cone (pullback-cone-isPullback p q) postcomposition-comparison : FP.postcomposition =₁ (FC.postcomposition ∘ Cone.left ordinary-cone) postcomposition-comparison = (funPost-comp {A = T} (Cone.left cone) p) ⁻¹ changed-fiber : Cone (FC.postcomposition ∘ Cone.left ordinary-cone) FC.point FP.category changed-fiber = cone-change-left postcomposition-comparison FP.fiber change : MAP FP.category (Pullback (FC.postcomposition ∘ Cone.left ordinary-cone) FC.point) change = pullback-factor changed-fiber change-isEquiv : IsEquiv change change-isEquiv = isPullback-change-left postcomposition-comparison FP.fiber (pullback-cone-isPullback FP.postcomposition FP.point) change-computation : ConeIdentification₁ (cone-restrict change (pullback-cone (FC.postcomposition ∘ Cone.left ordinary-cone) FC.point)) changed-fiber change-computation = pullback-factor-β changed-fiber module First = Nested (Cone.left ordinary-cone) FC.postcomposition FC.point FC.fiber (pullback-cone-isPullback FC.postcomposition FC.point) swap : MAP First.NestedVertex (Pullback FC.inclusion (Cone.left ordinary-cone)) swap = pullback-swap (Cone.left ordinary-cone) FC.inclusion swap-isEquiv : IsEquiv swap swap-isEquiv = pullback-swap-isEquiv (Cone.left ordinary-cone) FC.inclusion swap-computation : ConeIdentification₁ (cone-restrict swap (pullback-cone FC.inclusion (Cone.left ordinary-cone))) (cone-swap First.nested-cone) swap-computation = pullback-factor-β (cone-swap First.nested-cone) module Second = Nested FC.inclusion FC.postcomposition FD.postcomposition ordinary-cone ordinary-isPullback fiber-matching : (FC.postcomposition ∘ FC.inclusion) =₁ (FC.point ∘ Cone.right FC.fiber) fiber-matching = Cone.match FC.fiber changed-middle : Cone (FC.point ∘ Cone.right FC.fiber) FD.postcomposition Second.Outer changed-middle = cone-change-left fiber-matching Second.outer-cone change-middle : MAP Second.Outer (Pullback (FC.point ∘ Cone.right FC.fiber) FD.postcomposition) change-middle = pullback-factor changed-middle change-middle-isEquiv : IsEquiv change-middle change-middle-isEquiv = isPullback-change-left fiber-matching Second.outer-cone (pullback-cone-isPullback (FC.postcomposition ∘ FC.inclusion) FD.postcomposition) change-middle-computation : ConeIdentification₁ (cone-restrict change-middle (pullback-cone (FC.point ∘ Cone.right FC.fiber) FD.postcomposition)) changed-middle change-middle-computation = pullback-factor-β changed-middle module Last = Nested (Cone.right FC.fiber) FC.point FD.postcomposition (cone-swap FD.fiber) (SwappedCone.isPullback FD.fiber (pullback-cone-isPullback FD.postcomposition FD.point)) module Product = TerminalBase (Cone.right FC.fiber) (Cone.right FD.fiber) first-insertion : MAP FP.category First.NestedVertex first-insertion = First.insert ∘ change first-insertion-isEquiv : IsEquiv first-insertion first-insertion-isEquiv = equiv-compose change First.insert change-isEquiv First.insert-isEquiv exchanged : MAP FP.category Second.NestedVertex exchanged = swap ∘ first-insertion exchanged-isEquiv : IsEquiv exchanged exchanged-isEquiv = equiv-compose first-insertion swap first-insertion-isEquiv swap-isEquiv flattened : MAP FP.category Second.Outer flattened = Second.flatten ∘ exchanged flattened-isEquiv : IsEquiv flattened flattened-isEquiv = equiv-compose exchanged Second.flatten exchanged-isEquiv Second.flatten-isEquiv changed : MAP FP.category Last.Outer changed = change-middle ∘ flattened changed-isEquiv : IsEquiv changed changed-isEquiv = equiv-compose flattened change-middle flattened-isEquiv change-middle-isEquiv separated : MAP FP.category Last.NestedVertex separated = Last.insert ∘ changed separated-isEquiv : IsEquiv separated separated-isEquiv = equiv-compose changed Last.insert changed-isEquiv Last.insert-isEquiv comparison : MAP FP.category (FC.category × FD.category) comparison = Product.to-product ∘ separated comparison-isEquiv : IsEquiv comparison comparison-isEquiv = equiv-compose separated Product.to-product separated-isEquiv Product.to-product-isEquiv equivalence : Equiv FP.category (FC.category × FD.category) equivalence = record { functor = comparison; isEquiv = comparison-isEquiv } ``` ## The induced projections and their complete fiber diagrams The projections land in the actual relative functor categories. Their ordinary inclusions are native postcomposition by the two pullback legs. Transporting the complete restricted fiber cones along these triangles retains the specified matching, rather than asserting compatibility from the two underlying functors alone. ```agda to-C : MAP FP.category FC.category to-C = pr₁ ∘ comparison to-D : MAP FP.category FD.category to-D = pr₂ ∘ comparison private product-left : to-C =₁ (Cone.left Last.nested-cone ∘ separated) product-left = restrict-triangle pr₁ Product.to-product (pair-β₁ (Cone.left Last.nested-cone) (Cone.right Last.nested-cone)) separated product-right : to-D =₁ (Cone.right Last.nested-cone ∘ separated) product-right = restrict-triangle pr₂ Product.to-product (pair-β₂ (Cone.left Last.nested-cone) (Cone.right Last.nested-cone)) separated separated-left : (Cone.left Last.nested-cone ∘ separated) =₁ (Cone.right First.nested-cone ∘ first-insertion) separated-left = restrict-triangle (Cone.left Second.nested-cone) swap (ConeIdentification₁.left swap-computation) first-insertion ∙ (restrict-triangle (Cone.left Second.outer-cone) Second.flatten (ConeIdentification₁.left Second.flatten-β) exchanged ∙ (restrict-triangle (Cone.left Last.outer-cone) change-middle (ConeIdentification₁.left change-middle-computation) flattened ∙ restrict-triangle (Cone.left Last.nested-cone) Last.insert Last.insert-left changed)) first-right : (Cone.right First.nested-cone ∘ first-insertion) =₁ (First.inner ∘ change) first-right = restrict-triangle (Cone.right First.nested-cone) First.insert First.insert-right change first-left-image : (FC.inclusion ∘ (First.inner ∘ change)) =₁ (Cone.left ordinary-cone ∘ FP.inclusion) first-left-image = (Cone.left ordinary-cone ◁ ConeIdentification₁.left change-computation) ∙ (comp-assoc change (Cone.left First.outer-cone) (Cone.left ordinary-cone) ∙ restrict-triangle FC.inclusion First.inner First.inner-left change) separated-right : (Cone.right Last.nested-cone ∘ separated) =₁ (Last.inner ∘ changed) separated-right = restrict-triangle (Cone.right Last.nested-cone) Last.insert Last.insert-right changed last-right-image : (FD.inclusion ∘ (Last.inner ∘ changed)) =₁ (Cone.right Last.outer-cone ∘ changed) last-right-image = restrict-triangle FD.inclusion Last.inner Last.inner-right changed middle-right : (Cone.right Last.outer-cone ∘ changed) =₁ (Cone.right Second.outer-cone ∘ flattened) middle-right = restrict-triangle (Cone.right Last.outer-cone) change-middle (ConeIdentification₁.right change-middle-computation) flattened flat-right-image : (Cone.right Second.outer-cone ∘ flattened) =₁ (Cone.right ordinary-cone ∘ (Cone.right Second.nested-cone ∘ exchanged)) flat-right-image = comp-assoc exchanged (Cone.right Second.nested-cone) (Cone.right ordinary-cone) ∙ restrict-triangle (Cone.right Second.outer-cone) Second.flatten (ConeIdentification₁.right Second.flatten-β) exchanged exchanged-right : (Cone.right Second.nested-cone ∘ exchanged) =₁ (Cone.left First.nested-cone ∘ first-insertion) exchanged-right = restrict-triangle (Cone.right Second.nested-cone) swap (ConeIdentification₁.right swap-computation) first-insertion first-left : (Cone.left First.nested-cone ∘ first-insertion) =₁ FP.inclusion first-left = ConeIdentification₁.left change-computation ∙ restrict-triangle (Cone.left First.nested-cone) First.insert First.insert-left change left-inclusion : (FC.inclusion ∘ to-C) =₁ (funPost {C = T} (Cone.left cone) ∘ FP.inclusion) left-inclusion = first-left-image ∙ ((FC.inclusion ◁ first-right) ∙ ((FC.inclusion ◁ separated-left) ∙ (FC.inclusion ◁ product-left))) right-inclusion : (FD.inclusion ∘ to-D) =₁ (funPost {C = T} (Cone.right cone) ∘ FP.inclusion) right-inclusion = (Cone.right ordinary-cone ◁ first-left) ∙ ((Cone.right ordinary-cone ◁ exchanged-right) ∙ (flat-right-image ∙ (middle-right ∙ (last-right-image ∙ ((FD.inclusion ◁ separated-right) ∙ (FD.inclusion ◁ product-right)))))) left-projection-cone : Cone FC.postcomposition FC.point FP.category left-projection-cone = postcompose-cone-left (Cone.left ordinary-cone) FC.postcomposition changed-fiber left-projection-computation : ConeIdentification₁ (cone-restrict to-C FC.fiber) left-projection-cone left-projection-computation = cone-compose (postcompose-cone-left-identification (Cone.left ordinary-cone) FC.postcomposition change-computation) (cone-compose (postcompose-cone-left-restrict (Cone.left ordinary-cone) FC.postcomposition change First.outer-cone) (cone-compose (cone-identification-restrict-One change First.inner-β) (cone-compose (cone-inverse (cone-restrict-assoc change First.inner FC.fiber)) (cone-action-One FC.fiber (first-right ∙ (separated-left ∙ product-left)))))) right-projection-cone : Cone FC.point FD.postcomposition FP.category right-projection-cone = postcompose-cone-left (Cone.right FC.fiber) FC.point (cone-restrict flattened changed-middle) right-projection-computation : ConeIdentification₁ (cone-restrict to-D (cone-swap FD.fiber)) right-projection-cone right-projection-computation = cone-compose (postcompose-cone-left-identification (Cone.right FC.fiber) FC.point (cone-identification-restrict-One flattened change-middle-computation)) (cone-compose (postcompose-cone-left-identification (Cone.right FC.fiber) FC.point (cone-inverse (cone-restrict-assoc flattened change-middle Last.outer-cone))) (cone-compose (postcompose-cone-left-restrict (Cone.right FC.fiber) FC.point changed Last.outer-cone) (cone-compose (cone-identification-restrict-One changed Last.inner-β) (cone-compose (cone-inverse (cone-restrict-assoc changed Last.inner (cone-swap FD.fiber))) (cone-action-One (cone-swap FD.fiber) (separated-right ∙ product-right)))))) ```