module Cat.Instances.Graphs where
private variable
: Level o o' ℓ ℓ'
The category of graphs🔗
A graph (really, an 1) is given by a set of vertices and, for each pair of elements a set of edges from to That’s it: a set and a family of sets over
record Graph (o ℓ : Level) : Type (lsuc o ⊔ lsuc ℓ) where
no-eta-equality
field
: Type o
Vertex : Vertex → Vertex → Type ℓ
Edge : is-set Vertex
Vertex-is-set : ∀ {x y} → is-set (Edge x y) Edge-is-set
open Graph
open hlevel-projection
instance
: Underlying (Graph o ℓ)
Underlying-Graph = record { ⌞_⌟ = Graph.Vertex }
Underlying-Graph
: hlevel-projection (quote Graph.Vertex)
hlevel-proj-vertex .has-level = quote Graph.Vertex-is-set
hlevel-proj-vertex .get-level _ = pure (quoteTerm (suc (suc zero)))
hlevel-proj-vertex .get-argument (_ ∷ _ ∷ c v∷ _) = pure c
hlevel-proj-vertex {-# CATCHALL #-}
.get-argument _ = typeError []
hlevel-proj-vertex
: hlevel-projection (quote Graph.Edge)
hlevel-proj-edge .has-level = quote Graph.Edge-is-set
hlevel-proj-edge .get-level _ = pure (quoteTerm (suc (suc zero)))
hlevel-proj-edge .get-argument (_ ∷ _ ∷ c v∷ _) = pure c
hlevel-proj-edge {-# CATCHALL #-}
.get-argument _ = typeError [] hlevel-proj-edge
A graph homomorphism consists of a mapping of vertices along with a mapping of edges
record Graph-hom (G : Graph o ℓ) (H : Graph o' ℓ') : Type (o ⊔ o' ⊔ ℓ ⊔ ℓ') where
no-eta-equality
field
: ⌞ G ⌟ → ⌞ H ⌟
vertex : ∀ {x y} → G .Edge x y → H .Edge (vertex x) (vertex y) edge
private variable
: Graph o ℓ
G H K
open Graph-hom
unquoteDecl H-Level-Graph-hom = declare-record-hlevel 2 H-Level-Graph-hom (quote Graph-hom)
Graph-hom-pathp: {G : I → Graph o ℓ} {H : I → Graph o' ℓ'}
→ {f : Graph-hom (G i0) (H i0)} {g : Graph-hom (G i1) (H i1)}
→ (p0 : ∀ (x : ∀ i → G i .Vertex)
→ PathP (λ i → H i .Vertex)
(f .vertex (x i0)) (g .vertex (x i1)))
→ (p1 : ∀ {x y : ∀ i → G i .Vertex}
→ (e : ∀ i → G i .Edge (x i) (y i))
→ PathP (λ i → H i .Edge (p0 x i) (p0 y i))
(f .edge (e i0)) (g .edge (e i1)))
→ PathP (λ i → Graph-hom (G i) (H i)) f g
{G = G} {H = H} {f = f} {g = g} p0 p1 = pathp where
Graph-hom-pathp : I → Type _
vertex* = (G i) .Vertex
vertex* i
: (i : I) → vertex* i → vertex* i → Type _
edge* = (G i) .Edge x y
edge* i x y
: PathP (λ i → Graph-hom (G i) (H i)) f g
pathp .vertex x = p0 (λ j → coe vertex* i j x) i
pathp i .edge {x} {y} e =
pathp i {x = λ j → coe vertex* i j x} {y = λ j → coe vertex* i j y}
p1 (λ j → coe (λ j → edge* j (coe vertex* i j x) (coe vertex* i j y)) i j (e* j)) i
where
: (j : I) → vertex* i
x* y* = coei→i vertex* i x (~ j ∨ i)
x* j = coei→i vertex* i y (~ j ∨ i)
y* j
: (j : I) → edge* i (coe vertex* i i x) (coe vertex* i i y)
e* =
e* j (λ j → edge* i (x* j) (y* j)) ((~ i ∧ ~ j) ∨ (i ∧ j)) λ where
comp (k = i0) → e
k (i = i0) (j = i0) → e
k (i = i1) (j = i1) → e
k
Graph-hom-path: {f g : Graph-hom G H}
→ (p0 : ∀ x → f .vertex x ≡ g .vertex x)
→ (p1 : ∀ {x y} → (e : Graph.Edge G x y) → PathP (λ i → Graph.Edge H (p0 x i) (p0 y i)) (f .edge e) (g .edge e))
→ f ≡ g
{G = G} {H = H} p0 p1 =
Graph-hom-path {G = λ _ → G} {H = λ _ → H}
Graph-hom-pathp (λ x i → p0 (x i) i)
(λ e i → p1 (e i) i)
instance
: Funlike (Graph-hom G H) ⌞ G ⌟ λ _ → ⌞ H ⌟
Funlike-Graph-hom .Funlike._·_ = vertex
Funlike-Graph-hom
: {G : Graph o ℓ} → Graph-hom G G
Graph-hom-id .vertex v = v
Graph-hom-id .edge e = e Graph-hom-id
Graphs and graph homomorphisms can be organized into a category
: ∀ o ℓ → Precategory (lsuc (o ⊔ ℓ)) (o ⊔ ℓ)
Graphs .Precategory.Ob = Graph o ℓ
Graphs o ℓ .Precategory.Hom = Graph-hom
Graphs o ℓ .Precategory.Hom-set _ _ = hlevel 2
Graphs o ℓ .Precategory.id = Graph-hom-id
Graphs o ℓ .Precategory._∘_ f g .vertex v = f .vertex (g .vertex v)
Graphs o ℓ .Precategory._∘_ f g .edge e = f .edge (g .edge e)
Graphs o ℓ .Precategory.idr _ = Graph-hom-path (λ _ → refl) (λ _ → refl)
Graphs o ℓ .Precategory.idl _ = Graph-hom-path (λ _ → refl) (λ _ → refl)
Graphs o ℓ .Precategory.assoc _ _ _ = Graph-hom-path (λ _ → refl) (λ _ → refl) Graphs o ℓ
open Functor
open _=>_
module _ {o ℓ : Level} where
module Graphs = Cat.Reasoning (Graphs o ℓ)
: {x y : Graph o ℓ} (h : Graphs.Hom x y) → Graphs.is-invertible h → ∀ {x y} → is-equiv (h .edge {x} {y})
graph-iso-is-ff {x} {y} h inv {s} {t} = is-iso→is-equiv (iso from ir il) where
graph-iso-is-ff module h = Graphs.is-invertible inv
: ∀ {s t} → y .Edge (h · s) (h · t) → x .Edge s t
from = subst₂ (x .Edge) (ap vertex h.invr · _) (ap vertex h.invr · _) (h.inv .edge e)
from e
: is-right-inverse from (h .edge)
ir =
ir e let
= J₂
lemma (λ s'' t'' p q → ∀ e
→ h .edge (subst₂ (x .Edge) p q e)
(y .Edge) (ap· h p) (ap· h q) (h .edge e))
≡ subst₂ (λ e → ap (h .edge) (transport-refl _) ∙ sym (transport-refl _))
in lemma _ _ (h.inv .edge e)
(λ p q → subst₂ (y .Edge) {b' = h .vertex t} p q (h .edge (h.inv .edge e))) prop! prop!
∙∙ ap₂ (λ i → h.invl i .edge e)
∙∙ from-pathp
: is-left-inverse from (h .edge)
il = from-pathp λ i → h.invr i .edge e
il e
Graph-path: ∀ {x y : Graph o ℓ}
→ (p : x .Vertex ≡ y .Vertex)
→ (PathP (λ i → p i → p i → Type ℓ) (x .Edge) (y .Edge))
→ x ≡ y
{x = x} {y} p q i .Vertex = p i
Graph-path {x = x} {y} p q i .Edge = q i
Graph-path {x = x} {y} p q i .Vertex-is-set = is-prop→pathp
Graph-path (λ i → is-hlevel-is-prop {A = p i} 2) (x .Vertex-is-set) (y .Vertex-is-set) i
{x = x} {y} p q i .Edge-is-set {s} {t} =
Graph-path
is-prop→pathp(λ i → Π-is-hlevel 1 λ x → Π-is-hlevel 1 λ y → is-hlevel-is-prop {A = q i x y} 2)
(λ a b → x .Edge-is-set {a} {b}) (λ a b → y .Edge-is-set {a} {b}) i s t
: ∀ {x y : Graph o ℓ} (h : x Graphs.≅ y) → x ≡ y
graph-path {x = x} {y = y} h = Graph-path (ua v) (λ i → E i ) module graph-path where
graph-path module h = Graphs._≅_ h
: ⌞ x ⌟ ≃ ⌞ y ⌟
v = record
v { fst = h.to .vertex
; snd = is-iso→is-equiv (iso (h.from .vertex) (happly (ap vertex h.invl)) (happly (ap vertex h.invr)))
}
: (i : I) → ua v i → ua v i → Type ℓ
E = Glue (y .Edge (unglue s) (unglue t)) (λ where
E i s t (i = i0) → x .Edge s t , _ , graph-iso-is-ff h.to (Graphs.iso→invertible h)
(i = i1) → y .Edge s t , _ , id-equiv)
In particular, is a univalent category.
: is-category (Graphs o ℓ)
Graphs-is-category .to-path = graph-path
Graphs-is-category .to-path-over {a} {b} p = Graphs.≅-pathp _ _ $ Graph-hom-pathp pv pe where
Graphs-is-category open graph-path p
: (h : I → a .Vertex) → PathP (λ i → ua v i) (h i0) (h.to .vertex (h i1))
pv = ua-glue v i (λ { (i = i0) → h i }) (inS (h.to .vertex (h i)))
pv h i
: {x y : I → a .Vertex} (e : ∀ i → a .Edge (x i) (y i))
pe → PathP (λ i → graph-path p i .Edge (pv x i) (pv y i)) (e i0) (h.to .edge (e i1))
{x} {y} e i = attach (∂ i) (λ { (i = i0) → _ ; (i = i1) → _ }) (inS (h.to .edge (e i))) pe
Graphs as presheaves🔗
A graph may equivalently be seen as a diagram
of sets.
That is, a graph 2 is the same as functor from the walking parallel arrows category to Furthermore, presheaves and functors to are equivalent as this category is self-dual.
: Functor (Graphs o ℓ) (PSh (o ⊔ ℓ) ·⇇·)
graph→presheaf .F₀ G =
graph→presheaf {a = el! $ Σ[ s ∈ G .Vertex ] Σ[ t ∈ G .Vertex ] G .Edge s t }
Fork {el! $ Lift ℓ ⌞ G ⌟}
(lift ⊙ fst)
(lift ⊙ fst ⊙ snd)
.F₁ f =
graph→presheaf {u = λ (s , t , e) → f .vertex s , f .vertex t , f .edge e }
Fork-nt {v = λ { (lift v) → lift (f · v) } } refl refl
.F-id = Nat-path λ { true → refl ; false → refl }
graph→presheaf .F-∘ G H = Nat-path λ { true → refl ; false → refl }
graph→presheaf
: is-fully-faithful graph→presheaf
g→p-is-ff {x = x} {y = y} = is-iso→is-equiv (iso from ir il) where
g→p-is-ff : graph→presheaf · x => graph→presheaf · y → Graph-hom x y
from .vertex v = h .η true (lift v) .lower
from h .edge e =
from h let
(s' , t' , e') = h .η false (_ , _ , e)
= ap lower (sym (h .is-natural false true false $ₚ (_ , _ , e)))
ps = ap lower (sym (h .is-natural false true true $ₚ (_ , _ , e)))
pt in subst₂ (y .Edge) ps pt e'
: is-right-inverse from (graph→presheaf .F₁)
ir = ext λ where
ir h → refl
true x (s , t , e) →
false let
= ap lower (h .is-natural false true false $ₚ (s , t , e))
ps = ap lower (h .is-natural false true true $ₚ (s , t , e))
pt = h .η false (_ , _ , e)
s' , t' , e' in Σ-pathp ps (Σ-pathp pt λ i → coe1→i (λ j → y .Edge (ps j) (pt j)) i e')
: is-left-inverse from (graph→presheaf .F₁)
il = Graph-hom-path (λ _ → refl) (λ e → transport-refl _)
il h
private module _ {ℓ : Level} where
: ⌞ PSh ℓ ·⇇· ⌟ → Graph ℓ ℓ
presheaf→graph = g where
presheaf→graph F module F = Functor F
: Graph ℓ ℓ
g .Vertex = ⌞ F · true ⌟
g .Edge s d = Σ[ e ∈ ∣ F.₀ false ∣ ] F.₁ false e ≡ s × F.₁ true e ≡ d
g .Vertex-is-set = hlevel 2
g .Edge-is-set = hlevel 2
g
open is-precat-iso
open is-iso
: is-precat-iso (graph→presheaf {ℓ} {ℓ})
g→p-is-iso .has-is-ff = g→p-is-ff
g→p-is-iso .has-is-iso = is-iso→is-equiv F₀-iso where
g→p-is-iso : is-iso (graph→presheaf .F₀)
F₀-iso .inv = presheaf→graph
F₀-iso .rinv F = Functor-path
F₀-iso (λ { false → n-ua (Iso→Equiv (
(λ (_ , _ , x , _ , _) → x) , iso
(λ s → _ , _ , s , refl , refl)
(λ _ → refl)
(λ (_ , _ , s , p , q) i → p i , q i , s
(λ j → p (i ∧ j)) , (λ j → q (i ∧ j)))))
, ; true → n-ua (lower
(is-iso→is-equiv (iso lift (λ _ → refl) (λ _ → refl))))
, })
λ { {false} {false} e → ua→ λ _ → path→ua-pathp _ (sym (F .F-id {false} · _))
; {false} {true} false → ua→ λ (_ , _ , s , p , q) → path→ua-pathp _ (sym p)
; {false} {true} true → ua→ λ (_ , _ , s , p , q) → path→ua-pathp _ (sym q)
; {true} {true} e → ua→ λ _ → path→ua-pathp _ (sym (F .F-id {true} · _)) }
.linv G = let
F₀-iso : Lift ℓ ⌞ G ⌟ ≃ ⌞ G ⌟
eqv = Lift-≃
eqv
= Σ[ s ∈ G ] Σ[ t ∈ G ] G .Edge s t
ΣE
: Lift ℓ ⌞ G ⌟ → Lift ℓ ⌞ G ⌟ → Type _
E' = Σ[ (s , t , e) ∈ ΣE ] (lift s ≡ x × lift t ≡ y)
E' x y
: (u v : ⌞ G ⌟) → E' (lift u) (lift v) → G .Edge u v
from ((u' , v' , e) , p , q) = subst₂ (G .Edge) (ap lower p) (ap lower q) e
from u v
: (u v : ⌞ G ⌟) → is-iso (from u v)
frome = iso (λ e → ((_ , _ , e) , refl , refl)) (λ x → transport-refl _)
frome u v (λ ((u' , v' , e) , p , q) i →
( p (~ i) .lower , q (~ i) .lower
(λ i → G .Edge (p i .lower) (q i .lower)) (~ i) e )
, coe0→i (λ j → p (~ i ∨ j))
, (λ j → q (~ i ∨ j)))
, in Graph-path (ua eqv) λ i x y → Glue (G .Edge (ua-unglue eqv i x)
(ua-unglue eqv i y)) λ where
(i = i0) → E' x y , from (x .lower) (y .lower) , is-iso→is-equiv (frome _ _)
(i = i1) → G .Edge x y , _ , id-equiv
Thus, are presheaves and are thereby a topos.
: Equivalence (Graphs ℓ ℓ) (PSh ℓ ·⇇·)
graphs-are-presheaves = eqv where
graphs-are-presheaves open Equivalence
: Equivalence (Graphs ℓ ℓ) (PSh ℓ ·⇇·)
eqv .To = graph→presheaf
eqv .To-equiv = is-precat-iso→is-equivalence g→p-is-iso eqv
The underlying graph of a strict category🔗
Note that every strict category has an underlying graph, where the vertices are given by objects, and edges by morphisms. Moreover, functors between strict categories give rise to graph homomorphisms between underlying graphs. This gives rise to a functor from the category of strict categories to the category of graphs.
: Functor (Strict-cats o ℓ) (Graphs o ℓ)
Strict-cats↪Graphs .F₀ (C , C-strict) .Vertex = Precategory.Ob C
Strict-cats↪Graphs .F₀ (C , C-strict) .Edge = Precategory.Hom C
Strict-cats↪Graphs .F₀ (C , C-strict) .Vertex-is-set = C-strict
Strict-cats↪Graphs .F₀ (C , C-strict) .Edge-is-set = Precategory.Hom-set C _ _
Strict-cats↪Graphs .F₁ F .vertex = F .F₀
Strict-cats↪Graphs .F₁ F .edge = F .F₁
Strict-cats↪Graphs .F-id = Graph-hom-path (λ _ → refl) (λ _ → refl)
Strict-cats↪Graphs .F-∘ F G = Graph-hom-path (λ _ → refl) (λ _ → refl) Strict-cats↪Graphs
The underlying graph functor is faithful, as functors are graph homomorphisms with extra properties.
: is-faithful (Strict-cats↪Graphs {o} {ℓ})
Strict-cats↪Graphs-faithful =
Strict-cats↪Graphs-faithful p
Functor-path(λ x i → p i .vertex x)
(λ e i → p i .edge e)