rings-0.1.3: src/Data/Semimodule/Free.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}
module Data.Semimodule.Free (
-- * Types
type Free
-- * Vectors
, Vec(..)
, vmap
, join
, init
, (!*)
, (*!)
, (!*!)
-- * Covectors
, Cov(..)
, images
, cmap
, cojoin
, coinit
, comult
-- * Linear transformations
, Lin(..)
, End
, image
, invmap
, augment
, (!#)
, (#!)
, (!#!)
-- * Dimensional transforms
, braid
, cobraid
, split
, cosplit
, projl
, projr
, compl
, compr
, complr
-- * Algebraic transforms
, diagonal
, codiagonal
, initial
, coinitial
, convolve
) where
import safe Control.Applicative
import safe Control.Arrow
import safe Control.Category (Category, (<<<), (>>>))
import safe Control.Monad (MonadPlus(..))
import safe Data.Foldable (foldl')
import safe Data.Functor.Apply
import safe Data.Functor.Contravariant (Contravariant(..))
import safe Data.Functor.Rep
import safe Data.Profunctor
import safe Data.Profunctor.Sieve
import safe Data.Semimodule
import safe Data.Semiring
import safe Data.Tuple (swap)
import safe Prelude hiding (Num(..), Fractional(..), init, negate, sum, product)
import safe Test.Logic hiding (join)
import safe qualified Control.Category as C
import safe qualified Data.Profunctor.Rep as PR
-------------------------------------------------------------------------------
-- Vectors
-------------------------------------------------------------------------------
infixr 3 `runVec`
-- | A vector in a vector space or free semimodule.
--
-- Equivalent to < https://hackage.haskell.org/package/base/docs/Data-Functor-Contravariant.html#t:Op Op >.
--
-- Vectors transform contravariantly as a function of their bases.
--
-- See < https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors#Definition >.
--
newtype Vec a b = Vec { runVec :: b -> a }
infixr 7 !*
-- | Apply a covector to a vector on the right.
--
(!*) :: Vec a b -> Cov a b -> a
(!*) = flip (*!)
infixl 7 *!
-- | Apply a covector to a vector on the left.
--
(*!) :: Cov a b -> Vec a b -> a
(*!) f = runCov f . runVec
-- | Use a linear transformation to map over a vector space.
--
-- Note that the basis transforms < https://en.wikipedia.org/wiki/Covariant_transformation#Contravariant_transformation > contravariantly.
--
vmap :: Lin a b c -> Vec a c -> Vec a b
vmap f g = Vec $ runLin f (runVec g)
-- | Obtain a vector from a vector on the tensor product space.
--
join :: Algebra a b => Vec a (b, b) -> Vec a b
join = vmap diagonal
-- | Obtain a vector from the unit of a unital algebra.
--
-- @
-- 'init' a = 'vmap' 'initial' ('Vec' $ \_ -> a)
-- @
--
init :: Unital a b => a -> Vec a b
init = Vec . unital
infixr 7 !*!
-- | Multiplication operator on an algebra over a free semimodule.
--
-- >>> flip runVec E22 $ (vec $ V2 1 2) !*! (vec $ V2 7 4)
-- 8
--
-- /Caution/ in general 'mult' needn't be commutative, nor associative.
--
(!*!) :: Algebra a b => Vec a b -> Vec a b -> Vec a b
(!*!) x y = Vec $ joined (\i j -> runVec x i * runVec y j)
-------------------------------------------------------------------------------
-- Covectors
-------------------------------------------------------------------------------
infixr 3 `runCov`
-- | Linear functionals from elements of a free semimodule to a scalar.
--
-- @
-- f '!*' (x '+' y) = (f '!*' x) '+' (f '!*' y)
-- f '!*' (x '.*' a) = a '*' (f '!*' x)
-- @
--
-- /Caution/: You must ensure these laws hold when using the default constructor.
--
-- Co-vectors transform covariantly as a function of their bases.
--
-- See < https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors#Definition >.
--
newtype Cov a c = Cov { runCov :: (c -> a) -> a }
-- | Obtain a covector from a linear combination of basis elements.
--
-- >>> images [(2, E31),(3, E32)] !* vec (V3 1 1 1) :: Int
-- 5
--
images :: Semiring a => Foldable f => f (a, c) -> Cov a c
images f = Cov $ \k -> foldl' (\acc (a, c) -> acc + a * k c) zero f
-- | Use a linear transformation to map over a dual space.
--
-- Note that the basis transforms < https://en.wikipedia.org/wiki/Covariant_transformation covariantly >.
--
cmap :: Lin a b c -> Cov a b -> Cov a c
cmap f g = Cov $ runCov g . runLin f
-- | Obtain a covector from a covector on the tensor product space.
--
cojoin :: Coalgebra a c => Cov a (c, c) -> Cov a c
cojoin = cmap codiagonal
-- | Obtain a covector from the counit of a counital coalgebra.
--
-- @
-- 'coinit' = 'cmap' 'coinitial' ('Cov' $ \f -> f ())
-- @
--
coinit :: Counital a c => Cov a c
coinit = Cov counital
infixr 7 `comult`
-- | Multiplication operator on a coalgebra over a free semimodule.
--
-- >>> flip runCov (e2 1 1) $ comult (cov $ V2 1 2) (cov $ V2 7 4)
-- 11
--
-- /Caution/ in general 'comult' needn't be commutative, nor coassociative.
--
comult :: Coalgebra a c => Cov a c -> Cov a c -> Cov a c
comult (Cov f) (Cov g) = Cov $ \k -> f (\m -> g (cojoined k m))
-------------------------------------------------------------------------------
-- Linear transformations
-------------------------------------------------------------------------------
-- | A linear transformation between free semimodules indexed with bases /b/ and /c/.
--
-- @
-- f '!#' x '+' y = (f '!#' x) + (f '!#' y)
-- f '!#' (r '.*' x) = r '.*' (f '!#' x)
-- @
--
-- /Caution/: You must ensure these laws hold when using the default constructor.
--
-- Prefer 'image' or 'Data.Semimodule.Combinator.tran' where appropriate.
--
newtype Lin a b c = Lin { runLin :: (c -> a) -> b -> a }
-- | An endomorphism over a free semimodule.
--
-- >>> one + two !# V2 1 2 :: V2 Double
-- V2 3.0 6.0
--
type End a b = Lin a b b
-- | Create a 'Lin' from a linear combination of basis vectors.
--
-- >>> image (e2 [(2, E31),(3, E32)] [(1, E33)]) !# V3 1 1 1 :: V2 Int
-- V2 5 1
--
image :: Semiring a => (b -> [(a, c)]) -> Lin a b c
image f = Lin $ \k b -> sum [ a * k c | (a, c) <- f b ]
-- | 'Lin' is an invariant functor.
--
-- See also < http://comonad.com/reader/2008/rotten-bananas/ >.
--
invmap :: (a1 -> a2) -> (a2 -> a1) -> Lin a1 b c -> Lin a2 b c
invmap f g (Lin t) = Lin $ \x -> t (x >>> g) >>> f
-- | The < https://en.wikipedia.org/wiki/Augmentation_(algebra) augmentation > ring homomorphism.
--
augment :: Semiring a => Lin a b c -> b -> a
augment l = l !# const one
infixr 2 !#
-- | Apply a transformation to a vector.
--
(!#) :: Free f => Free g => Lin a (Rep f) (Rep g) -> g a -> f a
(!#) t = tabulate . runLin t . index
infixl 2 #!
-- | Apply a transformation to a vector.
--
(#!) :: Free f => Free g => g a -> Lin a (Rep f) (Rep g) -> f a
(#!) = flip (!#)
infixr 2 !#!
-- | Compose two transformations.
--
-- '!#!' = '<<<'
--
(!#!) :: Lin a c d -> Lin a b c -> Lin a b d
(!#!) = (C..)
-------------------------------------------------------------------------------
-- Common linear transformations
-------------------------------------------------------------------------------
-- | Swap components of a tensor product.
--
-- This is equivalent to a matrix transpose.
--
braid :: Lin a (b , c) (c , b)
braid = arr swap
{-# INLINE braid #-}
-- | Swap components of a direct sum.
--
cobraid :: Lin a (b + c) (c + b)
cobraid = arr eswap
{-# INLINE cobraid #-}
-- | TODO: Document
--
split :: (b -> (b1 , b2)) -> Lin a b1 c -> Lin a b2 c -> Lin a b c
split f x y = dimap f fst $ x *** y
{-# INLINE split #-}
-- | TODO: Document
--
cosplit :: ((c1 + c2) -> c) -> Lin a b c1 -> Lin a b c2 -> Lin a b c
cosplit f x y = dimap Left f $ x +++ y
{-# INLINE cosplit #-}
-- | Project onto the left-hand component of a direct sum.
--
projl :: Free f => Free g => (f++g) a -> f a
projl fg = arr Left !# fg
{-# INLINE projl #-}
-- | Project onto the right-hand component of a direct sum.
--
projr :: Free f => Free g => (f++g) a -> g a
projr fg = arr Right !# fg
{-# INLINE projr #-}
-- | Left (post) composition with a linear transformation.
--
compl :: Free f1 => Free f2 => Free g => Lin a (Rep f1) (Rep f2) -> (f2**g) a -> (f1**g) a
compl t fg = first t !# fg
-- | Right (pre) composition with a linear transformation.
--
compr :: Free f => Free g1 => Free g2 => Lin a (Rep g1) (Rep g2) -> (f**g2) a -> (f**g1) a
compr t fg = second t !# fg
-- | Left and right composition with a linear transformation.
--
-- @ 'complr' f g = 'compl' f '>>>' 'compr' g @
--
complr :: Free f1 => Free f2 => Free g1 => Free g2 => Lin a (Rep f1) (Rep f2) -> Lin a (Rep g1) (Rep g2) -> (f2**g2) a -> (f1**g1) a
complr t1 t2 fg = t1 *** t2 !# fg
-------------------------------------------------------------------------------
-- Algebraic transformations
-------------------------------------------------------------------------------
-- |
--
-- @
-- 'rmap' (\((c1,()),(c2,())) -> (c1,c2)) '$' ('C.id' '***' 'initial') 'C..' 'diagonal' = 'C.id'
-- 'rmap' (\(((),c1),((),c2)) -> (c1,c2)) '$' ('initial' '***' 'C.id') 'C..' 'diagonal' = 'C.id'
-- @
--
diagonal :: Algebra a b => Lin a b (b,b)
diagonal = Lin $ joined . curry
{-
prop_cojoined (~~) f = (codiagonal !# f) ~~ (Compose . tabulate $ \i -> tabulate $ \j -> cojoined (index f) i j)
-- trivial coalgebra
prop_codiagonal' (~~) f = (codiagonal !# f) ~~ (Compose $ flip imapRep f $ \i x -> flip imapRep f $ \j _ -> bool zero x $ (i == j))
-- trivial coalgebra
prop_codiagonal (~~) f = (codiagonal !# f) ~~ (flip bindRep id . getCompose $ f)
prop_diagonal (~~) f = (diagonal !# f) ~~ (tabulate $ joined (index . index (getCompose f)))
-}
-- |
--
-- @
-- 'lmap' (\(c1,c2) -> ((c1,()),(c2,()))) '$' ('C.id' '***' 'coinitial') 'C..' 'codiagonal' = 'C.id'
-- 'lmap' (\(c1,c2) -> (((),c1),((),c2))) '$' ('coinitial' '***' 'C.id') 'C..' 'codiagonal' = 'C.id'
-- @
--
codiagonal :: Coalgebra a c => Lin a (c,c) c
codiagonal = Lin $ uncurry . cojoined
-- | TODO: Document
--
initial :: Unital a b => Lin a b ()
initial = Lin $ \k -> unital $ k ()
-- | TODO: Document
--
coinitial :: Counital a c => Lin a () c
coinitial = Lin $ const . counital
{-
λ> foo = convolve (tran $ m22 1 0 0 1) (tran $ m22 1 0 0 1)
λ> foo !# V2 1 2 :: V2 Int
V2 1 2
λ> foo = convolve (tran $ m22 1 0 0 1) (tran $ m22 1 1 1 1)
λ> foo !# V2 1 2 :: V2 Int
V2 1 2
λ> foo = convolve (tran $ m22 1 1 1 1) (tran $ m22 1 1 1 1)
λ> foo !# V2 1 2 :: V2 Int
V2 3 3
-}
-- | Convolution with an associative algebra and coassociative coalgebra
--
convolve :: Algebra a b => Coalgebra a c => Lin a b c -> Lin a b c -> Lin a b c
convolve f g = codiagonal <<< (f *** g) <<< diagonal
-------------------------------------------------------------------------------
-- Vec instances
-------------------------------------------------------------------------------
addVec :: (Additive-Semigroup) a => Vec a b -> Vec a b -> Vec a b
addVec (Vec f) (Vec g) = Vec $ \b -> f b + g b
subVec :: (Additive-Group) a => Vec a b -> Vec a b -> Vec a b
subVec (Vec f) (Vec g) = Vec $ \b -> f b - g b
instance Contravariant (Vec a) where
contramap f g = Vec (runVec g . f)
instance Category Vec where
id = Vec id
Vec f . Vec g = Vec (g . f)
instance (Additive-Semigroup) a => Semigroup (Additive (Vec a b)) where
(<>) = liftA2 addVec
instance (Additive-Monoid) a => Monoid (Additive (Vec a b)) where
mempty = Additive . Vec $ const zero
instance (Additive-Group) a => Magma (Additive (Vec a b)) where
(<<) = liftA2 subVec
instance (Additive-Group) a => Quasigroup (Additive (Vec a b))
instance (Additive-Group) a => Loop (Additive (Vec a b))
instance (Additive-Group) a => Group (Additive (Vec a b))
instance Semiring a => LeftSemimodule a (Vec a b) where
lscale a v = Vec $ \b -> a *. runVec v b
instance Semiring a => LeftSemimodule (End a b) (Vec a b) where
lscale = vmap
instance Semiring a => RightSemimodule a (Vec a b) where
rscale a v = Vec $ \b -> runVec v b .* a
instance Semiring a => RightSemimodule (End a b) (Vec a b) where
rscale = vmap
instance Semiring a => Bisemimodule (End a b) (End a b) (Vec a b)
instance Bisemimodule a a a => Bisemimodule a a (Vec a b)
-------------------------------------------------------------------------------
-- Cov instances
-------------------------------------------------------------------------------
instance Functor (Cov a) where
fmap f m = Cov $ \k -> m `runCov` k . f
instance Applicative (Cov a) where
pure a = Cov $ \k -> k a
mf <*> ma = Cov $ \k -> mf `runCov` \f -> ma `runCov` k . f
instance Monad (Cov a) where
return a = Cov $ \k -> k a
m >>= f = Cov $ \k -> m `runCov` \a -> f a `runCov` k
instance (Additive-Monoid) a => Alternative (Cov a) where
Cov m <|> Cov n = Cov $ m + n
empty = Cov zero
instance (Additive-Monoid) a => MonadPlus (Cov a) where
Cov m `mplus` Cov n = Cov $ m + n
mzero = Cov zero
{-
newtype Vect a b = Vect (b -> a)
instance ((Additive-Semigroup) a) => Semigroup (Additive (Vect a b)) where
Additive (Vect f) <> Additive (Vect g) = Additive . Vect $ \b -> f b + g b
{-# INLINE (<>) #-}
instance ((Additive-Monoid) a) => Monoid (Additive (Vect a b)) where
mempty = Additive . Vect $ const zero
instance ((Additive-Group) a) => Magma (Additive (Vect a b)) where
Additive (Vect f) << Additive (Vect g) = Additive . Vect $ \b -> f b - g b
{-# INLINE (<<) #-}
instance ((Additive-Group) a) => Quasigroup (Additive (Vect a b))
instance ((Additive-Group) a) => Loop (Additive (Vect a b)) where
instance ((Additive-Group) a) => Group (Additive (Vect a b))
-}
instance (Additive-Semigroup) a => Semigroup (Additive (Cov a b)) where
(<>) = liftA2 $ \(Cov m) (Cov n) -> Cov $ m + n
instance (Additive-Monoid) a => Monoid (Additive (Cov a b)) where
mempty = Additive $ Cov zero
instance (Additive-Group) a => Magma (Additive (Cov a b)) where
(<<) = liftA2 $ \(Cov m) (Cov n) -> Cov $ m - n
instance (Additive-Group) a => Quasigroup (Additive (Cov a b))
instance (Additive-Group) a => Loop (Additive (Cov a b))
instance (Additive-Group) a => Group (Additive (Cov a b))
instance Semiring a => LeftSemimodule a (Cov a b) where
lscale s m = Cov $ \k -> s *. runCov m k
instance Counital a b => LeftSemimodule (End a b) (Cov a b) where
lscale = cmap
instance Semiring a => RightSemimodule a (Cov a b) where
rscale s m = Cov $ \k -> runCov m k .* s
instance Counital a b => RightSemimodule (End a b) (Cov a b) where
rscale = cmap
instance Counital a b => Bisemimodule (End a b) (End a b) (Cov a b)
instance Bisemimodule a a a => Bisemimodule a a (Cov a b)
-------------------------------------------------------------------------------
-- Lin instances
-------------------------------------------------------------------------------
addLin :: (Additive-Semigroup) a => Lin a b c -> Lin a b c -> Lin a b c
addLin (Lin f) (Lin g) = Lin $ f + g
subLin :: (Additive-Group) a => Lin a b c -> Lin a b c -> Lin a b c
subLin (Lin f) (Lin g) = Lin $ \h -> f h - g h
-- mulLin :: (Multiplicative-Semigroup) a => Lin a b c -> Lin a b c -> Lin a b c
-- mulLin (Lin f) (Lin g) = Lin $ \h -> f h * g h
instance Functor (Lin a b) where
fmap f m = Lin $ \k -> m !# k . f
instance Category (Lin a) where
id = Lin id
Lin f . Lin g = Lin $ g . f
instance Apply (Lin a b) where
mf <.> ma = Lin $ \k b -> (mf !# \f -> (ma !# k . f) b) b
instance Applicative (Lin a b) where
pure a = Lin $ \k _ -> k a
(<*>) = (<.>)
instance Profunctor (Lin a) where
-- | 'Lin' is a profunctor in the category of semimodules.
--
-- /Caution/: Arbitrary mapping functions may violate linearity.
--
-- >>> dimap id (e3 True True False) (arr id) !# 4 :+ 5 :: V3 Int
-- V3 5 5 4
--
dimap l r f = arr r <<< f <<< arr l
instance Strong (Lin a) where
first' = first
second' = second
instance Choice (Lin a) where
left' = left
right' = right
instance Sieve (Lin a) (Cov a) where
sieve l b = Cov $ \k -> (l !# k) b
instance PR.Representable (Lin a) where
type Rep (Lin a) = Cov a
tabulate f = Lin $ \k b -> runCov (f b) k
instance Monad (Lin a b) where
return a = Lin $ \k _ -> k a
m >>= f = Lin $ \k b -> (m !# \a -> (f a !# k) b) b
instance Arrow (Lin a) where
arr f = Lin (. f)
first m = Lin $ \k (a,c) -> (m !# \b -> k (b,c)) a
second m = Lin $ \k (c,a) -> (m !# \b -> k (c,b)) a
m *** n = Lin $ \k (a,c) -> (m !# \b -> (n !# \d -> k (b,d)) c) a
m &&& n = Lin $ \k a -> (m !# \b -> (n !# \c -> k (b,c)) a) a
instance ArrowChoice (Lin a) where
left m = Lin $ \k -> either (m !# k . Left) (k . Right)
right m = Lin $ \k -> either (k . Left) (m !# k . Right)
m +++ n = Lin $ \k -> either (m !# k . Left) (n !# k . Right)
m ||| n = Lin $ \k -> either (m !# k) (n !# k)
instance ArrowApply (Lin a) where
app = Lin $ \k (f,a) -> (f !# k) a
instance (Additive-Semigroup) a => Semigroup (Additive (Lin a b c)) where
(<>) = liftA2 addLin
instance (Additive-Monoid) a => Monoid (Additive (Lin a b c)) where
mempty = pure . Lin $ const zero
instance Presemiring a => Semigroup (Multiplicative (End a b)) where
(<>) = liftA2 (<<<)
instance Semiring a => Monoid (Multiplicative (End a b)) where
mempty = pure C.id
instance Presemiring a => Presemiring (End a b)
instance Semiring a => Semiring (End a b)
instance Ring a => Ring (End a b)
{-
instance Coalgebra a c => Semigroup (Multiplicative (Lin a b c)) where
(<>) = liftR2 $ \ f g -> Lin $ \k b -> (f !# \a -> (g !# cojoined k a) b) b
instance Counital a c => Monoid (Multiplicative (Lin a b c)) where
mempty = pure . Lin $ \k _ -> counital k
instance Coalgebra a c => Presemiring (Lin a b c)
instance Counital a c => Semiring (Lin a b c)
instance (Ring a, Counital a c) => Ring (Lin a b c)
-}
instance Counital a b => LeftSemimodule (Lin a b b) (Lin a b c) where
-- | Left matrix multiplication
lscale = (>>>)
instance Semiring a => LeftSemimodule a (Lin a b c) where
lscale l (Lin m) = Lin $ \k b -> l *. m k b
instance Counital a c => RightSemimodule (Lin a c c) (Lin a b c) where
-- | Right matrix multiplication
rscale = (<<<)
instance (Counital a b, Counital a c) => Bisemimodule (Lin a b b) (Lin a c c) (Lin a b c)
instance Semiring a => RightSemimodule a (Lin a b m) where
rscale r (Lin m) = Lin $ \k b -> m k b .* r
instance Bisemimodule a a a => Bisemimodule a a (Lin a b c)
instance (Additive-Group) a => Magma (Additive (Lin a b c)) where
(<<) = liftR2 subLin
instance (Additive-Group) a => Quasigroup (Additive (Lin a b c))
instance (Additive-Group) a => Loop (Additive (Lin a b c))
instance (Additive-Group) a => Group (Additive (Lin a b c))
{-
-- | An endomorphism of endomorphisms.
--
-- @ 'Cayley' a = (a -> a) -> (a -> a) @
--
type Cayley a = Lin a a a
-- | Lift a semiring element into a 'Cayley'.
--
-- @ 'runCayley' . 'cayley' = 'id' @
--
-- >>> runCayley . cayley $ 3.4 :: Double
-- 3.4
-- >>> runCayley . cayley $ m22 1 2 3 4 :: M22 Int
-- Compose (V2 (V2 1 2) (V2 3 4))
--
cayley :: Semiring a => a -> Cayley a
cayley a = Lin $ \k b -> a * k zero + b
-- | Extract a semiring element from a 'Cayley'.
--
-- >>> runCayley $ two * (one + (cayley 3.4)) :: Double
-- 8.8
-- >>> runCayley $ two * (one + (cayley $ m22 1 2 3 4)) :: M22 Int
-- Compose (V2 (V2 4 4) (V2 6 10))
--
runCayley :: Semiring a => Cayley a -> a
runCayley (Lin f) = f (one +) zero
-- ring homomorphism from a -> a^b
embed :: (Multiplicative-Semigroup) a => (Multiplicative-Monoid) c => (b -> a) -> Lin a b c
embed f = Lin $ \k b -> f b * k one
-- if the characteristic of s does not divide the order of a, then s[a] is semisimple
-- and if a has a length function, we can build a filtered algebra
-}