packages feed

gasp-1.4.0.0: Algebra/Linear.hs

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RebindableSyntax #-}

module Algebra.Linear where

import Algebra.Classes
import Algebra.Category.Laws (laws_bicartesian,testableCat)
import Prelude (Show(..),Eq(..),($),Ord,error,flip,IO,Bool,Int,Functor,fmap
               ,return)
import Control.Applicative
import Data.Foldable hiding (sum,product)
import Data.Traversable
import Control.Monad.State
import Algebra.Category
import Algebra.Types
import Data.Constraint
import Algebra.Category.Relation
import Algebra.Category.Objects
import Data.Functor.Rep
import Data.Distributive
import Test.QuickCheck hiding (collect, tabulate)

type VectorSpace scalar a = (Field scalar, Module scalar a, Group a)
-- Because of the existence of bases, vector spaces can always be made representable (Traversable, Applicative) functors.
-- So we'd be better off using the following definition:

-- | Representation of vector as traversable functor
-- ... but this is missing the link with *^ for module.  We should be
-- able to add forall s. PreRing s => Module s (v s), but this creates
-- problems when defining instances.

class (Finite (Rep v),Representable v, Foldable v, Applicative v) => VectorR v where
  vectorSplit :: (v ~ (f ⊗ g)) => Dict (VectorR f, VectorR g)
  vectorSplit = error "vectorSplit: not product type"
  vectorCut :: (v ~ (f ⊕ g)) => Dict (VectorR f, VectorR g)
  vectorCut = error "vectorCut: not sum type"

instance (VectorR v, VectorR w) => VectorR (v ⊗ w) where
  -- vectorSplit = Dict

instance (VectorR v, VectorR w) => VectorR (v ∘ w) where
  -- vectorCut = Dict

instance (VectorR One)
instance (VectorR Id)

{-instance SumObj VectorR where
  objsum = Dict
  objleftright = vectorCut
  objzero = Dict

instance ProdObj VectorR where
  objprod = Dict
  objfstsnd = vectorSplit
  objone = Dict
-}


class VectorR v => InnerProdSpace v where
  inner :: Field s => v s -> v s -> s

--------------------------------------------------------------
-- Construction of finite vectors

type VZero x = Zero x

data VNext v a = VNext {vnextInit :: !(v a), vnextLast :: !a} deriving (Functor,Foldable,Traversable,Show,Eq,Ord)

instance Distributive v => Distributive (VNext v) where
  collect f x = VNext (collect (vnextInit . f) x) (vnextLast . f <$> x)
instance Representable a => Representable (VNext a) where
  type Rep (VNext a) = One ⊕ (Rep a)
  index (VNext xs x) = \case
    Inj1 _ -> x
    Inj2 i -> index xs i
  tabulate f = VNext (tabulate (f . Inj2)) (f (Inj1 Unit))
instance VectorR a => VectorR (VNext a) where

data V f a where
  V0 :: V One a
  (:/) :: !(V f a) -> !a -> V (VNext f) a

deriving instance Functor (V f)
deriving instance Foldable (V f)
deriving instance Traversable (V f)
deriving instance Show a => Show (V f a)
deriving instance Eq a => Eq (V f a)

class (Foldable f,Applicative f) => IsVec f where
  reifyVec :: f a -> V f a

instance IsVec One where
  reifyVec FunctorOne = V0

instance IsVec f => IsVec (VNext f) where
  reifyVec (VNext xs x) = reifyVec xs :/ x

fromV :: V f a -> f a
fromV V0 = FunctorOne
fromV (xs :/ x) = VNext (fromV xs) x

instance IsVec f => Applicative (V f) where
  pure x = reifyVec (pure x)
  fs <*> xs = reifyVec (fromV fs <*> fromV xs)

instance Applicative v => Applicative (VNext v) where
  pure x = VNext (pure x) x
  VNext fs f <*> VNext xs x = VNext (fs <*> xs) (f x)


