module Homotopy.Space.Delooping where


A natural question to ask, given that all pointed types have a fundamental group, is whether every group arises as the fundamental group of some type. The answer to this question is β€œyes”, but in the annoying way that questions like these tend to be answered: Given any group we construct a type with We call the delooping of

  data Deloop : Type β„“ where
    base    : Deloop
    path    : ⌞ G ⌟ β†’ base ≑ base
    path-sq : (x y : ⌞ G ⌟) β†’ Square refl (path x) (path (x ⋆ y)) (path y)
    squash  : is-groupoid Deloop

  Deloopβˆ™ : Typeβˆ™ β„“
  Deloopβˆ™ = Deloop , base

The delooping is constructed as a higher inductive type. We have a generic base point, and a constructor expressing that Deloop is a groupoid; Since it is a groupoid, it has a set of loops point ≑ point: this is necessary, since otherwise we would not be able to prove that We then have the β€œinteresting” higher constructors: path lets us turn any element of to a path point ≑ point, and path-sq expresses that path is a group homomorphism. More specifically, path-sq fills the square below:

Using the uniqueness result for double composition, we derive that path is a homomorphism in the traditional sense:

    path-βˆ™ : βˆ€ x y β†’ path (x ⋆ y) ≑ path x βˆ™ path y
    path-βˆ™ x y i j =
      Β·Β·-unique refl (path x) (path y)
        (path (x ⋆ y)    , path-sq x y)
        (path x βˆ™ path y , βˆ™-filler _ _)
        i .fst j
And the standard argument shows that path, being a group homomorphism, preserves the group identity.
    path-unit : path unit ≑ refl
    path-unit =
      path unit                               β‰‘βŸ¨ sym (βˆ™-idr _) βŸ©β‰‘
      path unit βˆ™ ⌜ refl ⌝                    β‰‘Λ˜βŸ¨ apΒ‘ (βˆ™-invr _)  βŸ©β‰‘Λ˜
      path unit βˆ™ path unit βˆ™ sym (path unit) β‰‘βŸ¨ βˆ™-assoc _ _ _ βˆ™ apβ‚‚ _βˆ™_ (sym (path-βˆ™ _ _)) refl βŸ©β‰‘
      path ⌜ unit ⋆ unit ⌝ βˆ™ sym (path unit)  β‰‘βŸ¨ ap! G.idr βŸ©β‰‘
      path unit βˆ™ sym (path unit)             β‰‘βŸ¨ βˆ™-invr _  βŸ©β‰‘
      refl                                    ∎

We define an elimination principle for Deloop, which has the following monstruous type since it works for mapping into arbitrary groupoids. As usual, if we’re mapping into a family of types that’s more truncated (i.e.Β a family of sets or propositions), some of the higher cases become automatic.

    : βˆ€ {β„“'} (P : Deloop β†’ Type β„“')
    β†’ (βˆ€ x β†’ is-groupoid (P x))
    β†’ (p : P base)
    β†’ (ploop : βˆ€ x β†’ PathP (Ξ» i β†’ P (path x i)) p p)
    β†’ ( βˆ€ x y
        β†’ SquareP (Ξ» i j β†’ P (path-sq x y i j))
                  (Ξ» _ β†’ p) (ploop x) (ploop (x ⋆ y)) (ploop y))
    β†’ βˆ€ x β†’ P x

We can then proceed to characterise the type point ≑ x by an encode-decode argument. We define a family Code, where the fibre over base is definitionally G; Then we exhibit inverse equivalences base ≑ x β†’ Code x and Code x β†’ base ≑ x, which fit together to establish G ≑ (base ≑ base).

We’ll want to define the family Code by induction on Deloop. First, since we have to map into a groupoid, we’ll map into the type rather than This takes care of the truncation constructor (which is hidden from the page since it is entirely formulaic): let’s tackle the rest in order. We can also handle the base case, since Code base = G was already a part of our specification.

  Code : Deloop β†’ Set β„“
  Code = go module Code where
    open is-iso

    base-case : Set β„“
    ∣ base-case ∣    = ⌞ G ⌟
    base-case .is-tr = hlevel 2

To handle the path case, we’ll have to produce, given an element an equivalence by univalence, we can then lift this equivalence to a path which we can use as the path-case. While there might be many maps one is canonical: the Yoneda embedding map, sending to

    path-case : ⌞ G ⌟ β†’ base-case ≑ base-case
    path-case x = n-ua eqv module path-case where
      rem₁ : is-iso (_⋆ x)
      rem₁ .inv = _⋆ x ⁻¹
      rem₁ .rinv x = cancelr inversel
      rem₁ .linv x = cancelr inverser

      eqv : ⌞ G ⌟ ≃ ⌞ G ⌟
      eqv .fst = _
      eqv .snd = is-isoβ†’is-equiv rem₁

    open path-case

