index

⚠️ This module is not part of this project and is only included for reference.
It is either part of the 1lab, the cubical library, or a built-in Agda module.

<!--
```agda
open import 1Lab.Path.Reasoning
open import 1Lab.Path.Groupoid
open import 1Lab.HLevel
open import 1Lab.Path
open import 1Lab.Type

open is-contr
```
-->

```agda
module 1Lab.Equiv where
```

# Equivalences {defines=equivalence}

The big idea of homotopy type theory is that isomorphic types can be
identified: the [[univalence axiom]]. However, the notion of
`isomorphism`{.Agda ident=is-iso}, is, in a sense, not "coherent" enough
to be used in the definition. For that, we need a coherent definition of
_equivalence_, where "being an equivalence" is a [[proposition]].

To be more specific, what we need for a notion of equivalence
$\isequiv(f)$ to be "coherent" is:

- Being an `isomorphism`{.Agda ident=is-iso} implies being an
`equivalence`{.Agda ident=is-equiv} ($\isiso(f) \to
\isequiv(f)$)

- Being an equivalence implies being an isomorphism ($\isequiv(f)
\to \isiso(f)$); Taken together with the first point we may
summarise: "Being an equivalence and being an isomorphism are logically
equivalent."

- Most importantly, being an equivalence _must_ be a proposition.

