parent
67bb9a196c
commit
16c5e3ab81
6 changed files with 94 additions and 19 deletions
|
@ -52,21 +52,28 @@ const remarkAgda: RemarkPlugin = ({ base, publicDir }: Options) => {
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (childOutput.error || !existsSync(agdaOutFile)) {
|
// Locate output file
|
||||||
|
const directory = readdirSync(agdaOutDir);
|
||||||
|
const outputFilename = directory.find((name) => name.endsWith(".md"));
|
||||||
|
|
||||||
|
if (childOutput.status !== 0 || outputFilename === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Agda error:
|
`Agda output:
|
||||||
|
|
||||||
Stdout:
|
Stdout:
|
||||||
${childOutput.stdout}
|
${childOutput.stdout}
|
||||||
|
|
||||||
Stderr:
|
Stderr:
|
||||||
${childOutput.stderr}`,
|
${childOutput.stderr}
|
||||||
|
`,
|
||||||
{
|
{
|
||||||
cause: childOutput.error,
|
cause: childOutput.error,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const outputFile = join(agdaOutDir, outputFilename);
|
||||||
|
|
||||||
// // TODO: Handle child output
|
// // TODO: Handle child output
|
||||||
// console.error("--AGDA OUTPUT--");
|
// console.error("--AGDA OUTPUT--");
|
||||||
// console.error(childOutput);
|
// console.error(childOutput);
|
||||||
|
@ -106,7 +113,7 @@ const remarkAgda: RemarkPlugin = ({ base, publicDir }: Options) => {
|
||||||
|
|
||||||
const htmlname = parse(path).base.replace(/\.lagda.md/, ".html");
|
const htmlname = parse(path).base.replace(/\.lagda.md/, ".html");
|
||||||
|
|
||||||
const doc = readFileSync(agdaOutFile);
|
const doc = readFileSync(outputFile);
|
||||||
|
|
||||||
// This is the post-processed markdown with HTML code blocks replacing the Agda code blocks
|
// This is the post-processed markdown with HTML code blocks replacing the Agda code blocks
|
||||||
const tree2 = fromMarkdown(doc);
|
const tree2 = fromMarkdown(doc);
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
|
@ -6,13 +6,23 @@ tags: [algebraic-topology, hott]
|
||||||
draft: true
|
draft: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Imports</summary>
|
||||||
|
|
||||||
```
|
```
|
||||||
{-# OPTIONS --cubical #-}
|
{-# OPTIONS --cubical --allow-unsolved-metas #-}
|
||||||
open import Cubical.Foundations.Prelude
|
module 2024-09-15-circle-is-a-suspension-over-booleans.index where
|
||||||
open import Data.Nat
|
open import Cubical.Core.Primitives
|
||||||
open import Data.Bool
|
open import Cubical.Foundations.Function
|
||||||
|
open import Cubical.Foundations.Isomorphism
|
||||||
|
open import Cubical.Foundations.Equiv.Base
|
||||||
|
open import Cubical.HITs.S1.Base
|
||||||
|
open import Data.Bool.Base
|
||||||
|
open import Data.Nat.Base
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
One of the simpler yet still very interesting space in algebraic topology is the **circle**.
|
One of the simpler yet still very interesting space in algebraic topology is the **circle**.
|
||||||
Analytically, a circle can described as the set of all points that satisfy:
|
Analytically, a circle can described as the set of all points that satisfy:
|
||||||
|
|
||||||
|
@ -61,10 +71,6 @@ $$
|
||||||
It would be nice to have an iterative definition of spheres; one that doesn't rely on us using our intuition to form new ones.
|
It would be nice to have an iterative definition of spheres; one that doesn't rely on us using our intuition to form new ones.
|
||||||
Ideally, it would be a function $S^n : \mathbb{N} \rightarrow \mathcal{U}$, where we could plug in an $n$ of our choosing.
|
Ideally, it would be a function $S^n : \mathbb{N} \rightarrow \mathcal{U}$, where we could plug in an $n$ of our choosing.
|
||||||
|
|
||||||
```
|
|
||||||
S_ : ℕ → Type
|
|
||||||
```
|
|
||||||
|
|
||||||
For an iterative definition, we'd like some kind of base case.
|
For an iterative definition, we'd like some kind of base case.
|
||||||
What's the base case of spheres?
|
What's the base case of spheres?
|
||||||
What is a $0$-sphere?
|
What is a $0$-sphere?
|
||||||
|
@ -73,12 +79,65 @@ If we take our original analytic definition of spheres and plug in $0$, we find
|
||||||
The space of solutions is just a space with two elements!
|
The space of solutions is just a space with two elements!
|
||||||
In other words, the type of booleans is actually the $0$-sphere, $S^0$.
|
In other words, the type of booleans is actually the $0$-sphere, $S^0$.
|
||||||
|
|
||||||
```
|
![](./1.png)
|
||||||
S zero = Bool
|
|
||||||
```
|
|
||||||
|
|
||||||
How about the iterative case? How can we take an $n$-sphere and get an $(n+1)$-sphere?
|
How about the iterative case? How can we take an $n$-sphere and get an $(n+1)$-sphere?
|
||||||
|
This is where we need something called a _suspension_.
|
||||||
|
|
||||||
|
Suspensions are a form of higher inductive type (HIT). It can be defined like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
S (suc n) = Bool
|
data Susp (A : Type) : Type where
|
||||||
|
north : Susp A
|
||||||
|
south : Susp A
|
||||||
|
merid : A → north ≡ south
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Intuitively, you take the entirety of the type $A$, and imagine it as some kind of plane.
|
||||||
|
Then, add two points, $\mathsf{north}$ and $\mathsf{south}$, to the top and bottom respectively.
|
||||||
|
Now, each original point in $A$ defines a _path_ between $\mathsf{north}$ and $\mathsf{south}$.
|
||||||
|
|
||||||
|
![](./susp.png)
|
||||||
|
|
||||||
|
It may not be too obvious, but the circle $S^1$, can be represented as a suspension of the booleans $S^0$.
|
||||||
|
This is presented as Theorem 6.5.1 in the [HoTT book]. I'll go over it briefly here.
|
||||||
|
|
||||||
|
[hott book]: https://homotopytypetheory.org/book/
|
||||||
|
|
||||||
|
The main idea is to consider the paths generated by both the $\mathsf{true}$ and the $\mathsf{false}$ points to add up to a single $\mathsf{loop}$.
|
||||||
|
Here's a visual guide:
|
||||||
|
|
||||||
|
![](./boolsusp.png)
|
||||||
|
|
||||||
|
We can write functions going back and forth:
|
||||||
|
|
||||||
|
```
|
||||||
|
Σ2→S¹ : Susp Bool → S¹
|
||||||
|
Σ2→S¹ north = base -- map the north point to the base point
|
||||||
|
Σ2→S¹ south = base -- also map the south point to the base point
|
||||||
|
Σ2→S¹ (merid false i) = loop i -- for the path going through false, let's map this to the loop
|
||||||
|
Σ2→S¹ (merid true i) = base -- for the path going through true, let's map this to refl
|
||||||
|
|
||||||
|
S¹→Σ2 : S¹ → Susp Bool
|
||||||
|
S¹→Σ2 base = north
|
||||||
|
S¹→Σ2 (loop i) = {! !}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, to finish showing the equivalence, we need to prove that these functions concatenate into the identity in both directions:
|
||||||
|
|
||||||
|
```
|
||||||
|
rightInv : section Σ2→S¹ S¹→Σ2
|
||||||
|
rightInv = {! !}
|
||||||
|
|
||||||
|
leftInv : retract Σ2→S¹ S¹→Σ2
|
||||||
|
leftInv = {! !}
|
||||||
|
```
|
||||||
|
|
||||||
|
And this gives us our equivalence!
|
||||||
|
|
||||||
|
```
|
||||||
|
Σ2≃S¹ : Susp Bool ≃ S¹
|
||||||
|
Σ2≃S¹ = isoToEquiv (iso Σ2→S¹ S¹→Σ2 rightInv leftInv)
|
||||||
|
```
|
||||||
|
|
||||||
|
In fact, we can also show that the $2$-sphere is the suspension of the $1$-sphere.
|
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
|
@ -7,6 +7,7 @@ import Timestamp from "../../components/Timestamp.astro";
|
||||||
import { getImage } from "astro:assets";
|
import { getImage } from "astro:assets";
|
||||||
import TocWrapper from "../../components/TocWrapper.astro";
|
import TocWrapper from "../../components/TocWrapper.astro";
|
||||||
import TagList from "../../components/TagList.astro";
|
import TagList from "../../components/TagList.astro";
|
||||||
|
import portrait from "../../assets/self.png";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection("posts");
|
const posts = await getCollection("posts");
|
||||||
|
@ -22,7 +23,7 @@ const post = Astro.props;
|
||||||
const { Content, remarkPluginFrontmatter, headings } = await post.render();
|
const { Content, remarkPluginFrontmatter, headings } = await post.render();
|
||||||
const { title, toc, heroImage: heroImagePath, heroAlt, draft } = post.data;
|
const { title, toc, heroImage: heroImagePath, heroAlt, draft } = post.data;
|
||||||
|
|
||||||
let heroImage;
|
let heroImage = undefined;
|
||||||
if (heroImagePath) {
|
if (heroImagePath) {
|
||||||
heroImage = await getImage({ src: heroImagePath });
|
heroImage = await getImage({ src: heroImagePath });
|
||||||
}
|
}
|
||||||
|
@ -40,8 +41,16 @@ const excerpt = remarkPluginFrontmatter.excerpt?.replaceAll("\n", "");
|
||||||
<meta slot="head" property="og:description" content={excerpt} />
|
<meta slot="head" property="og:description" content={excerpt} />
|
||||||
<meta slot="head" property="og:type" content="article" />
|
<meta slot="head" property="og:type" content="article" />
|
||||||
<meta slot="head" property="og:locale" content="en_US" />
|
<meta slot="head" property="og:locale" content="en_US" />
|
||||||
{heroImage && <meta slot="head" property="og:image" content={heroImage.src} />}
|
{heroImage ?
|
||||||
{heroAlt && <meta slot="head" property="og:image:alt" content={heroAlt} />}
|
<>
|
||||||
|
<meta slot="head" property="og:image" content={heroImage.src} />
|
||||||
|
{heroAlt && <meta slot="head" property="og:image:alt" content={heroAlt} />}
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<meta slot="head" property="og:image" content={portrait.src} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
<meta slot="head" property="keywords" content={post.data.tags.join(", ")} />
|
<meta slot="head" property="keywords" content={post.data.tags.join(", ")} />
|
||||||
<meta slot="head" property="description" content={excerpt} />
|
<meta slot="head" property="description" content={excerpt} />
|
||||||
|
|
Loading…
Reference in a new issue