open import Algebra.Group.Ab.Hom
open import Algebra.Group.Ab
open import Algebra.Group

open import Cat.Displayed.Univalence.Thin
open import Cat.Instances.Product
open import Cat.Displayed.Total
open import Cat.Prelude

import Cat.Functor.Bifunctor as Bifunctor

module Algebra.Group.Ab.Tensor where


# Bilinear mapsð

private variable
â â' â'' : Level


A function where all types involved are equipped with abelian group structures, is called bilinear when it satisfies and it is a group homomorphism in each of its arguments.

record Bilinear (A : Abelian-group â) (B : Abelian-group â') (C : Abelian-group â'') : Type (â â â' â â'') where
private
module A = Abelian-group-on (A .snd)
module B = Abelian-group-on (B .snd)
module C = Abelian-group-on (C .snd)

field
map     : â A â â â B â â â C â
pres-*l : â x y z â map (x A.* y) z â¡ map x z C.* map y z
pres-*r : â x y z â map x (y B.* z) â¡ map x y C.* map x z

  fixl-is-group-hom : â a â
is-group-hom B.AbelianâGroup-on C.AbelianâGroup-on (map a)
fixl-is-group-hom a .is-group-hom.pres-â x y = pres-*r a x y

fixr-is-group-hom : â b â
is-group-hom A.AbelianâGroup-on C.AbelianâGroup-on (Î» a â map a b)
fixr-is-group-hom b .is-group-hom.pres-â x y = pres-*l x y b

module fixl {a} = is-group-hom (fixl-is-group-hom a)
module fixr {a} = is-group-hom (fixr-is-group-hom a)

open fixl
renaming ( pres-id   to pres-idr
; pres-inv  to pres-invr
; pres-diff to pres-diffr
)
hiding ( pres-â )
public
open fixr
renaming ( pres-id   to pres-idl
; pres-inv  to pres-invl
; pres-diff to pres-diffl
)
hiding ( pres-â )
public

