Timings for Core.v
Require Import Basics Types.
Require Import Cubical.DPath Cubical.PathSquare.
Require Import Homotopy.NullHomotopy.
Require Import Extensions.
Require Import Colimits.Pushout.
Require Import Truncations.Core Truncations.Connectedness.
Require Import Pointed.Core.
Require Import Spaces.Nat.Core.
Local Open Scope pointed_scope.
Local Open Scope path_scope.
(** * Joins *)
(** The join is the pushout of two types under their product. *)
Definition Join (A : Type@{i}) (B : Type@{j})
:= Pushout@{k i j k} (@fst A B) (@snd A B).
Definition joinl {A B} : A -> Join A B
:= fun a => @pushl (A*B) A B fst snd a.
Definition joinr {A B} : B -> Join A B
:= fun b => @pushr (A*B) A B fst snd b.
Definition jglue {A B} a b : joinl a = joinr b
:= @pglue (A*B) A B fst snd (a , b).
Definition Join_ind {A B : Type} (P : Join A B -> Type)
(P_A : forall a, P (joinl a)) (P_B : forall b, P (joinr b))
(P_g : forall a b, transport P (jglue a b) (P_A a) = (P_B b))
: forall (x : Join A B), P x.
apply (Pushout_ind P P_A P_B).
exact (fun ab => P_g (fst ab) (snd ab)).
Definition Join_ind_beta_jglue {A B : Type} (P : Join A B -> Type)
(P_A : forall a, P (joinl a)) (P_B : forall b, P (joinr b))
(P_g : forall a b, transport P (jglue a b) (P_A a) = (P_B b)) a b
: apD (Join_ind P P_A P_B P_g) (jglue a b) = P_g a b
:= Pushout_ind_beta_pglue _ _ _ _ _.
(** A version of [Join_ind] specifically for proving that two functions defined on a [Join] are homotopic. *)
Definition Join_ind_FlFr {A B P : Type} (f g : Join A B -> P)
(Hl : forall a, f (joinl a) = g (joinl a))
(Hr : forall b, f (joinr b) = g (joinr b))
(Hglue : forall a b, ap f (jglue a b) @ Hr b = Hl a @ ap g (jglue a b))
: f == g.
snapply (Join_ind _ Hl Hr).
Definition Join_ind_Flr {A B : Type} (f : Join A B -> Join A B)
(Hl : forall a, f (joinl a) = joinl a)
(Hr : forall b, f (joinr b) = joinr b)
(Hglue : forall a b, ap f (jglue a b) @ Hr b = Hl a @ jglue a b)
: forall x, f x = x.
snapply (Join_ind _ Hl Hr).
(** And a version for showing that a composite is homotopic to the identity. *)
Definition Join_ind_FFlr {A B P : Type} (f : Join A B -> P) (g : P -> Join A B)
(Hl : forall a, g (f (joinl a)) = joinl a)
(Hr : forall b, g (f (joinr b)) = joinr b)
(Hglue : forall a b, ap g (ap f (jglue a b)) @ Hr b = Hl a @ jglue a b)
: forall x, g (f x) = x.
snapply (Join_ind _ Hl Hr).
Definition Join_ind_FFlFr {A B C P : Type}
(f : Join A B -> C) (g : C -> P) (h : Join A B -> P)
(Hl : forall a, g (f (joinl a)) = h (joinl a))
(Hr : forall b, g (f (joinr b)) = h (joinr b))
(Hglue : forall a b, ap g (ap f (jglue a b)) @ Hr b = Hl a @ ap h (jglue a b))
: g o f == h.
snapply (Join_ind _ Hl Hr).
Definition Join_ind_FlFFr {A B C P : Type}
(f : Join A B -> C) (g : C -> P) (h : Join A B -> P)
(Hl : forall a, h (joinl a) = g (f (joinl a)))
(Hr : forall b, h (joinr b) = g (f (joinr b)))
(Hglue : forall a b, ap h (jglue a b) @ Hr b = Hl a @ ap g (ap f (jglue a b)))
: h == g o f.
snapply (Join_ind _ Hl Hr).
Definition Join_ind_FFlFFr {A B C D P : Type}
(f : Join A B -> C) (g : C -> P) (h : Join A B -> D) (i : D -> P)
(Hl : forall a, i (h (joinl a)) = g (f (joinl a)))
(Hr : forall b, i (h (joinr b)) = g (f (joinr b)))
(Hglue : forall a b, ap i (ap h (jglue a b)) @ Hr b = Hl a @ ap g (ap f (jglue a b)))
: i o h == g o f.
snapply (Join_ind _ Hl Hr).
Definition Join_rec {A B P : Type} (P_A : A -> P) (P_B : B -> P)
(P_g : forall a b, P_A a = P_B b) : Join A B -> P.
apply (Pushout_rec P P_A P_B).
exact (fun ab => P_g (fst ab) (snd ab)).
Definition Join_rec_beta_jglue {A B P : Type} (P_A : A -> P)
(P_B : B -> P) (P_g : forall a b, P_A a = P_B b) a b
: ap (Join_rec P_A P_B P_g) (jglue a b) = P_g a b.
exact (Pushout_rec_beta_pglue _ _ _ _ (a, b)).
(** If [A] is pointed, so is [Join A B]. *)
Definition pjoin (A : pType) (B : Type) : pType
:= [Join A B, joinl pt].
Arguments joinl {A B}%_type_scope _ , [A] B _.
Arguments joinr {A B}%_type_scope _ , A [B] _.
(** ** Zigzags in joins *)
(** These paths are very common, so we give them names. *)
Definition zigzag {A B : Type} (a a' : A) (b : B)
: joinl a = joinl a'
:= jglue a b @ (jglue a' b)^.
Definition zagzig {A B : Type} (a : A) (b b' : B)
: joinr b = joinr b'
:= (jglue a b)^ @ jglue a b'.
(** And we give a beta rule for zigzags. *)
Definition Join_rec_beta_zigzag {A B P : Type} (P_A : A -> P)
(P_B : B -> P) (P_g : forall a b, P_A a = P_B b) a a' b
: ap (Join_rec P_A P_B P_g) (zigzag a a' b) = P_g a b @ (P_g a' b)^.
exact (Join_rec_beta_jglue _ _ _ a b @@ inverse2 (Join_rec_beta_jglue _ _ _ a' b)).
(** * [Join_rec] gives an equivalence of 0-groupoids
We now prove many things about [Join_rec], for example, that it is an equivalence of 0-groupoids from the [JoinRecData] that we define next. The framework we use is a bit elaborate, but it parallels the framework used in TriJoin.v, where careful organization is essential. *)
Record JoinRecData {A B P : Type} := {
jl : A -> P;
jr : B -> P;
jg : forall a b, jl a = jr b;
}.
Arguments JoinRecData : clear implicits.
Arguments Build_JoinRecData {A B P}%_type_scope (jl jr jg)%_function_scope.
(** We use the name [join_rec] for the version of [Join_rec] defined on this data. *)
Definition join_rec {A B P : Type} (f : JoinRecData A B P)
: Join A B $-> P
:= Join_rec (jl f) (jr f) (jg f).
Definition join_rec_beta_jg {A B P : Type} (f : JoinRecData A B P) (a : A) (b : B)
: ap (join_rec f) (jglue a b) = jg f a b
:= Join_rec_beta_jglue _ _ _ a b.
(** We're next going to define a map in the other direction. We do it via showing that [JoinRecData] is a 0-coherent 1-functor to [Type]. We'll later show that it is a 1-functor to 0-groupoids. *)
Definition joinrecdata_fun {A B P Q : Type} (g : P -> Q) (f : JoinRecData A B P)
: JoinRecData A B Q.
snapply Build_JoinRecData.
exact (fun a b => ap g (jg f a b)).
(** The join itself has canonical [JoinRecData]. *)
Definition joinrecdata_join (A B : Type) : JoinRecData A B (Join A B)
:= Build_JoinRecData joinl joinr jglue.
(** Combining these gives a function going in the opposite direction to [join_rec]. *)
Definition join_rec_inv {A B P : Type} (f : Join A B -> P)
: JoinRecData A B P
:= joinrecdata_fun f (joinrecdata_join A B).
(** Under [Funext], [join_rec] and [join_rec_inv] should be inverse equivalences. We'll avoid [Funext] and show that they are equivalences of 0-groupoids, where we choose the path structures carefully. *)
(** ** The graph structure on [JoinRecData A B P]
Under [Funext], this type will be equivalent to the identity type. But without [Funext], this definition will be more useful. *)
Record JoinRecPath {A B P : Type} {f g : JoinRecData A B P} := {
hl : forall a, jl f a = jl g a;
hr : forall b, jr f b = jr g b;
hg : forall a b, jg f a b @ hr b = hl a @ jg g a b;
}.
Arguments JoinRecPath {A B P} f g.
(** In the special case where the first two components of [f] and [g] agree definitionally, [hl] and [hr] can be identity paths, and [hg] simplifies slightly. *)
Definition bundle_joinrecpath {A B P : Type} {jl' : A -> P} {jr' : B -> P}
{f g : forall a b, jl' a = jr' b}
(h : forall a b, f a b = g a b)
: JoinRecPath (Build_JoinRecData jl' jr' f) (Build_JoinRecData jl' jr' g).
snapply Build_JoinRecPath.
intros; apply equiv_p1_1q, h.
(** A tactic that helps us apply the previous result. *)
Ltac bundle_joinrecpath :=
hnf;
match goal with |- JoinRecPath ?F ?G =>
refine (bundle_joinrecpath (f:=jg F) (g:=jg G) _) end.
(** Using [JoinRecPath], we can restate the beta rule for [join_rec]. This says that [join_rec_inv] is split surjective. *)
Definition join_rec_beta {A B P : Type} (f : JoinRecData A B P)
: JoinRecPath (join_rec_inv (join_rec f)) f
:= bundle_joinrecpath (join_rec_beta_jg f).
(** [join_rec_inv] is essentially injective, as a map between 0-groupoids. *)
Definition isinj_join_rec_inv {A B P : Type} {f g : Join A B -> P}
(h : JoinRecPath (join_rec_inv f) (join_rec_inv g))
: f == g
:= Join_ind_FlFr _ _ (hl h) (hr h) (hg h).
(** ** Lemmas and tactics about intervals and squares
We now introduce several lemmas and tactics that will dispense with some routine goals. The idea is that a generic interval can be assumed to be trivial on the first vertex, and a generic square can be assumed to be the identity on the domain edge. In order to apply the [paths_ind] and [square_ind] lemmas that make this precise, we need to generalize various terms in the goal. *)
(** This destructs a three component term [f], generalizes each piece evaluated appropriately, and clears all pieces. *)
Ltac generalize_three f a b :=
let fg := fresh in let fr := fresh in let fl := fresh in
destruct f as [fl fr fg]; cbn;
generalize (fg a b); clear fg;
generalize (fr b); clear fr;
generalize (fl a); clear fl.
(** For [f : JoinRecData A B P], if we have [a] and [b] and are trying to prove a statement only involving [jl f a], [jr f b] and [jg f a b], we can assume [jr f b] is [jl f a] and that [jg f a b] is reflexivity. This is just path induction, but it requires generalizing the goal appropriately. *)
Ltac interval_ind f a b :=
generalize_three f a b;
intro f; (* We really only wanted two of them generalized here, so we intro one. *)
apply paths_ind.
(** Similarly, for [h : JoinRecPath f g], if we have [a] and [b] and are trying to prove a goal only involving [h] and [g] evaluated at those points, we can assume that [g] is [f] and that [h] is "reflexivity". For this, we first define a lemma that is like "path induction on h", and then a tactic that uses it. *)
Definition square_ind {P : Type} (a b : P) (ab : a = b)
(Q : forall (a' b' : P) (ab' : a' = b') (ha : a = a') (hb : b = b') (k : ab @ hb = ha @ ab'), Type)
(s : Q a b ab idpath idpath (equiv_p1_1q idpath))
: forall a' b' ab' ha hb k, Q a' b' ab' ha hb k.
revert k; equiv_intro (equiv_p1_1q (p:=ab) (q:=ab')) k'; destruct k'.
(* [g] should be the codomain of [h]. *)
Global Ltac square_ind g h a b :=
generalize_three h a b;
generalize_three g a b;
apply square_ind.
(** ** Use the WildCat library to organize things *)
(** We begin by showing that [JoinRecData A B P] is a 0-groupoid, one piece at a time. *)
Instance isgraph_joinrecdata (A B P : Type) : IsGraph (JoinRecData A B P)
:= {| Hom := JoinRecPath |}.
Instance is01cat_joinrecdata (A B P : Type) : Is01Cat (JoinRecData A B P).
snapply Build_JoinRecPath; intros; cbn beta.
exact (hl h1 a @ hl h2 a).
exact (hr h1 b @ hr h2 b).
(* Some simple path algebra works as well. *)
Instance is0gpd_joinrecdata (A B P : Type) : Is0Gpd (JoinRecData A B P).
snapply Build_JoinRecPath; intros; cbn beta.
(* Some simple path algebra works as well. *)
Definition joinrecdata_0gpd (A B P : Type) : ZeroGpd
:= Build_ZeroGpd (JoinRecData A B P) _ _ _.
(** ** [joinrecdata_0gpd A B] is a 1-functor from [Type] to [ZeroGpd]
It's a 1-functor that lands in [ZeroGpd], and the morphisms of [ZeroGpd] are 0-functors, so it's easy to get confused about the levels. *)
(** First we need to show that the induced map is a morphism in [ZeroGpd], i.e. that it is a 0-functor. *)
Instance is0functor_joinrecdata_fun {A B P Q : Type} (g : P -> Q)
: Is0Functor (@joinrecdata_fun A B P Q g).
snapply Build_JoinRecPath; intros; cbn.
(** [joinrecdata_0gpd A B] is a 0-functor from [Type] to [ZeroGpd] (one level up). *)
Instance is0functor_joinrecdata_0gpd (A B : Type) : Is0Functor (joinrecdata_0gpd A B).
exact (joinrecdata_fun g).
apply is0functor_joinrecdata_fun.
(** [joinrecdata_0gpd A B] is a 1-functor from [Type] to [ZeroGpd]. *)
Instance is1functor_joinrecdata_0gpd (A B : Type) : Is1Functor (joinrecdata_0gpd A B).
(* If [g1 g2 : P -> Q] are homotopic, then the induced maps are homotopic: *)
intros P Q g1 g2 h f; cbn in *.
snapply Build_JoinRecPath; intros; cbn.
(* The identity map [P -> P] is sent to a map homotopic to the identity. *)
bundle_joinrecpath; intros; cbn.
(* It respects composition. *)
intros P Q R g1 g2 f; cbn.
bundle_joinrecpath; intros; cbn.
Definition joinrecdata_0gpd_fun (A B : Type) : Fun11 Type ZeroGpd
:= Build_Fun11 _ _ (joinrecdata_0gpd A B).
(** By the Yoneda lemma, it follows from [JoinRecData] being a 1-functor that given [JoinRecData] in [J], we get a map [(J -> P) $-> (JoinRecData A B P)] of 0-groupoids which is natural in [P]. Below we will specialize to the case where [J] is [Join A B] with the canonical [JoinRecData]. *)
Definition join_nattrans_recdata {A B J : Type} (f : JoinRecData A B J)
: NatTrans (opyon_0gpd J) (joinrecdata_0gpd_fun A B).
(** Thus we get a map [(Join A B -> P) $-> (JoinRecData A B P)] of 0-groupoids, natural in [P]. The underlying map is [join_rec_inv A B P]. *)
Definition join_rec_inv_nattrans (A B : Type)
: NatTrans (opyon_0gpd (Join A B)) (joinrecdata_0gpd_fun A B)
:= join_nattrans_recdata (joinrecdata_join A B).
(** This natural transformation is in fact a natural equivalence of 0-groupoids. *)
Definition join_rec_inv_natequiv (A B : Type)
: NatEquiv (opyon_0gpd (Join A B)) (joinrecdata_0gpd_fun A B).
1: apply join_rec_inv_nattrans.
apply isequiv_0gpd_issurjinj.
exact (@isinj_join_rec_inv A B P).
(** It will be handy to name the inverse natural equivalence. *)
Definition join_rec_natequiv (A B : Type)
:= natequiv_inverse (join_rec_inv_natequiv A B).
(** [join_rec_natequiv A B P] is an equivalence of 0-groupoids whose underlying function is definitionally [join_rec]. *)
Local Definition join_rec_natequiv_check (A B P : Type)
: equiv_fun_0gpd (join_rec_natequiv A B P) = @join_rec A B P
:= idpath.
(** It follows that [join_rec A B P] is a 0-functor. *)
Instance is0functor_join_rec (A B P : Type) : Is0Functor (@join_rec A B P).
change (Is0Functor (equiv_fun_0gpd (join_rec_natequiv A B P))).
(** And that [join_rec A B] is natural. The [$==] in the statement is just [==], but we use WildCat notation so that we can invert and compose these with WildCat notation. *)
Definition join_rec_nat (A B : Type) {P Q : Type} (g : P -> Q) (f : JoinRecData A B P)
: join_rec (joinrecdata_fun g f) $== g o join_rec f.
exact (isnat (join_rec_natequiv A B) g f).
(** We restate the previous two results using [Join_rec] for convenience. *)
Definition Join_rec_homotopic (A B : Type) {P : Type}
(fl : A -> P) (fr : B -> P) (fg : forall a b, fl a = fr b)
(fl' : A -> P) (fr' : B -> P) (fg' : forall a b, fl' a = fr' b)
(hl : forall a, fl a = fl' a)
(hr : forall b, fr b = fr' b)
(hg : forall a b, fg a b @ hr b = hl a @ fg' a b)
: Join_rec fl fr fg == Join_rec fl' fr' fg'
:= fmap join_rec (Build_JoinRecPath _ _ _
{| jl:=fl; jr:=fr; jg:=fg |} {| jl:=fl'; jr:=fr'; jg:=fg' |} hl hr hg).
Definition Join_rec_nat (A B : Type) {P Q : Type} (g : P -> Q)
(fl : A -> P) (fr : B -> P) (fg : forall a b, fl a = fr b)
: Join_rec (g o fl) (g o fr) (fun a b => ap g (fg a b)) == g o Join_rec fl fr fg
:= join_rec_nat _ _ g {| jl:=fl; jr:=fr; jg:=fg |}.
(** * Various types of equalities between paths in joins *)
(** Naturality squares for given paths in [A] and [B]. *)
Definition join_natsq {A B : Type} {a a' : A} {b b' : B}
(p : a = a') (q : b = b')
: (ap joinl p) @ (jglue a' b') = (jglue a b) @ (ap joinr q).
Definition join_natsq_v {A B : Type} {a a' : A} {b b' : B}
(p : a = a') (q : b = b')
: PathSquare (ap joinl p) (ap joinr q) (jglue a b) (jglue a' b').
Definition join_natsq_h {A B : Type} {a a' : A} {b b' : B}
(p : a = a') (q : b = b')
: PathSquare (jglue a b) (jglue a' b') (ap joinl p) (ap joinr q).
(** The triangles that arise when one of the given paths is reflexivity. *)
Definition triangle_h {a a' : A} (b : B) (p : a = a')
: ap joinl p @ (jglue a' b) = jglue a b.
Definition triangle_h' {a a' : A} (b : B) (p : a = a')
: zigzag a a' b = ap joinl p.
Definition triangle_v (a : A) {b b' : B} (p : b = b')
: jglue a b @ ap joinr p = jglue a b'.
Definition triangle_v' (a : A) {b b' : B} (p : b = b')
: zagzig a b b' = ap joinr p.
(** For just one of the above, we give a rule for how it behaves on inverse paths. *)
Definition triangle_v_V (a : A) {b b' : B} (p : b = b')
: triangle_v a p^ = (1 @@ ap_V joinr p) @ moveR_pV _ _ _ (triangle_v a p)^.
symmetry; apply concat_pV_p.
(** Diamond lemmas for Join *)
Definition Diamond (a a' : A) (b b' : B)
:= PathSquare (jglue a b) (jglue a' b')^ (jglue a b') (jglue a' b)^.
Definition diamond_h {a a' : A} (b b' : B) (p : a = a')
: zigzag a a' b = zigzag a a' b'.
exact (concat_pV _ @ (concat_pV _)^).
Definition diamond_h_sq {a a' : A} (b b' : B) (p : a = a')
: Diamond a a' b b'.
by apply sq_path, diamond_h.
Definition diamond_v (a a' : A) {b b' : B} (p : b = b')
: zigzag a a' b = zigzag a a' b'
:= ap (zigzag a a') p.
Definition diamond_v_sq (a a' : A) {b b' : B} (p : b = b')
: Diamond a a' b b'.
by apply sq_path, diamond_v.
Lemma diamond_symm (a : A) (b : B)
: diamond_v_sq a a 1 = diamond_h_sq b b 1.
unfold diamond_v_sq, diamond_h_sq, diamond_v, diamond_h.
symmetry; apply ap, concat_pV.
Definition diamond_twist {A : Type} {a a' : A} (p : a = a')
: DPath (fun x => Diamond a' x a x) p
(diamond_v_sq a' a 1) (diamond_h_sq a a' 1).
(** * Functoriality of Join. *)
(** In some cases, we'll need to refer to the recursion data that defines [functor_join], so we make it a separate definition. *)
Definition functor_join_recdata {A B C D} (f : A -> C) (g : B -> D)
: JoinRecData A B (Join C D)
:= {| jl := joinl o f; jr := joinr o g; jg := fun a b => jglue (f a) (g b); |}.
Definition functor_join {A B C D} (f : A -> C) (g : B -> D)
: Join A B -> Join C D
:= join_rec (functor_join_recdata f g).
Definition functor_join_beta_jglue {A B C D : Type} (f : A -> C) (g : B -> D)
(a : A) (b : B)
: ap (functor_join f g) (jglue a b) = jglue (f a) (g b)
:= join_rec_beta_jg _ a b.
Definition functor_join_beta_zigzag {A B C D : Type} (f : A -> C) (g : B -> D)
(a a' : A) (b : B)
: ap (functor_join f g) (zigzag a a' b) = zigzag (f a) (f a') (g b)
:= Join_rec_beta_zigzag _ _ _ a a' b.
Definition functor_join_compose {A B C D E F}
(f : A -> C) (g : B -> D) (h : C -> E) (i : D -> F)
: functor_join (h o f) (i o g) == functor_join h i o functor_join f g.
lhs napply functor_join_beta_jglue; symmetry.
lhs nrefine (ap _ (functor_join_beta_jglue _ _ _ _)).
napply (functor_join_beta_jglue _ _ (f a) (g b)).
Definition functor_join_idmap {A B}
: functor_join idmap idmap == (idmap : Join A B -> Join A B).
lhs napply functor_join_beta_jglue.
symmetry; apply ap_idmap.
Definition functor2_join {A B C D} {f f' : A -> C} {g g' : B -> D}
(h : f == f') (k : g == g')
: functor_join f g == functor_join f' g'.
simpl; intros; apply ap, h.
simpl; intros; apply ap, k.
lhs napply (functor_join_beta_jglue _ _ _ _ @@ 1).
lhs napply (1 @@ functor_join_beta_jglue _ _ _ _).
#[export] Instance isequiv_functor_join {A B C D}
(f : A -> C) `{!IsEquiv f} (g : B -> D) `{!IsEquiv g}
: IsEquiv (functor_join f g).
snapply isequiv_adjointify.
exact (functor_join f^-1 g^-1).
1: symmetry; apply functor_join_compose.
1: exact (functor2_join (eisretr f) (eisretr g)).
exact functor_join_idmap.
1: symmetry; apply functor_join_compose.
1: exact (functor2_join (eissect f) (eissect g)).
apply functor_join_idmap.
Definition equiv_functor_join {A B C D} (f : A <~> C) (g : B <~> D)
: Join A B <~> Join C D := Build_Equiv _ _ (functor_join f g) _.
#[export] Instance is0bifunctor_join : Is0Bifunctor Join.
snapply Build_Is0Bifunctor'.
exact (functor_join f g).
#[export] Instance is1bifunctor_join : Is1Bifunctor Join.
snapply Build_Is1Bifunctor'.
exact (functor2_join p q).
intros A; exact functor_join_idmap.
intros A B C [f g] [h k].
exact (functor_join_compose f g h k).
(** * Symmetry of Join
We'll use the recursion equivalence above to prove the symmetry of Join, using the Yoneda lemma. The idea is that [Join A B -> P] is equivalent (as a 0-groupoid) to [JoinRecData A B P], and the latter is very symmetrical by construction, which makes it easy to show that it is equivalent to [JoinRecData B A P]. Going back along the first equivalence gets us to [Join B A -> P]. These equivalences are natural in [P], so the symmetry equivalence follows from the Yoneda lemma. This is mainly meant as a warm-up to proving the associativity of the join. *)
Definition joinrecdata_sym (A B P : Type)
: joinrecdata_0gpd A B P $-> joinrecdata_0gpd B A P.
(* The map of types [JoinRecData A B P -> JoinRecData B A P]: *)
snapply (Build_JoinRecData fr fl).
intros b a; exact (fg a b)^.
(* It respects the paths. *)
snapply Build_JoinRecPath; simpl.
(** This map is its own inverse in the 1-category of 0-groupoids. *)
Definition joinrecdata_sym_inv (A B P : Type)
: joinrecdata_sym B A P $o joinrecdata_sym A B P $== Id _.
(** We get the symmetry natural equivalence on [TriJoinRecData]. *)
Definition joinrecdata_sym_natequiv (A B : Type)
: NatEquiv (joinrecdata_0gpd_fun A B) (joinrecdata_0gpd_fun B A).
(* An equivalence of 0-groupoids for each [P]: *)
1, 2: apply joinrecdata_sym.
1, 2: apply joinrecdata_sym_inv.
snapply Build_Is1Natural.
(** Combining with the recursion equivalence [join_rec_inv_natequiv] and its inverse gives the symmetry natural equivalence between the representable functors. *)
Definition joinrecdata_fun_sym (A B : Type)
: NatEquiv (opyon_0gpd (Join A B)) (opyon_0gpd (Join B A))
:= natequiv_compose (join_rec_natequiv B A)
(natequiv_compose (joinrecdata_sym_natequiv A B) (join_rec_inv_natequiv A B)).
(** The Yoneda lemma for 0-groupoid valued functors therefore gives us an equivalence between the representing objects. We mark this with a prime, since we'll use a homotopic map with a slightly simpler definition. *)
Definition equiv_join_sym' (A B : Type)
: Join A B <~> Join B A.
tapply (opyon_equiv_0gpd (A:=Type)).
apply joinrecdata_fun_sym.
(** It has the nice property that the underlying function of the inverse is again [equiv_join_sym'], with arguments permuted. *)
Local Definition equiv_join_sym_check1 (A B : Type)
: (equiv_join_sym' A B)^-1 = equiv_fun (equiv_join_sym' B A)
:= idpath.
(** The definition we end up with is almost the same as the obvious one, but has an extra [ap idmap] in it. *)
Local Definition equiv_join_sym_check2 (A B : Type)
: equiv_fun (equiv_join_sym' A B) = Join_rec (fun a : A => joinr a) (fun b : B => joinl b)
(fun (a : A) (b : B) => (ap idmap (jglue b a))^)
:= idpath.
(** The next two give the obvious definition. *)
Definition join_sym_recdata (A B : Type)
: JoinRecData A B (Join B A)
:= Build_JoinRecData joinr joinl (fun a b => (jglue b a)^).
Definition join_sym (A B : Type)
: Join A B -> Join B A
:= join_rec (join_sym_recdata A B).
Definition join_sym_beta_jglue {A B} (a : A) (b : B)
: ap (join_sym A B) (jglue a b) = (jglue b a)^
:= Join_rec_beta_jglue _ _ _ _ _.
(** The obvious definition is homotopic to the definition via the Yoneda lemma. *)
Definition join_sym_homotopic (A B : Type)
: join_sym A B == equiv_join_sym' A B.
(** Both sides are [join_rec] applied to [JoinRecData]: *)
bundle_joinrecpath; intros; cbn.
(** Therefore the obvious definition is also an equivalence, and the inverse function can also be chosen to be [join_sym]. *)
Definition equiv_join_sym (A B : Type) : Join A B <~> Join B A
:= equiv_homotopic_inverse (equiv_join_sym' A B)
(join_sym_homotopic A B)
(join_sym_homotopic B A).
#[export] Instance isequiv_join_sym A B : IsEquiv (join_sym A B)
:= equiv_isequiv (equiv_join_sym A B).
(** It's also straightforward to directly prove that [join_sym] is an equivalence. The above approach is meant to illustrate the Yoneda lemma. In the case of [equiv_trijoin_twist], the Yoneda approach seems to be more straightforward. *)
Definition join_sym_inv A B : join_sym A B o join_sym B A == idmap.
snapply (Join_ind_FFlr (join_sym B A)).
refine (ap _ (join_rec_beta_jg _ a b) @ _); cbn.
refine (ap_V _ (jglue b a) @ _).
refine (ap inverse (join_rec_beta_jg _ b a) @ _).
(** Finally, one can also prove that the join is symmetric using [pushout_sym] and [equiv_prod_symm], but this results in an equivalence whose inverse isn't of the same form. *)
(** We give a direct proof that [join_sym] is natural. *)
Definition join_sym_nat {A B A' B'} (f : A -> A') (g : B -> B')
: join_sym A' B' o functor_join f g == functor_join g f o join_sym A B.
lhs nrefine (ap _ (functor_join_beta_jglue _ _ _ _)).
lhs napply join_sym_beta_jglue.
lhs nrefine (ap _ (join_sym_beta_jglue a b)).
refine (ap_V _ (jglue b a) @ ap inverse _).
apply functor_join_beta_jglue.
(** * Other miscellaneous results about joins *)
(** Relationship to truncation levels and connectedness. *)
(** Joining with a contractible type produces a contractible type *)
#[export] Instance contr_join A B `{Contr A} : Contr (Join A B).
apply (Build_Contr _ (joinl (center A))).
intros a; apply ap, contr.
lhs napply transport_paths_r.
(** The join of hprops is an hprop *)
#[export] Instance ishprop_join `{Funext} A B `{IsHProp A} `{IsHProp B} : IsHProp (Join A B).
apply hprop_inhabited_contr.
intros a; apply contr_join.
exact (contr_inhabited_hprop A a).
intros b; refine (contr_equiv (Join B A) (equiv_join_sym B A)).
exact (contr_inhabited_hprop B b).
(* The two proofs of contractibility are equal because [Contr] is an [HProp]. This uses [Funext]. *)
intros a b; apply path_ishprop.
Lemma equiv_into_hprop `{Funext} {A B P : Type} `{IsHProp P} (f : A -> P)
: (Join A B -> P) <~> (B -> P).
1: exact (fun f => f o joinr).
(** And coincides with their disjunction *)
Definition equiv_join_hor `{Funext} A B `{IsHProp A} `{IsHProp B}
: Join A B <~> hor A B.
exact (Join_rec (fun a => tr (inl a)) (fun b => tr (inr b)) (fun _ _ => path_ishprop _ _)).
(** Joins add connectivity *)
#[export] Instance isconnected_join `{Funext} {m n : trunc_index}
(A B : Type) `{IsConnected m A} `{IsConnected n B}
: IsConnected (m +2+ n) (Join A B).
apply isconnected_from_elim; intros C ? k.
transparent assert (f : (A -> {s : Unit -> C &
forall x, s tt = k (joinr x)})).
intros a; exists (fun _ => k (joinl a)); intros b.
exact (ap k (jglue a b)).
assert (h : NullHomotopy f).
rapply (isconnected_elim m).
rapply (istrunc_extension_along_conn (n:=n)).
unfold NullHomotopy in *; destruct h as [[c g] h].
exact (ap10 (h a)..1 tt).
apply moveL_pV, moveR_Mp.
lhs_V napply (apD10 (h a)..2 b); simpl.
lhs napply transport_forall_constant.
napply transport_paths_Fl.
Definition equiv_join_empty_right A : Join A Empty <~> A.
snapply equiv_adjointify.
apply join_rec; snapply (Build_JoinRecData idmap); contradiction.
snapply Join_ind; [reflexivity| |]; contradiction.
Definition equiv_join_empty_left A : Join Empty A <~> A
:= equiv_join_empty_right _ oE equiv_join_sym _ _.
#[export] Instance join_right_unitor : RightUnitor Join Empty.
exact equiv_join_empty_right.
snapply Build_Is1Natural.
cbn -[equiv_join_empty_right].
#[export] Instance join_left_unitor : LeftUnitor Join Empty.
exact equiv_join_empty_left.
snapply Build_Is1Natural.
cbn -[equiv_join_empty_right].
rhs_V rapply (isnat_natequiv join_right_unitor).
cbn -[equiv_join_empty_right].
Arguments equiv_join_empty_right : simpl never.
(** Iterated Join powers of a type. *)
(** The join of [n.+1] copies of a type. This is convenient because it produces [A] definitionally when [n] is [0]. We annotate the universes to reduce universe variables. *)
Definition iterated_join (A : Type@{u}) (n : nat) : Type@{u}
:= nat_iter n (Join A) A.
(** The join of [n] copies of a type. This is sometimes convenient for proofs by induction as it gives a trivial base case. *)
Definition join_power (A : Type@{u}) (n : nat) : Type@{u}
:= nat_iter n (Join A) (Empty : Type@{u}).
Definition equiv_join_powers (A : Type) (n : nat) : join_power A n.+1 <~> iterated_join A n.
induction n as [|n IHn]; simpl.
exact (equiv_join_empty_right A).
exact (equiv_functor_join equiv_idmap IHn).
(** ** Double recursion for Join *)
Context {A B C D : Type} (P : Type)
(P_AC : A -> C -> P) (P_AD : A -> D -> P) (P_BC : B -> C -> P) (P_BD : B -> D -> P)
(P_gAx : forall a c d, P_AC a c = P_AD a d)
(P_gBx : forall b c d, P_BC b c = P_BD b d)
(P_gxC : forall c a b, P_AC a c = P_BC b c)
(P_gxD : forall d a b, P_AD a d = P_BD b d)
(P_g : forall a b c d, P_gAx a c d @ P_gxD d a b = P_gxC c a b @ P_gBx b c d).
Definition Join_rec2 : Join A B -> Join C D -> P.
1: intros a; exact (Join_rec (P_AC a) (P_AD a) (P_gAx a) y).
1: intros b; exact (Join_rec (P_BC b) (P_BD b) (P_gBx b) y).
1: intros c; exact (P_gxC c a b).
1: intros d; exact (P_gxD d a b).
1: apply Join_rec_beta_jglue.
2: apply Join_rec_beta_jglue.