(* begin hide *) (**********************************************************************) (* Equations *) (* Copyright (c) 2009-2021 Matthieu Sozeau *) (**********************************************************************) (* This file is distributed under the terms of the *) (* GNU Lesser General Public License Version 2.1 *) (**********************************************************************) (* end hide *) (** * HoTT-light ** A lightweight version of the Homotopy Type Theory library prelude. *) Set Warnings "-notation-overridden". From Stdlib Require Export Unicode.Utf8. From Stdlib Require Import Program.Tactics Setoid Relations. (** Switches to constants in Type *) From Equations.Type Require Import All. (** This imports the polymorphic identity and sigma types in Type and their associated notations. *) Import Id_Notations. Import Sigma_Notations. Local Open Scope equations_scope. Set Warnings "-deprecated-option". Set Universe Polymorphism. Set Primitive Projections. (** We want our definitions to stay transparent. *) Set Equations Transparent. Set Implicit Arguments. (** Redefine a polymorphic identity function *) Definition id {A : Type} (a : A) : A := a. From Stdlib Require Import CRelationClasses CMorphisms. #[local] Instance id_reflexive A : Reflexive (@Id A). Proof. exact (@id_refl A). Defined. #[local] Instance eq_symmetric A : Symmetric (@Id A). Proof. exact (@id_sym A). Defined. #[local] Instance eq_transitive A : Transitive (@Id A). Proof. exact (@id_trans A). Defined. (** Non-dependent cartesian products are just sigma types. *) Equations fst {A B} (p : A * B) : A := fst (a, b) := a. Equations snd {A B} (p : A * B) : B := snd (a, b) := b. Definition Sect {A B : Type} (s : A -> B) (r : B -> A) := forall x : A, r (s x) = x. Equations ap {A B : Type} (f : A -> B) {x y : A} (p : x = y) : f x = f y := ap f id_refl := id_refl. (** We define [transport] with the arguments in the order we like. *) Equations transport {A : Type} (P : A -> Type) {x y : A} (p : x = y) (u : P x) : P y := transport P id_refl u := u. Notation "p # x" := (transport _ p x) (right associativity, at level 65, only parsing). (** We use [1] to refer to the reflexivity proof. *) Notation "1" := id_refl : equations_scope. (** Notation for the inverse *) #[warnings="-notation-incompatible-prefix"] Reserved Notation "p ^" (at level 1, format "p '^'"). #[warnings="-notation-incompatible-prefix"] Notation "p ^" := (id_sym p%equations) : equations_scope. Equations apd {A} {B : A -> Type} (f : forall x : A, B x) {x y : A} (p : x = y) : p # f x = f y := apd f 1 := 1. (** *** Equivalence *) (** A typeclass that includes the data making [f] into an adjoint equivalence*) Class IsEquiv {A B : Type} (f : A -> B) := BuildIsEquiv { equiv_inv : B -> A ; eisretr : Sect equiv_inv f; eissect : Sect f equiv_inv; eisadj : forall x : A, eisretr (f x) = ap f (eissect x) }. Arguments eisretr {A B}%_type_scope f%_function_scope {_} _. Arguments eissect {A B}%_type_scope f%_function_scope {_} _. Arguments eisadj {A B}%_type_scope f%_function_scope {_} _. Arguments IsEquiv {A B}%_type_scope f%_function_scope. (** A record that includes all the data of an adjoint equivalence. *) Record Equiv A B := BuildEquiv { equiv_fun : A -> B ; equiv_isequiv : IsEquiv equiv_fun }. Coercion equiv_fun : Equiv >-> Funclass. Global Existing Instance equiv_isequiv. Arguments equiv_fun {A B} _ _. Arguments equiv_isequiv {A B} _. Bind Scope equiv_scope with Equiv. Reserved Infix "<~>" (at level 85). Notation "A <~> B" := (Equiv A B) (at level 85) : type_scope. Notation "f ^^-1" := (@equiv_inv _ _ f _) (at level 1). (** *** Functional extensionality *) Definition pointwise_paths {A} {P:A->Type} (f g:forall x:A, P x) := forall x:A, f x = g x. #[export] Hint Unfold pointwise_paths : typeclass_instances. Notation "f == g" := (pointwise_paths f g) (at level 70, no associativity) : type_scope. (** This definition has slightly changed: the match on the Id is external to the function. *) Equations apD10 {A} {B : A -> Type} {f g : forall x, B x} (h : f = g) : f == g := apD10 1 := fun h => 1. Class Funext := { isequiv_apD10 :: forall (A : Type) (P : A -> Type) f g, IsEquiv (@apD10 A P f g) }. Axiom funext : Funext. #[local] Existing Instance funext. Definition path_forall `{Funext} {A : Type} {P : A -> Type} (f g : forall x : A, P x) : f == g -> f = g := (@apD10 A P f g)^^-1. (** *** Path spaces in sigma types and product types *) Equations path_sigma {A : Type} (P : A -> Type) (u v : sigma P) (p : u.1 = v.1) (q : p # u.2 = v.2) : u = v := path_sigma (_, _) (_, _) 1 1 := 1. Equations path_prod_uncurried {A B : Type} (z z' : A * B) (pq : (z.1 = z'.1) * (z.2 = z'.2)): z = z' := path_prod_uncurried (_, _) (_, _) (1, 1) := 1. Definition path_prod {A B : Type} (z z' : A * B) (e : z.1 = z'.1) (f : z.2 = z'.2) : z = z' := path_prod_uncurried _ _ (e, f). Equations path_prod_eq {A B : Type} (z z' : A * B) (e : z.1 = z'.1) (f : z.2 = z'.2) : z = z' := path_prod_eq (_, _) (_, _) 1 1 := 1. Equations eta_path_prod {A B : Type} {z z' : A * B} (p : z = z') : path_prod _ _ (ap pr1 p) (ap (fun x : A * B => pr2 x) p) = p := eta_path_prod 1 := 1. Definition path_prod' {A B : Type} {x x' : A} {y y' : B} : (x = x') -> (y = y') -> ((x,y) = (x',y')) := fun p q => path_prod (x, y) (x', y') p q. Equations ap_fst_path_prod {A B : Type} {z z' : A * B} (p : z.1 = z'.1) (q : z.2 = z'.2) : ap fst (path_prod _ _ p q) = p := ap_fst_path_prod (z:=(_, _)) (z':=(_, _)) 1 1 := 1. Equations ap_snd_path_prod {A B : Type} {z z' : A * B} (p : z.1 = z'.1) (q : z.2 = z'.2) : ap snd (path_prod _ _ p q) = q := ap_snd_path_prod (z:=(_, _)) (z':=(_, _)) 1 1 := 1. #[local] Instance isequiv_path_prod {A B : Type} {z z' : A * B} : IsEquiv (path_prod_uncurried z z'). Proof. unshelve refine (BuildIsEquiv _ _ _). - exact (fun r => (ap fst r, ap snd r)). - intro. apply eta_path_prod. - intros [p q]. exact (path_prod' (ap_fst_path_prod p q) (ap_snd_path_prod p q)). - destruct z as [x y], z' as [x' y']. intros [p q]; simpl in p, q. destruct p, q; apply 1. Defined. Equations path_sigma_uncurried {A : Type} {P : A -> Type} (u v : sigma P) (pq : Σ p, p # u.2 = v.2) : u = v := path_sigma_uncurried (u1, u2) (_, _) (1, 1) := 1. Definition pr1_path {A} {P : A -> Type} {u v : sigma P} (p : u = v) : u.1 = v.1 := ap (@pr1 _ _) p. Notation "p ..1" := (pr1_path p) (at level 1). Definition pr2_path {A} `{P : A -> Type} {u v : sigma P} (p : u = v) : p..1 # u.2 = v.2. destruct p. apply 1. Defined. Notation "p ..2" := (pr2_path p) (at level 1). Definition eta_path_sigma_uncurried {A} `{P : A -> Type} {u v : sigma P} (p : u = v) : path_sigma_uncurried _ _ (p..1, p..2) = p. destruct p. apply 1. Defined. Definition eta_path_sigma A `{P : A -> Type} {u v : sigma P} (p : u = v) : path_sigma _ _ (p..1) (p..2) = p := eta_path_sigma_uncurried p. Definition path_sigma_equiv {A : Type} (P : A -> Type) (u v : sigma P): IsEquiv (path_sigma_uncurried u v). unshelve refine (BuildIsEquiv _ _ _). - exact (fun r => (r..1, r..2)). - intro. apply eta_path_sigma_uncurried. - destruct u, v; intros [p q]; simpl in *. destruct p. simpl in *. destruct q. reflexivity. - destruct u, v; intros [p q]; simpl in *; destruct p. simpl in *. destruct q; simpl in *. apply 1. Defined. (** *** Groupoid laws for equality *) Equations concat {A} {x y z : A} (e : x = y) (e' : y = z) : x = z := concat 1 q := q. Notation "p @ q" := (concat p q) (at level 20). Definition concat2 {A} {x y z : A} {p p' : x = y} {q q' : y = z} (h : p = p') (h' : q = q') : p @ q = p' @ q' := match h, h' with 1, 1 => 1 end. Reserved Notation "p @@ q" (at level 20). Notation "p @@ q" := (concat2 p q)%equations : equations_scope. Definition moveR_E A B (f:A -> B) {H : IsEquiv f} (x : A) (y : B) (p : x = f^^-1 y) : (f x = y) := ap f p @ (@eisretr _ _ f _ y). (** One can use the shortcut notation [| p] to give patterns for the explicit arguments without repeating the function name. *) Equations concat_1p {A : Type} {x y : A} (p : x = y) : 1 @ p = p := | 1 := 1. Equations concat_p1 {A : Type} {x y : A} (p : x = y) : p @ 1 = p := | 1 := 1. Equations concat_Vp {A : Type} {x y : A} (p : x = y) : p^ @ p = 1 := | 1 := 1. Equations concat_pV {A : Type} {x y : A} (p : x = y) : p @ p^ = 1 := | 1 := 1. Equations concat_p_pp {A : Type} {x y z t : A} (p : x = y) (q : y = z) (r : z = t) : p @ (q @ r) = (p @ q) @ r := concat_p_pp 1 _ _ := 1. #[export] Hint Rewrite @concat_p1 @concat_Vp @concat_pV : concat. #[local] Instance Id_equiv A : Equivalence (@Id A) := {}. #[local] Instance concat_morphism (A : Type) x y z : Proper (Id ==> Id ==> Id) (@concat A x y z). Proof. reduce. destruct X. destruct X0. destruct x0. reflexivity. Defined. Definition trans_co_eq_inv_arrow_morphism@{i j k} : ∀ (A : Type@{i}) (R : crelation@{i j} A), Transitive R → Proper@{k j} (respectful@{i j k j k j} R (respectful@{i j k j k j} Id (@flip@{k k k} _ _ Type@{j} arrow))) R. Proof. reduce. transitivity y. assumption. now destruct X1. Defined. #[local] Existing Instance trans_co_eq_inv_arrow_morphism. Equations concat_pp_A1 {A : Type} {g : A -> A} (p : forall x, x = g x) {x y : A} (q : x = y) {w : A} (r : w = x) : (r @ p x) @ ap g q = (r @ q) @ p y := concat_pp_A1 _ 1 1 := concat_p1 _. Equations whiskerL {A : Type} {x y z : A} (p : x = y) {q r : y = z} (h : q = r) : p @ q = p @ r := whiskerL _ 1 := 1. Equations whiskerR {A : Type} {x y z : A} {p q : x = y} (h : p = q) (r : y = z) : p @ r = q @ r := whiskerR 1 _ := 1. Equations moveL_M1 {A : Type} {x y : A} (p q : x = y) : id_sym q @ p = 1 -> p = q := moveL_M1 _ 1 := fun e => e. Definition inverse2 {A : Type} {x y : A} {p q : x = y} (h : p = q) : id_sym p = id_sym q := ap (@id_sym _ _ _) h. Equations ap02 {A B : Type} (f:A->B) {x y:A} {p q:x=y} (r:p=q) : ap f p = ap f q := ap02 f 1 := 1. Equations ap_p_pp {A B : Type} (f : A -> B) {w : B} {x y z : A} (r : w = f x) (p : x = y) (q : y = z) : r @ (ap f (p @ q)) = (r @ ap f p) @ (ap f q) := ap_p_pp f _ 1 _ := concat_p_pp _ 1 _. Equations ap_compose {A B C : Type} (f : A -> B) (g : B -> C) {x y : A} (p : x = y) : ap (fun x => g (f x)) p = ap g (ap f p) := ap_compose f g 1 := 1. (** An example of the [with] construct doing abstraction in the context and conclusion. Here [p x] is abstracted first, then [g x], resulting in a goal [gx : A, px : gx = x]. *) Equations concat_A1p {A : Type} {g : A -> A} (p : forall x, g x = x) {x y : A} (q : x = y) : (ap g q) @ (p y) = (p x) @ q := concat_A1p p 1 with p x, g x := { concat_A1p p (x:=?(x)) 1 1 x := 1 }. (** Dummy example using functional elimination on a proof-relevant function using [with]. *) Lemma concat_A1p_lemma {A} (f : A -> A) (p : forall x, f x = x) {x y : A} (q : x = y) : (concat_A1p p q) = (concat_A1p p q). Proof. apply_funelim (concat_A1p p q). clear; intros. simpl. elim Heq0 using Logic.Id_rect_r. simpl. reflexivity. Qed. Equations ap_pp {A B : Type} (f : A -> B) {x y z : A} (p : x = y) (q : y = z) : ap f (p @ q) = (ap f p) @ (ap f q) := ap_pp _ 1 1 => 1. Equations concat_pp_V {A : Type} {x y z : A} (p : x = y) (q : y = z) : (p @ q) @ id_sym q = p := concat_pp_V 1 1 => 1. Equations ap_V {A B : Type} (f : A -> B) {x y : A} (p : x = y) : ap f (id_sym p) = id_sym (ap f p) := ap_V f 1 => 1. #[export] Hint Rewrite @ap_pp @ap_V : ap. #[export] Hint Rewrite @concat_pp_V : concat. Equations concat_pA1 {A : Type} {f : A -> A} (p : forall x, x = f x) {x y : A} (q : x = y) : (p x) @ (ap f q) = q @ (p y) := concat_pA1 p 1 := concat_p1 (p _). Equations concat_p_Vp {A : Type} {x y z : A} (p : x = y) (q : x = z) : p @ (id_sym p @ q) = q := concat_p_Vp 1 1 := 1. Equations concat_pV_p {A : Type} {x y z : A} (p : x = z) (q : y = z) : (p @ id_sym q) @ q = p := concat_pV_p 1 1 := 1. #[export] Hint Rewrite @concat_pA1 @concat_p_Vp @concat_pV_p : concat. Definition concat_pA1_p {A : Type} {f : A -> A} (p : forall x, f x = x) {x y : A} (q : x = y) {w : A} (r : w = f x) : (r @ ap f q) @ p y = (r @ p x) @ q. Proof. destruct q; simpl. now rewrite !concat_p1. (* now simp concat. *) Defined. Equations ap_p {A B : Type} (f : A -> B) {x y : A} (p q: x = y) (e : p = q) : ap f p = ap f q := ap_p f 1 := 1. #[local] Instance ap_morphism (A : Type) (B : Type) x y f : Proper (@Id (@Id A x y) ==> @Id (@Id B (f x) (f y))) (@ap A B f x y). Proof. reduce. now apply ap_p. Defined. #[local] Instance reflexive_proper_proxy : ∀ (A : Type) (R : crelation A), Reflexive R → ∀ x : A, ProperProxy R x. Proof. intros. reduce. apply X. Defined. #[local] Instance isequiv_inverse A B (f:A -> B) (H:IsEquiv f) : IsEquiv (f^^-1) | 1000. Proof. refine (BuildIsEquiv (@eissect _ _ f _) (@eisretr _ _ f _) _). intros b. rewrite <- (concat_1p (eissect _ _)). rewrite <- (concat_Vp (ap f^^-1 (eisretr _ (f (f^^-1 b))))). rewrite (whiskerR (inverse2 (ap02 f^^-1 (eisadj _ (f^^-1 b)))) _). refine (whiskerL _ (id_sym (concat_1p (eissect _ _))) @ _). rewrite <- (concat_Vp (eissect _ (f^^-1 (f (f^^-1 b))))). rewrite <- (whiskerL _ (concat_1p (eissect _ (f^^-1 (f (f^^-1 b)))))). rewrite <- (concat_pV (ap f^^-1 (eisretr _ (f (f^^-1 b))))). apply moveL_M1. repeat rewrite concat_p_pp. (* Now we apply lots of naturality and cancel things. *) rewrite <- (concat_pp_A1 (fun a => id_sym (eissect _ a)) _ _). rewrite (ap_compose f f^^-1). rewrite <- (ap_p_pp _ _ (ap f (ap f^^-1 (eisretr _ (f (f^^-1 b))))) _). rewrite <- (ap_compose f^^-1 f). rewrite (concat_A1p (@eisretr _ _ f _) _). rewrite ap_pp, concat_p_pp. rewrite (concat_pp_V _ (ap f^^-1 (eisretr _ (f (f^^-1 b))))). repeat rewrite <- ap_V. rewrite <- ap_pp. rewrite <- (concat_pA1 (fun y => id_sym (eissect _ y)) _). rewrite ap_compose, <- (ap_compose f^^-1 f). rewrite <- ap_p_pp. rewrite (concat_A1p (@eisretr _ _ f _) _). rewrite concat_p_Vp. rewrite <- ap_compose. rewrite (concat_pA1_p (@eissect _ _ f _) _). rewrite concat_pV_p; apply concat_Vp. Defined. Definition transport_inv A {P : A -> Type} (x y :A) (e : x = y) (u:P x) v: u = e^ # v -> e # u = v. destruct e. exact id. Defined. Definition moveR_M1 {A : Type} {x y : A} (p q : x = y) : 1 = p^ @ q -> p = q. Proof. destruct p. intro h. exact (h @ (concat_1p _)). Defined. Definition moveL_Vp {A : Type} {x y z : A} (p : x = z) (q : y = z) (r : x = y) : r @ q = p -> q = r^ @ p. Proof. destruct r. intro h. exact ((concat_1p _)^ @ h @ (concat_1p _)^). Defined. (** *** Contractibility *) Class Contr (A : Type) := BuildContr { center : A ; contr : (forall y : A, center = y) }. Arguments center A {Contr}. Lemma contr_equiv A B (f : A -> B) `{IsEquiv A B f} `{Contr A} : Contr B. Proof. exists (f (center A)). intro y. eapply moveR_E. apply contr. Qed. Global Instance contr_forall A {P : A -> Type} {H : forall a, Contr (P a)} : Contr (forall a, P a) | 100. Proof. exists (fun a => @center _ (H a)). intro f. apply path_forall. intro a. apply contr. Defined. Global Instance contr_unit : Contr unit | 0 := {| center := tt; contr := fun t : unit => match t with tt => 1 end |}. Definition path_contr {A} {H:Contr A} (x y : A) : x = y := concat (id_sym (@contr _ H x)) (@contr _ H y). Definition path2_contr {A} {H:Contr A} {x y : A} (p q : x = y) : p = q. assert (K : forall (r : x = y), r = path_contr x y). intro r; destruct r; symmetry; now apply concat_Vp. apply (transitivity (y:=path_contr x y)). - apply K. - symmetry; apply K. Defined. #[local] Instance contr_paths_contr A {H:Contr A} (x y : A) : Contr (x = y) | 10000 := let c := {| center := concat (id_sym (contr x)) (contr y); contr := path2_contr (concat (id_sym (contr x)) (contr y)) |} in c. Global Program Instance contr_prod A B {CA : Contr A} {CB : Contr B} : Contr (prod A B). Next Obligation. exact (@center _ CA, @center _ CB). Defined. Next Obligation. apply path_prod; apply contr. Defined. Equations singletons_contr {A : Type} (x : A) : Contr (Σ y : A, x = y) := singletons_contr x := {| center := (x, 1); contr := contr |} where contr : forall y : (Σ y : A, x = y), (x, 1) = y := contr (x, 1) := 1. #[local] Existing Instance singletons_contr. Notation " 'rew' H 'in' c " := (@Logic.Id_rew_r _ _ _ c _ H) (at level 20). Notation " 'rewd' H 'in' c " := (@Logic.Id_rect_r _ _ _ c _ H) (at level 20). (** *** Singletons are contractible as a no-confusion principle The (heterogeneous) NoConfusion principle for equality, i.e. [NoConfusiom (Σ y, x = y)] is equivalent to the proof that singletons are contractible, i.e that this type has a definitional equivalence with [unit]. *) Definition NoConfusion_singleton {A : Type} (x : A) (p q : Σ y : A, x = y) : Type := unit. Unset Implicit Arguments. Equations noConfusion_singleton {A} (x : A) (p q : Σ y : A, x = y) : NoConfusion_singleton p q -> p = q := noConfusion_singleton x (x, 1) (x, 1) tt => 1. Equations noConfusion_singleton_inv {A} (x : A) (p q : Σ y : A, x = y) : p = q -> NoConfusion_singleton p q := noConfusion_singleton_inv x (x, 1) ?((x, 1)) 1 => tt. Definition NoConfusionIdPackage_singleton {A} (x : A) : NoConfusionPackage (Σ y : A, x = y). Proof. refine {| NoConfusion := @NoConfusion_singleton _ x; noConfusion := noConfusion_singleton x; noConfusion_inv := noConfusion_singleton_inv x |}. - intros a b e. (* also, this is an equality in the unit type... *) dependent elimination a as [(a, 1)]. dependent elimination b as [(a, 1)]. hnf in e. destruct e. reflexivity. - intros a b e. (* apply path2_contr. *) dependent elimination e as [1]. dependent elimination a as [(a, 1)]. reflexivity. Defined. Definition contr_sigma A {P : A -> Type} {H : Contr A} `{H0 : forall a, Contr (P a)} : Contr (sigma P). Proof. exists (center A, center (P (center A))). intros [a Ha]. unshelve refine (path_sigma _ _ _ _). simpl. apply H. simpl. apply transport_inv. apply (H0 (center A)). Defined. (** *** Adjointification: producing an equivalence from an iso *) Section Adjointify. Context {A B : Type} (f : A -> B) (g : B -> A). Context (isretr : Sect g f) (issect : Sect f g). (* This is the modified [eissect]. *) Let issect' := fun x => ap g (ap f (issect x)^) @ ap g (isretr (f x)) @ issect x. Let is_adjoint' (a : A) : isretr (f a) = ap f (issect' a). Proof. unfold issect'. apply moveR_M1. repeat rewrite ap_pp, concat_p_pp; rewrite <- ap_compose. rewrite (concat_pA1 (fun b => (isretr b)^) (ap f (issect a)^)). repeat rewrite concat_pp_p; rewrite ap_V. rewrite <- concat_p_pp. rewrite <- concat_p_pp. apply moveL_Vp. rewrite concat_p1. rewrite concat_p_pp, <- ap_compose. rewrite (concat_pA1 (fun b => (isretr b)^) (isretr (f a))). rewrite concat_pV, concat_1p; reflexivity. Qed. (** We don't make this a typeclass instance, because we want to control when we are applying it. *) Definition isequiv_adjointify : IsEquiv f := @BuildIsEquiv A B f g isretr issect' is_adjoint'. Definition equiv_adjointify : A <~> B := @BuildEquiv A B f isequiv_adjointify. End Adjointify. Arguments isequiv_adjointify {A B}%_type_scope (f g)%_function_scope isretr issect. Arguments equiv_adjointify {A B}%_type_scope (f g)%_function_scope isretr issect. (** *** Congruence preserves equivalence If [f] is an equivalence, then so is [ap f]. We are lazy and use [adjointify]. *) Global Instance isequiv_ap {A B} f `{IsEquiv A B f} (x y : A) : IsEquiv (@ap A B f x y) | 1000 := isequiv_adjointify (ap f) (fun q => (eissect f x)^ @ ap f^^-1 q @ eissect f y) (fun q => ap_pp f _ _ @ whiskerR (ap_pp f _ _) _ @ ((ap_V f _ @ inverse2 (eisadj f _)^) @@ (ap_compose f^^-1 f _)^ @@ (eisadj f _)^) @ concat_pA1_p (eisretr f) _ _ @ whiskerR (concat_Vp _) _ @ concat_1p _) (fun p => whiskerR (whiskerL _ (ap_compose f f^^-1 _)^) _ @ concat_pA1_p (eissect f) _ _ @ whiskerR (concat_Vp _) _ @ concat_1p _). (** The definition of homotopy fiber. *) Definition hfiber {A B : Type} (f : A -> B) (y : B) := Σ (x : A), f x = y. Global Arguments hfiber {A B}%_type_scope f%_function_scope y.