diff --git a/.tokeignore b/.tokeignore
index 04849f7..698f6c8 100644
--- a/.tokeignore
+++ b/.tokeignore
@@ -1,3 +1,4 @@
public
package-lock.json
-src/styles/fork-awesome
\ No newline at end of file
+src/styles/fork-awesome
+pnpm-lock.yaml
diff --git a/src/content/posts/2024-09-18-hcomp/2d.jpg b/src/content/posts/2024-09-18-hcomp/2d.jpg
new file mode 100644
index 0000000..308def1
Binary files /dev/null and b/src/content/posts/2024-09-18-hcomp/2d.jpg differ
diff --git a/src/content/posts/2024-09-18-hcomp/fg.jpg b/src/content/posts/2024-09-18-hcomp/fg.jpg
new file mode 100644
index 0000000..750c2f5
Binary files /dev/null and b/src/content/posts/2024-09-18-hcomp/fg.jpg differ
diff --git a/src/content/posts/2024-09-18-hcomp/goal2.jpg b/src/content/posts/2024-09-18-hcomp/goal2.jpg
new file mode 100644
index 0000000..eb189f4
Binary files /dev/null and b/src/content/posts/2024-09-18-hcomp/goal2.jpg differ
diff --git a/src/content/posts/2024-09-18-hcomp/index.lagda.md b/src/content/posts/2024-09-18-hcomp/index.lagda.md
index bdaff64..12adc4e 100644
--- a/src/content/posts/2024-09-18-hcomp/index.lagda.md
+++ b/src/content/posts/2024-09-18-hcomp/index.lagda.md
@@ -14,7 +14,7 @@ draft: true
Imports
```
-{-# OPTIONS --cubical --allow-unsolved-metas #-}
+{-# OPTIONS --cubical #-}
module 2024-09-18-hcomp.index where
open import Cubical.Foundations.Prelude hiding (isProp→isSet)
open import Cubical.Foundations.Equiv.Base
@@ -33,6 +33,8 @@ In two dimensions, hcomp can be understood as the double-composition operation.
"Single" composition (between two paths rather than three) is typically implemented as a double composition with the left leg as $\mathsf{refl}$.
Without the double composition, this looks like:
+![](./2d.jpg)
+
```
path-comp : {A : Type} {x y z : A} → x ≡ y → y ≡ z → x ≡ z
path-comp {x = x} p q i =
@@ -51,11 +53,13 @@ This result exists in the cubical standard library, but let's go over it here.
```
isProp→isSet : {A : Type} → isProp A → isSet A
-isProp→isSet {A} A-isProp = goal where
+isProp→isSet {A} f = goal where
goal : (x y : A) → (p q : x ≡ y) → p ≡ q
goal x y p q j i = -- ...
```
+We're given a type $A$, some proof that it's a mere proposition, let's call it $f : \mathsf{isProp}(A)$.
+
Now let's construct an hcomp. In a set, we'd want paths $p$ and $q$ between the same points $x$ and $y$ to be equal.
Suppose $p$ and $q$ operate over the same dimension, $i$.
If we want to find a path between $p$ and $q$, we'll want another dimension.
@@ -79,13 +83,18 @@ Before getting started, let's familiarize ourselves with the dimensions we're wo
We can map both $p(i)$ and $q(i)$ down to a square that has $x$ on all corners and $\mathsf{refl}_x$ on all sides.
Let's start with the left and right faces $(i = \{ \mathsf{i0} , \mathsf{i1} \})$.
-These can be produced using the $\mathsf{isProp}(A)$ that we are given.
-$\mathsf{isProp}(A)$ tells us that $x$ and $y$ are the same, so $x \equiv x$ and $x \equiv y$.
-This means we can define the left face as $\mathsf{isProp}(A, x, x, k)$ and the right face as $\mathsf{isProp}(A, x, y, k)$.
+These can be produced using the proof $f : \mathsf{isProp}(A)$ that we are given.
+$f$ tells us that $x$ and $y$ are the same, so $f(x, x) = x \equiv x$ and $f(x, y) = x \equiv y$.
+This means we can define the left face as $f(x, x, k)$ and the right face as $f(x, y, k)$.
(Remember, $k$ is the direction going from bottom to top)
![](./sides.jpg)
+We can write this down as a mapping:
+
+* $(i = \mathsf{i0}) \rightarrow f(x, x, k)$
+* $(i = \mathsf{i1}) \rightarrow f(x, y, k)$
+
Since $k$ is only the bottom-to-top dimension, the front-to-back dimension isn't changing.
So we can use $\mathsf{refl}$ for those bottom edges.
@@ -102,10 +111,10 @@ Putting this all together, we can using $\mathsf{hcomp}$ to complete the top fac
```
let u = λ k → λ where
- (i = i0) → A-isProp x x k
- (i = i1) → A-isProp x y k
- (j = i0) → A-isProp x (p i) k
- (j = i1) → A-isProp x (q i) k
+ (i = i0) → f x x k
+ (i = i1) → f x y k
+ (j = i0) → f x (p i) k
+ (j = i1) → f x (q i) k
in hcomp u x
```
@@ -118,7 +127,10 @@ Let's move on to a more complicated example.
Suspensions are an example of a higher inductive type.
It can be shown that spheres can be iteratively defined in terms of suspensions.
-Since the $0$-sphere is just two points (solutions to $\| \bm{x} \|_2 = 1$ in 1 dimension), we can show that a suspension over this is equivalent to the classic $1$-sphere, or the circle.
+![](./numberline.jpg)
+
+Since the $0$-sphere is just two points (solutions to $\| \bm{x} \|_2 = 1$ in 1 dimension), we represent this as $S^0 :\equiv 2$.
+We can show that a suspension over this, $\Sigma 2$, is equivalent to the classic $1$-sphere, the circle $S^1$.
Let's state the lemma:
@@ -136,11 +148,23 @@ In this model, we're going to define $f$ and $g$ by having both the north and so
The choice of side is arbitrary, so I'll choose $\mathsf{true}$.
This way, $\mathsf{true}$ is suspended into the $\mathsf{refl}$ path, and $\mathsf{false}$ is suspended into the $\mathsf{loop}$.
-
-
-
+Here's a picture of our function $f$:
-
+![](./suspbool.jpg)
+
+The left setup is presented in the traditional suspension layout, with meridians going through $\mathsf{true}$ and $\mathsf{false}$ while the right side "squishes" the north and south poles using $\mathsf{refl}$, while having the other path represent the $\mathsf{loop}$.
+
+On the other side, $g$, we want to map back from $S^1$ into $\Sigma 2$.
+We can map the $\mathsf{loop}$ back into $\mathsf{merid} \; \mathsf{false}$, but the types mismatch, since $\mathsf{loop} : \mathsf{base} \equiv \mathsf{base}$ but $\mathsf{merid} \; \mathsf{false} : \mathsf{north} \equiv \mathsf{south}$.
+But since $\mathsf{merid} \; \mathsf{true}$ is $\mathsf{refl}$, we can just concatenate with its inverse to get the full path:
+
+$$
+\mathsf{merid} \; \mathsf{false} \cdot (\mathsf{merid} \; \mathsf{true})^{-1} : \mathsf{north} \equiv \mathsf{north}
+$$
+
+In Agda, we can write it like this:
+
+
```
f : Susp Bool → S¹
@@ -150,34 +174,24 @@ This way, $\mathsf{true}$ is suspended into the $\mathsf{refl}$ path, and $\math
f (merid false i) = loop i
```
- |
-
-
-
```
g : S¹ → Susp Bool
g base = north
g (loop i) = (merid false ∙ sym (merid true)) i
-
-
```
- |
+
-
-
-
-
-Now, the fun part is to show the isomorphisms.
+Now, the fun part is to show the extra requirements that is needed to show that these two functions indeed form an isomorphism.
Starting with the first, let's show $f(g(s)) \equiv s$.
-The base case is easily handled by $\mathsf{refl}$.
+The base case is easily handled by $\mathsf{refl}$, since $f(g(\mathsf{base})) = f(\mathsf{north}) = \mathsf{base}$ definitionally by the reduction rules we gave above.
```
fg : (s : S¹) → f (g s) ≡ s
fg base = refl
```
-The loop case is trickier. Let's solve it algebraically first:
+The loop case is trickier. If we were using book HoTT, here's how we would solve this (this result is given in Lemma 6.5.1 of the HoTT book):
$$
\begin{align*}
@@ -191,21 +205,43 @@ $$
\end{align*}
$$
-Between the second and third steps, I used functoriality of the $\mathsf{ap}$ operation.
+Between the second and third steps, I used functoriality of the $\mathsf{ap}$ operation (equation _(i)_ of Lemma 2.2.2 in the HoTT book).
+
+How can we construct a cube to solve this? Like in the first example, let's start out by writing down the face we want to end up with:
+
+![](./goal2.jpg)
+
+We're looking for the path in the bottom-to-top $j$ dimension.
+We would like to have $\mathsf{hcomp}$ give us this face.
+This time, let's see the whole cube first and then pick apart how it was constructed.
+
+![](./fg.jpg)
+
+First of all, note that unrolling $f(g(\mathsf{loop} \; i))$ would give us $f((\mathsf{merid} \; \mathsf{false} \cdot (\mathsf{merid} \; \mathsf{true})^{-1}) \; i)$, which is the same as $f(\mathsf{merid} \; \mathsf{false} \; i) \cdot f(\mathsf{merid} \; \mathsf{true} \; (\sim i))$.
+Since this is a composition, it deserves its own face of the cube, with our goal $f(g(\mathsf{loop} \; i))$ being the lid.
+
+We can express this as an $\mathsf{hfill}$ operation, which is similar to $\mathsf{hcomp}$ in that it takes the same face arguments, but produces the contents of the face rather than just the upper lid.
+
+For the rest of the faces, we can just fill in opposite sides until the cube is complete.
+The Agda translation looks like this:
```
- fg (loop i) k =
+ fg (loop i) j =
let
- u = λ j → λ where
+ u = λ k → λ where
(i = i0) → base
- (i = i1) → f (merid true (~ j))
- (k = i0) → compPath-filler
- (λ i → f (merid false i))
- (λ j → f (merid true (~ j))) j i
- (k = i1) → loop i
+ (i = i1) → f (merid true (~ k))
+ (j = i0) →
+ let u = λ k' → λ where
+ (i = i0) → base
+ (i = i1) → f (merid true (~ k'))
+ in hfill u (inS (f (merid false i))) k
+ (j = i1) → loop i
in hcomp u (f (merid false i))
```
+Nothing should be too surprising here, other than the use of a nested $\mathsf{hfill}$ which is needed to describe the face that contains the composition.
+
```
gf : (b : Susp Bool) → g (f b) ≡ b
gf north = refl
diff --git a/src/content/posts/2024-09-18-hcomp/numberline.jpg b/src/content/posts/2024-09-18-hcomp/numberline.jpg
new file mode 100644
index 0000000..b73b52a
Binary files /dev/null and b/src/content/posts/2024-09-18-hcomp/numberline.jpg differ
diff --git a/src/content/posts/2024-09-18-hcomp/suspbool.jpg b/src/content/posts/2024-09-18-hcomp/suspbool.jpg
new file mode 100644
index 0000000..59709df
Binary files /dev/null and b/src/content/posts/2024-09-18-hcomp/suspbool.jpg differ
diff --git a/src/styles/leftNav.scss b/src/styles/leftNav.scss
index 500d44a..e16b56b 100644
--- a/src/styles/leftNav.scss
+++ b/src/styles/leftNav.scss
@@ -37,7 +37,7 @@
}
.portrait {
- aspect-ratio: 1;
+ aspect-ratio: 1 / 1;
}
a.portrait {
@@ -47,8 +47,8 @@
a.portrait img {
border-radius: 100%;
- width: 100%;
- height: 100%;
+ width: auto;
+ height: auto;
}
.bio {
diff --git a/src/styles/post.scss b/src/styles/post.scss
index 51d19e9..d94323a 100644
--- a/src/styles/post.scss
+++ b/src/styles/post.scss
@@ -99,7 +99,7 @@
p>img {
margin: auto;
max-width: 75%;
- max-height: 240px;
+ max-height: 280px;
width: auto;
height: auto;
@@ -280,4 +280,19 @@ hr.endline {
--admonition-color: var(--warning-color);
--admonition-bg-color: var(--warning-bg-color);
}
+}
+
+.halfSplit {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ overflow-y: auto;
+
+ @media screen and (max-width: 960px) {
+ flex-direction: column;
+
+ pre.Agda {
+ margin: 0;
+ }
+ }
}
\ No newline at end of file