type V1' = VNext One
type V2' = VNext V1'
type V3' = VNext V2'

pattern V1' :: a -> V1' a
pattern V1' x = VNext FunctorOne x
pattern V2' :: forall a. a -> a -> V2' a
pattern V2' x y = VNext (V1' x) y
pattern V3' :: forall a. a -> a -> a -> V3' a
pattern V3' x y z = VNext (V2' x y) z

--------------------------------------------
-- Euclidean spaces with a (inner product)

-- | Make a Euclidean vector out of a traversable functor. (The p)
newtype Euclid f a = Euclid {fromEuclid :: f a}
  deriving (Functor,Foldable,Traversable,Show,Eq,Ord,Applicative)

deriving via App f a instance (Applicative f, Additive a) => Additive (Euclid f a)
deriving via App f a instance (Applicative f, Group a) => Group (Euclid f a)

type V3 = Euclid V3'
type V2 = Euclid V2'

pattern V2 :: forall a. a -> a -> Euclid V2' a
pattern V2 x y = Euclid (V2' x y)
pattern V3 :: forall a. a -> a -> a -> Euclid V3' a
pattern V3 x y z = Euclid (V3' x y z)

instance (Functor f, Scalable s a) => Scalable s (Euclid f a) where
  s *^ Euclid t = Euclid (((s*^) <$>) t)

pureMat :: (Applicative v, Applicative w) => s -> Mat s v w
pureMat x = Mat (pure (pure x))

instance (Applicative f,Applicative g,Additive a) => Additive (Mat a f g) where
  zero = pureMat zero
  x + y = matFlat ((+) <$> flatMat x <*> flatMat y)
instance (Applicative f,Applicative g,AbelianAdditive a) => AbelianAdditive (Mat a f g) where
instance (Applicative f,Applicative g,Group a) => Group (Mat a f g) where
  negate x = matFlat (negate <$> flatMat x)
  x - y = matFlat ((-) <$> flatMat x <*> flatMat y)

instance (Functor f, Functor g,Scalable s a) => Scalable s (Mat a f g) where
  s *^ Mat t = Mat (((s*^) <$>) <$> t)

-- | Hadamard product
(⊙) :: Applicative v => Multiplicative s => v s -> v s -> v s
x ⊙ y = (*) <$> x <*> y

instance Distributive f => Distributive (Euclid f) where
  collect f = Euclid . collect (fromEuclid . f)
instance Representable f => Representable (Euclid f) where
  type Rep (Euclid f) = Rep f
  index (Euclid x) = index x
  tabulate f = Euclid (tabulate f)
instance VectorR f => VectorR (Euclid f)
instance (VectorR f) => InnerProdSpace (Euclid f) where
  inner x y = sum (x ⊙ y) -- fixme

(·) :: Field s => InnerProdSpace v => v s -> v s -> s
(·) = inner

sqNorm :: Field s => InnerProdSpace v => v s -> s
sqNorm x = inner x x

norm :: Algebraic s => InnerProdSpace v => v s  -> s
norm = sqrt . sqNorm

normalize :: (VectorSpace s (v s)) => Algebraic s => InnerProdSpace v => v s -> v s
normalize v = recip (norm v) *^ v

-- | Cross product in 3 dimensions https://en.wikipedia.org/wiki/Cross_product
(×) :: Ring a => V3 a -> V3 a -> V3 a
(V3 a1 a2 a3) × (V3 b1 b2 b3) = V3 (a2*b3 - a3*b2)  (negate (a1*b3 - a3*b1)) (a1*b2 - a2*b1)

type SqMat v s = Mat s v v

-- | Matrix type. (w s) is a column. (v s) is a row.
newtype Mat s w v = Mat {fromMat :: w (v s)} deriving Show

-- | View of the matrix as a composition of functors.
newtype Flat w v s = Flat {fromFlat :: w (v s)} deriving (Show,Functor,Foldable)
flatMat :: Mat s w v -> Flat w v s
flatMat (Mat x) = (Flat x)
matFlat :: Flat w v s -> Mat s w v
matFlat (Flat x) = (Mat x)

instance (Applicative w, Applicative v) => Applicative (Flat w v) where
  pure x = Flat (pure (pure x))
  Flat f <*> Flat a = Flat (((<*>) <$> f) <*> a)


instance Ring s => Category (Mat s) where
  type Obj (Mat s) = VectorR
  (.) = matMul
  id = fromRel id

fromRel :: (VectorR a, VectorR b) => Rel s (Rep a) (Rep b) -> Mat s a b
fromRel (Rel f) = Mat (tabulate (\i -> tabulate (\j -> f i j)))
  
instance Ring s => Monoidal (∘) Id (Mat s) where
  assoc = fromRel assoc
  assoc_ = fromRel assoc_
  unitorR = fromRel unitorR
  unitorR_ = fromRel unitorR_
  Mat f ⊗ Mat g = Mat (Comp (fmap (\x -> fmap Comp  (fmap (\y -> liftA2 (liftA2 (*)) (fmap pure x) (pure y)) g)) f))


instance Ring s => Symmetric (∘) Id (Mat s) where
instance Ring s => Braided (∘) Id (Mat s) where
  swap = fromRel swap

instance Ring s => Monoidal (⊗) One (Mat s) where
  assoc = fromRel assoc
  assoc_ = fromRel assoc_
  unitorR = fromRel unitorR
  unitorR_ = fromRel unitorR_
  Mat f ⊗ Mat g = Mat (FunctorProd
                        ((flip FunctorProd (pure zero)) <$> f)
                        (FunctorProd (pure zero) <$> g))

instance Ring s => Cartesian (⊗) One (Mat s) where
  Mat f ▵ Mat g = Mat (FunctorProd <$> f <*> g)
  dis = fromRel dis

instance Ring s => Braided (⊗) One (Mat s) where
  swap = fromRel swap
instance Ring s => Symmetric (⊗) One (Mat s) where

instance Ring s => CoCartesian (⊗) One (Mat s) where
  inl = fromRel inl
  inr = fromRel inr
  new = fromRel new
  jam = fromRel jam
  Mat f ▿ Mat g = Mat (FunctorProd f g)
  
type Mat3x3 s = SqMat V3 s
type Mat2x2 s = SqMat V2 s

pattern Mat2x2 :: forall s. s -> s -> s -> s -> Mat s V2 V2
pattern Mat2x2 a b c d = Mat (V2 (V2 a c)
                                 (V2 b d))

pattern Mat3x3 :: forall s. s -> s -> s -> s -> s -> s -> s -> s -> s -> Mat s V3 V3
pattern Mat3x3 a b c d e f g h i = Mat (V3 (V3 a d g)
                                           (V3 b e h)
                                           (V3 c f i))


rotation2d :: Transcendental a => a -> Mat2x2 a
rotation2d θ = transpose $ Mat $ V2 (V2 (cos θ) (-sin θ))
                                    (V2 (sin θ)  (cos θ))

-- >>> rotation2d (pi/2)
-- Mat {fromMat = V2' (V2' 6.123233995736766e-17 (-1.0)) (V2' 1.0 6.123233995736766e-17)}


crossProductMatrix :: Group a => V3 a -> Mat3x3 a
crossProductMatrix (V3 a1 a2 a3) = Mat3x3 zero  (-a3) a2
                                          a3    zero  (-a1)
                                          (-a2) a1    zero

outerWith :: (Applicative v, Applicative w)
           => (s -> t -> u) -> w s -> v t -> Mat u v w
outerWith f v1 v2 = matFlat (f <$> Flat (pure v1) <*> Flat (pure <$> v2))


-- | Outer product 
outer :: (Applicative v, Applicative w, Multiplicative s)
    => Euclid w s -> Euclid v s -> Mat s (Euclid w) (Euclid v)
v1 `outer` v2 = outerWith (*) v2 v1


diagonal :: Eq (Rep v) => Representable v => Ring s => Applicative v => v s -> SqMat v s
diagonal v = outerWith (\x (y,a) -> if x == y then a else zero) (tabulate id) ((,) <$> (tabulate id) <*> v)

-- | 3d rotation around given axis
rotation3d :: Transcendental a => a -> V3 a -> Mat3x3 a
rotation3d θ u = cos θ *^ id +
                 sin θ *^ crossProductMatrix u +
                 (1 - cos θ) *^ (u `outer` u)


-- | 3d rotation mapping the direction of 'from' to that of 'to'
rotationFromTo :: forall a. (Algebraic a)
               => V3 a -> V3 a -> Mat3x3 a
rotationFromTo from to = c *^ id + s *^ crossProductMatrix v + (1-c) *^ (v `outer` v)
  where y = to
        x = from
        v :: V3 a
        v = x × y -- axis of rotation
        c = inner x y -- cos of angle
        s = norm v -- sin of angle

-- >>> let u = (V3 (1::Double) 0 0); v = (V3 0 1 1); in (rotationFromTo u v) `matVecMul` u
-- Euclid {fromEuclid = VNext (VNext (VNext VZero 0.0) 1.4142135623730951) 1.4142135623730951}

-- | Transposition as distribution
transpose :: Functor f => Distributive g => Mat a f g -> Mat a g f
transpose = Mat . distribute . fromMat

instance Ring s => Dagger (Mat s) where
  dagger = transpose

matMul :: (Foldable u, Ring s, Applicative w, Applicative v, Applicative u) => Mat s u w -> Mat s v u -> Mat s v w
matMul a (Mat b) = Mat (matVecMul a <$> b)


(<+>) :: (Applicative f, Additive b) => f b -> f b -> f b
u <+> v = (+) <$> u <*> v

matVecMul :: forall s v w. (Ring s, Foldable v,Applicative v,Applicative w) => Mat s v w -> v s -> w s
matVecMul (Mat m) x = foldr (<+>) (pure zero) ((*<) <$> x <*> m)

-- >>> let t1 = rotation2d (1::Double) in matMul (transpose t1) t1
-- Mat {fromMat = VNext (VNext VZero (VNext (VNext VZero 1.0) 0.0)) (VNext (VNext VZero 0.0) 1.0)}

instance (Arbitrary s, Arbitrary1 a, Arbitrary1 b) => Arbitrary (Mat s a b) where
  arbitrary = Mat <$> liftArbitrary arbitrary1
instance (TestEqual s, Arbitrary s, Arbitrary1 a, Arbitrary1 b,Show (a (b s)), VectorR b, VectorR a) => TestEqual (Mat s a b) where
  Mat m =.= Mat n = product (product <$> ( liftA2 (=.=) <$> m <*> n))



prop_linear_with_functor_laws :: Property
prop_linear_with_functor_laws =
  laws_bicartesian @(Mat Int)
  (testableCat
     (\k -> forallType @(∘) @Id @(⊗) @One (\t -> k t
       \\ reprCon @VectorR t))
     (\tx ty k -> forallMorphism tx ty k
       \\ reprCon1Comp @Int showCompClosed tx ty
       \\ reprCon @Arbitrary1 tx
       \\ reprCon @Arbitrary1 ty)
     (\a b -> Dict
       \\ reprCon1Comp @Int showCompClosed a b
       \\ reprCon @Arbitrary1 a
       \\ reprCon @Arbitrary1 b
       \\ reprCon @VectorR a
       \\ reprCon @VectorR b)
     RPlus
     RZero)


return []
runTests :: IO Bool
runTests = $quickCheckAll