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.Prototypes.LargeIR.Code
meta import GebMeta -- shake: keep; supplies the cite docstring roleset_option doc.verso truePrototype: base-cartesian presheaf polynomial endofunctors on the walking arrow are codes
Throwaway exploration, not upstream-eligible content. Every declaration here
is Classical.choice-free.
GebProto.LargeIR.arrowPshCodeEquiv reads one presheaf polynomial
endofunctor on the walking arrow, the transcription of a slice polynomial
functor, as a large inductive-recursive code on Fam(Type). This module
states the general case. A presheaf polynomial endofunctor F on the
walking arrow has shapes at two levels, each shape a having directions
D₀ a at level 0 and D₁ a at level 1 with a
restriction ρ : D₁ a → D₀ a, and a level-1 shape a₁
restricting to a level-0 shape with reindexings of directions at each
level. Its value at a walking-arrow presheaf Z, read as the family
(U, T), is computed by elemEquiv: an element is a shape
a, a base assignment g : D₀ a → U, and an assignment
w : Π d : D₁ a, T (g (ρ d)).
The reading as a code needs one condition, BaseCartesian: for every
level-1 shape, the reindexing of level-0 directions from its
restriction is a bijection. Then the code is
σ (Shape 0) (fun a ↦ δ (D₀ a) (fun A ↦ σ (Π d : D₁ a, A (ρ d)) (fun w ↦ ι (fibre a A w)))),
genCode, whose σ indices are the level-0 shapes and the
level-1 assignments of a level-0 shape, whose δ arities
are its level-0 directions, and whose ι decodes to the
level-1 shapes over it with the assignments restricting to w,
genFibre. genCodeEquiv is the isomorphism of the functor's
output presheaf with the presheaf of the code's interpretation at the family
of the input, and arrowPshBaseCartesian exhibits the transcription of
a slice polynomial functor as an instance.
The condition is not an artefact. Without it a level-1 shape carries
a level-0 direction not reached from its restriction, whose value
under a base assignment is an element of U unconstrained by the
level-0 element below, so the fibre of the output family over that
element has the index type U itself as a factor. The decoding of a
code mentions U only through T ∘ g, and the interpretation of
every code preserves the cartesian morphisms of families, those whose
decoding maps are bijections, while a functor with such a factor does not;
that argument is not formalized here.
Main definitions
-
Elem,elemEquiv— the value of a presheaf polynomial endofunctor on the walking arrow in dependent-type terms. -
ElemAt,objLevel— the elements over a level, andrestrElemthe restriction from level1to level0on them. -
BaseCartesian— the condition on reindexing at level0. -
genFibre,genCode— the fibre type and the code. -
arrowPshBaseCartesian— the transcription of a slice polynomial functor is base-cartesian.
Main statements
-
objLevel_objRestr— the restriction of the output presheaf isrestrElemon elements. -
genCodeEquiv— the output presheaf of a base-cartesian functor is the presheaf of the code's interpretation.
References
-
[GhaniNordvallForsbergMalatesta2015]
Neil Ghani, Fredrik Nordvall Forsberg, Lorenzo Malatesta (2015). “Positive Inductive-Recursive Definitions”. Logical Methods in Computer Science 11(1). https://doi.org/10.2168/LMCS-11(1:13)2015. -
[Weber2007]
Mark Weber (2007). “Familial 2-functors and parametric right adjoints”. Theory and Applications of Categories 18(22), pp. 665–732. http://www.tac.mta.ca/tac/volumes/18/22/18-22abs.html.
Tags
prototype, presheaf, walking arrow, parametric right adjoint, inductive-recursive, code, cartesian
@[expose] public sectionopen CategoryTheory PresheafIRUniv IndRecnamespace GebProto.LargeIRvariable (F : PresheafPFunctor.{0, 0, 0, 0, 0, 0} (Fin 2) (Fin 2))The value in dependent-type terms
The restriction of a shape's directions from level 1 to level
0.
abbrev dirRestr (a : F.A) : F.Direction a 1 → F.Direction a 0 :=
F.directionRestr a waHom
The restriction of a level-1 shape to level 0.
abbrev restrShape₁ (a₁ : F.Shape 1) : F.Shape 0 :=
F.shapeRestr waHom a₁
The reindexing of level-0 directions from the restricted shape.
abbrev reindex₀ (a₁ : F.Shape 1) :
F.Direction (restrShape₁ F a₁).1 0 → F.Direction a₁.1 0 :=
F.reindex waHom a₁ (i := 0)
The reindexing of level-1 directions from the restricted shape.
abbrev reindex₁ (a₁ : F.Shape 1) :
F.Direction (restrShape₁ F a₁).1 1 → F.Direction a₁.1 1 :=
F.reindex waHom a₁ (i := 1)Reindexing commutes with the restriction of directions.
theorem dirRestr_reindex₁ (a₁ : F.Shape 1) (d : F.Direction (restrShape₁ F a₁).1 1) :
dirRestr F a₁.1 (reindex₁ F a₁ d) = reindex₀ F a₁ (dirRestr F _ d) :=
congrFun (F.isFunctorial.reindex_naturality waHom a₁ waHom) dvariable (Z : (Fin 2)ᵒᵖ ⥤ Type)
The value of F at Z in dependent-type terms: a shape, a
base assignment of its level-0 directions in Z 0, and an
assignment of its level-1 directions in the fibres of Z 1 → Z 0
over the base assignment at the restricted directions.
def Elem : Type :=
Σ a : F.A, Σ g : F.Direction a 0 → Z.obj ⟨0⟩,
∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))The other index of the walking arrow.
theorem eq_one_of_ne_zero {i : Fin 2} (h : i ≠ 0) : i = 1 := i:Fin 2h:i ≠ 0⊢ i = 1
All goals completed! 🐙
The raw assignment of an element built from a base assignment and a
level-1 assignment: at a direction over 0 the base assignment,
at one over 1 the level-1 assignment.
def assign {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) (b : F.B a) :
Σ i : Fin 2, Z.obj ⟨i⟩ :=
if h : F.r ⟨a, b⟩ = 0 then ⟨0, g ⟨b, h⟩⟩ else ⟨1, (w ⟨b, eq_one_of_ne_zero h⟩).1⟩
The raw assignment at a direction over 0.
theorem assign_zero {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) {b : F.B a}
(h : F.r ⟨a, b⟩ = 0) : assign F Z g w b = ⟨0, g ⟨b, h⟩⟩ :=
dite_eq_left h
The raw assignment at a direction over 1.
theorem assign_one {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) {b : F.B a}
(h : F.r ⟨a, b⟩ = 1) : assign F Z g w b = ⟨1, (w ⟨b, h⟩).1⟩ :=
dite_eq_right fun h' ↦ Fin.zero_ne_one (h'.symm.trans h)The raw assignment is compatible with the direction-input map.
theorem assign_compatible {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) :
F.Compatible (PresheafDomPFunctorData.elemProj Z) a (assign F Z g w) := F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))⊢ F.Compatible (PresheafDomPFunctorData.elemProj Z) a (assign F Z g w)
F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B a⊢ (PresheafDomPFunctorData.elemProj Z ∘ assign F Z g w) b = (F.r ∘ Sigma.mk a) b
F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:F.r ⟨a, b⟩ = 0⊢ (PresheafDomPFunctorData.elemProj Z ∘ assign F Z g w) b = (F.r ∘ Sigma.mk a) bF:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:¬F.r ⟨a, b⟩ = 0⊢ (PresheafDomPFunctorData.elemProj Z ∘ assign F Z g w) b = (F.r ∘ Sigma.mk a) b
F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:F.r ⟨a, b⟩ = 0⊢ (PresheafDomPFunctorData.elemProj Z ∘ assign F Z g w) b = (F.r ∘ Sigma.mk a) b F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:F.r ⟨a, b⟩ = 0⊢ (assign F Z g w b).fst = F.r ⟨a, b⟩
pos F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:F.r ⟨a, b⟩ = 0⊢ ⟨0, g ⟨b, h⟩⟩.fst = F.r ⟨a, b⟩
exact h.symm All goals completed! 🐙
· neg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:¬F.r ⟨a, b⟩ = 0⊢ (PresheafDomPFunctorData.elemProj Z ∘ assign F Z g w) b = (F.r ∘ Sigma.mk a) b change (assign F Z g w b).1 = F.r ⟨a, b⟩ neg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:¬F.r ⟨a, b⟩ = 0⊢ (assign F Z g w b).fst = F.r ⟨a, b⟩
rw [assign_one F Z g w (eq_one_of_ne_zero h) neg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:¬F.r ⟨a, b⟩ = 0⊢ ⟨1, ↑(w ⟨b, ⋯⟩)⟩.fst = F.r ⟨a, b⟩] neg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)w:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))b:F.B ah:¬F.r ⟨a, b⟩ = 0⊢ ⟨1, ↑(w ⟨b, ⋯⟩)⟩.fst = F.r ⟨a, b⟩
exact (eq_one_of_ne_zero h).symm All goals completed! 🐙
The element built from a base assignment and a level-1 assignment,
before its naturality.
def assignObj {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) :
F.toSliceDomPFunctor.Obj (PresheafDomPFunctorData.elemProj Z) :=
⟨⟨a, assign F Z g w⟩, assign_compatible F Z g w⟩
The value the built element gives a level-0 direction is the base
assignment.
theorem value_assignObj_zero {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) (d : F.Direction a 0) :
F.value (assignObj F Z g w) d = g d :=
eq_of_heq (Sigma.mk.inj_iff.mp
((sigma_value Z F.toPresheafDomPFunctorData (assignObj F Z g w) d).trans
(assign_zero F Z g w d.2))).2
The value the built element gives a level-1 direction is the
level-1 assignment.
theorem value_assignObj_one {a : F.A} (g : F.Direction a 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) (d : F.Direction a 1) :
F.value (assignObj F Z g w) d = (w d).1 :=
eq_of_heq (Sigma.mk.inj_iff.mp
((sigma_value Z F.toPresheafDomPFunctorData (assignObj F Z g w) d).trans
(assign_one F Z g w d.2))).2
Two elements of Elem with the same shape are equal when their base
assignments agree and their level-1 assignments agree in Z 1.
theorem gw_ext {a : F.A} {g g' : F.Direction a 0 → Z.obj ⟨0⟩} (hg : g' = g)
{w : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))}
{w' : ∀ d : F.Direction a 1, (famOfPsh Z).2 (g' (dirRestr F a d))}
(hw : ∀ d, (w' d).1 = (w d).1) :
(⟨g', w'⟩ : Σ g : F.Direction a 0 → Z.obj ⟨0⟩,
∀ d : F.Direction a 1, (famOfPsh Z).2 (g (dirRestr F a d))) = ⟨g, w⟩ := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag:F.Direction a 0 → Z.obj (Opposite.op 0)g':F.Direction a 0 → Z.obj (Opposite.op 0)hg:g' = gw:(d : F.Direction a 1) → (famOfPsh Z).snd (g (dirRestr F a d))w':(d : F.Direction a 1) → (famOfPsh Z).snd (g' (dirRestr F a d))hw:∀ (d : F.Direction a 1), ↑(w' d) = ↑(w d)⊢ ⟨g', w'⟩ = ⟨g, w⟩
subst hg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typea:F.Ag':F.Direction a 0 → Z.obj (Opposite.op 0)w':(d : F.Direction a 1) → (famOfPsh Z).snd (g' (dirRestr F a d))w:(d : F.Direction a 1) → (famOfPsh Z).snd (g' (dirRestr F a d))hw:∀ (d : F.Direction a 1), ↑(w' d) = ↑(w d)⊢ ⟨g', w'⟩ = ⟨g', w⟩
exact Sigma.ext rfl (heq_of_eq (funext fun d ↦ Subtype.ext (hw d))) All goals completed! 🐙
The value of F at Z is Elem F Z.
def elemEquiv : F.obj Z ≃ Elem F Z where
toFun x := ⟨x.shape, fun d ↦ F.value x.1 d, fun d ↦ ⟨F.value x.1 d, (x.2 waHom d).symm⟩⟩
invFun e := ⟨assignObj F Z e.2.1 e.2.2,
(isNatural_iff F.toPresheafDomPFunctorData F.isFunctorial.directionRestr_id Z _).mpr fun d ↦
(value_assignObj_zero F Z _ _ _).trans
((e.2.2 d).2.symm.trans (congrArg _ (value_assignObj_one F Z _ _ d).symm))⟩
left_inv x := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Z⊢ (fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x) =
x
refine Subtype.ext (Subtype.ext (Sigma.ext rfl (heq_of_eq (funext fun b ↦ ?_)))) F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zb:F.B
(↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).fst⊢ (↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).snd
b =
(↑↑x).snd b
by_cases h : F.r ⟨x.shape, b⟩ = 0 pos F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zb:F.B
(↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).fsth:F.r ⟨x.shape, b⟩ = 0⊢ (↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).snd
b =
(↑↑x).snd bneg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zb:F.B
(↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).fsth:¬F.r ⟨x.shape, b⟩ = 0⊢ (↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).snd
b =
(↑↑x).snd b
· pos F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zb:F.B
(↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).fsth:F.r ⟨x.shape, b⟩ = 0⊢ (↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).snd
b =
(↑↑x).snd b exact (assign_zero F Z _ _ h).trans (sigma_value Z F.toPresheafDomPFunctorData x.1 ⟨b, h⟩) All goals completed! 🐙
· neg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zb:F.B
(↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).fsth:¬F.r ⟨x.shape, b⟩ = 0⊢ (↑↑((fun e ↦ ⟨assignObj F Z e.snd.fst e.snd.snd, ⋯⟩)
((fun x ↦ ⟨x.shape, ⟨fun d ↦ F.value (↑x) d, fun d ↦ ⟨F.value (↑x) d, ⋯⟩⟩⟩) x))).snd
b =
(↑↑x).snd b exact (assign_one F Z _ _ (eq_one_of_ne_zero h)).trans
(sigma_value Z F.toPresheafDomPFunctorData x.1 ⟨b, eq_one_of_ne_zero h⟩) All goals completed! 🐙
right_inv e :=
Sigma.ext rfl (heq_of_eq (gw_ext F Z (funext fun d ↦ value_assignObj_zero F Z _ _ d)
fun d ↦ value_assignObj_one F Z _ _ d))Elements by level, and the restriction
The elements over a level: those of Elem whose shape lies over it.
def ElemAt (j : Fin 2) : Type :=
Σ a : F.Shape j, Σ g : F.Direction a.1 0 → Z.obj ⟨0⟩,
∀ d : F.Direction a.1 1, (famOfPsh Z).2 (g (dirRestr F a.1 d))
Two elements of Elem reassembled with their shapes' level
conditions are equal when they are.
theorem elemAt_congr {j : Fin 2} (t s : Elem F Z) (e : t = s) (h : F.q t.1 = j) :
(⟨⟨t.1, h⟩, t.2⟩ : ElemAt F Z j) = ⟨⟨s.1, e ▸ h⟩, s.2⟩ := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typej:Fin 2t:Elem F Zs:Elem F Ze:t = sh:F.q t.fst = j⊢ ⟨⟨t.fst, h⟩, t.snd⟩ = ⟨⟨s.fst, ⋯⟩, s.snd⟩
subst e F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typej:Fin 2t:Elem F Zh:F.q t.fst = j⊢ ⟨⟨t.fst, h⟩, t.snd⟩ = ⟨⟨t.fst, ⋯⟩, t.snd⟩
rfl All goals completed! 🐙
The elements of the output presheaf over a level are the elements of
Elem over it.
def objLevel (j : Fin 2) : { x : F.obj Z // F.q x.shape = j } ≃ ElemAt F Z j where
toFun x := ⟨⟨(elemEquiv F Z x.1).1, x.2⟩, (elemEquiv F Z x.1).2⟩
invFun e := ⟨(elemEquiv F Z).symm ⟨e.1.1, e.2⟩, e.1.2⟩
left_inv x := Subtype.ext ((elemEquiv F Z).left_inv x.1)
right_inv e := elemAt_congr F Z _ _ ((elemEquiv F Z).right_inv ⟨e.1.1, e.2⟩) _
The restriction from level 1 to level 0 on elements:
restrict the shape and precompose the assignments with the reindexings.
def restrElem (e : ElemAt F Z 1) : ElemAt F Z 0 :=
⟨restrShape₁ F e.1, fun d ↦ e.2.1 (reindex₀ F e.1 d), fun d ↦
⟨(e.2.2 (reindex₁ F e.1 d)).1,
(e.2.2 (reindex₁ F e.1 d)).2.trans (congrArg e.2.1 (dirRestr_reindex₁ F e.1 d))⟩⟩The value the restriction of an element gives a direction is the value the element gives its reindexing.
theorem value_objRestr (x : F.obj Z) (hx : F.q x.shape = 1) ⦃i : Fin 2⦄
(d : F.Direction (F.objRestr waHom x hx).shape i) :
F.value (F.objRestr waHom x hx).1 d = F.value x.1 (F.reindex waHom ⟨x.shape, hx⟩ d) := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zhx:F.q x.shape = 1i:Fin 2d:F.Direction (F.objRestr waHom x hx).shape i⊢ F.value (↑(F.objRestr waHom x hx)) d = F.value (↑x) (F.reindex waHom ⟨x.shape, hx⟩ d)
obtain ⟨b, rfl⟩ := d F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ Typex:F.obj Zhx:F.q x.shape = 1b:F.B (F.objRestr waHom x hx).shape⊢ F.value ↑(F.objRestr waHom x hx) ⟨b, ⋯⟩ = F.value (↑x) (F.reindex waHom ⟨x.shape, hx⟩ ⟨b, ⋯⟩)
rfl All goals completed! 🐙
The restriction of the output presheaf is restrElem on elements.
theorem objLevel_objRestr (x : F.obj Z) (hx : F.q x.shape = 1) :
objLevel F Z 0 ⟨F.objRestr waHom x hx, (F.shapeRestr waHom ⟨x.shape, hx⟩).2⟩ =
restrElem F Z (objLevel F Z 1 ⟨x, hx⟩) :=
Sigma.ext rfl (heq_of_eq (gw_ext F Z (funext fun d ↦ value_objRestr F Z x hx d)
fun d ↦ value_objRestr F Z x hx d))The condition and the code
A presheaf polynomial endofunctor on the walking arrow is base-cartesian
when, for every level-1 shape, the reindexing of level-0
directions from its restriction is a bijection.
The inverse of the reindexing at level 0.
The inverse is a left inverse.
The inverse is a right inverse.
structure BaseCartesian where inv : ∀ a₁ : F.Shape 1, F.Direction a₁.1 0 → F.Direction (restrShape₁ F a₁).1 0 inv_reindex : ∀ (a₁ : F.Shape 1) d, inv a₁ (reindex₀ F a₁ d) = d reindex_inv : ∀ (a₁ : F.Shape 1) d, reindex₀ F a₁ (inv a₁ d) = dvariable {F} (hF : BaseCartesian F)The inverse reindexing sends the restriction of a reindexed direction to the restriction of the direction.
theorem BaseCartesian.inv_dirRestr_reindex₁ (a₁ : F.Shape 1)
(d : F.Direction (restrShape₁ F a₁).1 1) :
hF.inv a₁ (dirRestr F a₁.1 (reindex₁ F a₁ d)) = dirRestr F _ d := by F:PresheafPFunctor (Fin 2) (Fin 2)hF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1d:F.Direction (↑(restrShape₁ F a₁)) 1⊢ hF.inv a₁ (dirRestr F (↑a₁) (reindex₁ F a₁ d)) = dirRestr F (↑(restrShape₁ F a₁)) d
rw [dirRestr_reindex₁ F:PresheafPFunctor (Fin 2) (Fin 2)hF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1d:F.Direction (↑(restrShape₁ F a₁)) 1⊢ hF.inv a₁ (reindex₀ F a₁ (dirRestr F (↑(restrShape₁ F a₁)) d)) = dirRestr F (↑(restrShape₁ F a₁)) d] F:PresheafPFunctor (Fin 2) (Fin 2)hF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1d:F.Direction (↑(restrShape₁ F a₁)) 1⊢ hF.inv a₁ (reindex₀ F a₁ (dirRestr F (↑(restrShape₁ F a₁)) d)) = dirRestr F (↑(restrShape₁ F a₁)) d
exact hF.inv_reindex a₁ _ All goals completed! 🐙
The fibre at a level-1 shape: assignments of its level-1
directions, the family read at the inverse reindexing of the restricted
direction, that restrict along the reindexing to a given assignment of the
restricted shape.
def genFibre₁ (a₁ : F.Shape 1) (A : F.Direction (restrShape₁ F a₁).1 0 → Type)
(w : ∀ d : F.Direction (restrShape₁ F a₁).1 1, A (dirRestr F _ d)) : Type :=
{ w₁ : ∀ d₁ : F.Direction a₁.1 1, A (hF.inv a₁ (dirRestr F a₁.1 d₁)) //
∀ d, HEq (w₁ (reindex₁ F a₁ d)) (w d) }
The fibre at a level-1 shape over a level-0 shape it
restricts to, by transport along the restriction equation.
def genFibreAt (a₁ : F.Shape 1) :
(a : F.Shape 0) → restrShape₁ F a₁ = a → (A : F.Direction a.1 0 → Type) →
(∀ d : F.Direction a.1 1, A (dirRestr F a.1 d)) → Type
| _, rfl, A, w => genFibre₁ hF a₁ A w
The decoding of the code at a level-0 element: the level-1
shapes over its shape, each with its fibre.
def genFibre (a : F.Shape 0) (A : F.Direction a.1 0 → Type)
(w : ∀ d : F.Direction a.1 1, A (dirRestr F a.1 d)) : Type :=
Σ a₁ : { a₁ : F.Shape 1 // restrShape₁ F a₁ = a }, genFibreAt hF a₁.1 a a₁.2 A wThe code of a base-cartesian presheaf polynomial endofunctor on the walking arrow.
def genCode : IR.{0, 0, 1, 1} Type Type :=
IR.sigma Type Type (F.Shape 0) fun a ↦
IR.delta Type Type (F.Direction a.1 0) fun A ↦
IR.sigma Type Type (∀ d : F.Direction a.1 1, A (dirRestr F a.1 d)) fun w ↦
IR.iota Type Type (genFibre hF a A w)The isomorphism
The level-0 elements are the index type of the code's
interpretation.
def genBaseEquiv : ElemAt F Z 0 ≃ (IR.interpObj Type Type (genCode hF) (famOfPsh Z)).1 where
toFun e := ⟨e.1, e.2.1, e.2.2, ⟨()⟩⟩
invFun s := ⟨s.1, s.2.1, s.2.2.1⟩
left_inv _ := rfl
right_inv s :=
match s with
| ⟨_, _, _, ⟨⟨⟩⟩⟩ => rfl
Two elements of the fibre over equal points are heterogeneously equal
when they agree in Z 1.
theorem fibre_heq {u u' : Z.obj ⟨0⟩} (e : u = u') (x : (famOfPsh Z).2 u) (y : (famOfPsh Z).2 u')
(h : x.1 = y.1) : HEq x y := by Z:(Fin 2)ᵒᵖ ⥤ Typeu:Z.obj (Opposite.op 0)u':Z.obj (Opposite.op 0)e:u = u'x:(famOfPsh Z).snd uy:(famOfPsh Z).snd u'h:↑x = ↑y⊢ x ≍ y
subst e Z:(Fin 2)ᵒᵖ ⥤ Typeu:Z.obj (Opposite.op 0)x:(famOfPsh Z).snd uy:(famOfPsh Z).snd uh:↑x = ↑y⊢ x ≍ y
exact heq_of_eq (Subtype.ext h) All goals completed! 🐙
Heterogeneously equal elements of the fibre over equal points agree in
Z 1.
theorem fst_of_fibre_heq {u u' : Z.obj ⟨0⟩} (e : u = u') {x : (famOfPsh Z).2 u}
{y : (famOfPsh Z).2 u'} (h : HEq x y) : x.1 = y.1 := by Z:(Fin 2)ᵒᵖ ⥤ Typeu:Z.obj (Opposite.op 0)u':Z.obj (Opposite.op 0)e:u = u'x:(famOfPsh Z).snd uy:(famOfPsh Z).snd u'h:x ≍ y⊢ ↑x = ↑y
subst e Z:(Fin 2)ᵒᵖ ⥤ Typeu:Z.obj (Opposite.op 0)x:(famOfPsh Z).snd uy:(famOfPsh Z).snd uh:x ≍ y⊢ ↑x = ↑y
exact congrArg Subtype.val (eq_of_heq h) All goals completed! 🐙
An element of the code's total space with a level-1 shape at its
own restriction, from the base assignment, the level-1 assignment and
the fibre element.
def genTotalMk (a₁ : F.Shape 1) (g : F.Direction (restrShape₁ F a₁).1 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction (restrShape₁ F a₁).1 1, (famOfPsh Z).2 (g (dirRestr F _ d)))
(f : genFibre₁ hF a₁ ((famOfPsh Z).2 ∘ g) w) :
Σ p : (IR.interpObj Type Type (genCode hF) (famOfPsh Z)).1,
(IR.interpObj Type Type (genCode hF) (famOfPsh Z)).2 p :=
⟨⟨restrShape₁ F a₁, g, w, ⟨()⟩⟩, ⟨⟨a₁, rfl⟩, f⟩⟩
Two elements of the code's total space at the same level-1 shape
are equal when their base assignments agree and their assignments agree in
Z 1.
theorem genTotalMk_ext (a₁ : F.Shape 1) {g g' : F.Direction (restrShape₁ F a₁).1 0 → Z.obj ⟨0⟩}
(hg : g' = g)
{w : ∀ d : F.Direction (restrShape₁ F a₁).1 1, (famOfPsh Z).2 (g (dirRestr F _ d))}
{w' : ∀ d : F.Direction (restrShape₁ F a₁).1 1, (famOfPsh Z).2 (g' (dirRestr F _ d))}
(hw : ∀ d, (w' d).1 = (w d).1)
{f : genFibre₁ hF a₁ ((famOfPsh Z).2 ∘ g) w} {f' : genFibre₁ hF a₁ ((famOfPsh Z).2 ∘ g') w'}
(hf : ∀ d₁, (f'.1 d₁).1 = (f.1 d₁).1) :
genTotalMk Z hF a₁ g' w' f' = genTotalMk Z hF a₁ g w f := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1g:F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)g':F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)hg:g' = gw:(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g (dirRestr F (↑(restrShape₁ F a₁)) d))w':(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g' (dirRestr F (↑(restrShape₁ F a₁)) d))hw:∀ (d : F.Direction (↑(restrShape₁ F a₁)) 1), ↑(w' d) = ↑(w d)f:genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g) wf':genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') w'hf:∀ (d₁ : F.Direction (↑a₁) 1), ↑(↑f' d₁) = ↑(↑f d₁)⊢ genTotalMk Z hF a₁ g' w' f' = genTotalMk Z hF a₁ g w f
subst hg F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1g':F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)w':(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g' (dirRestr F (↑(restrShape₁ F a₁)) d))f':genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') w'w:(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g' (dirRestr F (↑(restrShape₁ F a₁)) d))hw:∀ (d : F.Direction (↑(restrShape₁ F a₁)) 1), ↑(w' d) = ↑(w d)f:genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') whf:∀ (d₁ : F.Direction (↑a₁) 1), ↑(↑f' d₁) = ↑(↑f d₁)⊢ genTotalMk Z hF a₁ g' w' f' = genTotalMk Z hF a₁ g' w f
obtain rfl : w' = w := funext fun d ↦ Subtype.ext (hw d) F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1g':F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)w':(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g' (dirRestr F (↑(restrShape₁ F a₁)) d))f':genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') w'hw:∀ (d : F.Direction (↑(restrShape₁ F a₁)) 1), ↑(w' d) = ↑(w' d)f:genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') w'hf:∀ (d₁ : F.Direction (↑a₁) 1), ↑(↑f' d₁) = ↑(↑f d₁)⊢ genTotalMk Z hF a₁ g' w' f' = genTotalMk Z hF a₁ g' w' f
obtain rfl : f' = f := Subtype.ext (funext fun d₁ ↦ Subtype.ext (hf d₁)) F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1g':F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)w':(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g' (dirRestr F (↑(restrShape₁ F a₁)) d))f':genFibre₁ hF a₁ ((famOfPsh Z).snd ∘ g') w'hw:∀ (d : F.Direction (↑(restrShape₁ F a₁)) 1), ↑(w' d) = ↑(w' d)hf:∀ (d₁ : F.Direction (↑a₁) 1), ↑(↑f' d₁) = ↑(↑f' d₁)⊢ genTotalMk Z hF a₁ g' w' f' = genTotalMk Z hF a₁ g' w' f'
rfl All goals completed! 🐙
The inverse of the level-1 equivalence at a level-1 shape
over a level-0 shape it restricts to.
def genTotalInv (a₁ : F.Shape 1) :
(a : F.Shape 0) → (h : restrShape₁ F a₁ = a) → (g : F.Direction a.1 0 → Z.obj ⟨0⟩) →
(w : ∀ d : F.Direction a.1 1, (famOfPsh Z).2 (g (dirRestr F a.1 d))) →
genFibreAt hF a₁ a h ((famOfPsh Z).2 ∘ g) w → ElemAt F Z 1
| _, rfl, g, _, f => ⟨a₁, fun d ↦ g (hF.inv a₁ d), fun d₁ ↦ f.1 d₁⟩
The level-1 equivalence on an element: restrict to the
level-0 element below, and keep the level-1 assignment as the
fibre element, reindexed by the inverse at level 0.
def genTotalTo (e : ElemAt F Z 1) :
Σ p : (IR.interpObj Type Type (genCode hF) (famOfPsh Z)).1,
(IR.interpObj Type Type (genCode hF) (famOfPsh Z)).2 p :=
genTotalMk Z hF e.1 (fun d ↦ e.2.1 (reindex₀ F e.1 d))
(fun d ↦ ⟨(e.2.2 (reindex₁ F e.1 d)).1,
(e.2.2 (reindex₁ F e.1 d)).2.trans (congrArg e.2.1 (dirRestr_reindex₁ F e.1 d))⟩)
⟨fun d₁ ↦ ⟨(e.2.2 d₁).1, (e.2.2 d₁).2.trans (congrArg e.2.1 (hF.reindex_inv e.1 _).symm)⟩,
fun d ↦ fibre_heq Z
(congrArg (fun d ↦ e.2.1 (reindex₀ F e.1 d)) (hF.inv_dirRestr_reindex₁ e.1 d)) _ _ rfl⟩
The level-1 equivalence inverts its inverse.
theorem genTotalTo_genTotalInv (a₁ : F.Shape 1) (a : F.Shape 0) (h : restrShape₁ F a₁ = a)
(g : F.Direction a.1 0 → Z.obj ⟨0⟩)
(w : ∀ d : F.Direction a.1 1, (famOfPsh Z).2 (g (dirRestr F a.1 d)))
(f : genFibreAt hF a₁ a h ((famOfPsh Z).2 ∘ g) w) :
genTotalTo Z hF (genTotalInv Z hF a₁ a h g w f) = ⟨⟨a, g, w, ⟨()⟩⟩, ⟨⟨a₁, h⟩, f⟩⟩ := by F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1a:F.toSlicePFunctor.Shape 0h:restrShape₁ F a₁ = ag:F.Direction (↑a) 0 → Z.obj (Opposite.op 0)w:(d : F.Direction (↑a) 1) → (famOfPsh Z).snd (g (dirRestr F (↑a) d))f:genFibreAt hF a₁ a h ((famOfPsh Z).snd ∘ g) w⊢ genTotalTo Z hF (genTotalInv Z hF a₁ a h g w f) = ⟨⟨a, ⟨g, ⟨w, { down := () }⟩⟩⟩, ⟨⟨a₁, h⟩, f⟩⟩
subst h F:PresheafPFunctor (Fin 2) (Fin 2)Z:(Fin 2)ᵒᵖ ⥤ TypehF:BaseCartesian Fa₁:F.toSlicePFunctor.Shape 1g:F.Direction (↑(restrShape₁ F a₁)) 0 → Z.obj (Opposite.op 0)w:(d : F.Direction (↑(restrShape₁ F a₁)) 1) → (famOfPsh Z).snd (g (dirRestr F (↑(restrShape₁ F a₁)) d))f:genFibreAt hF a₁ (restrShape₁ F a₁) ⋯ ((famOfPsh Z).snd ∘ g) w⊢ genTotalTo Z hF (genTotalInv Z hF a₁ (restrShape₁ F a₁) ⋯ g w f) =
⟨⟨restrShape₁ F a₁, ⟨g, ⟨w, { down := () }⟩⟩⟩, ⟨⟨a₁, ⋯⟩, f⟩⟩
exact genTotalMk_ext Z hF a₁ (funext fun d ↦ congrArg g (hF.inv_reindex a₁ d))
(fun d ↦ fst_of_fibre_heq Z (congrArg g (hF.inv_dirRestr_reindex₁ a₁ d)) (f.2 d))
fun _ ↦ rfl All goals completed! 🐙
The level-1 elements are the total space of the code's
interpretation: the level-0 element below, and the fibre element.
def genTotalEquiv :
ElemAt F Z 1 ≃ Σ p : (IR.interpObj Type Type (genCode hF) (famOfPsh Z)).1,
(IR.interpObj Type Type (genCode hF) (famOfPsh Z)).2 p where
toFun := genTotalTo Z hF
invFun s := genTotalInv Z hF s.2.1.1 s.1.1 s.2.1.2 s.1.2.1 s.1.2.2.1 s.2.2
left_inv e :=
Sigma.ext rfl (heq_of_eq (gw_ext F Z (funext fun d ↦ congrArg e.2.1 (hF.reindex_inv e.1 d))
fun _ ↦ rfl))
right_inv s := genTotalTo_genTotalInv Z hF s.2.1.1 s.1.1 s.2.1.2 s.1.2.1 s.1.2.2.1 s.2.2The output presheaf of a base-cartesian presheaf polynomial endofunctor on the walking arrow is the presheaf of the interpretation of its code at the family of the input.
def genCodeEquiv :
PshEquiv (F.objPresheaf Z) (pshOfFam (IR.interpObj Type Type (genCode hF) (famOfPsh Z))) where
base := (objLevel F Z 0).trans (genBaseEquiv Z hF)
total := (objLevel F Z 1).trans (genTotalEquiv Z hF)
map_total t :=
congrArg (fun e : ElemAt F Z 0 ↦ genBaseEquiv Z hF e) (objLevel_objRestr F Z t.1 t.2).symmThe transcription is an instance
The transcription of a slice polynomial functor is base-cartesian: every
shape has the level-0 directions X, on which reindexing is the
identity.
def arrowPshBaseCartesian {X Y : Type} (P : SlicePFunctor.{0, 0, 0, 0} X Y) :
BaseCartesian (arrowPsh P) where
inv a₁ d :=
match a₁, d with
| ⟨.inl _, h⟩, _ => absurd h Fin.zero_ne_one
| ⟨.inr _, _⟩, ⟨.inl x, _⟩ => ⟨.inl x, rfl⟩
| ⟨.inr _, _⟩, ⟨.inr _, h⟩ => absurd h Fin.zero_ne_one.symm
inv_reindex a₁ d :=
match a₁, d with
| ⟨.inl _, h⟩, _ => absurd h Fin.zero_ne_one
| ⟨.inr _, _⟩, ⟨.inl _, _⟩ => rfl
| ⟨.inr _, _⟩, ⟨.inr e, _⟩ => PEmpty.elim e
reindex_inv a₁ d :=
match a₁, d with
| ⟨.inl _, h⟩, _ => absurd h Fin.zero_ne_one
| ⟨.inr _, _⟩, ⟨.inl _, _⟩ => rfl
| ⟨.inr _, _⟩, ⟨.inr _, h⟩ => absurd h Fin.zero_ne_one.symmend GebProto.LargeIR