module _ {A : Abelian-group â} {B : Abelian-group â'} {C : Abelian-group â''} where
private
module A = Abelian-group-on (A .snd)
module B = Abelian-group-on (B .snd)
module C = Abelian-group-on (C .snd)

Bilinear-path
: {ba bb : Bilinear A B C}
â (â x y â Bilinear.map ba x y â¡ Bilinear.map bb x y)
â ba â¡ bb
Bilinear-path {ba = ba} {bb} p = q where
open Bilinear

q : ba â¡ bb
q i .map x y = p x y i
q i .pres-*l x y z = is-propâpathp (Î» i â C.has-is-set (p (x A.* y) z i) (p x z i C.* p y z i))
(ba .pres-*l x y z) (bb .pres-*l x y z) i
q i .pres-*r x y z = is-propâpathp (Î» i â C.has-is-set (p x (y B.* z) i) (p x y i C.* p x z i))
(ba .pres-*r x y z) (bb .pres-*r x y z) i

instance
Extensional-bilinear
: â {âr} âŠ ef : Extensional (â A â â â B â â â C â) âr âŠ â Extensional (Bilinear A B C) âr
Extensional-bilinear âŠ ef âŠ = injectionâextensional! (Î» h â Bilinear-path (Î» x y â h # x # y)) ef

module _ {â} (A B C : Abelian-group â) where


We have already noted that the set of group homomorphisms between a pair of abelian groups is an abelian group, under pointwise multiplication. The type of bilinear maps is equivalent to the type of group homomorphisms

  curry-bilinear : Bilinear A B C â Ab.Hom A Ab[ B , C ]
curry-bilinear f .hom a .hom       = f .Bilinear.map a
curry-bilinear f .hom a .preserves = Bilinear.fixl-is-group-hom f a
curry-bilinear f .preserves .is-group-hom.pres-â x y = ext Î» z â
f .Bilinear.pres-*l _ _ _

curry-bilinear-is-equiv : is-equiv curry-bilinear
curry-bilinear-is-equiv = is-isoâis-equiv morp where
morp : is-iso curry-bilinear
morp .is-iso.inv uc .Bilinear.map x y = uc # x # y
morp .is-iso.inv uc .Bilinear.pres-*l x y z = ap (_# _) (uc .preserves .is-group-hom.pres-â _ _)
morp .is-iso.inv uc .Bilinear.pres-*r x y z = (uc # _) .preserves .is-group-hom.pres-â _ _
morp .is-iso.rinv uc = trivial!
morp .is-iso.linv uc = trivial!


## The tensor productð

Thinking about the currying isomorphism we set out to search for an abelian group which lets us âassociateâ Bilinear in the other direction: Bilinear maps are equivalent to group homomorphisms but is there a construction ââ, playing the role of product type, such that is also the type of bilinear maps

module _ {â â'} (A : Abelian-group â) (B : Abelian-group â') where
private
module A = Abelian-group-on (A .snd)
module B = Abelian-group-on (B .snd)


The answer is yes: rather than we write this infix as and refer to it as the tensor product of abelian groups. Since is determined by the maps out of it, we can construct it directly as a higher inductive type. We add a constructor for every operation we want, and for the equations these should satisfy: should be an Abelian group, and it should admit a bilinear map

  data Tensor : Type (â â â') where
:1       : Tensor
_:*_     : Tensor â Tensor â Tensor
:inv     : Tensor â Tensor
squash   : is-set Tensor
t-invl   : â {x} â :inv x :* x â¡ :1
t-idl    : â {x} â :1 :* x â¡ x
t-assoc  : â {x y z} â x :* (y :* z) â¡ (x :* y) :* z
t-comm   : â {x y} â x :* y â¡ y :* x

_,_       : â A â â â B â â Tensor
t-pres-*r : â {x y z} â (x , y B.* z) â¡ (x , y) :* (x , z)
t-pres-*l : â {x y z} â (x A.* y , z) â¡ (x , z) :* (y , z)


The first 8 constructors are, by definition, exactly what we need to make Tensor into an abelian group. The latter three are the bilinear map Since this is an inductive type, itâs the initial object equipped with these data.

  open make-abelian-group
make-abelian-tensor : make-abelian-group Tensor
make-abelian-tensor .ab-is-set   = squash
make-abelian-tensor .mul         = _:*_
make-abelian-tensor .inv         = :inv
make-abelian-tensor .1g          = :1
make-abelian-tensor .idl x       = t-idl
make-abelian-tensor .assoc x y z = t-assoc
make-abelian-tensor .invl x      = t-invl
make-abelian-tensor .comm x y    = t-comm

_â_ : Abelian-group (â â â')
_â_ = to-ab make-abelian-tensor

to-tensor : Bilinear A B _â_
to-tensor .Bilinear.map           = _,_
to-tensor .Bilinear.pres-*l x y z = t-pres-*l
to-tensor .Bilinear.pres-*r x y z = t-pres-*r

  Tensor-elim-prop
: â {â'} {P : Tensor â Type â'}
â (â x â is-prop (P x))
â (â x y â P (x , y))
â (â {x y} â P x â P y â P (x :* y))
â (â {x} â P x â P (:inv x))
â P :1
â â x â P x
Tensor-elim-prop {P = P} pprop ppair padd pinv pz = go where
go : â x â P x
go (x , y) = ppair x y
go :1 = pz
go (x :* y) = padd (go x) (go y)
go (:inv x) = pinv (go x)
go (squash x y p q i j) = is-propâsquarep (Î» i j â pprop (squash x y p q i j))
(Î» i â go x) (Î» i â go (p i)) (Î» i â go (q i)) (Î» i â go y) i j
go (t-invl {x} i) = is-propâpathp (Î» i â pprop (t-invl i)) (padd (pinv (go x)) (go x)) pz i
go (t-idl {x} i) = is-propâpathp (Î» i â pprop (t-idl i)) (padd pz (go x)) (go x) i
go (t-assoc {x} {y} {z} i) =
is-propâpathp (Î» i â pprop (t-assoc i))
i
go (t-comm {x} {y} i) =
is-propâpathp (Î» i â pprop (t-comm i)) (padd (go x) (go y)) (padd (go y) (go x)) i
go (t-pres-*r {x} {y} {z} i) = is-propâpathp (Î» i â pprop (t-pres-*r i)) (ppair x (y B.* z)) (padd (ppair x y) (ppair x z)) i
go (t-pres-*l {x} {y} {z} i) = is-propâpathp (Î» i â pprop (t-pres-*l i)) (ppair (x A.* y) z) (padd (ppair x z) (ppair y z)) i

module _ {â â' â''} (A : Abelian-group â) (B : Abelian-group â') (C : Abelian-group â'') where
private
module A = Abelian-group-on (A .snd)
module B = Abelian-group-on (B .snd)
module C = Abelian-group-on (C .snd)


If we have any bilinear map we can first extend it to a function of sets by recursion, then prove that this is a group homomorphism. It turns out that this extension is definitionally a group homomorphism.

  bilinear-mapâfunction : Bilinear A B C â Tensor A B â â C â
bilinear-mapâfunction bilin = go module bilinear-mapâfunction where
go : Tensor A B â â C â
go :1       = C.1g
go (x :* y) = go x C.* go y
go (:inv x) = go x C.â»Â¹
go (x , y)  = Bilinear.map bilin x y

go (squash x y p q i j)      = C.has-is-set (go x) (go y) (Î» i â go (p i)) (Î» i â go (q i)) i j
go (t-invl {x} i)            = C.inversel {x = go x} i
go (t-idl {x} i)             = C.idl {x = go x} i
go (t-assoc {x} {y} {z} i)   = C.associative {x = go x} {go y} {go z} i
go (t-comm {x} {y} i)        = C.commutes {x = go x} {y = go y} i
go (t-pres-*r {a} {b} {c} i) = Bilinear.pres-*r bilin a b c i
go (t-pres-*l {a} {b} {c} i) = Bilinear.pres-*l bilin a b c i

{-# DISPLAY bilinear-mapâfunction.go b = bilinear-mapâfunction b #-}

module _ {â} (A B C : Abelian-group â) where
private
module A = Abelian-group-on (A .snd)
module B = Abelian-group-on (B .snd)
module C = Abelian-group-on (C .snd)


  from-bilinear-map : Bilinear A B C â Ab.Hom (A â B) C
from-bilinear-map bilin .hom = bilinear-mapâfunction A B C bilin
from-bilinear-map bilin .preserves .is-group-hom.pres-â x y = refl


Itâs also easy to construct a function in the opposite direction, turning group homomorphisms into bilinear maps, but proving that this is an equivalence requires appealing to an induction principle of Tensor which handles the equational fields: Tensor-elim-prop.

  to-bilinear-map : Ab.Hom (A â B) C â Bilinear A B C
to-bilinear-map gh .Bilinear.map x y = gh # (x , y)
to-bilinear-map gh .Bilinear.pres-*l x y z =
ap (apply gh) t-pres-*l â gh .preserves .is-group-hom.pres-â _ _
to-bilinear-map gh .Bilinear.pres-*r x y z =
ap (apply gh) t-pres-*r â gh .preserves .is-group-hom.pres-â _ _

from-bilinear-map-is-equiv : is-equiv from-bilinear-map
from-bilinear-map-is-equiv = is-isoâis-equiv morp where
morp : is-iso from-bilinear-map
morp .is-iso.inv = to-bilinear-map
morp .is-iso.rinv hom = ext \$ Tensor-elim-prop A B (Î» x â C.has-is-set _ _)
(Î» x y â refl)
(Î» x y â apâ C._*_ x y â sym (hom .preserves .is-group-hom.pres-â _ _))
(Î» x â ap C._â»Â¹ x â sym (is-group-hom.pres-inv (hom .preserves)))
(sym (is-group-hom.pres-id (hom .preserves)))
morp .is-iso.linv x = trivial!

  BilinearâHom : Bilinear A B C â Ab.Hom (A â B) C
BilinearâHom = from-bilinear-map , from-bilinear-map-is-equiv

HomâBilinear : Ab.Hom (A â B) C â Bilinear A B C
HomâBilinear = BilinearâHom eâ»Â¹

module BilinearâHom = Equiv BilinearâHom
module HomâBilinear = Equiv HomâBilinear

module _ {â} {A B C : Abelian-group â} where instance
Extensional-tensor-hom
: â {âr} âŠ ef : Extensional (â A â â â B â â â C â) âr âŠ â Extensional (Ab.Hom (A â B) C) âr
Extensional-tensor-hom âŠ ef âŠ =
injectionâextensional!
{f = Î» f x y â f .hom (x , y)}
(Î» {x} p â HomâBilinear.injective _ _ _ (ext (subst (ef .Patháµ _) p (ef .refláµ _))))
auto
{-# OVERLAPS Extensional-tensor-hom #-}


open Functor


Since we have a construction satisfying weâre driven, being category theorists, to question its naturality: Is the tensor product a functor, and is this equivalence of Hom-sets an adjunction?

The answer is yes, and the proofs are essentially plugging together existing definitions, other than the construction of the functorial action of

Ab-tensor-functor : Functor (Ab â Ãá¶ Ab â) (Ab â)
Ab-tensor-functor .Fâ (A , B) = A â B
Ab-tensor-functor .Fâ (f , g) = from-bilinear-map _ _ _ bilin where
bilin : Bilinear _ _ _
bilin .Bilinear.map x y       = f # x , g # y
bilin .Bilinear.pres-*l x y z = ap (_, g # z) (f .preserves .is-group-hom.pres-â _ _) â t-pres-*l
bilin .Bilinear.pres-*r x y z = ap (f # x ,_) (g .preserves .is-group-hom.pres-â _ _) â t-pres-*r
Ab-tensor-functor .F-id    = trivial!
Ab-tensor-functor .F-â f g = trivial!

Tensorâ£Hom : (A : Abelian-group â) â Bifunctor.Left Ab-tensor-functor A â£ Bifunctor.Right Ab-hom-functor A
Tensorâ£Hom A = hom-isoâadjoints to to-eqv nat where
to : â {x y} â Ab.Hom (x â A) y â Ab.Hom x Ab[ A , y ]
to f = curry-bilinear _ _ _ (to-bilinear-map _ _ _ f)

to-eqv : â {x y} â is-equiv (to {x} {y})
to-eqv = â-is-equiv
(HomâBilinear _ _ _ .snd)
(curry-bilinear-is-equiv _ _ _)

nat : hom-iso-natural {L = Bifunctor.Left Ab-tensor-functor A} {R = Bifunctor.Right Ab-hom-functor A} to
nat f g h = trivial!