{-

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  : Type where
  base : 
  loop : base  base

-- We can write functions on S¹ using pattern-matching equations:
double :   
double base = base
double (loop i) = (loop  loop) i

helix :   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   × 
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 :  ×   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 :  × )  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   × 
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!