Quest 0 - Working with the Circle
In this series of quests we will prove that the fundamental group
of S¹ is ℤ.
In fact, our strategy will also show that the higher homotopy groups of
S¹ are all trivial.
You don’t need to know any prerequisites - in particular we will define
the fundamental group and higher homotopy groups
if you don’t know what they are already.
Important
In your cloned copy of the HoTT Game locate the file
1FundamentalGroup/Quest0.agda,
and open this file in emacs.
Before starting it is important to have a look at
our guide to emacs and list of emacs commands.
Part 0 - The Circle
Theory - Definition of the Circle
We begin by formalising the problem statement.
A construction of “the circle” is :
a point called
basean edge from that point to itself called
loop
Here is our definition of the circle in agda.
data S¹ : Type where
base : S¹
loop : base ≡ base
This reads :
S¹is a point inType, the space of spaces. In other words,S¹is a space.baseis a point in the spaceS¹loopis a path inS¹frombaseto itself. This is phrased as sayingloopis a point inbase ≡ basethe space of paths frombasetobase.
Path
We think of a path in a space A as consisting
of its starting point, its end point,
and some generic point in the middle
agreeing on the boundary.
You can see this as defining the circle via a CW-complex.
Type theory notation
In general a : A is read as a is a point
in the space A.
Note that in the above definition S¹ is seen
both as a point and a space depending on the context.
In cubical agda,
everything is a point in a “unique” space.
Type theory notation
In general when a b : A
(a and b are points in a space A),
we have a path space a ≡ b,
whose points are paths from
a to b in the space A.
Exercise - defining the constant path Refl
There are other paths in S¹,
for example the constant path at base.
In 1FundamentalGroup/Quest0.agda navigate to
Refl : base ≡ base
Refl = {!!}
We will guide you through defining it.
We are about to construct a path Refl : base ≡ base,
a path from base to base.
Tip
The {!!} are called holes.
These are blanks in the agda file that you can fill
to complete the quest.
You can write ? to make a new hole.
We will fill the hole Refl = {!!}.
Make sure you are in insert mode by pressing
i. To escape insert mode pressESC.Note
We have compiled a list of useful
emacsandagdacommands in Emacs Commands.Enter
C-c C-l(this meansCtrl-c Ctrl-l).
- Whenever you do this,
agdawill check the document is written correctly. We say
agdacompiles or loads the file. This will open the*Agda Information*window looking like?0 : base ≡ base ?1 : (something) ?2 : (something) ...
This is the list of unfilled holes that are in your file currently. You should see that the holes in the file have changed in appearance, for example :
Refl : base ≡ base Refl = { }0
These are what holes look like when the file is compiled. The numbering is just for reference and may change upon reloading.
Navigate between holes using
C-c C-f(forward) orC-c C-b(backward).Navigate to the first hole, making sure your cursor is inside the hole. Check the goal using
C-c C-,(this meansCtrl-c Ctrl-comma). Whenever you doC-c C-,,agdawill tell you what kind of “point” it expects in the hole. The*Agda Information*window should be focused on this hole only :Goal: base ≡ base
This says
agdais expecting a path frombasetobasein the hole. Making sure your cursor is still inside the hole, enterC-c C-r. Therstands for refine. Whenever you do this whilst having your cursor in a hole,agdawill try to help you.You should now see
λ i → {!!}. This is theagdaway of writingi ↦ {!!}. Load the file again (usingC-c C-l) and the*Agda Information*window should now look like :?0 : S¹ ... ?6 : (something) ———— Errors ——————————————— Failed to solve the following constraints: ?0 (i = i1) = base : S¹ (blocked on _3) ?0 (i = i0) = base : S¹ (blocked on _3)
Do not worry about the errors, we will soon explain it.
Navigate (
C-c C-fandC-c C-b) to that new hole inλ i → {!!}and enterC-c C-,to check the goal. The*Agda information*window should look like :Goal: S¹ ————————————————————————— i : I ———— Constraints —————————————— ?0 (i = i1) = base : S¹ (blocked on _3, belongs to problem 4) ?0 (i = i0) = base : S¹ (blocked on _3, belongs to problem 4) _4 := λ i → ?0 (i = i) (blocked on problem 4)
This says :
agdais expecting a point inS¹for this hole.you have a point
iinIavailable to you. You can think ofIas the “unit interval” andias a generic point in the interval.The point in
S¹that you give has to satisfy the constraints that it isbasewhen “i = 1” and “i = 0”. Inagda,i0andi1are the “start” and “end” point ofI. Afterall, we are defining a path frombaseto itself.Don’t worry about the last line.
Since
Reflis meant to be the constant path atbase, writebasein the hole.Press
C-c C-SPCto fill the hole withbase. In general when you have some text (and your cursor) in a hole, doingC-c C-SPCwill tellagdato replace the hole with that text.agdawill give you an error if it can’t make sense of your text.Tip
Everytime you are filling a hole, it is recommended that you first write what you want to fill in the hole then do
C-c C-SPC. You can do it in the reverse order, however the recommended order has other benefits down the line.Load the file again (
C-c C-l). The*Agda Information*window should now look like this :?0 : Bool ?1 : Bool ≅ Bool ?2 : Bool ≡ Bool ?3 : Type ?4 : doubleCover base ?5 : ⊥
The
?0 : S¹has disappeared. This meansagdahas accepted what you filled this hole with.If you want to play around with this you reset this question by replacing what you wrote with
?and doingC-c C-l.
Part 1 - Refl ≡ loop is empty
To get a better feel of S¹,
we show that the space of paths (homotopies) between
Refl and loop, written Refl ≡ loop, is empty.
Paths between paths
In general if we have p q : a ≡ b in a space A
then a path Path : p ≡ q in the path space a ≡ b
consists of
the starting path
pthe end path
qand some generic path in between
Path i : a ≡ bthat agrees on the boundary
In algebraic topology this is called a path homotopy.
First, we define the empty space and what it means for a space to be empty.
Here is what this looks like in agda :
data ⊥ : Type where
This says “the empty space ⊥ is a space with no points in it”.
Here are three candidate definitions for a space A to be empty :
there is a point
f : A → ⊥in the space of functions fromAto the empty spacethere is a path
p : A ≡ ⊥in the space of spacesTypefromAto the empty spacethere is an isomorphism
i : A ≅ ⊥of spaces
These turn out to be “the same” (see Different notions of “empty”), however for our present purposes we will use the first definition. Our goal is therefore to produce a point in the function space
( Refl ≡ loop ) → ⊥
The authors of this series have thought long and hard about how one would come up with the following argument. Unfortunately, sometimes mathematics is in need of a new trick and this was one of them.
The trick
We make a path p : true ≡ false
from the assumed path (homotopy) h : Refl ≡ loop by
constructing a non-trivial Bool-bundle over the circle,
hence obtaining a map ( Refl ≡ loop ) → ⊥.
To elaborate :
Bool here refers to the discrete space with two points true, false.
We will create a map doubleCover : S¹ → Type that sends
base to Bool and the path loop to
a non-trivial path flipPath : Bool ≡ Bool in the space of spaces.
Viewing the picture vertically,
for each point x : S¹,
we call doubleCover x the fiber of doubleCover over x.
All the fibers look like Bool, hence our choice of the name Bool- *bundle*.
Homotopy type theory
A path p : X ≡ Y between two spaces X Y : Type
(viewed as points in the space of spaces)
can be visualised as follows :
Two spaces
XandYas end points.For the generic point
i : Iin the intervalp i : Typeis a space that varies continuously with to respect toisuch thatp 0isXandp 1isY.
The continuity guarantees that each point along the path looks “the same”. In particular the end points look “the same”.
We will get a path from true to false
in the fiber of doubleCover over base
by “lifting the homotopy” h : Refl ≡ loop and
considering the end points of the “lifted paths”.
Refl will “lift” to a “constant path” and loop will “lift” to
Let’s assume for the moment that we have flipPath already and
define doubleCover.
Make sure you are in insert mode.
Navigate to the definition of
doubleCoverand make sure you have loaded the file withC-c C-l.doubleCover : S¹ → Type doubleCover x = {!!}
Navigate to the hole and check the goal. It should look like
Goal: Type ———————————————————— x : S¹
This says it is expecting a point in
Type, the space of spaces, i.e. it expects a space. We will first case on x by writingxin the hole and doingC-c C-c(cfor cases). You should now see two new holes :doubleCover : S¹ → Type doubleCover base = {!!} doubleCover (loop i) = {!!}
This means :
S¹is made from a pointbaseand an edgeloop, so a map out ofS¹to a space is the same as choosing a point to mapbaseto, and an edge to maploopto respectively. Sinceloopis a path frombaseto itself, its image must also be a path from the image ofbaseto itself.Navigate to the first new hole and check the goal. We want to map
baseto the spaceBoolso writeBoolin the hole, then doC-c C-SPCto fill it.Navigate to the second new hole and check the goal. Here
loop iis a generic point in the pathloop, wherei : Iis a generic point of the “unit interval”. We are assuming we haveflipPathdefined already and want to maplooptoflipPath, soloop ishould map to a generic point in the pathflipPath.Note
We can use
flipPathwithout completing the definition offlipPath.Try filling the hole.
Once you think you are done, reload the
agdafile withC-c C-land if it doesn’t complain this means there are no problems with your definition. Compare your definition to that in1FundamentalGroup/Quest0Solutions.agdato check that your definition is the one we want. To navigate to solutions file escape insert mode usingESCand doSPC f fto find the file, see Emacs and Unicode Commands. Here is a definition thatagdawill accept, but is not what we need:Bad definition
doubleCover : S¹ → Type doubleCover base = Bool doubleCover (loop i) = Bool
Defining flipPath is quite involved and we will do so in the following part.
Part 2 - Defining flipPath via Univalence
In this part, we will define the path flipPath : Bool ≡ Bool.
Recall the picture of doubleCover.
This means we need flipPath to correspond to
the unique non-identity permutation of Bool
that flips true and false.
The function
We proceed in steps :
Define the function
Flip : Bool → Bool.Promote this to an isomorphism
flipIso : Bool ≅ Bool.We use univalence to turn
flipIsointo a pathflipPath : Bool ≡ Bool. The univalence axiom asserts that paths inType- the space of spaces - correspond to homotopy-equivalences of spaces. As a corollary, we can make paths inTypefrom isomorphisms inType.
Isomorphism
One with a topological mindset might worry if isomorphism means homeomorphism, homotopy equivalence, bijection or something else; one might even wonder what continuity is here. The answer is that this is synthetic homotopy theory, where
there is no need for real numbers
every map is continuous in the sense that they respect paths
an isomorphism
A ≅ Bis given by the data offun : A → Binv : B → ArightInvthat says (extensionally)fun ∘ invis homotopic to the identity, i.e. given anyb : Bwe have a pathfun ∘ inv b ≡ b.leftInvthat says (extensionally)inv ∘ funis homotopic to the identity.
You might notice that the above looks like the classical definition of homotopy equivalence. (They turn out to be “the same”.)
Univalence
We have described paths between points as giving a starting point, an ending point, and a generic point between that agrees on the boundary. Drawing a path between spaces in the space of spaces, we can see that such a path is the data of two spaces that “continuously look the same”:
We already have a notion of “the same” for spaces, which is isomorphism. Hence we assume the following “univalence” axiom : Any isomorphism can be turned into a path between spaces.
In
1FundamentalGroup/Quest0.agda, navigate to :
Flip : Bool → Bool
Flip x = {!!}
Make sure you are in insert mode.
Check the goal. It should be asking for a point in
Bool, since we have already given it anx : Boolat the front.
Tip
Whenever you encounter a new hole, you should first check the goal.
Write
xinside the hole, and case onxusingC-c C-cwith your cursor still inside. You should now see :Flip : Bool → Bool Flip false = {!!} Flip true = {!!}
This means : the space
Boolis made of two pointsfalse, trueand nothing else, so to map out ofBoolit suffices to find images forfalseandtruerespectively.Since we want
Flipto fliptrueandfalse, fill the first hole withtrueand the second withfalse.To check things have worked, try
C-c C-d(dstands for deduce its space). Thenagdawill ask you to input an expression. EnterFlip. In the*Agda Information*window, you should seeBool → Bool
This means
agdarecognisesFlipas a well-formulated term and is a point in the space of maps fromBooltoBool.We can also ask
agdato compute outputs ofFlip. TryC-c C-n(nstands for normalise),agdashould again be asking for an expression. EnterFlip true. In the*Agda Information*window, you should seefalse, as desired.
The isomorphism
Navigate to
flipIso : Bool ≅ Bool flipIso = {!!}
Refine with
C-c C-r. You should now seeflipIso : Bool ≅ Bool flipIso = iso {!!} {!!} {!!} {!!}
Given two spaces
AandB,iso(with respect toAandB) belongs to the following space :iso : (fun : A → B) (inv : B → A) (rightInv : section fun inv) (leftInv : retract fun inv) → A ≅ Bwhich says that
isowill produce an isomorphism fromAtoBgiven a mapfunforwards and an inverseinvbackwards, and points of the spacesection fun invandretract fun inv. Try to find out whatsectionandretractare by doingC-c C-nand entering their respective names. They should respectively say thatinvis a right and left inverse offun.Check the first two holes,
agdashould expect functionsBool → Boolto go in both of them. This is because it is expecting a function and its inverse, respectively, so fill them withFlipand its inverseFlip.Check the goal of the next two holes. They should be
section Flip Flip
and
retract Flip Flip
Add the following to your code (make sure you copy it exactly) :
flipIso : Bool ≅ Bool flipIso = iso Flip Flip {!!} {!!} where rightInv : section Flip Flip rightInv x = {!!} leftInv : retract Flip Flip leftInv x = {!!}
Then load the file with
C-c C-l. Ifagdagives an error it could be due tomissing spaces;
agdais space sensitivewrong indentation before
rightInvandleftInv;agdais indentation sensitivemissing the
wherein the second line.lower and upper case differences
where to use
whereThe
whereallows you to make definitions local to the current definition, in the sense that you will not be able to accessrightInvandleftInvoutside this proof. We will eventually fill the missing holes from before withrightInvandleftInv. If you like you can also place the definitions ofrightInvandleftInvbeforeflipIso.Check the goal of the hole
rightInv x = {!!}. In the*Agda Information*window, you should seeGoal: Flip (Flip x) ≡ x ————————————————————————————————— x : Bool
This says
rightInvshould give for eachx : Boola pathp : Flip (Flip x) ≡ x. We gave anx : Boolin front, so the goal is simply to give a pathp : Flip (Flip x) ≡ x. Try to give such a path.Hint
You need to case on what
xcan be. Then for the case offalse,Flip (Flip false)should just befalseby design, so you need to give a path fromfalsetofalse.The benefit of having
xbefore the=is that we can case on whatxcan be. This is called pattern matching.Do the same for
leftInv x = {!!}.Fill in the missing goals from the original problem using
rightInv,leftInv.If you got the definition right then
agdashould not have any errors when you load usingC-c C-l.
The path
Navigate to
flipPath : Bool ≡ Bool flipPath = {!!}
In the hole, write in
isoToPathand refine withC-c C-r. You should now haveflipPath : Bool ≡ Bool flipPath = isoToPath {!!}
If you check the new hole, you should see that
agdais expecting an isomorphismBool ≅ Bool.isoToPathis the function from the cubical library that converts isomorphisms between spaces into paths between the corresponding points in the space of spacesType.Fill in the hole with
flipIsoand useC-c C-dto checkagdais happy withflipPath.Try to normalise
pathToFun flipPath false. You should gettruein the*Agda Information*window.What
pathToFundid is it took the pathflipPathin the space of spacesTypeand followed the pointfalseasBoolis transformed alongflipPath. The end result is of coursetrue, sinceflipPathis the path obtained fromflip! Try to follow whatpathToFundoes in the animation.pathToFunpathToFunis calledtransportin the cubical library.
Part 3 - Lifting paths using doubleCover
By the end of this page we will have shown that
refl ≡ loop is an empty space.
In 1FundamentalGroup/Quest0.agda locate
Refl≢loop : Refl ≡ loop → ⊥
Refl≢loop h = ?
The cubical library has the result
true≢false : true ≡ false → ⊥
which says that the space of paths in Bool
from true to false is empty.
We will assume it here and leave the proof as a side quest,
see Proving true≢false.
Load the file with
C-c C-land navigate to the hole. Writetrue≢false(input\==nfor≢; see Emacs and Unicode Commands) in the hole and refine usingC-c C-r,agdaknowstrue≢falsemaps to⊥so it automatically will make a new hole.Check the goal in the new hole using
C-c C-,it should be asking for a path fromtruetofalse.
To give this path we need to visualise “lifting” Refl, loop
and the homotopy h : Refl ≡ loop
along the Bool-bundle doubleCover.
When we “lift” Refl - starting at the point true : doubleCover base -
it will still be a constant path at true,
drawn as a dot true.
When we “lift” loop - starting at the point true : doubleCover base -
it will look like
The homotopy h : Refl ≡ loop is “lifted”
(starting at “lifted Refl”)
to some kind of surface
According to the pictures the end point of the “lifted”
Refl is true and the end point of the “lifted” loop is false.
We are interested in the end points of each
“lifted paths” in the “lifted homotopy”,
since this forms a path in the endpoint fiber doubleCover base
from true to false.
We can evaluate the end points of both “lifted paths” by using
something in the cubical library (called subst) which we call endPt.
endPt : (B : A → Type) (p : x ≡ y) (bx : B x) → B y
Note
It says given a bundle B over space A,
a path p from x : A to y : A, and
a point bx above x,
we can get the end point of “lifted p starting at bx”.
So let’s make the function that takes
a path from base to base and spits out the end point
of the “lifted paths” starting at true.
endPtOfTrue : base ≡ base → doubleCover base
endPtOfTrue p = {!!}
Check the goal. It should be asking for
Goal: Bool —————————————————————————─ p : base ≡ base ———— Constraints ——————————————— ?0 (p = loop) = false : Bool (blocked on _29, belongs to problem 90) ?0 (p = Refl) = true : Bool (blocked on _29, belongs to problem 90) _40 := λ p i → endPtOfTrue (p i) (blocked on problem 90)
We want to use
endPt, which can output something in the spaceB y(as described above). In this case we wantB yto beBool.agdais smart and can figure out how to useendPt:Type
endPtinto the hole and doC-c C-r.
Tip
In general if the goal of the hole
Goal: Y ———————————————————————— f : X → Y ...
is to find a point in a space
Yand you have a functionf : X → Ythen you can writefin the hole and doC-c C-r.You should see
endPtOfTrue : base ≡ base → doubleCover base endPtOfTrue p = endPt {!!} {!!} {!!}
Try to fill in these holes.
Once you think you are done, you can verify our expectation that
endPtOfTrue ReflistrueandendPtOfTrue loopisfalseusingC-c C-n.
Lastly we need to make the function endPtOfTrue
take the path h : Refl ≡ loop to a path from true to false.
In general if f : A → B is a function and p is a path
between points x y : A then we get a map cong f p
from f x to f y.
(Note that p here is actually a homotopy h.)
cong : (f : A → B) → (p : x ≡ y) → f x ≡ f y
We will define cong in a side quest
Using cong and endPtOfTrue you should be able to complete Quest0.
If you have done everything correctly you can reload agda and see that
you have no remaining goals.