Imports
/- Copyright (c) 2026 Terence Rokop. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Terence Rokop -/ module public import Geb.Mathlib.CategoryTheory.DiscreteFibration.FiberPresheaf

Discrete fibrations: the packaged statements

The forms of the discrete-fibration correspondence that bundle their content into a mathlib construction: the converse of the pullback formulation, the equalities of composite functors, the equivalence of categories, and the natural isomorphism.

Main definitions

    IsDiscreteFibration.equiv and IsDiscreteFibration.toDiscreteFibration, recovering lifting data from a bijective codPair.

    DiscreteFibration.elementsEquivalence, the equivalence between C and the category of elements of its own fibre presheaf.

    Functor.CoElements.fiberPresheafIso, the natural isomorphism between the fibre presheaf of π F and F.

Main statements

    isDiscreteFibration_iff_nonempty: the two formulations of a discrete fibration agree.

    DiscreteFibration.toElements_comp_π and DiscreteFibration.ofElements_comp: both functors lie over B.

    DiscreteFibration.toElements_comp_ofElements and DiscreteFibration.ofElements_comp_toElements: both composites are identity functors.

Implementation notes

Every declaration here depends on Classical.choice, and only through a mathlib construction that does: Equiv.ofBijective, Functor.comp in each statement mentioning , Equivalence.mk, and NatIso.ofComponents. Each has an elementwise counterpart in Geb/Mathlib/CategoryTheory/DiscreteFibration/Pullback.lean or Geb/Mathlib/CategoryTheory/DiscreteFibration/FiberPresheaf.lean that carries the same content within the propext/Quot.sound budget, which is why this module is the only one of the group admitted to GebMeta.classicalAllowedModules.

References

    [LoregianRiehl2018]

    [nLabDiscreteFibration]

Tags

discrete fibration, equivalence of categories, category of elements

@[expose] public sectionuniverse v₁ v₂ v₃ u₁ u₂namespace CategoryTheoryopen Oppositevariable {C : Type u₁} [Category.{v₁} C] {B : Type u₂} [Category.{v₂} B]

The bijection Arrow C ≃ Arrow B ×_B C of a discrete fibration in the pullback-square sense. Classical: Equiv.ofBijective uses choice.

noncomputable def IsDiscreteFibration.equiv {p : C B} (H : IsDiscreteFibration p) : Arrow C CodPullback p := Equiv.ofBijective _ H

equiv acts as codPair.

theorem IsDiscreteFibration.equiv_apply {p : C B} (H : IsDiscreteFibration p) (a : Arrow C) : H.equiv a = codPair p a := rfl

Classical converse: a bijective codPair p yields lifting data. Uses Classical.choice; kept apart from the constructive development.

