packages feed

synapse-0.1.0.0: src/Synapse/Tensors/Vec.hs

{- | Implementation of mathematical vector.

'Vec' is only a newtype wrapper around 'Data.Vector', which
implements several mathematical operations on itself.

'Vec' offers meaningful abstraction and easy interface
(you can unwrap it to perform more complex tasks).
-}


-- 'TypeFamilies' are needed to instantiate 'Indexable', 'ElementwiseScalarOps', 'SingletonOps', 'VecOps' typeclasses.
{-# LANGUAGE TypeFamilies #-}


module Synapse.Tensors.Vec
    ( -- * 'Vec' datatype and simple getters.

      Vec (Vec, unVec)

    , size

      -- * Constructors

    , empty
    , singleton
    , fromList
    , generate
    , replicate

      -- * Concatenation and splitting

    , cons
    , snoc
    , (++)
    , concat

    , splitAt

      -- * Combining

    , map
    , imap
    , for
    , zipWith
    , zip

    -- * Mathematics

    , zeroes
    , ones

    , squaredMagnitude
    , magnitude
    , clampMagnitude
    , normalized

    , linearCombination
    , dot

    , angleBetween
    , lerp
    ) where


import Synapse.Tensors (DType, Indexable(..), ElementwiseScalarOps(..), SingletonOps(..), VecOps(..))

import Prelude hiding ((++), concat, splitAt, map, replicate, zip, zipWith)
import Data.Foldable (Foldable(..))
import Data.Ord (clamp)

import qualified Data.Vector as V


-- | Mathematical vector (collection of elements).
newtype Vec a = Vec
    { unVec :: V.Vector a  -- ^ Internal representation.
    } deriving (Eq, Read)

-- | Size of a vector - number of elements.
size :: Vec a -> Int
size = V.length . unVec


-- Typeclasses

instance Show a => Show (Vec a) where
    show (Vec x) = show x


type instance DType (Vec a) = a


instance Indexable (Vec a) where
    type Index (Vec a) = Int

    unsafeIndex (Vec x) = V.unsafeIndex x
    (!) (Vec x) = (V.!) x
    (!?) (Vec x) = (V.!?) x


instance Num a => Num (Vec a) where
    (+) = zipWith (+)
    (-) = zipWith (-)
    negate = fmap (0 -)
    (*) = zipWith (*)
    abs = fmap abs
    signum = fmap signum
    fromInteger = singleton . fromInteger

instance Fractional a => Fractional (Vec a) where
    (/) = zipWith (/)
    recip = fmap (1/)
    fromRational = singleton . fromRational

instance Floating a => Floating (Vec a) where
    pi = singleton pi
    (**) = zipWith (**)
    sqrt = fmap sqrt
    exp = fmap exp
    log = fmap log
    sin = fmap sin
    cos = fmap cos
    asin = fmap asin
    acos = fmap acos
    atan = fmap atan
    sinh = fmap sinh
    cosh = fmap cosh
    asinh = fmap asinh
    acosh = fmap acosh
    atanh = fmap atanh

instance ElementwiseScalarOps (Vec a) where
    (+.) x n = fmap (+ n) x
    (-.) x n = fmap (subtract n) x
    (*.) x n = fmap (* n) x
    (/.) x n = fmap (/ n) x
    (**.) x n = fmap (** n) x

    elementsMin x n = fmap (min n) x
    elementsMax x n = fmap (max n) x

instance SingletonOps (Vec a) where
    singleton = pure
    isSingleton vec = size vec == 1
    unSingleton vec
        | not $ isSingleton vec = error "Vector is not a singleton"
        | otherwise             = unsafeIndex vec 0

    extendSingleton vec reference = replicate (size reference) (unSingleton vec)

    elementsSum = singleton . V.sum . unVec
    elementsProduct = singleton . V.product . unVec
    mean x = elementsSum x /. fromIntegral (size x)
    norm x = sqrt $ elementsSum $ x * x

instance Functor Vec where
    fmap f = Vec . V.map f . unVec
    (<$) = fmap . const

instance Applicative Vec where
    pure = Vec . V.singleton
    (<*>) = zipWith (\f x -> f x)

instance Foldable Vec where
    foldr f x = V.foldr f x . unVec
    foldl f x = V.foldl f x . unVec

    foldr' f x = V.foldr' f x . unVec
    foldl' f x = V.foldl' f x . unVec

    foldr1 f = V.foldr1 f . unVec
    foldl1 f = V.foldl1 f . unVec

    toList = V.toList . unVec

    null x = size x == 0

    length = size

instance Traversable Vec where
    traverse f (Vec x) = Vec <$> traverse f x


-- Constructors

-- | Creates empty 'Vec'.
empty :: Vec a
empty = Vec V.empty

-- | Creates 'Vec' from list.
fromList :: [a] -> Vec a
fromList = Vec . V.fromList

-- | Creates 'Vec' of given length using generating function.
generate :: Int -> (Int -> a) -> Vec a
generate n = Vec . V.generate n

-- | Creates 'Vec' of given length filled with given element.
replicate :: Int -> a -> Vec a
replicate n = generate n . const


-- Concatenation and splitting

-- | Prepend 'Vec' with given element.
cons :: a -> Vec a -> Vec a
cons x = Vec . V.cons x . unVec

-- | Append 'Vec' with given element.
snoc :: Vec a -> a -> Vec a
snoc (Vec vec) x = Vec $ V.snoc vec x

-- | Concatenate two 'Vec's.
infixr 5 ++
(++) :: Vec a -> Vec a -> Vec a
(++) (Vec x) (Vec y) = Vec $ x V.++ y

-- | Concatenate all 'Vec's.
concat :: [Vec a] -> Vec a
concat = foldr1 (++)

-- | Splits 'Vec' into two 'Vec's at a given index.
splitAt :: Int -> Vec a -> (Vec a, Vec a)
splitAt i (Vec v) = let (v1, v2) = V.splitAt i v
                    in (Vec v1, Vec v2)


-- Combining

-- | Map a function over a 'Vec'.
map :: (a -> b) -> Vec a -> Vec b
map = fmap

-- | Apply a function to every element of a 'Vec' and its index.
imap :: (Int -> a -> b) -> Vec a -> Vec b
imap f = Vec . V.imap f . unVec

-- | 'map' with its arguments flipped.
for :: Vec a -> (a -> b) -> Vec b
for = flip fmap

-- | Zips two 'Vec's with the given function.
zipWith :: (a -> b -> c) -> Vec a -> Vec b -> Vec c
zipWith f (Vec a) (Vec b) = Vec $ V.zipWith f a b

-- | Zips two 'Vec's.
zip :: Vec a -> Vec b -> Vec (a, b)
zip = zipWith (,)


-- Functions that work on mathematical vector (type constraint refers to a number)

-- | Creates 'Vec' of given length filled with zeroes.
zeroes :: Num a => Int -> Vec a
zeroes = flip generate (const 0)

-- | Creates 'Vec' of given length filled with ones.
ones :: Num a => Int -> Vec a
ones = flip generate (const 1)


-- | Squared magnitude of a 'Vec'.
squaredMagnitude :: Num a => Vec a -> a
squaredMagnitude x = sum (fmap (^ (2 :: Int)) x)

-- | Magnitude of a 'Vec'.
magnitude :: Floating a => Vec a -> a
magnitude = sqrt . squaredMagnitude

-- | Clamps 'Vec' magnitude.
clampMagnitude :: (Floating a, Ord a) => a -> Vec a -> Vec a
clampMagnitude m x = x *. (min (magnitude x) m / magnitude x)

-- | Normalizes 'Vec' by dividing each component by 'Vec' magnitude.
normalized :: Floating a => Vec a -> Vec a
normalized x = x /. magnitude x


-- | Computes linear combination of 'Vec's. Returns empty 'Vec' if empty list was passed to this function.
linearCombination :: Num a => [(a, Vec a)] -> Vec a
linearCombination [] = empty
linearCombination (x:xs) = foldl' (\acc (a, vec) -> acc + vec *. a) (snd x *. fst x) xs


instance Num a => VecOps (Vec a) where
    dot a b = elementsSum $ a * b


-- | Calculates an angle between two 'Vec's.
angleBetween :: Floating a => Vec a -> Vec a -> a
angleBetween a b = acos $ unSingleton (a `dot` b) / (magnitude a * magnitude b)

-- | Linearly interpolates between two 'Vec's. Given parameter will be clamped between [0.0, 1.0].
lerp :: (Floating a, Ord a) => a -> Vec a -> Vec a -> Vec a
lerp k a b = b - (b - a) *. clamp (0.0, 1.0) k