{-
Cubical Agda - A Dependently Typed PL with Univalence and HITs
==============================================================
Anders Mörtberg
Every Proof Assistant - September 17, 2020
Link to slides: https://staff.math.su.se/anders.mortberg/slides/EPA2020.pdf
Link to video: https://vimeo.com/459020971
-}
-- To make Agda cubical add the --cubical option.
-- This is implicitly added for files in the cubical library via the cubical.agda-lib file.
{-# OPTIONS --safe #-}
module Cubical.Talks.EPA2020 where
-- The "Foundations" package contain a lot of important results (in
-- particular the univalence theorem)
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Data.Int
open import Cubical.Data.Prod.Base
-- The interval in Cubical Agda is written I and the endpoints i0 and i1.
apply0 : (A : Type) (p : I → A) → A
apply0 A p = p i0
-- We omit the universe level ℓ for simplicity in this talk. In the
-- library everything is maximally universe polymorphic.
-- We can write functions out of the interval using λ-abstraction:
refl' : {A : Type} (x : A) → x ≡ x
refl' x = λ i → x
-- In fact, x ≡ y is short for PathP (λ _ → A) x y
refl'' : {A : Type} (x : A) → PathP (λ _ → A) x x
refl'' x = λ _ → x
-- In general PathP A x y has A : I → Type, x : A i0 and y : A i1
-- PathP = Dependent paths (Path over a Path)
-- We cannot pattern-match on interval variables as I is not inductively defined
-- foo : {A : Type} → I → A
-- foo r = {!r!} -- Try typing C-c C-c
-- cong has a direct proof
cong' : {A B : Type} (f : A → B) {x y : A} → x ≡ y → f x ≡ f y
cong' f p i = f (p i)
-- function extensionality also has a direct proof.
-- It also has computational content: swap the arguments.
funExt' : {A B : Type} {f g : A → B} (p : (x : A) → f x ≡ g x) → f ≡ g
funExt' p i x = p x i
-- Transport is more complex as ≡ isn't inductively defined (so we
-- can't define it by pattern-matching on p)
transport' : {A B : Type} → A ≡ B → A → B
transport' p a = transp (λ i → p i) i0 a
-- This lets us define subst (which is called "transport" in the HoTT book)
subst' : {A : Type} (P : A → Type) {x y : A} (p : x ≡ y) → P x → P y
subst' P p pa = transport (λ i → P (p i)) pa
-- The transp operation reduces differently for different types
-- formers. For paths it reduces to another primitive operation called
-- hcomp.
-- We can also define the J eliminator (aka path induction)
J' : {A : Type} {B : A → Type} {x : A}
(P : (z : A) → x ≡ z → Type)
(d : P x refl) {y : A} (p : x ≡ y) → P y p
J' P d p = transport (λ i → P (p i) (λ j → p (i ∧ j))) d
-- So J is provable, but it doesn't satisfy computation rule
-- definitionally. This is almost never a problem in practice as the
-- cubical primitives satisfy many new definitional equalities.
-- Another key concept in HoTT/UF is the Univalence Axiom. In Cubical
-- Agda this is provable, we hence refer to it as the Univalence
-- Theorem.
-- The univalence theorem: equivalences of types give paths of types
ua' : {A B : Type} → A ≃ B → A ≡ B
ua' = ua
-- Any isomorphism of types gives rise to an equivalence
isoToEquiv' : {A B : Type} → Iso A B → A ≃ B
isoToEquiv' = isoToEquiv
-- And hence to a path
isoToPath' : {A B : Type} → Iso A B → A ≡ B
isoToPath' e = ua' (isoToEquiv' e)
-- ua satisfies the following computation rule
-- This suffices to be able to prove the standard formulation of univalence.
uaβ' : {A B : Type} (e : A ≃ B) (x : A)
→ transport (ua' e) x ≡ fst e x
uaβ' e x = transportRefl (equivFun e x)
-- Time for an example!
-- Booleans
data Bool : Type where
false true : Bool
not : Bool → Bool
not false = true
not true = false
notPath : Bool ≡ Bool
notPath = isoToPath' (iso not not rem rem)
where
rem : (b : Bool) → not (not b) ≡ b
rem false = refl
rem true = refl
_ : transport notPath true ≡ false
_ = refl
-- Another example, integers:
sucPath : ℤ ≡ ℤ
sucPath = isoToPath' (iso sucℤ predℤ sucPred predSuc)
_ : transport sucPath (pos 0) ≡ pos 1
_ = refl
_ : transport (sucPath ∙ sucPath) (pos 0) ≡ pos 2
_ = refl
_ : transport (sym sucPath) (pos 0) ≡ negsuc 0
_ = refl
-----------------------------------------------------------------------------
-- Higher inductive types
-- The following definition of finite multisets is due to Vikraman
-- Choudhury and Marcelo Fiore.
infixr 5 _∷_
data FMSet (A : Type) : Type where
[] : FMSet A
_∷_ : (x : A) → (xs : FMSet A) → FMSet A
comm : (x y : A) (xs : FMSet A) → x ∷ y ∷ xs ≡ y ∷ x ∷ xs
-- trunc : (xs ys : FMSet A) (p q : xs ≡ ys) → p ≡ q
-- We need to add the trunc constructor for FMSets to be sets, omitted
-- here for simplicity.
_++_ : ∀ {A : Type} (xs ys : FMSet A) → FMSet A
[] ++ ys = ys
(x ∷ xs) ++ ys = x ∷ xs ++ ys
comm x y xs i ++ ys = comm x y (xs ++ ys) i
-- trunc xs zs p q i j ++ ys =
-- trunc (xs ++ ys) (zs ++ ys) (cong (_++ ys) p) (cong (_++ ys) q) i j
unitr-++ : {A : Type} (xs : FMSet A) → xs ++ [] ≡ xs
unitr-++ [] = refl
unitr-++ (x ∷ xs) = cong (x ∷_) (unitr-++ xs)
unitr-++ (comm x y xs i) j = comm x y (unitr-++ xs j) i
-- unitr-++ (trunc xs ys x y i j) = {!!}
-- This is a special case of set quotients! Very useful for
-- programming and set level mathematics
data _/_ (A : Type) (R : A → A → Type) : Type where
[_] : A → A / R
eq/ : (a b : A) → R a b → [ a ] ≡ [ b ]
trunc : (a b : A / R) (p q : a ≡ b) → p ≡ q
-- Proving that they are effective ((a b : A) → [ a ] ≡ [ b ] → R a b)
-- requires univalence for propositions.
-------------------------------------------------------------------------
-- Topological examples of things that are not sets
-- We can define the circle as the following simple data declaration:
data S¹ : Type where
base : S¹
loop : base ≡ base
-- We can write functions on S¹ using pattern-matching equations:
double : S¹ → S¹
double base = base
double (loop i) = (loop ∙ loop) i
helix : S¹ → Type
helix base = ℤ
helix (loop i) = sucPathℤ i
ΩS¹ : Type
ΩS¹ = base ≡ base
winding : ΩS¹ → ℤ
winding p = subst helix p (pos 0)
_ : winding (λ i → double ((loop ∙ loop) i)) ≡ pos 4
_ = refl
-- We can define the Torus as:
data Torus : Type where
point : Torus
line1 : point ≡ point
line2 : point ≡ point
square : PathP (λ i → line1 i ≡ line1 i) line2 line2
-- And prove that it is equivalent to two circle:
t2c : Torus → S¹ × S¹
t2c point = (base , base)
t2c (line1 i) = (loop i , base)
t2c (line2 j) = (base , loop j)
t2c (square i j) = (loop i , loop j)
c2t : S¹ × S¹ → Torus
c2t (base , base) = point
c2t (loop i , base) = line1 i
c2t (base , loop j) = line2 j
c2t (loop i , loop j) = square i j
c2t-t2c : (t : Torus) → c2t (t2c t) ≡ t
c2t-t2c point = refl
c2t-t2c (line1 _) = refl
c2t-t2c (line2 _) = refl
c2t-t2c (square _ _) = refl
t2c-c2t : (p : S¹ × S¹) → t2c (c2t p) ≡ p
t2c-c2t (base , base) = refl
t2c-c2t (base , loop _) = refl
t2c-c2t (loop _ , base) = refl
t2c-c2t (loop _ , loop _) = refl
-- Using univalence we get the following equality:
Torus≡S¹×S¹ : Torus ≡ S¹ × S¹
Torus≡S¹×S¹ = isoToPath' (iso t2c c2t t2c-c2t c2t-t2c)
windingTorus : point ≡ point → ℤ × ℤ
windingTorus l = ( winding (λ i → proj₁ (t2c (l i)))
, winding (λ i → proj₂ (t2c (l i))))
_ : windingTorus (line1 ∙ sym line2) ≡ (pos 1 , negsuc 0)
_ = refl
-- We have many more topological examples, including Klein bottle, RP^n,
-- higher spheres, suspensions, join, wedges, smash product:
open import Cubical.HITs.KleinBottle
open import Cubical.HITs.RPn
open import Cubical.HITs.S2
open import Cubical.HITs.S3
open import Cubical.HITs.Susp
open import Cubical.HITs.Join
open import Cubical.HITs.Wedge
open import Cubical.HITs.SmashProduct
-- There's also a proof of the "3x3 lemma" for pushouts in less than
-- 200LOC. In HoTT-Agda this took about 3000LOC. For details see:
-- https://github.com/HoTT/HoTT-Agda/tree/master/theorems/homotopy/3x3
open import Cubical.HITs.Pushout
-- We also defined the Hopf fibration and proved that its total space
-- is S³ in about 300LOC:
open import Cubical.Homotopy.Hopf
open S¹Hopf
-- There is also some integer cohomology:
open import Cubical.ZCohomology.Everything
-- To compute cohomology groups of various spaces we need a bunch of
-- interesting theorems: Freudenthal suspension theorem,
-- Mayer-Vietoris sequence...
open import Cubical.Homotopy.Freudenthal
open import Cubical.ZCohomology.MayerVietorisUnreduced
-------------------------------------------------------------------------
-- The structure identity principle
-- A more efficient version of finite multisets based on association lists
open import Cubical.HITs.AssocList.Base
-- data AssocList (A : Type) : Type where
-- ⟨⟩ : AssocList A
-- ⟨_,_⟩∷_ : (a : A) (n : ℕ) (xs : AssocList A) → AssocList A
-- per : (a b : A) (m n : ℕ) (xs : AssocList A)
-- → ⟨ a , m ⟩∷ ⟨ b , n ⟩∷ xs ≡ ⟨ b , n ⟩∷ ⟨ a , m ⟩∷ xs
-- agg : (a : A) (m n : ℕ) (xs : AssocList A)
-- → ⟨ a , m ⟩∷ ⟨ a , n ⟩∷ xs ≡ ⟨ a , m + n ⟩∷ xs
-- del : (a : A) (xs : AssocList A) → ⟨ a , 0 ⟩∷ xs ≡ xs
-- trunc : (xs ys : AssocList A) (p q : xs ≡ ys) → p ≡ q
-- Programming and proving is more complicated with AssocList compared
-- to FMSet. This kind of example occurs everywhere in programming and
-- mathematics: one representation is easier to work with, but not
-- efficient, while another is efficient but difficult to work with.
-- Solution: substitute using univalence
substIso : {A B : Type} (P : Type → Type) (e : Iso A B) → P A → P B
substIso P e = subst P (isoToPath e)
-- Can transport for example Monoid structure from FMSet to AssocList
-- this way, but the achieved Monoid structure is not very efficient
-- to work with. A better solution is to prove that FMSet and
-- AssocList are equal *as monoids*, but how to do this?
-- Solution: structure identity principle (SIP)
-- This is a very useful consequence of univalence
open import Cubical.Foundations.SIP
sip' : {ℓ : Level} {S : Type ℓ → Type ℓ} {ι : StrEquiv S ℓ}
(θ : UnivalentStr S ι) (A B : TypeWithStr ℓ S) → A ≃[ ι ] B → A ≡ B
sip' = sip
-- The tricky thing is to prove that (S,ι) is a univalent structure.
-- Luckily we provide automation for this in the library, see for example:
open import Cubical.Algebra.Monoid.Base
-- Another cool application of the SIP: matrices represented as
-- functions out of pairs of Fin's and vectors are equal as abelian
-- groups:
open import Cubical.Algebra.Matrix
-- The end, back to slides!