Finally, we can satisfy the coherence case path-sq by an algebraic calculation on paths:

    coh : βˆ€ x y β†’ Square refl (path-case x) (path-case (x ⋆ y)) (path-case y)
    coh x y = n-Type-square $ transport (sym Square≑double-composite-path) $
      ua (eqv x) βˆ™ ua (eqv y) β‰‘Λ˜βŸ¨ uaβˆ™ βŸ©β‰‘Λ˜
      ua (eqv x βˆ™e eqv y)     β‰‘βŸ¨ ap ua (Ξ£-prop-path! (funext Ξ» _ β†’ sym associative)) βŸ©β‰‘
      ua (eqv (x ⋆ y))        ∎

We can then define the encoding and decoding functions. The encoding function encode is given by lifting a path from Deloop to Code. For decoding, we do induction on Deloop with Code x .fst β†’ base ≑ x as the motive.

    encode : βˆ€ x β†’ base ≑ x β†’ Code Κ» x
    encode x p = subst (Code Κ»_) p unit

  decode : βˆ€ x β†’ Code Κ» x β†’ base ≑ x
  decode = go where
    coh : βˆ€ x β†’ PathP (Ξ» i β†’ Code Κ» path x i β†’ base ≑ path x i) path path
    coh x i c j = hcomp (βˆ‚ i ∨ βˆ‚ j) Ξ» where
      k (k = i0) β†’ path (ua-unglue (Code.path-case.eqv x) i c) j
      k (i = i0) β†’ path-sq c x (~ k) j
      k (i = i1) β†’ path c j
      k (j = i0) β†’ base
      k (j = i1) β†’ path x (i ∨ ~ k)

    go : βˆ€ x β†’ Code Κ» x β†’ base ≑ x
    go base c = path c
    go (path x i) c = coh x i c
    go (path-sq x y i j) = is-set→squarep
      (Ξ» i j β†’ fun-is-hlevel {A = Code Κ» path-sq x y i j} 2 (Deloop.squash base (path-sq x y i j)) )
      (Ξ» i β†’ path) (coh x) (coh (x ⋆ y)) (coh y) i j
    go (squash x y p q Ξ± Ξ² i j k) =
      is-hlevelβ†’is-hlevel-dep {B = Ξ» x β†’ Code Κ» x β†’ base ≑ x} 2 (Ξ» x β†’ hlevel 3)
        (go x) (go y) (Ξ» i β†’ go (p i)) (Ξ» i β†’ go (q i))
        (Ξ» i j β†’ go (Ξ± i j)) (Ξ» i j β†’ go (Ξ² i j)) (squash x y p q Ξ± Ξ²) i j k

Proving that these are inverses finishes the proof. For one direction, we use path induction to reduce to the case decode base (encode base refl) ≑ refl; A quick argument tells us that encode base refl is the group identity, and since path is a group homomorphism, we have path unit = refl, as required.

    unfolding encode

    encodeβ†’decode : βˆ€ {x} (p : base ≑ x) β†’ decode x (encode x p) ≑ p
    encode→decode p =
      J (Ξ» y p β†’ decode y (encode y p) ≑ p)
        (ap path (transport-refl _) βˆ™ path-unit)

In the other direction, we do induction on deloopings; Since the motive is a family of propositions, we can use Deloop-elim-prop instead of the full Deloop-elim, which reduces the goal to proving

    decodeβ†’encode : βˆ€ x (c : Code Κ» x) β†’ encode x (decode x c) ≑ c
    decode→encode =
        (Ξ» x β†’ (c : Code Κ» x) β†’ encode x (decode x c) ≑ c)
        (Ξ» x β†’ Ξ -is-hlevel 1 Ξ» _ β†’ Code x .is-tr _ _)
        Ξ» c β†’ transport-refl _ βˆ™ G.idl

This completes the proof, and lets us establish that the fundamental group of Deloop is G, which is what we wanted.

  G≃ΩB : ⌞ G ⌟ ≃ (base ≑ base)
  G≃ΩB = Isoβ†’Equiv (decode base , iso (encode base) encodeβ†’decode (decodeβ†’encode base))

  G≑π₁B : G ≑ Ο€β‚™β‚Šβ‚ 0 (Deloop , base)
  G≑π₁B = ∫-Path Groups-equational
    (total-hom (Ξ» x β†’ inc (path x))
      record { pres-⋆ = Ξ» x y β†’ ap βˆ₯_βˆ₯β‚€.inc (path-βˆ™ _ _) })
    (βˆ™-is-equiv (G≃ΩB .snd) (βˆ₯-βˆ₯β‚€-idempotent (squash base base)))