noncomputable def IsDiscreteFibration.toDiscreteFibration {p : C B} (H : IsDiscreteFibration p) : DiscreteFibration p := C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration pDiscreteFibration p C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qDiscreteFibration p C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = cDiscreteFibration p C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gDiscreteFibration p C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cp.map ((H.equiv.symm (Arrow.mk g, c), ).hom eqToHom ) = eqToHom gC:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh gc', h = (H.equiv.symm (Arrow.mk g, c), ).left, (H.equiv.symm (Arrow.mk g, c), ).hom eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cp.map ((H.equiv.symm (Arrow.mk g, c), ).hom eqToHom ) = eqToHom g C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj chX:p.obj (H.equiv.symm (Arrow.mk g, c), ).left = bhY:p.obj (H.equiv.symm (Arrow.mk g, c), ).right = p.obj chf:p.map (H.equiv.symm (Arrow.mk g, c), ).hom = eqToHom hX g eqToHom p.map ((H.equiv.symm (Arrow.mk g, c), ).hom eqToHom ) = eqToHom g C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj chX:p.obj (H.equiv.symm (Arrow.mk g, c), ).left = bhY:p.obj (H.equiv.symm (Arrow.mk g, c), ).right = p.obj chf:p.map (H.equiv.symm (Arrow.mk g, c), ).hom = eqToHom hX g eqToHom (eqToHom hX g eqToHom ) eqToHom = eqToHom g All goals completed! 🐙 C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh gc', h = (H.equiv.symm (Arrow.mk g, c), ).left, (H.equiv.symm (Arrow.mk g, c), ).hom eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh gH.equiv.symm (Arrow.mk g, c), = Arrow.mk h C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh gH.equiv (H.equiv.symm (Arrow.mk g, c), ) = H.equiv (Arrow.mk h) C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh g(Arrow.mk g, c), = codPair p (Arrow.mk h) exact Subtype.ext (Prod.ext ((Arrow.mk_eq_mk_iff _ _).2 eh, rfl, C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BH:IsDiscreteFibration phl: (q : CodPullback p), codPair p (H.equiv.symm q) = qhr: {b : B} {c : C} (g : b p.obj c), (H.equiv.symm (Arrow.mk g, c), ).right = chm: {b : B} {c : C} (g : b p.obj c), Arrow.mk (p.map (H.equiv.symm (Arrow.mk g, c), ).hom) = Arrow.mk gb:Bc:Cg:b p.obj cc':Ch:c' ceh:p.obj c' = bhh:p.map h = eqToHom eh gp.map (Arrow.mk h).hom = eqToHom eh g eqToHom All goals completed! 🐙).symm rfl)

A discrete fibration in the pullback sense admits lifting data.

theorem IsDiscreteFibration.nonempty {p : C B} (H : IsDiscreteFibration p) : Nonempty (DiscreteFibration p) := H.toDiscreteFibration

The two formulations agree: the codomain square is a pullback exactly when lifting data exists. Lifting data is a Subsingleton, so the Nonempty on the right loses nothing.

theorem isDiscreteFibration_iff_nonempty {p : C B} : IsDiscreteFibration p Nonempty (DiscreteFibration p) := fun H => H.nonempty, fun D => D.isDiscreteFibration
namespace DiscreteFibrationvariable {p : C B} (D : DiscreteFibration p)include D

toElements lies over B.

theorem toElements_comp_π : D.toElements Functor.CoElements.π D.fiberPresheaf = p := rfl

ofElements lies over B.

theorem ofElements_comp : D.ofElements p = Functor.CoElements.π D.fiberPresheaf := Functor.ext (fun x => D.obj_elt x) (fun x y f => C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration px:D.fiberPresheaf.CoElementsy:D.fiberPresheaf.CoElementsf:x y(D.ofElements p).map f = eqToHom (Functor.CoElements.π D.fiberPresheaf).map f eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration px:D.fiberPresheaf.CoElementsy:D.fiberPresheaf.CoElementsf:x yeqToHom Functor.CoElements.homBase f eqToHom = eqToHom (Functor.CoElements.π D.fiberPresheaf).map f eqToHom All goals completed! 🐙)

ofElements undoes toElements.

theorem toElements_comp_ofElements : D.toElements D.ofElements = 𝟭 C := Functor.ext (fun c => rfl) (fun c c' f => C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration pc:Cc':Cf:c c'(D.toElements D.ofElements).map f = eqToHom (𝟭 C).map f eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration pc:Cc':Cf:c c'D.ofElements.map (D.toElements.map f) = 𝟙 c f 𝟙 c' All goals completed! 🐙)

toElements undoes ofElements.

theorem ofElements_comp_toElements : D.ofElements D.toElements = 𝟭 D.fiberPresheaf.CoElements := Functor.ext (fun x => (D.coElements_eq_toElements_obj x).symm) (fun x y f => C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration px:D.fiberPresheaf.CoElementsy:D.fiberPresheaf.CoElementsf:x y(D.ofElements D.toElements).map f = eqToHom (𝟭 D.fiberPresheaf.CoElements).map f eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration py:D.fiberPresheaf.CoElementsc:Cf:D.toElements.obj c y(D.ofElements D.toElements).map f = eqToHom (𝟭 D.fiberPresheaf.CoElements).map f eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration pc:Cc':Cf:D.toElements.obj c D.toElements.obj c'(D.ofElements D.toElements).map f = eqToHom (𝟭 D.fiberPresheaf.CoElements).map f eqToHom C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} Bp:C BD:DiscreteFibration pc:Cc':Cf:D.toElements.obj c D.toElements.obj c'D.toElements.map (D.ofElements.map f) = 𝟙 (D.toElements.obj c) f 𝟙 (D.toElements.obj c') All goals completed! 🐙)

The two functors assemble into an equivalence, so a discrete fibration over B is the category of elements of its own fibre presheaf. Both composites are identity functors on the nose, so the unit and counit are eqToIsos.

def elementsEquivalence : C D.fiberPresheaf.CoElements := .mk D.toElements D.ofElements (eqToIso D.toElements_comp_ofElements.symm) (eqToIso D.ofElements_comp_toElements)
end DiscreteFibrationnamespace Functor.CoElementsvariable (F : Bᵒᵖ Type v₃)

The fiber presheaf of π F is naturally isomorphic to F (up to the universe lift forced by F.CoElements : Type (max u₂ v₃)): the fiber over b is {x : F.CoElements // x.base = b}, and x ↦ x.elt, transported along x.base = b, is a bijection onto F b.

def fiberPresheafIso : (discreteFibration F).fiberPresheaf F uliftFunctor.{u₂, v₃} := NatIso.ofComponents (fun b => ((fiberPresheafEquiv F b.unop).trans Equiv.ulift.symm).toIso) (fun {b b'} g => ConcreteCategory.hom_ext _ _ fun x => C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} BF:Bᵒᵖ Type v₃b:Bᵒᵖb':Bᵒᵖg:b b'x:(discreteFibration F).fiberPresheaf.obj b(ConcreteCategory.hom ((discreteFibration F).fiberPresheaf.map g ((fiberPresheafEquiv F (unop b')).trans Equiv.ulift.symm).toIso.hom)) x = (ConcreteCategory.hom (((fiberPresheafEquiv F (unop b)).trans Equiv.ulift.symm).toIso.hom (F uliftFunctor.{u₂, v₃}).map g)) x C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} BF:Bᵒᵖ Type v₃b:Bᵒᵖb':Bᵒᵖg:b b'x:(discreteFibration F).fiberPresheaf.obj bh:(fiberPresheafEquiv F (unop b')) ((discreteFibration F).restrict g.unop x) = (ConcreteCategory.hom (F.map g.unop.op)) ((fiberPresheafEquiv F (unop b)) x)(ConcreteCategory.hom ((discreteFibration F).fiberPresheaf.map g ((fiberPresheafEquiv F (unop b')).trans Equiv.ulift.symm).toIso.hom)) x = (ConcreteCategory.hom (((fiberPresheafEquiv F (unop b)).trans Equiv.ulift.symm).toIso.hom (F uliftFunctor.{u₂, v₃}).map g)) x C:Type u₁inst✝¹:Category.{v₁, u₁} CB:Type u₂inst✝:Category.{v₂, u₂} BF:Bᵒᵖ Type v₃b:Bᵒᵖb':Bᵒᵖg:b b'x:(discreteFibration F).fiberPresheaf.obj bh:(fiberPresheafEquiv F (unop b')) ((discreteFibration F).restrict g.unop x) = (ConcreteCategory.hom (F.map g)) ((fiberPresheafEquiv F (unop b)) x)(ConcreteCategory.hom ((discreteFibration F).fiberPresheaf.map g ((fiberPresheafEquiv F (unop b')).trans Equiv.ulift.symm).toIso.hom)) x = (ConcreteCategory.hom (((fiberPresheafEquiv F (unop b)).trans Equiv.ulift.symm).toIso.hom (F uliftFunctor.{u₂, v₃}).map g)) x All goals completed! 🐙)
end Functor.CoElementsend CategoryTheory