goal-geometry-0.1: Goal/Geometry/Map/Multilinear.hs
-- | The Map module provides tools for developing function space 'Manifold's.
-- A map is a 'Manifold' where the 'Point's of the Manifold represent
-- parametric functions between 'Manifold's. The defining feature of 'Map's is
-- that they have a particular 'Domain' and 'Codomain', which themselves are
-- 'Manifold's.
module Goal.Geometry.Map.Multilinear (
-- * Tensors
Tensor (Tensor)
-- ** Construction
, (>.<)
-- ** Matrix Operations
, (<#>)
, matrixRank
, matrixInverse
, matrixTranspose
, matrixSquareRoot
, matrixApply
, matrixMap
, matrixDiagonalConcatenate
-- ** Cartesian
, coordinateTransform
, linearProjection
-- ** HMatrix Conversion
, toHMatrix
, fromHMatrix
-- * Affine Functions
, Affine (Affine)
, splitAffine
, joinAffine
) where
--- Imports ---
import Prelude hiding (map,minimum,maximum)
-- Package --
import Goal.Core
import Goal.Geometry.Set
import Goal.Geometry.Manifold
import Goal.Geometry.Linear
import Goal.Geometry.Map
-- Qualified --
import qualified Data.Vector.Storable as C
import qualified Numeric.LinearAlgebra.HMatrix as H
--import Data.Vector.Storable.UnsafeSerialize
--- Affine Functions ---
-- | 'Manifold's of 'Affine' functions.
data Affine m n = Affine m n deriving (Eq, Read, Show)
splitAffine :: (Manifold m, Manifold n) => Function c d :#: Affine m n -> (d :#: m, Function c d :#: Tensor m n)
-- | Splits an 'Point' on an 'Affine' space into a matrix and a constant.
splitAffine aff =
let (Affine m n) = manifold aff
tns = Tensor m n
css = coordinates aff
(mcs,mtxcs) = C.splitAt (dimension m) css
in (fromCoordinates m mcs, fromCoordinates tns mtxcs)
joinAffine :: (Manifold m, Manifold n) => d :#: m -> Function c d :#: Tensor m n -> Function c d :#: Affine m n
-- | Combines a matrix and a constant into 'Point' on an 'Affine' space.
joinAffine dm mtx =
let (Tensor m n) = manifold mtx
in fromCoordinates (Affine m n) $ coordinates dm C.++ coordinates mtx
-- Tensor Products --
-- | 'Manifold' of 'Tensor's given by the tensor product of the underlying pair of 'Manifold's.
data Tensor m n = Tensor m n deriving (Eq, Read, Show)
toHMatrix :: Manifold n => c :#: Tensor m n -> H.Matrix Double
-- | Converts a point on a 'Tensor' product manifold to a matrix for snappy
-- calculation.
toHMatrix pq =
let (Tensor _ m) = manifold pq
in H.reshape (dimension m) $ coordinates pq
fromHMatrix :: (Manifold m, Manifold n) => Tensor m n -> H.Matrix Double -> c :#: Tensor m n
fromHMatrix tns = fromCoordinates tns . H.flatten
matrixRank :: (Manifold m, Manifold n) => c :#: Tensor m n -> Int
matrixRank = H.rank . toHMatrix
(>.<) :: (Manifold m, Manifold n) => d :#: m -> c :#: n -> Function (Dual c) d :#: Tensor m n
-- | '>.<' denotes the outer product between two points. It provides a way of
-- constructing matrices of the 'Tensor' product space.
(>.<) p q = fromHMatrix (Tensor (manifold p) $ manifold q) $ coordinates p `H.outer` coordinates q
(<#>) :: (Manifold m, Manifold n, Manifold o)
=> Function d e :#: Tensor m n -> Function c d :#: Tensor n o -> Function c e :#: Tensor m o
-- | Tensor product composition.
(<#>) p q =
let (Tensor m _) = manifold p
(Tensor _ o) = manifold q
in fromHMatrix (Tensor m o) $ toHMatrix p <> toHMatrix q
matrixSquareRoot :: Manifold m => c :#: Tensor m m -> c :#: Tensor m m
-- | The square root of a matrix.
matrixSquareRoot pq = fromHMatrix (manifold pq) . H.sqrtm $ toHMatrix pq
matrixInverse :: (Manifold n, Manifold m) => Function c d :#: Tensor m n -> Function d c :#: Tensor n m
-- | The inverse of a given 'Tensor' point.
matrixInverse pq =
let Tensor m n = manifold pq
in fromHMatrix (Tensor n m) . H.inv $ toHMatrix pq
matrixTranspose :: (Manifold m, Manifold n) => Function c d :#: Tensor m n -> Function (Dual d) (Dual c) :#: Tensor n m
-- | The transpose of a given 'Tensor' point.
matrixTranspose pq =
let Tensor m n = manifold pq
in fromHMatrix (Tensor n m) . H.tr $ toHMatrix pq
matrixDiagonalConcatenate :: (Manifold m, Manifold n, Manifold o, Manifold p)
=> Function c d :#: Tensor m n
-> Function e f :#: Tensor o p
-> Function (c,e) (d,f) :#: Tensor (m,o) (n,p)
-- | Creates a block diagonal matrix.
matrixDiagonalConcatenate cdmn efop =
let (Tensor m n) = manifold cdmn
(Tensor o p) = manifold efop
in fromHMatrix (Tensor (m,o) (n,p)) $ H.diagBlock [toHMatrix cdmn, toHMatrix efop]
coordinateTransform :: Manifold m => [c :#: m] -> Function Cartesian c :#: Tensor m Euclidean
-- | Returns the coordinate transformation from 'Euclidean' space into the space
-- defined by the given basis vectors. This is a glorified fromColumns function.
coordinateTransform bss =
fromHMatrix (Tensor (manifold $ head bss) . Euclidean $ length bss) . H.fromColumns $ coordinates <$> bss
linearProjection :: Manifold m => [Cartesian :#: m] -> Function Cartesian Cartesian :#: Tensor m m
-- | Returns the linear projection operator for the given subset of basis vectors.
linearProjection bss =
let mtx = coordinateTransform bss
mtxt = matrixTranspose mtx
in mtx <#> matrixInverse (mtxt <#> mtx) <#> mtxt
matrixApply :: (Manifold m, Manifold n) => (Function c d :#: Tensor n m) -> (c :#: m) -> d :#: n
-- | Matrix vector multiplication.
matrixApply pq p =
let (Tensor n _) = manifold pq
in fromCoordinates n $ toHMatrix pq H.#> coordinates p
{-
let (Tensor n m) = manifold pq
in if m == manifold p
then fromCoordinates n $ toHMatrix pq H.#> coordinates p
else error "matrix applied to wrong Manifold"
-}
matrixMap :: (Manifold m, Manifold n) => (Function c d :#: Tensor m n) -> [c :#: n] -> [d :#: m]
-- | Mapped matrix vector multiplication, where we first turn the input vectors into a matrix itself (this can greatly improve computation time).
matrixMap pq ps =
let (Tensor n _) = manifold pq
mtx = toHMatrix pq
xs = H.fromColumns $ coordinates <$> ps
in map (fromCoordinates n) . H.toColumns $ mtx <> xs
{-
let (Tensor n m) = manifold pq
mtx = toHMatrix pq
xs = H.fromColumns $ coordinates <$> ps
in if all (== m) $ manifold <$> ps
then map (fromCoordinates n) . H.toColumns $ mtx <> xs
else error "matrix applied to wrong Manifold"
-}
--- Instances ---
-- Tensor Products --
instance (Manifold m, Manifold n) => Manifold (Tensor n m) where
dimension (Tensor n m) = dimension m * dimension n
instance (Manifold m, Manifold n) => Map (Tensor m n) where
type Domain (Tensor m n) = n
domain (Tensor _ n) = n
type Codomain (Tensor m n) = m
codomain (Tensor m _) = m
instance (Manifold m, Manifold n) => Apply c d (Tensor m n) where
(>.>) = matrixApply
(>$>) = matrixMap
-- Affine Map --
instance (Manifold m, Manifold n) => Manifold (Affine m n) where
dimension (Affine m n) = dimension m * dimension n + dimension m
instance (Manifold m, Manifold n) => Map (Affine m n) where
type Domain (Affine m n) = n
domain (Affine _ n) = n
type Codomain (Affine m n) = m
codomain (Affine m _) = m
instance (Manifold m, Manifold n) => Apply c d (Affine m n) where
(>.>) p x =
let (b,mtx) = splitAffine p
in mtx >.> x <+> b
(>$>) p xs =
let (b,mtx) = splitAffine p
in map (<+> b) $ mtx >$> xs