Since Deloop is a groupoid, each of its loop spaces is automatically a set, so we do not necessarily need the truncation when taking its fundamental group. This alternative construction is worth mentioning since it allows us to trade a proof that encode preserves multiplication for proofs that it also preserves the identity, inverses, differences, etc.

    : is-group-hom (π₁Groupoid.on-Ξ© Deloopβˆ™ squash) (G .snd) (encode base)
  encode-is-group-hom .is-group-hom.pres-⋆ x y = eqv.injectiveβ‚‚ (eqv.Ξ΅ _) $
    path (encode base x ⋆ encode base y)          β‰‘βŸ¨ path-βˆ™ (encode base x) (encode base y) βŸ©β‰‘
    path (encode base x) βˆ™ path (encode base y)   β‰‘βŸ¨ apβ‚‚ _βˆ™_ (eqv.Ξ΅ _) (eqv.Ξ΅ _) βŸ©β‰‘
    x βˆ™ y                                         ∎
    where module eqv = Equiv G≃ΩB

For abelian groupsπŸ”—

If is an abelian group, then we can characterise the loop spaces of based at totally arbitrary points, rather than the above characterisation which only applies for the loop space at base. Our proof starts with the following immediate observation: multiplication in is commutative as well.

    βˆ™-comm : (p q : Path (Deloop G) base base) β†’ p βˆ™ q ≑ q βˆ™ p
    βˆ™-comm p q = encode.injective G
      (encode.pres-⋆ G _ _ Β·Β· ab _ _ Β·Β· sym (encode.pres-⋆ G _ _))

We’ll construct a function that computes the β€œwinding number” of a loop with arbitrary base.

  winding : {x : Deloop G} β†’ x ≑ x β†’ ⌞ G ⌟
  winding {x = x} = go x module windingⁱ where
      deg : base ≑ base β†’ ⌞ G ⌟
      deg = encode G base

      go base loop = deg loop

If the loop is indeed based at the basepoint constructor, then we can appeal to the existing construction; We’ll abbreviate it as deg for this construction.

Since our codomain is a set, the higher cases are both handled mechanically; We omit them from the page in the interest of parsimony. We’re left with tackling the path case, which means constructing a term exhibiting the coherence below:

      coherence = inS ( βˆ€ b β†’
        PathP (Ξ» i β†’ path b i ≑ path b i β†’ ⌞ G ⌟) deg deg)

This condition is a bit funky, since at first glance it looks like all we must do is equate deg with itself. However, we’re doing this over a non-trivial identification in the domain. By extensionality for dependent functions, the above is equivalent to showing that deg produces identical results given an element and loops fiting into a commutative square

Since commutativity of the diagram above says precisely that is the conjugate of by we can reason about conjugation instead; And since we’ve shown that this conjugation is just again. That finishes the construction:

        coh b = funext-dep Ξ» {xβ‚€} {x₁} p β†’ ap deg $ sym $
          x₁               β‰‘Λ˜βŸ¨ pathpβ†’conj p βŸ©β‰‘Λ˜
          conj (path b) xβ‚€ β‰‘βŸ¨ conj-commutative (βˆ™-comm xβ‚€ (path b)) βŸ©β‰‘
          xβ‚€               ∎

      go (path x i) loop = coh x i loop

We could go on to define the inverse to winding similar to how we constructed decode, but there’s a trick: since being an equivalence is a proposition, if we want to show is an equivalence for arbitrary it suffices to do so for but we’ve already shown that’s an equivalence! A similar remark allows us to conclude that is a group homomorphism

    winding-is-equiv : βˆ€ x β†’ is-equiv (winding {x})
    winding-is-equiv = Deloop-elim-prop G _ (Ξ» _ β†’ hlevel 1) $
      Equiv.inverse (G≃ΩB G) .snd

    winding-is-group-hom : βˆ€ x β†’
      is-group-hom (π₁Groupoid.on-Ξ© (Deloop G , x) (hlevel 3))
        (G .snd) (winding {x})
    winding-is-group-hom = Deloop-elim-prop G _ (Ξ» x β†’ hlevel 1) Ξ» where
      .pres-⋆ x y β†’ encode.pres-⋆ G x y

We can then obtain a nice interface for working with winding.

  module winding {x} where
    open Equiv (winding , winding-is-equiv x) public
    open is-group-hom (winding-is-group-hom x) public

  pathᡇ : (x : Deloop G) β†’ ⌞ G ⌟ β†’ x ≑ x
  pathᡇ _ = winding.from