The notion we adopt is due to Voevodsky: An equivalence is one that has
`contractible`{.Agda ident=is-contr} `fibres`{.Agda ident=fibre}. Other
definitions are possible (e.g.: [bi-invertible maps]) --- but
contractible fibres are "privileged" in Cubical Agda because for
[glueing] to work, we need a proof that `equivalences have contractible
fibres`{.Agda ident=is-eqv'} anyway.

[bi-invertible maps]: 1Lab.Equiv.Biinv.html
[glueing]: 1Lab.Univalence.html#glue

```agda
private
  variable
    ℓ₁ ℓ₂ : Level
    A B : Type ℓ₁
```

::: {.definition #fibre}
A _homotopy fibre_, _fibre_ or _preimage_ of a function `f` at a point
`y : B` is the collection of all elements of `A` that `f` maps to `y`.
Since many choices of name are possible, we settle on the one that is
shortest and most aesthetic: `fibre`{.Agda}.

```agda
fibre : (A  B)  B  Type _
fibre f y = Σ _ λ x  f x  y
```
:::

A function `f` is an equivalence if every one of its fibres is
[[contractible]] - that is, for any element `y` in the range, there is
exactly one element in the domain which `f` maps to `y`. Using
set-theoretic language, `f` is an equivalence if the preimage of every
element of the codomain is a singleton.

```agda
record is-equiv (f : A  B) : Type (level-of A  level-of B) where
  no-eta-equality
  field
    is-eqv : (y : B)  is-contr (fibre f y)

open is-equiv public

_≃_ :  {ℓ₁ ℓ₂}  Type ℓ₁  Type ℓ₂  Type _
_≃_ A B = Σ (A  B) is-equiv

id-equiv : is-equiv {A = A}  x  x)
id-equiv .is-eqv y =
  contr (y , λ i  y)
    λ { (y' , p) i  p (~ i) , λ j  p (~ i  j) }
```

<!--
```
-- This helper is for functions f, g that cancel eachother, up to
-- definitional equality, without any case analysis on the argument:

strict-fibres :  { ℓ'} {A : Type } {B : Type ℓ'} {f : A  B} (g : B  A) (b : B)
   Σ[ t  fibre f (f (g b)) ]
    ((t' : fibre f b)  Path (fibre f (f (g b))) t
                          (g (f (t' .fst)) , ap (f  g) (t' .snd)))
strict-fibres {f = f} g b .fst = (g b , refl)
strict-fibres {f = f} g b .snd (a , p) i = (g (p (~ i)) , λ j  f (g (p (~ i  j))))

-- This is more efficient than using Iso→Equiv. When f (g x) is definitionally x,
-- the type reduces to essentially is-contr (fibre f b).
```
-->

For Cubical Agda, the type of equivalences is distinguished, so we have
to make a small wrapper to match the interface Agda expects. This is the
geometric definition of contractibility, in terms of [partial elements]
and extensibility.

[partial elements]: 1Lab.Path.html#extensibility

```agda
{-# BUILTIN EQUIV _≃_ #-}
{-# BUILTIN EQUIVFUN fst #-}

is-eqv' :  {a b} (A : Type a) (B : Type b)
         (w : A  B) (a : B)
         (ψ : I)
         (p : Partial ψ (fibre (w .fst) a))
         fibre (w .fst) a [ ψ  p ]
is-eqv' A B (f , is-equiv) a ψ u0 = inS (
  hcomp ( ψ) λ where
    i (ψ = i0)  c .centre
    i (ψ = i1)  c .paths (u0 1=1) i
    i (i = i0)  c .centre)
  where c = is-equiv .is-eqv a

{-# BUILTIN EQUIVPROOF is-eqv' #-}
```

<!--
```
equiv-centre : (e : A  B) (y : B)  fibre (e .fst) y
equiv-centre e y = e .snd .is-eqv y .centre

equiv-path : (e : A  B) (y : B) 
  (v : fibre (e .fst) y)  Path _ (equiv-centre e y) v
equiv-path e y = e .snd .is-eqv y .paths
```
-->

## is-equiv is propositional

A function can be an equivalence in at most one way. This follows from
propositions being closed under dependent products, and `is-contr`{.Agda}
being a proposition.

```agda
module _ where private
  is-equiv-is-prop : (f : A  B)  is-prop (is-equiv f)
  is-equiv-is-prop f x y i .is-eqv p = is-contr-is-prop (x .is-eqv p) (y .is-eqv p) i
```

<details>
<summary>
Even though the proof above works, we use the direct cubical proof in
this `<details>` tag (lifted from the Cubical Agda library) in the rest
of the development for efficiency concerns.
</summary>

```agda
is-equiv-is-prop : (f : A  B)  is-prop (is-equiv f)
is-equiv-is-prop f p q i .is-eqv y =
  let p2 = p .is-eqv y .paths
      q2 = q .is-eqv y .paths
  in contr (p2 (q .is-eqv y .centre) i) λ w j  hcomp ( i   j) λ where
     k (i = i0)  p2 w j
     k (i = i1)  q2 w (j  ~ k)
     k (j = i0)  p2 (q2 w (~ k)) i
     k (j = i1)  w
     k (k = i0)  p2 w (i  j)
```
</details>

# Isomorphisms from equivalences

For this section, we need a definition of _isomorphism_. This is the
same as ever! An isomorphism is a function that has a two-sided inverse.
We first define what it means for a function to invert another on the
left and on the right:

```agda
is-left-inverse : (B  A)  (A  B)  Type _
is-left-inverse g f = (x : _)  g (f x)  x

is-right-inverse : (B  A)  (A  B)  Type _
is-right-inverse g f = (x : _)  f (g x)  x
```

A proof that a function $f$ is an isomorphism consists of a function $g$
in the other direction, together with homotopies exhibiting $g$ as a
left- and right- inverse to $f$.

```agda
record is-iso (f : A  B) : Type (level-of A  level-of B) where
  no-eta-equality
  constructor iso
  field
    inv : B  A
    rinv : is-right-inverse inv f
    linv : is-left-inverse inv f

  inverse : is-iso inv
  inv inverse = f
  rinv inverse = linv
  linv inverse = rinv

Iso :  {ℓ₁ ℓ₂}  Type ℓ₁  Type ℓ₂  Type _
Iso A B = Σ (A  B) is-iso
```

Any function that is an equivalence is an isomorphism:

```agda
equiv→inverse : {f : A  B}  is-equiv f  B  A
equiv→inverse eqv y = eqv .is-eqv y .centre .fst

equiv→counit
  :  {f : A  B} (eqv : is-equiv f) x  f (equiv→inverse eqv x)  x
equiv→counit eqv y = eqv .is-eqv y .centre .snd

equiv→unit
  :  {f : A  B} (eqv : is-equiv f) x  equiv→inverse eqv (f x)  x
equiv→unit {f = f} eqv x i = eqv .is-eqv (f x) .paths (x , refl) i .fst

equiv→zig
  :  {f : A  B} (eqv : is-equiv f) x
   ap f (equiv→unit eqv x)  equiv→counit eqv (f x)
equiv→zig {f = f} eqv x i j = hcomp ( i   j) λ where
   k (i = i0)  f (equiv→unit eqv x j)
   k (i = i1)  equiv→counit eqv (f x) (j  ~ k)
   k (j = i0)  equiv→counit eqv (f x) (i  ~ k)
   k (j = i1)  f x
   k (k = i0)  eqv .is-eqv (f x) .paths (x , refl) j .snd i

equiv→zag
  :  {f : A  B} (eqv : is-equiv f) x
   ap (equiv→inverse eqv) (equiv→counit eqv x)
   equiv→unit eqv (equiv→inverse eqv x)
equiv→zag {f = f} eqv b =
  subst  b  ap g (ε b)  η (g b)) (ε b) (helper (g b)) where
  g = equiv→inverse eqv
  ε = equiv→counit eqv
  η = equiv→unit eqv

  helper :  a  ap g (ε (f a))  η (g (f a))
  helper a i j = hcomp ( i   j) λ where
    k (i = i0)  g (ε (f a) (j  ~ k))
    k (i = i1)  η (η a (~ k)) j
    k (j = i0)  g (equiv→zig eqv a (~ i) (~ k))
    k (j = i1)  η a (i  ~ k)
    k (k = i0)  η a (i  j)

is-equiv→is-iso : {f : A  B}  is-equiv f  is-iso f
is-iso.inv (is-equiv→is-iso eqv) = equiv→inverse eqv
```

We can get an element of `x` from the proof that `f` is an equivalence -
it's the point of `A` mapped to `y`, which we get from centre of
contraction for the fibres of `f` over `y`.

```agda
is-iso.rinv (is-equiv→is-iso eqv) y =
  eqv .is-eqv y .centre .snd
```

Similarly, that one fibre gives us a proof that the function above is a
right inverse to `f`.

```agda
is-iso.linv (is-equiv→is-iso {f = f} eqv) x i =
  eqv .is-eqv (f x) .paths (x , refl) i .fst
```

The proof that the function is a _left_ inverse comes from the fibres of
`f` over `y` being contractible. Since we have a fibre - namely, `f`
maps `x` to `f x` by `refl`{.Agda} - we can get any other we want!

# Equivalences from isomorphisms

Any isomorphism can be upgraded into an equivalence, in a way that
preserves the function $f$, its inverse $g$, _and_ the proof $s$ that
$g$ is a right inverse to $f$. We can not preserve _everything_, though,
as is usual when making something "more coherent". Furthermore, if
everything was preserved, `is-iso`{.Agda} would be a proposition, and
this is [provably not the case].

[provably not the case]: 1Lab.Counterexamples.IsIso.html

The argument presented here is done directly in cubical style, but a
less direct proof is also available, by showing that every isomorphism
is a [half-adjoint equivalence], and that half-adjoint equivalences have
contractible fibres.

[half-adjoint equivalence]: 1Lab.Equiv.HalfAdjoint.html

```agda
module _ {f : A  B} (i : is-iso f) where

  open is-iso i renaming ( inv to g
                         ; rinv to s
                         ; linv to t
                         )
```

Suppose, then, that $f : A \to B$ and $g : B \to A$, and we're given
witnesses $s : f (g\ x) = x$ and $t : g (f\ x) = x$ (named for
**s**ection and re**t**raction) that $f$ and $g$ are inverses. We want
to show that, for any $y$, the `fibre`{.Agda} of $f$ over $y$ is
contractible. It suffices to show that the fibre is propositional, and
that it is inhabited.

<!--
```agda
  _ = PathP
```
-->

We begin with showing that the fibre over $y$ is propositional, since
that's the harder of the two arguments. Suppose that we have $y$, $x_0$,
$x_1$, $p_0$ and $p_1$ as below; Note that $(x_0, p_0)$ and $(x_1, p_1)$
are fibres of $f$ over $y$. What we need to show is that we have some
$\pi : x_0 ≡ x_1$ and $p_0 ≡ p_1$ _`over`{.Agda ident=PathP}_ $\pi$.

```agda
  private module _ (y : B) (x0 x1 : A) (p0 : f x0  y) (p1 : f x1  y) where
```

As an intermediate step in proving that $p_0 ≡ p_1$, we _must_ show that
$x_0 ≡ x_1$ - without this, we can't even _state_ that $p_0$ and $p_1$
are identified, since they live in different types!  To this end, we
will build $\pi : x_0 ≡ x_1$, parts of which will be required to
assemble the overall proof that $p_0 ≡ p_1$.

We'll detail the construction of $\pi_0$; for $\pi_1$, the same method
is used. We want to construct a _line_, which we can do by exhibiting
that line as the missing face in a _square_. We have equations $g\ y ≡
g\ y$ (`refl`{.Agda}), $g\ (f\ x_0) ≡ g\ y$ (the action of `g` on `p0`),
and $g\ (f\ x_0) = x_0$ by the assumption that $g$ is a right inverse to
$f$.  Diagrammatically, these fit together into a square:

~~~{.quiver}
\[\begin{tikzcd}
  {g\ y} && {g\ (f\ x_0)} \\
  \\
  {g\ y} && {x_0}
  \arrow["{g\ y}"', from=1-1, to=3-1]
  \arrow["{t\ x_0\ k}", from=1-3, to=3-3]
  \arrow[""{name=0, anchor=center, inner sep=0}, "{g\ (p_0\ \neg i)}", from=1-1, to=1-3]
  \arrow[""{name=1, anchor=center, inner sep=0}, "{\pi_0}"', dashed, from=3-1, to=3-3]
  \arrow["{\theta_0}"{description}, Rightarrow, draw=none, from=0, to=1]
\end{tikzcd}\]
~~~

The missing line in this square is $\pi_0$. Since the _inside_ (the
`filler`{.Agda ident=hfill}) will be useful to us later, we also give it
a name: $\theta$.

```agda
    π₀ : g y  x0
    π₀ i = hcomp ( i) λ where
      k (i = i0)  g y
      k (i = i1)  t x0 k
      k (k = i0)  g (p0 (~ i))

    θ₀ : Square (ap g (sym p0)) refl (t x0) π₀
    θ₀ i j = hfill ( i) j λ where
      k (i = i0)  g y
      k (i = i1)  t x0 k
      k (k = i0)  g (p0 (~ i))
```

Since the construction of $\pi_1$ is analogous, I'll simply present the
square. We correspondingly name the missing face $\pi_1$ and the filler
$\theta_1$.

<div class=mathpar>

~~~{.quiver}
\[\begin{tikzcd}
  {g\ y} && {g\ (f\ x_1)} \\
  \\
  {g\ y} && {x_1}
  \arrow["{g\ y}"', from=1-1, to=3-1]
  \arrow["{t\ x_1\ k}", from=1-3, to=3-3]
  \arrow[""{name=0, anchor=center, inner sep=0}, "{g\ (p_1\ \neg i)}", from=1-1, to=1-3]
  \arrow[""{name=1, anchor=center, inner sep=0}, "{\pi_1}"', dashed, from=3-1, to=3-3]
  \arrow["{\theta_1}"{description}, Rightarrow, draw=none, from=0, to=1]
\end{tikzcd}\]
~~~

```agda
    π₁ : g y  x1
    π₁ i = hcomp ( i) λ where
      j (i = i0)  g y
      j (i = i1)  t x1 j
      j (j = i0)  g (p1 (~ i))

    θ₁ : Square (ap g (sym p1)) refl (t x1) π₁
    θ₁ i j = hfill ( i) j λ where
      j (i = i0)  g y
      j (i = i1)  t x1 j
      j (j = i0)  g (p1 (~ i))
```
</div>

Joining these paths by their common $g\ y$ face, we obtain $\pi : x_0 ≡
x_1$. This square _also_ has a filler, connecting $\pi_0$ and $\pi_1$
over the line $g\ y ≡ \pi\ i$.

<div class=mathpar>
~~~{.quiver}
\[\begin{tikzcd}
  {g\ y} && {g\ y} \\
  \\
  {x_0} && {x_1}
  \arrow[""{name=0, anchor=center, inner sep=0}, "{g\ y}", from=1-1, to=1-3]
  \arrow["{\pi_0}\ k"', from=1-1, to=3-1]
  \arrow[""{name=1, anchor=center, inner sep=0}, "\pi"', dashed, from=3-1, to=3-3]
  \arrow["{\pi_1}\ k", from=1-3, to=3-3]
  \arrow["\theta"{description}, Rightarrow, draw=none, from=0, to=1]
\end{tikzcd}\]
~~~

```agda
    π : x0  x1
    π i = hcomp ( i) λ where
      j (j = i0)  g y
      j (i = i0)  π₀ j
      j (i = i1)  π₁ j
```
</div>

This concludes the construction of $\pi$, and thus, the 2D part of the
proof. Now, we want to show that $p_0 ≡ p_1$ over a path induced by
$\pi$. This is a _square_ with a specific boundary, which can be built
by constructing an appropriate _open cube_, where the missing face is
that square. As an intermediate step, we define $\theta$ to be the
filler for the square above.

```agda
    θ : Square refl π₀ π₁ π
    θ i j = hfill ( i) j λ where
      k (i = i1)  π₁ k
      k (i = i0)  π₀ k
      k (k = i0)  g y
```

Observe that we can coherently alter $\theta$ to get $\iota$ below,
which expresses that $\ap g\ p_0$ and $\ap g\ p_1$ are identified.

```agda
    ι : Square (ap (g  f) π) (ap g p0) (ap g p1) refl
    ι i j = hcomp ( i   j) λ where
      k (k = i0)  θ i (~ j)
      k (i = i0)  θ₀ (~ j) (~ k)
      k (i = i1)  θ₁ (~ j) (~ k)
      k (j = i0)  t (π i) (~ k)
      k (j = i1)  g y
```

This composition can be visualised as the _red_ (front) face in the
diagram below. The back face is $\theta\ i\ (\neg\ j)$, i.e. `(θ i (~
j))` in the code. Similarly, the $j = \rm{i1}$ (bottom) face is `g y`,
the $j = \rm{i0}$ (top) face is `t (π i) (~ k)`, and similarly for $i =
\rm{i0}$ (left) and $i = \rm{i1}$ (right).

~~~{.quiver .tall-2}
\[\begin{tikzcd}
  \textcolor{rgb,255:red,214;green,92;blue,92}{g\ (f\ (g\ x_0))} &&&& \textcolor{rgb,255:red,214;green,92;blue,92}{g\ (f\ (g\ x_1))} \\
  & \textcolor{rgb,255:red,92;green,92;blue,214}{x_0} && \textcolor{rgb,255:red,92;green,92;blue,214}{x_1} \\
  \\
  & \textcolor{rgb,255:red,92;green,92;blue,214}{g\ y} && \textcolor{rgb,255:red,92;green,92;blue,214}{g\ y} \\
  \textcolor{rgb,255:red,214;green,92;blue,92}{g\ y} &&&& \textcolor{rgb,255:red,214;green,92;blue,92}{g\ y}
  \arrow[""{name=0, anchor=center, inner sep=0}, "\pi"', color={rgb,255:red,92;green,92;blue,214}, from=2-2, to=2-4]
  \arrow[""{name=1, anchor=center, inner sep=0}, "{g\ y}", color={rgb,255:red,92;green,92;blue,214}, from=4-2, to=4-4]
  \arrow[""{name=2, anchor=center, inner sep=0}, "{\pi_0}", color={rgb,255:red,92;green,92;blue,214}, from=2-2, to=4-2]
  \arrow[""{name=3, anchor=center, inner sep=0}, "{\pi_1}"', color={rgb,255:red,92;green,92;blue,214}, from=2-4, to=4-4]
  \arrow[from=4-4, to=5-5]
  \arrow[""{name=4, anchor=center, inner sep=0}, "{\ap (g \circ f)\ \pi}", color={rgb,255:red,214;green,92;blue,92}, from=1-1, to=1-5]
  \arrow[""{name=5, anchor=center, inner sep=0}, "{g\ y}"', color={rgb,255:red,214;green,92;blue,92}, from=5-1, to=5-5]
  \arrow[from=2-4, to=1-5]
  \arrow[""{name=6, anchor=center, inner sep=0}, "{\ap g\ p_1}", color={rgb,255:red,214;green,92;blue,92}, from=1-5, to=5-5]
  \arrow[""{name=7, anchor=center, inner sep=0}, "{\ap g\ p_0}"', color={rgb,255:red,214;green,92;blue,92}, from=1-1, to=5-1]
  \arrow[from=2-2, to=1-1]
  \arrow[from=4-2, to=5-1]
  \arrow["{\theta\ i\ (\neg\ j)}"{description}, color={rgb,255:red,92;green,92;blue,214}, Rightarrow, draw=none, from=1, to=0]
  \arrow["{t\ (\pi\ i)\ (\neg\ k)}"{description}, Rightarrow, draw=none, from=0, to=4]
  \arrow["{\theta_0(\neg j)(\neg k)}"{description}, Rightarrow, draw=none, from=2, to=7]
  \arrow["{g\ y}"{description}, Rightarrow, draw=none, from=1, to=5]
  \arrow["{\theta_1(\neg j)(\neg k)}"{description}, Rightarrow, draw=none, from=3, to=6]
\end{tikzcd}\]
~~~

The fact that $j$ only appears as $\neg j$ can be understood as the
diagram above being _upside-down_. Indeed, $\pi_0$ and $\pi_1$ in the
boundary of $\theta$ (the inner, blue face) are inverted when their
types are considered. We're in the home stretch: Using our assumption $s
: f (g\ x) = x$, we can cancel all of the $f \circ g$s in the diagram
above to get what we wanted: $p_0 ≡ p_1$.

```agda
    sq1 : Square (ap f π) p0 p1 refl
    sq1 i j = hcomp ( i   j) λ where
       k (i = i0)  s (p0 j) k
       k (i = i1)  s (p1 j) k
       k (j = i0)  s (f (π i)) k
       k (j = i1)  s y k
       k (k = i0)  f (ι i j)
```

The composition above can be visualised as the front (red) face in the
cubical diagram below. Once more, left is $i = \rm{i0}$, right is $i =
\rm{i1}$, up is $j = \rm{i0}$, and down is $j = \rm{i1}$.

~~~{.quiver .tall-2}
\small
\[\begin{tikzcd}
  \textcolor{rgb,255:red,214;green,92;blue,92}{f(x_0)} &&&& \textcolor{rgb,255:red,214;green,92;blue,92}{f(x_1)} \\
  & \textcolor{rgb,255:red,92;green,92;blue,214}{f(g(f(g\ x_0)))} && \textcolor{rgb,255:red,92;green,92;blue,214}{f(g(f(g\ x_1)))} \\
  && \textcolor{rgb,255:red,92;green,92;blue,214}{f\ (\iota\ i\ j)} \\
  & \textcolor{rgb,255:red,92;green,92;blue,214}{f\ (g\ y)} && \textcolor{rgb,255:red,92;green,92;blue,214}{f\ (g\ y)} \\
  \textcolor{rgb,255:red,214;green,92;blue,92}{y} &&&& \textcolor{rgb,255:red,214;green,92;blue,92}{y}
  \arrow[""{name=0, anchor=center, inner sep=0}, "{p_1}", from=1-5, to=5-5]
  \arrow[""{name=1, anchor=center, inner sep=0}, "{\ap f\ \pi}", color={rgb,255:red,214;green,92;blue,92}, from=1-1, to=1-5]
  \arrow[""{name=2, anchor=center, inner sep=0}, "{p_0}"', color={rgb,255:red,214;green,92;blue,92}, from=1-1, to=5-1]
  \arrow[""{name=3, anchor=center, inner sep=0}, "y"', color={rgb,255:red,214;green,92;blue,92}, from=5-1, to=5-5]
  \arrow[""{name=4, anchor=center, inner sep=0}, "{\ap (f \circ g\circ f)\ \pi}"', color={rgb,255:red,92;green,92;blue,214}, from=2-2, to=2-4]
  \arrow[""{name=5, anchor=center, inner sep=0}, "{f(g\ y)}", color={rgb,255:red,92;green,92;blue,214}, from=4-2, to=4-4]
  \arrow[""{name=6, anchor=center, inner sep=0}, "{\ap f\ p_1}"', color={rgb,255:red,92;green,92;blue,214}, from=2-4, to=4-4]
  \arrow[""{name=7, anchor=center, inner sep=0}, "{\ap f\ p_0}", color={rgb,255:red,92;green,92;blue,214}, from=2-2, to=4-2]
  \arrow[from=2-2, to=1-1]
  \arrow[from=2-4, to=1-5]
  \arrow[from=4-4, to=5-5]
  \arrow[from=4-2, to=5-1]
  \arrow["{s\ y\ k}"{description}, Rightarrow, draw=none, from=5, to=3]
  \arrow["{s\ (f\ (\pi\ i))\ k}"{description}, Rightarrow, draw=none, from=4, to=1]
  \arrow["{s\ (p1\ j)\ k}"{description}, Rightarrow, draw=none, from=6, to=0]
  \arrow["{s\ (p0\ j)\ k}"{description}, Rightarrow, draw=none, from=7, to=2]
\end{tikzcd}\]
~~~

Putting all of this together, we get that $(x_0, p_0) ≡ (x_1, p_1)$.
Since there were no assumptions on any of the variables under
consideration, this indeed says that the fibre over $y$ is a proposition
for any choice of $y$.

```agda
    is-iso→fibre-is-prop : (x0 , p0)  (x1 , p1)
    is-iso→fibre-is-prop i .fst = π i
    is-iso→fibre-is-prop i .snd = sq1 i
```

Since the fibre over $y$ is inhabited by $(g\ y, s\ y)$, we get that any
isomorphism has contractible fibres:

```agda
  is-iso→is-equiv : is-equiv f
  is-iso→is-equiv .is-eqv y .centre .fst = g y
  is-iso→is-equiv .is-eqv y .centre .snd = s y
  is-iso→is-equiv .is-eqv y .paths z =
    is-iso→fibre-is-prop y (g y) (fst z) (s y) (snd z)
```

Applying this to the `Iso`{.Agda} and `_≃_`{.Agda} pairs, we can turn
any isomorphism into a coherent equivalence.

```agda
Iso→Equiv :  {ℓ₁ ℓ₂} {A : Type ℓ₁} {B : Type ℓ₂}
           Iso A B
           A  B
Iso→Equiv (f , is-iso) = f , is-iso→is-equiv is-iso
```

A helpful lemma: Any function between contractible types is an equivalence:

```agda
is-contr→is-equiv :  {ℓ₁ ℓ₂} {A : Type ℓ₁} {B : Type ℓ₂}
                   is-contr A  is-contr B  {f : A  B}
                   is-equiv f
is-contr→is-equiv cA cB = is-iso→is-equiv f-is-iso where
  f-is-iso : is-iso _
  is-iso.inv f-is-iso _ = cA .centre
  is-iso.rinv f-is-iso _ = is-contr→is-prop cB _ _
  is-iso.linv f-is-iso _ = is-contr→is-prop cA _ _

is-contr→≃ :  {ℓ₁ ℓ₂} {A : Type ℓ₁} {B : Type ℓ₂}
            is-contr A  is-contr B  A  B
is-contr→≃ cA cB =  _  cB .centre) , is-iso→is-equiv f-is-iso where
  f-is-iso : is-iso _
  is-iso.inv f-is-iso _ = cA .centre
  is-iso.rinv f-is-iso _ = is-contr→is-prop cB _ _
  is-iso.linv f-is-iso _ = is-contr→is-prop cA _ _

is-contr→≃⊤ :  {} {A : Type }  is-contr A  A  
is-contr→≃⊤ c = is-contr→≃ c ⊤-is-contr
```

...and so is any function into an empty type:

```agda
¬-is-equiv :  {} {A : Type } (f : A  )  is-equiv f
¬-is-equiv f .is-eqv ()

is-empty→≃⊥ :  {} {A : Type }  ¬ A  A  
is-empty→≃⊥ ¬a = _ , ¬-is-equiv ¬a
```

Any involution is an equivalence:

```agda
is-involutive→is-equiv :  {} {A : Type } {f : A  A}  (∀ a  f (f a)  a)  is-equiv f
is-involutive→is-equiv inv = is-iso→is-equiv (iso _ inv inv)
```

# Closure properties

We can prove a **2-out-of-3** property for equivalences: given two functions
$f : A \to B$, $g : B \to C$, if any two of $f$, $g$ and $g \circ f$ is an
equivalence, then so is the third.

```agda
module _
  { ℓ₁ ℓ₂} {A : Type } {B : Type ℓ₁} {C : Type ℓ₂}
  {f : A  B} {g : B  C}
  where

  ∙-is-equiv
    : is-equiv f  is-equiv g
     is-equiv  x  g (f x))

  equiv-cancell
    : is-equiv g  is-equiv  x  g (f x))
     is-equiv f

  equiv-cancelr
    : is-equiv f  is-equiv  x  g (f x))
     is-equiv g
```

<!--
```agda
  ∙-is-equiv ef eg = is-iso→is-equiv (iso inv right left) where
    inv : C  A
    inv x = equiv→inverse ef (equiv→inverse eg x)
    opaque
      right : is-right-inverse inv  x  g (f x))
      right x = ap g (equiv→counit ef _)  equiv→counit eg x
      left : is-left-inverse inv  x  g (f x))
      left x = ap (equiv→inverse ef) (equiv→unit eg _)  equiv→unit ef x

  equiv-cancell eg egf = is-iso→is-equiv (iso inv right left) where
    inv : B  A
    inv x = equiv→inverse egf (g x)
    opaque
      right : is-right-inverse inv f
      right x =
        f (equiv→inverse egf (g x))                        ≡˘⟨ equiv→unit eg _ 
        equiv→inverse eg (g (f (equiv→inverse egf (g x)))) ≡⟨ ap (equiv→inverse eg) (equiv→counit egf _) 
        equiv→inverse eg (g x)                             ≡⟨ equiv→unit eg _ 
        x                                                  
      left : is-left-inverse inv f
      left x = equiv→unit egf x

  equiv-cancelr ef egf = is-iso→is-equiv (iso inv right left) where
    inv : C  B
    inv x = f (equiv→inverse egf x)
    right : is-right-inverse inv g
    right x = equiv→counit egf x
    left : is-left-inverse inv g
    left x =
      f (equiv→inverse egf (g x))                        ≡˘⟨ ap (f  equiv→inverse egf  g) (equiv→counit ef _) 
      f (equiv→inverse egf (g (f (equiv→inverse ef x)))) ≡⟨ ap f (equiv→unit egf _) 
      f (equiv→inverse ef x)                             ≡⟨ equiv→counit ef _ 
      x                                                  
```
-->

In particular, any left or right inverse of an equivalence is an equivalence:

```agda
left-inverse→equiv
  :  { ℓ'} {A : Type } {B : Type ℓ'} {f : A  B} {g : B  A}
   is-left-inverse g f
   is-equiv f  is-equiv g
left-inverse→equiv linv ef = equiv-cancelr ef
  (subst is-equiv (sym (funext linv)) id-equiv)

right-inverse→equiv
  :  { ℓ'} {A : Type } {B : Type ℓ'} {f : A  B} {g : B  A}
   is-right-inverse g f
   is-equiv f  is-equiv g
right-inverse→equiv rinv ef = equiv-cancell ef
  (subst is-equiv (sym (funext rinv)) id-equiv)
```

# Equivalence reasoning

To make composing equivalences more intuitive, we implement operators to
do equivalence reasoning in the same style as equational reasoning.

```agda
id≃ :  {} {A : Type }  A  A
id≃ = id , id-equiv

_∙e_ :  { ℓ₁ ℓ₂} {A : Type } {B : Type ℓ₁} {C : Type ℓ₂}
      A  B  B  C  A  C
_∙e_ (f , ef) (g , eg) =  x  g (f x)) , ∙-is-equiv ef eg

_e⁻¹ :  { ℓ₁} {A : Type } {B : Type ℓ₁}
      A  B  B  A
_e⁻¹ eqv = Iso→Equiv ( equiv→inverse (eqv .snd)
                     , record { inv  = eqv .fst
                              ; rinv = equiv→unit (eqv .snd)
                              ; linv = equiv→counit (eqv .snd)
                              })
```

<!--
```agda
infixr 30 _∙e_
infix 31 _e⁻¹

module Equiv { ℓ'} {A : Type } {B : Type ℓ'} (f : A  B) where
  to = f .fst
  from = equiv→inverse (f .snd)
  η = equiv→unit (f .snd)
  ε = equiv→counit (f .snd)
  zig = equiv→zig (f .snd)
  zag = equiv→zag (f .snd)

  injective :  {x y}  to x  to y  x  y
  injective p = ap fst $ is-contr→is-prop (f .snd .is-eqv _) (_ , refl) (_ , sym p)

  injective₂ :  {x y z}  to x  z  to y  z  x  y
  injective₂ p q = ap fst $ is-contr→is-prop (f .snd .is-eqv _)
    (_ , p) (_ , q)

  inverse : B  A
  inverse = f e⁻¹

  adjunctl :  {x y}  to x  y  x  from y
  adjunctl p = sym (η _)  ap from p

  adjunctr :  {x y}  x  from y  to x  y
  adjunctr p = ap to p  ε _

  adjunct :  {x y}  (to x  y)  (x  from y)
  adjunct {x} {y} = Iso→Equiv (adjunctl , iso adjunctr
     p  J  _ p  sym (η _)  ap from (ap to (sym p)  ε _)  sym p)
      (sym (∙-swapl (∙-idr _  sym (zag _)  sym (∙-idl _)  sym (ap-∙ from _ _))))
      (sym p))
    (J  _ p  ap to (sym (η _)  ap from p)  ε _  p)
      (sym (∙-swapr (∙-idl _  ap sym (sym (zig _))  sym (∙-idr _)  sym (ap-∙ to _ _))))))
```
-->

The proofs that equivalences are closed under composition assemble
nicely into transitivity operators resembling equational reasoning:

```agda
≃⟨⟩-syntax :  { ℓ₁ ℓ₂} (A : Type ) {B : Type ℓ₁} {C : Type ℓ₂}
            B  C  A  B  A  C
≃⟨⟩-syntax A g f = f ∙e g

infixr 2 ≃⟨⟩-syntax
syntax ≃⟨⟩-syntax x q p = x ≃⟨ p  q

_≃˘⟨_⟩_ :  { ℓ₁ ℓ₂} (A : Type ) {B : Type ℓ₁} {C : Type ℓ₂}
         B  A  B  C  A  C
A ≃˘⟨ f  g = f e⁻¹ ∙e g

_≃⟨⟩_ :  { ℓ₁} (A : Type ) {B : Type ℓ₁}  A  B  A  B
x ≃⟨⟩ x≡y = x≡y

_≃∎ :  {} (A : Type )  A  A
x ≃∎ = id≃

infixr 2 _≃⟨⟩_ _≃˘⟨_⟩_
infix  3 _≃∎
```

# Propositional extensionality (redux)

The following observation is not very complex, but it is incredibly
useful: Equivalence of propositions is the same as biimplication.

```agda
module
  _ { ℓ'} {P : Type } {Q : Type ℓ'}
    (pprop : is-prop P) (qprop : is-prop Q)
  where

  biimp-is-equiv : (f : P  Q)  (Q  P)  is-equiv f
  biimp-is-equiv f g .is-eqv y .centre .fst = g y
  biimp-is-equiv f g .is-eqv y .centre .snd = qprop (f (g y)) y
  biimp-is-equiv f g .is-eqv y .paths (p' , path) = Σ-pathp
    (pprop (g y) p')
    (is-prop→squarep  _ _  qprop) _ _ _ _)

  prop-ext : (P  Q)  (Q  P)  P  Q
  prop-ext p→q q→p .fst = p→q
  prop-ext p→q q→p .snd = biimp-is-equiv p→q q→p
```

<!--
```agda
sym-equiv :  {} {A : Type } {x y : A}  (x  y)  (y  x)
sym-equiv = sym , is-iso→is-equiv (iso sym  _  refl)  _  refl))

∙-pre-equiv :  {} {A : Type } {x y z : A}  x  y  (y  z)  (x  z)
∙-pre-equiv p = Iso→Equiv $ (p ∙_) , iso (sym p ∙_)
   q  ∙-assoc p _ _ ·· ap (_∙ q) (∙-invr p) ·· ∙-idl q)
   q  ∙-assoc (sym p) _ _ ·· ap (_∙ q) (∙-invl p) ·· ∙-idl q)

∙-post-equiv :  {} {A : Type } {x y z : A}  y  z  (x  y)  (x  z)
∙-post-equiv p = Iso→Equiv $ (_∙ p) , iso (_∙ sym p)
   q  sym (∙-assoc q _ _) ·· ap (q ∙_) (∙-invl p) ·· ∙-idr q)
   q  sym (∙-assoc q _ _) ·· ap (q ∙_) (∙-invr p) ·· ∙-idr q)

lift-inj
  :  { ℓ'} {A : Type } {a b : A }
   lift { = ℓ'} a  lift { = ℓ'} b
   a  b
lift-inj p = ap Lift.lower p

Lift-≃ :  {a } {A : Type a}  Lift  A  A
Lift-≃ .fst (lift a) = a
Lift-≃ .snd .is-eqv a .centre = lift a , refl
Lift-≃ .snd .is-eqv a .paths f = Σ-pathp (ap lift (sym (f .snd))) λ i j  f .snd (~ i  j)

Lift-ap
  :  {a b  ℓ'} {A : Type a} {B : Type b}
   A  B
   Lift  A  Lift ℓ' B
Lift-ap eqv = Lift-≃ ∙e eqv ∙e (Lift-≃ e⁻¹)
```
-->