multilinear-0.2.2: src/Multilinear/Tensor.hs
{-|
Module : Multilinear.Tensor
Description : Tensors constructors (finitely- or infinitely-dimensional)
Copyright : (c) Artur M. Brodzki, 2018
License : BSD3
Maintainer : artur@brodzki.org
Stability : experimental
Portability : Windows/POSIX
- This module provides convenient constructors that generate a arbitrary finitely- or infinitely-dimensional tensors.
- Finitely-dimensional tensors provide much greater performance than inifitely-dimensional
-}
module Multilinear.Tensor (
-- * Generators
Multilinear.Tensor.fromIndices,
Multilinear.Tensor.generate,
Multilinear.Tensor.const,
Multilinear.Tensor.randomDouble,
Multilinear.Tensor.randomDoubleSeed,
Multilinear.Tensor.randomInt,
Multilinear.Tensor.randomIntSeed
) where
import Control.Monad.Primitive
import qualified Data.Vector as Boxed
import Multilinear.Generic
import Multilinear.Index.Finite as Finite
import Statistics.Distribution
import qualified System.Random.MWC as MWC
invalidIndices :: (String, [Int]) -> (String, [Int]) -> String
invalidIndices us ds = "Indices and its sizes incompatible, upper indices: " ++ show us ++", lower indices: " ++ show ds
{-| Generate tensor as functions of its indices -}
{-# INLINE fromIndices #-}
fromIndices :: (
Num a
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> ([Int] -> [Int] -> a) -- ^ Generator function (f [u1,u2,...] [d1,d2,...] returns a tensor element at t [u1,u2,...] [d1,d2,...])
-> Tensor a -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
fromIndices ([u],[s]) ([],[]) f =
SimpleFinite (Contravariant s [u]) $ Boxed.generate s $ \x -> f [x] []
-- If only one lower index is given, generate a SimpleFinite tensor with lower index
fromIndices ([],[]) ([d],[s]) f =
SimpleFinite (Covariant s [d]) $ Boxed.generate s $ \x -> f [] [x]
-- If many indices are given, first generate upper indices recursively from indices list
fromIndices (u:us,s:size) d f =
FiniteTensor (Contravariant s [u]) $ Boxed.generate s (\x -> fromIndices (us,size) d (\uss dss -> f (x:uss) dss) )
-- After upper indices, generate lower indices recursively from indices list
fromIndices u (d:ds,s:size) f =
FiniteTensor (Covariant s [d]) $ Boxed.generate s (\x -> fromIndices u (ds,size) (\uss dss -> f uss (x:dss)) )
-- If there are indices without size or sizes without names, throw an error
fromIndices us ds _ = Err $ invalidIndices us ds
{-| Generate tensor composed of other tensors -}
{-# INLINE generate #-}
generate :: (
Num a
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> ([Int] -> [Int] -> Tensor a) -- ^ Generator function (f [u1,u2,...] [d1,d2,...] returns a tensor element at t [u1,u2,...] [d1,d2,...])
-> Tensor a -- ^ Generated tensor
-- If no indices are given, generate a tensor by using generator function
generate ([],[]) ([],[]) f = f [] []
-- If many indices are given, first generate upper indices recursively from indices list
generate (u:us,s:size) d f =
FiniteTensor (Contravariant s [u]) $ Boxed.generate s (\x -> generate (us,size) d (\uss dss -> f (x:uss) dss) )
-- After upper indices, generate lower indices recursively from indices list
generate u (d:ds,s:size) f =
FiniteTensor (Covariant s [d]) $ Boxed.generate s (\x -> generate u (ds,size) (\uss dss -> f uss (x:dss)) )
-- If there are indices without size or sizes without names, throw an error
generate us ds _ = Err $ invalidIndices us ds
{-| Generate tensor with all components equal to @v@ -}
{-# INLINE Multilinear.Tensor.const #-}
const :: (
Num a
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> a -- ^ Tensor elements value
-> Tensor a -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
const ([u],[s]) ([],[]) v =
SimpleFinite (Contravariant s [u]) $ Boxed.replicate s v
-- If only ine lower index is given, generate a SimpleFinite tensor with lower index
const ([],[]) ([d],[s]) v =
SimpleFinite (Covariant s [d]) $ Boxed.replicate s v
-- If many indices are given, first generate upper indices recursively from indices list
const (u:us,s:size) d v =
FiniteTensor (Contravariant s [u]) $ Boxed.replicate (fromIntegral s) $ Multilinear.Tensor.const (us,size) d v
-- After upper indices, generate lower indices recursively from indices list
const u (d:ds,s:size) v =
FiniteTensor (Covariant s [d]) $ Boxed.replicate (fromIntegral s) $ Multilinear.Tensor.const u (ds,size) v
-- If there are indices without size or sizes without names, throw an error
const us ds _ = Err $ invalidIndices us ds
{-| Generate tensor with random real components with given probability distribution.
The tensor is wrapped in the IO monad. -}
{-| Available probability distributions: -}
{-| - Beta : "Statistics.Distribution.BetaDistribution" -}
{-| - Cauchy : "Statistics.Distribution.CauchyLorentz" -}
{-| - Chi-squared : "Statistics.Distribution.ChiSquared" -}
{-| - Exponential : "Statistics.Distribution.Exponential" -}
{-| - Gamma : "Statistics.Distribution.Gamma" -}
{-| - Geometric : "Statistics.Distribution.Geometric" -}
{-| - Normal : "Statistics.Distribution.Normal" -}
{-| - StudentT : "Statistics.Distribution.StudentT" -}
{-| - Uniform : "Statistics.Distribution.Uniform" -}
{-| - F : "Statistics.Distribution.FDistribution" -}
{-| - Laplace : "Statistics.Distribution.Laplace" -}
{-# INLINE randomDouble #-}
randomDouble :: (
ContGen d
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> d -- ^ Continuous probability distribution (as from "Statistics.Distribution")
-> IO (Tensor Double) -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
randomDouble ([u],[s]) ([],[]) distr = do
gen <- MWC.createSystemRandom
component <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen
return $ SimpleFinite (Contravariant s [u]) component
-- If only one lower index is given, generate a SimpleFinite tensor with lower index
randomDouble ([],[]) ([d],[s]) distr = do
gen <- MWC.createSystemRandom
component <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen
return $ SimpleFinite (Covariant s [d]) component
-- If many indices are given, first generate upper indices recursively from indices list
randomDouble (u:us,s:size) d distr = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomDouble (us,size) d distr
return $ FiniteTensor (Contravariant s [u]) tensors
-- After upper indices, generate lower indices recursively from indices list
randomDouble u (d:ds,s:size) distr = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomDouble u (ds,size) distr
return $ FiniteTensor (Covariant s [d]) tensors
-- If there are indices without size or sizes without names, throw an error
randomDouble us ds _ = return $ Err $ invalidIndices us ds
{-| Generate tensor with random integer components with given probability distribution.
The tensor is wrapped in the IO monad. -}
{-| Available probability distributions: -}
{-| - Binomial : "Statistics.Distribution.Binomial" -}
{-| - Poisson : "Statistics.Distribution.Poisson" -}
{-| - Geometric : "Statistics.Distribution.Geometric" -}
{-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -}
{-# INLINE randomInt #-}
randomInt :: (
DiscreteGen d
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> d -- ^ Discrete probability distribution (as from "Statistics.Distribution")
-> IO (Tensor Int) -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
randomInt ([u],[s]) ([],[]) distr = do
gen <- MWC.createSystemRandom
component <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen
return $ SimpleFinite (Contravariant s [u]) component
-- If only one lower index is given, generate a SimpleFinite tensor with lower index
randomInt ([],[]) ([d],[s]) distr = do
gen <- MWC.createSystemRandom
component <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen
return $ SimpleFinite (Covariant s [d]) component
-- If many indices are given, first generate upper indices recursively from indices list
randomInt (u:us,s:size) d distr = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomInt (us,size) d distr
return $ FiniteTensor (Contravariant s [u]) tensors
-- After upper indices, generate lower indices recursively from indices list
randomInt u (d:ds,s:size) distr = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomInt u (ds,size) distr
return $ FiniteTensor (Covariant s [d]) tensors
-- If there are indices without size or sizes without names, throw an error
randomInt us ds _ = return $ Err $ invalidIndices us ds
{-| Generate tensor with random real components with given probability distribution and given seed.
The tensor is wrapped in a monad. -}
{-| Available probability distributions: -}
{-| - Beta : "Statistics.Distribution.BetaDistribution" -}
{-| - Cauchy : "Statistics.Distribution.CauchyLorentz" -}
{-| - Chi-squared : "Statistics.Distribution.ChiSquared" -}
{-| - Exponential : "Statistics.Distribution.Exponential" -}
{-| - Gamma : "Statistics.Distribution.Gamma" -}
{-| - Geometric : "Statistics.Distribution.Geometric" -}
{-| - Normal : "Statistics.Distribution.Normal" -}
{-| - StudentT : "Statistics.Distribution.StudentT" -}
{-| - Uniform : "Statistics.Distribution.Uniform" -}
{-| - F : "Statistics.Distribution.FDistribution" -}
{-| - Laplace : "Statistics.Distribution.Laplace" -}
{-# INLINE randomDoubleSeed #-}
randomDoubleSeed :: (
ContGen d, PrimMonad m
) => (String,[Int]) -- ^ Upper indices names (one character per index) and its sizes
-> (String,[Int]) -- ^ Lower indices names (one character per index) and its sizes
-> d -- ^ Continuous probability distribution (as from "Statistics.Distribution")
-> Int -- ^ Randomness seed
-> m (Tensor Double) -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
randomDoubleSeed ([u],[s]) ([],[]) distr seed = do
gen <- MWC.initialize (Boxed.singleton $ fromIntegral seed)
component <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen
return $ SimpleFinite (Contravariant s [u]) component
-- If only one lower index is given, generate a SimpleFinite tensor with lower index
randomDoubleSeed ([],[]) ([d],[s]) distr seed = do
gen <- MWC.initialize (Boxed.singleton $ fromIntegral seed)
component <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen
return $ SimpleFinite (Covariant s [d]) component
-- If many indices are given, first generate upper indices recursively from indices list
randomDoubleSeed (u:us,s:size) d distr seed = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomDoubleSeed (us,size) d distr seed
return $ FiniteTensor (Contravariant s [u]) tensors
-- After upper indices, generate lower indices recursively from indices list
randomDoubleSeed u (d:ds,s:size) distr seed = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomDoubleSeed u (ds,size) distr seed
return $ FiniteTensor (Covariant s [d]) tensors
-- If there are indices without size or sizes without names, throw an error
randomDoubleSeed us ds _ _ = return $ Err $ invalidIndices us ds
{-| Generate tensor with random integer components with given probability distribution and given seed.
The tensor is wrapped in a monad. -}
{-| Available probability distributions: -}
{-| - Binomial : "Statistics.Distribution.Binomial" -}
{-| - Poisson : "Statistics.Distribution.Poisson" -}
{-| - Geometric : "Statistics.Distribution.Geometric" -}
{-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -}
{-# INLINE randomIntSeed #-}
randomIntSeed :: (
DiscreteGen d, PrimMonad m
) => (String,[Int]) -- ^ Index name (one character)
-> (String,[Int]) -- ^ Number of elements
-> d -- ^ Discrete probability distribution (as from "Statistics.Distribution")
-> Int -- ^ Randomness seed
-> m (Tensor Int) -- ^ Generated tensor
-- If only one upper index is given, generate a SimpleFinite tensor with upper index
randomIntSeed ([u],[s]) ([],[]) distr seed = do
gen <- MWC.initialize (Boxed.singleton $ fromIntegral seed)
component <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen
return $ SimpleFinite (Contravariant s [u]) component
-- If only one lower index is given, generate a SimpleFinite tensor with lower index
randomIntSeed ([],[]) ([d],[s]) distr seed = do
gen <- MWC.initialize (Boxed.singleton $ fromIntegral seed)
component <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen
return $ SimpleFinite (Covariant s [d]) component
-- If many indices are given, first generate upper indices recursively from indices list
randomIntSeed (u:us,s:size) d distr seed = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomIntSeed (us,size) d distr seed
return $ FiniteTensor (Contravariant s [u]) tensors
-- After upper indices, generate lower indices recursively from indices list
randomIntSeed u (d:ds,s:size) distr seed = do
tensors <- sequence $ Boxed.generate s $ \_ -> randomIntSeed u (ds,size) distr seed
return $ FiniteTensor (Covariant s [d]) tensors
-- If there are indices without size or sizes without names, throw an error
randomIntSeed us ds _ _ = return $ Err $ invalidIndices us ds