multilinear 0.2.3.0 → 0.3.0.0
raw patch · 15 files changed
+422/−739 lines, 15 filesdep +weighsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: weigh
API changes (from Hackage documentation)
- Multilinear.Class: iMap :: Accessible t a => ([Int] -> a -> b) -> t a -> t b
- Multilinear.Class: infix 8 $|
- Multilinear.Class: infixl 9 $$|
- Multilinear.Form: const' :: Num a => String -> a -> Tensor a
- Multilinear.Form: fromIndices' :: Num a => String -> (Int -> a) -> Tensor a
- Multilinear.Generic: Err :: String -> Tensor a
- Multilinear.Generic: FiniteTensor :: Index -> Vector (Tensor a) -> Tensor a
- Multilinear.Generic: InfiniteTensor :: Index -> [Tensor a] -> Tensor a
- Multilinear.Generic: Scalar :: a -> Tensor a
- Multilinear.Generic: SimpleFinite :: Index -> Vector a -> Tensor a
- Multilinear.Generic: [errMessage] :: Tensor a -> String
- Multilinear.Generic: [scalarVal] :: Tensor a -> a
- Multilinear.Generic: [tensorFiniteIndex] :: Tensor a -> Index
- Multilinear.Generic: [tensorInfiniteIndex] :: Tensor a -> Index
- Multilinear.Generic: [tensorScalars] :: Tensor a -> Vector a
- Multilinear.Generic: [tensorsFinite] :: Tensor a -> Vector (Tensor a)
- Multilinear.Generic: [tensorsInfinite] :: Tensor a -> [Tensor a]
- Multilinear.Generic: instance (GHC.Num.Num a, Data.Bits.Bits a) => Data.Bits.Bits (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance (GHC.Show.Show a, GHC.Num.Num a) => GHC.Show.Show (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance GHC.Base.Functor Multilinear.Generic.Tensor
- Multilinear.Generic: instance GHC.Classes.Eq a => GHC.Classes.Eq (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance GHC.Classes.Ord a => GHC.Classes.Ord (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance GHC.Float.Floating a => GHC.Float.Floating (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance GHC.Num.Num a => GHC.Num.Num (Multilinear.Generic.Tensor a)
- Multilinear.Generic: instance GHC.Num.Num a => Multilinear.Class.Accessible Multilinear.Generic.Tensor a
- Multilinear.Generic: instance GHC.Num.Num a => Multilinear.Class.Multilinear Multilinear.Generic.Tensor a
- Multilinear.Generic: instance GHC.Real.Fractional a => GHC.Real.Fractional (Multilinear.Generic.Tensor a)
- Multilinear.Generic: isInfiniteTensor :: Tensor a -> Bool
- Multilinear.Generic: mergeScalars :: Tensor a -> Tensor a
+ Multilinear.Class: infix 9 $|
+ Multilinear.Class: infixl 4 .-.
+ Multilinear.Class: infixl 5 .*.
+ Multilinear.Class: infixl 6 <<<|
+ Multilinear.Generic: [FiniteTensor] :: {tensorFiniteIndex :: Index " Finite index "Mutltilinear.Index.Finite" of tensor ", tensorsFinite :: Vector (Tensor a) " Array of tensors on deeper recursion level "} -> Tensor a
+ Multilinear.Generic: [Scalar] :: {scalarVal :: a} -> Tensor a
+ Multilinear.Generic: [SimpleFinite] :: {tensorFiniteIndex :: Index, tensorScalars :: Vector a} -> Tensor a
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Float.Floating a, Control.DeepSeq.NFData a) => GHC.Float.Floating (Multilinear.Generic.Tensor a)
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Num.Num a, Control.DeepSeq.NFData a) => GHC.Num.Num (Multilinear.Generic.Tensor a)
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Num.Num a, Control.DeepSeq.NFData a) => Multilinear.Class.Accessible Multilinear.Generic.Tensor a
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Num.Num a, Control.DeepSeq.NFData a) => Multilinear.Class.Multilinear Multilinear.Generic.Tensor a
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Real.Fractional a, Control.DeepSeq.NFData a) => GHC.Real.Fractional (Multilinear.Generic.Tensor a)
+ Multilinear.Generic: instance (Data.Vector.Unboxed.Base.Unbox a, GHC.Show.Show a, GHC.Num.Num a, Control.DeepSeq.NFData a) => GHC.Show.Show (Multilinear.Generic.Tensor a)
+ Multilinear.Generic: instance (GHC.Classes.Eq a, Data.Vector.Unboxed.Base.Unbox a) => GHC.Classes.Eq (Multilinear.Generic.Tensor a)
+ Multilinear.Generic: instance (GHC.Classes.Ord a, Data.Vector.Unboxed.Base.Unbox a) => GHC.Classes.Ord (Multilinear.Generic.Tensor a)
- Multilinear.Class: class (Num (t a), Functor t) => Multilinear t a
+ Multilinear.Class: class (Unbox a, Num (t a)) => Multilinear t a
- Multilinear.Class: infixl 7 \/
+ Multilinear.Class: infixl 7 $$|
- Multilinear.Class: infixl 8 .*
+ Multilinear.Class: infixl 8 \/
- Multilinear.Class: map :: Multilinear t a => (a -> b) -> t a -> t b
+ Multilinear.Class: map :: (Multilinear t a, Unbox b) => (a -> b) -> t a -> t b
- Multilinear.Form: const :: Num a => String -> Int -> a -> Tensor a
+ Multilinear.Form: const :: (Num a, Unbox a) => String -> Int -> a -> Tensor a
- Multilinear.Form: fromIndices :: Num a => String -> Int -> (Int -> a) -> Tensor a
+ Multilinear.Form: fromIndices :: (Num a, Unbox a) => String -> Int -> (Int -> a) -> Tensor a
- Multilinear.Generic: (!) :: Tensor a -> Int -> Tensor a
+ Multilinear.Generic: (!) :: Unbox a => Tensor a -> Int -> Tensor a
- Multilinear.Generic: _elemByElem :: Num a => Tensor a -> Tensor a -> (a -> a -> a) -> (Tensor a -> Tensor a -> Tensor a) -> Tensor a
+ Multilinear.Generic: _elemByElem :: (Num a, Unbox a, NFData a) => Tensor a -> Tensor a -> (a -> a -> a) -> (Tensor a -> Tensor a -> Tensor a) -> Tensor a
- Multilinear.Generic: _standardize :: Num a => Tensor a -> Tensor a
+ Multilinear.Generic: _standardize :: (Num a, Unbox a, NFData a) => Tensor a -> Tensor a
- Multilinear.Generic: dot :: Num a => Tensor a -> Tensor a -> Tensor a
+ Multilinear.Generic: dot :: (Num a, Unbox a, NFData a) => Tensor a -> Tensor a -> Tensor a
- Multilinear.Generic: isFiniteTensor :: Tensor a -> Bool
+ Multilinear.Generic: isFiniteTensor :: Unbox a => Tensor a -> Bool
- Multilinear.Generic: isScalar :: Tensor a -> Bool
+ Multilinear.Generic: isScalar :: Unbox a => Tensor a -> Bool
- Multilinear.Generic: isSimple :: Tensor a -> Bool
+ Multilinear.Generic: isSimple :: Unbox a => Tensor a -> Bool
- Multilinear.Generic: tensorIndex :: Tensor a -> TIndex
+ Multilinear.Generic: tensorIndex :: Unbox a => Tensor a -> TIndex
- Multilinear.Matrix: const :: Num a => String -> Int -> Int -> a -> Tensor a
+ Multilinear.Matrix: const :: (Num a, Unbox a) => String -> Int -> Int -> a -> Tensor a
- Multilinear.Matrix: fromIndices :: Num a => String -> Int -> Int -> (Int -> Int -> a) -> Tensor a
+ Multilinear.Matrix: fromIndices :: (Num a, Unbox a) => String -> Int -> Int -> (Int -> Int -> a) -> Tensor a
- Multilinear.NForm: const :: Num a => String -> [Int] -> a -> Tensor a
+ Multilinear.NForm: const :: (Num a, Unbox a) => String -> [Int] -> a -> Tensor a
- Multilinear.NForm: cross :: Num a => String -> Int -> Tensor a
+ Multilinear.NForm: cross :: (Num a, Unbox a) => String -> Int -> Tensor a
- Multilinear.NForm: dot :: Num a => String -> Int -> Tensor a
+ Multilinear.NForm: dot :: (Num a, Unbox a) => String -> Int -> Tensor a
- Multilinear.NForm: fromIndices :: Num a => String -> [Int] -> ([Int] -> a) -> Tensor a
+ Multilinear.NForm: fromIndices :: (Num a, Unbox a) => String -> [Int] -> ([Int] -> a) -> Tensor a
- Multilinear.NVector: const :: Num a => String -> [Int] -> a -> Tensor a
+ Multilinear.NVector: const :: (Num a, Unbox a) => String -> [Int] -> a -> Tensor a
- Multilinear.NVector: fromIndices :: Num a => String -> [Int] -> ([Int] -> a) -> Tensor a
+ Multilinear.NVector: fromIndices :: (Num a, Unbox a) => String -> [Int] -> ([Int] -> a) -> Tensor a
- Multilinear.Tensor: const :: Num a => (String, [Int]) -> (String, [Int]) -> a -> Tensor a
+ Multilinear.Tensor: const :: (Num a, Unbox a) => (String, [Int]) -> (String, [Int]) -> a -> Tensor a
- Multilinear.Tensor: fromIndices :: Num a => (String, [Int]) -> (String, [Int]) -> ([Int] -> [Int] -> a) -> Tensor a
+ Multilinear.Tensor: fromIndices :: (Num a, Unbox a) => (String, [Int]) -> (String, [Int]) -> ([Int] -> [Int] -> a) -> Tensor a
- Multilinear.Tensor: generate :: Num a => (String, [Int]) -> (String, [Int]) -> ([Int] -> [Int] -> Tensor a) -> Tensor a
+ Multilinear.Tensor: generate :: (Num a, Unbox a) => (String, [Int]) -> (String, [Int]) -> ([Int] -> [Int] -> Tensor a) -> Tensor a
- Multilinear.Vector: const :: Num a => String -> Int -> a -> Tensor a
+ Multilinear.Vector: const :: (Num a, Unbox a) => String -> Int -> a -> Tensor a
- Multilinear.Vector: fromIndices :: Num a => String -> Int -> (Int -> a) -> Tensor a
+ Multilinear.Vector: fromIndices :: (Num a, Unbox a) => String -> Int -> (Int -> a) -> Tensor a
Files
- ChangeLog.md +8/−1
- Setup.hs +2/−2
- benchmark/Bench.hs +0/−30
- benchmark/memory/Bench.hs +37/−0
- benchmark/time/Bench.hs +36/−0
- multilinear.cabal +30/−16
- src/Multilinear/Class.hs +37/−26
- src/Multilinear/Form.hs +16/−43
- src/Multilinear/Generic.hs +124/−541
- src/Multilinear/Matrix.hs +15/−14
- src/Multilinear/NForm.hs +15/−14
- src/Multilinear/NVector.hs +9/−8
- src/Multilinear/Tensor.hs +38/−29
- src/Multilinear/Vector.hs +15/−14
- test/Spec.hs +40/−1
ChangeLog.md view
@@ -1,4 +1,11 @@-# 0.2.2.2, 2018-11-02 +# 0.3.0.0, 2018-11-14 +- moved to Vector.Unboxed, to improve performance +- simplified error handling - removed separate Err Tensor value +- temporarily removed Infinite tensors constructors and Generic +- added memory benchmark +- added simple unit tests + +# 0.2.3, 2018-11-02 - fixed conflicting dependencies - added criterion benchmarks
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple -main = defaultMain +import Distribution.Simple+main = defaultMain
− benchmark/Bench.hs
@@ -1,30 +0,0 @@-{-| -Module : Bench -Description : Benchmark of Multilinear library -Copyright : (c) Artur M. Brodzki, 2018 -License : BSD3 -Maintainer : artur@brodzki.org -Stability : experimental -Portability : Windows/POSIX - --} - -module Main ( - main -) where - -import Criterion.Main -import qualified Multilinear.Matrix as Matrix - -gen :: Int -> Int -> Double -gen j k = sin (fromIntegral j) + cos (fromIntegral k) - -sizedMatrixBench :: Int -> Benchmark -sizedMatrixBench s = - bench ((show s) ++ "x" ++ (show s)) $ - nf ((Matrix.fromIndices "ij" s s gen) *) (Matrix.fromIndices "jk" s s gen) - -main :: IO () -main = defaultMain [ - bgroup "matrix multiplication" $ sizedMatrixBench <$> [10, 20, 50, 80, 160, 320] - ]
+ benchmark/memory/Bench.hs view
@@ -0,0 +1,37 @@+{-| +Module : Bench +Description : Benchmark of Multilinear library +Copyright : (c) Artur M. Brodzki, 2018 +License : BSD3 +Maintainer : artur@brodzki.org +Stability : experimental +Portability : Windows/POSIX + +-} + +module Main ( + main +) where + +import Weigh +import qualified Multilinear.Matrix as Matrix +import qualified Multilinear.Vector as Vector + +gen :: Int -> Int -> Double +gen j k = sin (fromIntegral j) + cos (fromIntegral k) + +main :: IO () +main = mainWith (do + setColumns [Case, Allocated, GCs, Live, Max] + func "vector 1 elem generation" (Vector.fromIndices "i" 1) id + func "vector 2 elem generation" (Vector.fromIndices "i" 2) id + func "vector 3 elem generation" (Vector.fromIndices "i" 3) id + func "matrix 1024 x 1024 generation" + (Matrix.fromIndices "ij" 1024 1024) gen + func "matrix 10214 x 1024 addition" + (+ Matrix.fromIndices "ab" 1024 1024 gen) + (Matrix.fromIndices "ab" 1024 1024 (\a b -> fromIntegral a + fromIntegral b)) + func "matrix 40 x 60,000 multiplication" + (* Matrix.fromIndices "jk" 60000 40 gen) + (Matrix.fromIndices "ij" 40 60000 gen) + )
+ benchmark/time/Bench.hs view
@@ -0,0 +1,36 @@+{-| +Module : Bench +Description : Benchmark of Multilinear library +Copyright : (c) Artur M. Brodzki, 2018 +License : BSD3 +Maintainer : artur@brodzki.org +Stability : experimental +Portability : Windows/POSIX + +-} + +module Main ( + main +) where + +import Criterion.Main +import qualified Multilinear.Matrix as Matrix + +gen :: Int -> Int -> Double +gen j k = sin (fromIntegral j) + cos (fromIntegral k) + +sizedMatrixMultBench :: Int -> Benchmark +sizedMatrixMultBench s = + bench ((show s) ++ "x" ++ (show s)) $ + nf ((Matrix.fromIndices "ij" s s gen) *) (Matrix.fromIndices "jk" s s gen) + +sizedMatrixAddBench :: Int -> Benchmark +sizedMatrixAddBench s = + bench ((show s) ++ "x" ++ (show s)) $ + nf ((Matrix.fromIndices "ij" s s gen) +) (Matrix.fromIndices "ij" s s gen) + +main :: IO () +main = defaultMain [ + bgroup "matrix multiplication" $ sizedMatrixMultBench <$> [64, 128, 256, 512], + bgroup "matrix addition" $ sizedMatrixAddBench <$> [64, 128, 256, 512] + ]
multilinear.cabal view
@@ -1,13 +1,11 @@-cabal-version: 1.12 ---- This file has been generated from package.yaml by hpack version 0.31.0.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: 7041c3d6136ceb4cb94868756b2c05085d09b4eb61eadb530dee0253bb02434c+-- hash: 9a78adbc87a7e540f962949e44f5f5e34d8cbdfd6a8e7b8b77c7c70a029e6df0 name: multilinear-version: 0.2.3.0+version: 0.3.0.0 synopsis: Comprehensive and efficient (multi)linear algebra implementation. description: Comprehensive and efficient (multi)linear algebra implementation, based on generic tensor formalism and concise Ricci-Curbastro index syntax. More information available on GitHub: <https://github.com/ArturB/multilinear#readme> category: Machine learning@@ -19,9 +17,10 @@ license: BSD3 license-file: LICENSE build-type: Simple+cabal-version: >= 1.10 extra-source-files:- README.md ChangeLog.md+ README.md source-repository head type: git@@ -45,8 +44,8 @@ Paths_multilinear hs-source-dirs: src- default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses- ghc-options: -O2 -Wall+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances GADTs MultiParamTypeClasses ScopedTypeVariables StandaloneDeriving+ ghc-options: -O2 -W build-depends: base >=4.7 && <5 , containers@@ -57,30 +56,45 @@ , vector default-language: Haskell2010 -test-suite multilinear-test+test-suite all type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: Paths_multilinear hs-source-dirs: test- default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses- ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances GADTs MultiParamTypeClasses ScopedTypeVariables StandaloneDeriving+ ghc-options: -O2 -W -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , criterion+ , deepseq , multilinear default-language: Haskell2010 -benchmark multilinear-bench+benchmark memory type: exitcode-stdio-1.0 main-is: Bench.hs other-modules: Paths_multilinear hs-source-dirs:- benchmark- default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses- ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N+ benchmark/memory+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances GADTs MultiParamTypeClasses ScopedTypeVariables StandaloneDeriving+ ghc-options: -O2 -W -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.7 && <5+ , multilinear+ , weigh+ default-language: Haskell2010++benchmark time+ type: exitcode-stdio-1.0+ main-is: Bench.hs+ other-modules:+ Paths_multilinear+ hs-source-dirs:+ benchmark/time+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances GADTs MultiParamTypeClasses ScopedTypeVariables StandaloneDeriving+ ghc-options: -O2 -W -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 , criterion
src/Multilinear/Class.hs view
@@ -149,54 +149,56 @@ import Data.Maybe import Data.Set +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Index {-| Multidimensional array treated as multilinear map - tensor -} class ( - Num (t a), -- Tensors may be added, subtracted and multiplicated - Functor t -- Tensor should be a Functor for convenience + Unboxed.Unbox a, + Num (t a) -- Tensors may be added, subtracted and multiplicated ) => Multilinear t a where {-| Add scalar @a@ to each element of tensor @t@ -} - infixl 7 +. + infixl 4 +. (+.) :: a -> t a -> t a {-| Subtract each element of tensor @t@ from scalar scalar left -} - infixl 7 -. + infixl 4 -. (-.) :: a -> t a -> t a {-| Multiply scalar @a@ by each element of tensor @t@ -} - infixl 8 *. + infixl 5 *. (*.) :: a -> t a -> t a {-| Add each element of tensor @t@ to scalar @a@ -} - infixl 7 .+ + infixl 4 .+ (.+) :: t a -> a -> t a {-| Subtract scalar @a@ from each element of tensor @t@ -} - infixl 7 .- + infixl 4 .- (.-) :: t a -> a -> t a {-| Multiply each element of tensor @t@ by scalar @a@ -} - infixl 8 .* + infixl 5 .* (.*) :: t a -> a -> t a {-| Tensor adding - functionally equal to Num (+) but more efficient -} - infixl 7 .+. + infixl 4 .+. (.+.) :: t a -> t a -> t a {-| Tensor subtracting - functionally equal to Num (-) but more efficient -} - infixl 7 .-. + infixl 4 .-. (.-.) :: t a -> t a -> t a {-| Tensor multiplication - functionally equal to Num (*) but more efficient -} - infixl 7 .*. + infixl 5 .*. (.*.) :: t a -> t a -> t a {-| List of all tensor indices -} indices :: t a -> [TIndex] {-| List of tensor indices names -} + {-# INLINE indicesNames #-} indicesNames :: t a -> [String] indicesNames t = indexName <$> indices t @@ -208,33 +210,39 @@ size :: t a -> String -> Int {-| Check if tensors are equivalent (have same indices but in different order) -} + {-# INLINE equiv #-} equiv :: t a -> t a -> Bool equiv t1 t2 = Data.Set.fromList (indices t1) == Data.Set.fromList (indices t2) {-| Infix equivalent of 'equiv'. Has low priority equal to 1. |-} + {-# INLINE (|==|) #-} infixl 1 |==| (|==|) :: t a -> t a -> Bool t1 |==| t2 = equiv t1 t2 {-| @t $| "ij" "kl"@ renames upper indices of tensor @t@ to @ij@ and lower indices to @kl@ -} - infix 8 $| + infix 9 $| ($|) :: t a -> (String,String) -> t a {-| @raise t "i"@ raises an index @i@ of tensor @t@ -} + {-# INLINE raise #-} raise :: t a -> String -> t a raise t i = t /\ i {-| Infix equivalent of 'raise' -} - infixl 7 /\ + {-# INLINE (/\) #-} + infixl 8 /\ (/\) :: t a -> String -> t a t /\ i = raise t i {-| @lower t "i"@ lowers an index @i@ of tensor @t@ -} + {-# INLINE lower #-} lower :: t a -> String -> t a lower t i = t \/ i {-| Infix equivalent of 'lower' -} - infixl 7 \/ + {-# INLINE (\/) #-} + infixl 8 \/ (\/) :: t a -> String -> t a t \/ i = lower t i @@ -244,6 +252,7 @@ {-| Shift tensor index right -} {-| @shiftRight t "i"@ moves index @i@ of tensor @t@ one level depeer in recursion. Elements of tensor as indexed with indices names becomes unchanged. -} + {-# INLINE shiftRight #-} shiftRight :: t a -> String -> t a -- ^ Right shift of an index is equivalent to left shift of its successor in recursion @s@, if only @s@ exists, so: -- Given a tensor @t[i1,i2,i3,...]@: @shiftRight t "i2" == t[i1,i3,i2,...] == shiftLeft t "i3"@ @@ -256,24 +265,28 @@ {-| Infix equivalent of 'shiftRight' -} {-| @t |>> "i"@ moves index @i@ of tensor @t@ one level depeer in recursion -} - infixl 9 |>> + {-# INLINE (|>>) #-} + infixl 6 |>> (|>>) :: t a -> String -> t a t |>> n = shiftRight t n {-| Shift tensor index rightmost -} {-| @shiftRightmost t "i"@ moves index @i@ of tensor @t@ to the deepest level in recursion. Elements of tensor as indexed with indices names becomes unchanged. -} + {-# INLINE shiftRightmost #-} shiftRightmost :: t a -> String -> t a shiftRightmost t n = until (\x -> n == last (indicesNames x)) (|>> n) t {-| Infix equivalent of 'shiftRightmost' -} {-| @t |>>> "i"@ moves index @i@ of tensor @t@ to the deepest level in recursion -} - infixl 9 |>>> + {-# INLINE (|>>>) #-} + infixl 6 |>>> (|>>>) :: t a -> String -> t a t |>>> n = shiftRightmost t n {-| Shift tensor index left. Elements of tensor as indexed with indices names becomes unchanged. -} {-| @shiftLeft t "i"@ moves index @i@ of tensor @t@ one level up in recursion -} + {-# INLINE shiftLeft #-} shiftLeft :: t a -> String -> t a -- ^ Left shift of an index is equivalent to right shift of its predecessor in recursion @p@, if only @p@ exists, so: -- Given a tensor t[i1,i2,i3,...]: @shiftLeft t "i3" == t[i1,i3,i2,...] == shiftRight t "i2"@ @@ -286,25 +299,27 @@ {-| Infix equivalent to 'shiftLeft' -} {-| @t <<| "i"@ moves index @i@ of tensor @t@ one level up in recursion -} - infixl 9 <<| + {-# INLINE (<<|) #-} + infixl 6 <<| (<<|) :: t a -> String -> t a t <<| n = shiftLeft t n {-| Shift tensor index leftmost. Elements of tensor as indexed with indices names becomes unchanged. -} {-| @shiftLeftmost t "i"@ moves index @i@ of tensor @t@ to the first level in recursion -} + {-# INLINE shiftLeftmost #-} shiftLeftmost :: t a -> String -> t a shiftLeftmost t n = until (\x -> n == head (indicesNames x)) (<<| n) t {-| Infix equivalent of 'shiftLeftmost' -} {-| @t <<<| "i"@ moves index @i@ of tensor @t@ to the first level in recursion -} - infixl 9 <<<| + {-# INLINE (<<<|) #-} + infixl 6 <<<| (<<<|) :: t a -> String -> t a t <<<| n = shiftLeftmost t n {-| Simple mapping -} {-| @map f t@ returns tensor @t2@ in which @t2[i1,i2,...] = f t[i1,i2,...]@ -} - map :: (a -> b) -> t a -> t b - map = fmap + map :: Unboxed.Unbox b => (a -> b) -> t a -> t b {-| If container on which tensor instance is built, allows for random access of its elements, then the tensor can be instanced as Accessible -} @@ -318,11 +333,7 @@ el :: t a -> (String,[Int]) -> t a {-| Infix equivalent for el -} - infixl 9 $$| + {-# INLINE ($$|) #-} + infixl 7 $$| ($$|) :: t a -> (String,[Int]) -> t a t $$| is = el t is - - {-| Mapping with indices - mapping function takes not only a tensor element value but also its indices in tensor -} - {-| @iMap f t@ return tensor @t2@ in which @t2[i1,i2,...] = f [i1,i2,...] t[i1,i2,...]@ -} - iMap :: ([Int] -> a -> b) -> t a -> t b - -- // TODO
src/Multilinear/Form.hs view
@@ -20,15 +20,12 @@ Multilinear.Form.randomDouble, Multilinear.Form.randomDoubleSeed, Multilinear.Form.randomInt, - Multilinear.Form.randomIntSeed, - -- ** Infinite functionals - Multilinear.Form.fromIndices', - Multilinear.Form.const' + Multilinear.Form.randomIntSeed ) where import Control.Monad.Primitive +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic -import Multilinear.Index.Infinite as Infinite import Multilinear.Tensor as Tensor import Statistics.Distribution @@ -38,28 +35,28 @@ -- * Finite functional generators {-| Generate linear functional as function of indices -} -{-# INLINE fromIndices #-} + fromIndices :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Index name (one character) -> Int -- ^ Number of elements -> (Int -> a) -- ^ Generator function - returns a linear functional component at index @i@ -> Tensor a -- ^ Generated linear functional fromIndices [i] s f = Tensor.fromIndices ([],[]) ([i],[s]) $ \[] [x] -> f x -fromIndices _ _ _ = Err invalidIndices +fromIndices _ _ _ = error invalidIndices {-| Generate linear functional with all components equal to some @v@ -} -{-# INLINE Multilinear.Form.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Index name (one character) -> Int -- ^ Number of elements -> a -- ^ Value of each element -> Tensor a -- ^ Generated linear functional const [i] s = Tensor.const ([],[]) ([i],[s]) -const _ _ = \_ -> Err invalidIndices +const _ _ = \_ -> error invalidIndices {-| Generate linear functional with random real components with given probability distribution. The functional is wrapped in the IO monad. -} @@ -74,7 +71,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDouble #-} + randomDouble :: ( ContGen d ) => String -- ^ Index name (one character) @@ -83,7 +80,7 @@ -> IO (Tensor Double) -- ^ Generated linear functional randomDouble [i] s = Tensor.randomDouble ([],[]) ([i],[s]) -randomDouble _ _ = \_ -> return $ Err invalidIndices +randomDouble _ _ = \_ -> return $ error invalidIndices {-| Generate linear functional with random integer components with given probability distribution. The functional is wrapped in the IO monad. -} @@ -92,7 +89,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomInt #-} + randomInt :: ( DiscreteGen d ) => String -- ^ Index name (one character) @@ -101,7 +98,7 @@ -> IO (Tensor Int) -- ^ Generated linear functional randomInt [i] s = Tensor.randomInt ([],[]) ([i],[s]) -randomInt _ _ = \_ -> return $ Err invalidIndices +randomInt _ _ = \_ -> return $ error invalidIndices {-| Generate linear functional with random real components with given probability distribution and given seed. The functional is wrapped in a monad. -} @@ -116,7 +113,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDoubleSeed #-} + randomDoubleSeed :: ( ContGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -126,7 +123,7 @@ -> m (Tensor Double) -- ^ Generated linear functional randomDoubleSeed [i] s = Tensor.randomDoubleSeed ([],[]) ([i],[s]) -randomDoubleSeed _ _ = \_ _ -> return $ Err invalidIndices +randomDoubleSeed _ _ = \_ _ -> return $ error invalidIndices {-| Generate linear functional with random integer components with given probability distribution and given seed. The functional is wrapped in a monad. -} @@ -135,7 +132,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomIntSeed #-} + randomIntSeed :: ( DiscreteGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -145,29 +142,5 @@ -> m (Tensor Int) -- ^ Generated linear functional randomIntSeed [i] s = Tensor.randomIntSeed ([],[]) ([i],[s]) -randomIntSeed _ _ = \_ _ -> return $ Err invalidIndices - -{-| Generate linear functional as function of indices -} -{-# INLINE fromIndices' #-} -fromIndices' :: ( - Num a - ) => String -- ^ Index name (one character) - -> (Int -> a) -- ^ Generator function - returns a linear functional component at index @i@ - -> Tensor a -- ^ Generated linear functional - -fromIndices' i = case i of - [d] -> \f -> InfiniteTensor (Infinite.Covariant [d]) $ (Scalar . f) <$> [0..] - _ -> \_ -> Err invalidIndices - -{-| Generate linear functional with all components equal to some @v@ -} -{-# INLINE Multilinear.Form.const' #-} -const' :: ( - Num a - ) => String -- ^ Index name (one character) - -> a -- ^ Value of each element - -> Tensor a -- ^ Generated linear functional - -const' i = case i of - [d] -> \v -> InfiniteTensor (Infinite.Covariant [d]) $ (\_ -> Scalar v) <$> ([0..] :: [Int]) - _ -> \_ -> Err invalidIndices +randomIntSeed _ _ = \_ _ -> return $ error invalidIndices
src/Multilinear/Generic.hs view
@@ -12,22 +12,21 @@ -} module Multilinear.Generic ( - Tensor(..), (!), mergeScalars, - isScalar, isSimple, isFiniteTensor, isInfiniteTensor, + Tensor(..), (!), + isScalar, isSimple, isFiniteTensor, dot, _elemByElem, contractionErr, tensorIndex, _standardize ) where import Control.DeepSeq -import Data.Bits import Data.Foldable import Data.List import Data.Maybe import qualified Data.Vector as Boxed +import qualified Data.Vector.Unboxed as Unboxed import GHC.Generics import Multilinear.Class as Multilinear import qualified Multilinear.Index as Index import qualified Multilinear.Index.Finite as Finite -import qualified Multilinear.Index.Infinite as Infinite {-| ERROR MESSAGES -} incompatibleTypes :: String @@ -36,148 +35,102 @@ scalarIndices :: String scalarIndices = "Scalar has no indices!" -infiniteIndex :: String -infiniteIndex = "Index is infinitely-dimensional!" - -infiniteTensor :: String -infiniteTensor = "This tensor is infinitely-dimensional and cannot be printed!" - indexNotFound :: String indexNotFound = "This tensor has not such index!" {-| Tensor defined recursively as scalar or list of other tensors -} {-| @c@ is type of a container, @i@ is type of index size and @a@ is type of tensor elements -} -data Tensor a = +data Tensor a where {-| Scalar -} - Scalar { - {-| value of scalar -} + Scalar :: { scalarVal :: a - } | + } -> Tensor a {-| Simple, one-dimensional finite tensor -} - SimpleFinite { + SimpleFinite :: { tensorFiniteIndex :: Finite.Index, - tensorScalars :: Boxed.Vector a - } | + tensorScalars :: Unboxed.Vector a + } -> Tensor a {-| Finite array of other tensors -} - FiniteTensor { + FiniteTensor :: { {-| Finite index "Mutltilinear.Index.Finite" of tensor -} tensorFiniteIndex :: Finite.Index, {-| Array of tensors on deeper recursion level -} tensorsFinite :: Boxed.Vector (Tensor a) - } | - {-| Infinite list of other tensors -} - InfiniteTensor { - {-| Infinite index "Mutltilinear.Index.Infinite" of tensor -} - tensorInfiniteIndex :: Infinite.Index, - {-| Infinite list of tensors on deeper recursion level -} - tensorsInfinite :: [Tensor a] - } | - {-| Operations on tensors may throw an error -} - Err { - {-| Error message -} - errMessage :: String - } deriving (Eq, Generic) + } -> Tensor a + deriving (Eq, Generic) + {-| Return true if tensor is a scalar -} {-# INLINE isScalar #-} -isScalar :: Tensor a -> Bool +isScalar :: Unboxed.Unbox a => Tensor a -> Bool isScalar x = case x of Scalar _ -> True _ -> False {-| Return true if tensor is a simple tensor -} {-# INLINE isSimple #-} -isSimple :: Tensor a -> Bool +isSimple :: Unboxed.Unbox a => Tensor a -> Bool isSimple x = case x of SimpleFinite _ _ -> True _ -> False {-| Return True if tensor is a complex tensor -} {-# INLINE isFiniteTensor #-} -isFiniteTensor :: Tensor a -> Bool +isFiniteTensor :: Unboxed.Unbox a => Tensor a -> Bool isFiniteTensor x = case x of FiniteTensor _ _ -> True _ -> False -{-| Return True if tensor is a infinite tensor -} -{-# INLINE isInfiniteTensor #-} -isInfiniteTensor :: Tensor a -> Bool -isInfiniteTensor x = case x of - InfiniteTensor _ _ -> True - _ -> False - -{- Return True if tensor is a error tensor -} -{-# INLINE isErrTensor #-} -isErrTensor :: Tensor a -> Bool -isErrTensor x = case x of - Err _ -> True - _ -> False - {-| Return generic tensor index -} {-# INLINE tensorIndex #-} -tensorIndex :: Tensor a -> Index.TIndex +tensorIndex :: Unboxed.Unbox a => Tensor a -> Index.TIndex tensorIndex x = case x of Scalar _ -> error scalarIndices SimpleFinite i _ -> Index.toTIndex i FiniteTensor i _ -> Index.toTIndex i - InfiniteTensor i _ -> Index.toTIndex i - Err msg -> error msg -{-| Return True if tensor has no elements -} -{-# INLINE isEmptyTensor #-} -isEmptyTensor :: Tensor a -> Bool -isEmptyTensor x = case x of - Scalar _ -> False - SimpleFinite _ ts -> Boxed.null ts - FiniteTensor _ ts -> Boxed.null ts - InfiniteTensor _ ts -> null ts - Err _ -> False - -{-| Returns sample element of the tensor. Used to determine some features common for all elements, like bit-qualities. -} -{-# INLINE firstElem #-} -firstElem :: Tensor a -> a -firstElem x = case x of - Scalar val -> val - SimpleFinite _ ts -> Boxed.head ts - FiniteTensor _ ts -> firstElem $ Boxed.head ts - InfiniteTensor _ ts -> firstElem $ head ts - Err msg -> error msg - {-| Returns sample tensor on deeper recursion level.Used to determine some features common for all tensors -} {-# INLINE firstTensor #-} -firstTensor :: Tensor a -> Tensor a +firstTensor :: Unboxed.Unbox a => Tensor a -> Tensor a firstTensor x = case x of FiniteTensor _ ts -> Boxed.head ts - InfiniteTensor _ ts -> Data.List.head ts _ -> x {-| Recursive indexing on list tensor @t ! i = t[i]@ -} {-# INLINE (!) #-} -(!) :: Tensor a -- ^ tensor @t@ +(!) :: Unboxed.Unbox a => Tensor a -- ^ tensor @t@ -> Int -- ^ index @i@ -> Tensor a -- ^ tensor @t[i]@ t ! i = case t of - Scalar _ -> Err scalarIndices - Err msg -> Err msg - SimpleFinite ind ts -> if i >= Finite.indexSize ind then error ("Index + " ++ show ind ++ " out of bonds!") else Scalar $ ts Boxed.! i - FiniteTensor ind ts -> if i >= Finite.indexSize ind then error ("Index + " ++ show ind ++ " out of bonds!") else ts Boxed.! i - InfiniteTensor _ ts -> ts !! i + Scalar _ -> error scalarIndices + SimpleFinite ind ts -> + if i >= Finite.indexSize ind then + error ("Index + " ++ show ind ++ " out of bonds!") + else Scalar $ ts Unboxed.! i + FiniteTensor ind ts -> + if i >= Finite.indexSize ind then + error ("Index + " ++ show ind ++ " out of bonds!") + else ts Boxed.! i -- NFData instance instance NFData a => NFData (Tensor a) -- move contravariant indices to lower recursion level -_standardize :: Num a => Tensor a -> Tensor a -_standardize tens = foldr' (\i t -> if Index.isContravariant i then t <<<| Index.indexName i else t) tens $ indices tens +_standardize :: (Num a, Unboxed.Unbox a, NFData a) => Tensor a -> Tensor a +_standardize tens = foldr' f tens $ indices tens + where + f i t = if Index.isContravariant i then + t <<<| Index.indexName i + else t -- Print tensor instance ( - Show a, Num a + Unboxed.Unbox a, Show a, Num a, NFData a ) => Show (Tensor a) where -- merge errors first and then print whole tensor - show = show' . _standardize . _mergeErr + show = show' . _standardize where show' x = case x of -- Scalar is showed simply as its value @@ -185,71 +138,23 @@ -- SimpleFinite is shown dependent on its index... SimpleFinite index ts -> show index ++ "S: " ++ case index of -- If index is contravariant, show tensor components vertically - Finite.Contravariant _ _ -> _showVertical ts + Finite.Contravariant _ _ -> "\n" ++ tail (Unboxed.foldl' (\string e -> string ++ "\n |" ++ show e) "" ts) -- If index is covariant or indifferent, show tensor compoments horizontally - _ -> _showHorizontal ts + _ -> "[" ++ tail (Unboxed.foldl' (\string e -> string ++ "," ++ show e) "" ts) ++ "]" -- FiniteTensor is shown dependent on its index... FiniteTensor index ts -> show index ++ "T: " ++ case index of -- If index is contravariant, show tensor components vertically - Finite.Contravariant _ _ -> _showVertical ts + Finite.Contravariant _ _ -> "\n" ++ tail (Boxed.foldl' (\string e -> string ++ "\n |" ++ show e) "" ts) -- If index is covariant or indifferent, show tensor compoments horizontally - _ -> _showHorizontal ts - -- Infinite tensor print erorr message as it cannot be fully shown - InfiniteTensor _ _ -> show infiniteTensor - -- Error prints its error message - Err msg -> show msg - - -- Merge many errors in tensor to the first one - _mergeErr x = case x of - -- Error tensor is passed further - Err msg -> Err msg - -- FiniteTensor is merged to first error on deeper recursion level - FiniteTensor _ ts -> - -- find first error if present - let err = Data.List.find isErrTensor (_mergeErr <$> ts) - -- and return this error if found, whole tensor otherwise - in fromMaybe x err - -- in other types of tensor cannot be any error - _ -> x - - -- print container elements vertically - -- used to show contravariant components of tensor, which by convention are written vertically - _showVertical :: (Show a, Foldable c) => c a -> String - _showVertical container = - "\n" ++ tail (foldl' (\string e -> string ++ "\n |" ++ show e) "" container) - - -- print container elements horizontally - -- used to show covariant (or indifferent) components of tensor, which by convention are written horizontally - _showHorizontal :: (Show a, Foldable c) => c a -> String - _showHorizontal container = - "[" ++ tail (foldl' (\string e -> string ++ "," ++ show e) "" container) ++ "]" - --- Tensor is a functor -instance Functor Tensor where - - {-# INLINE fmap #-} - fmap f x = case x of - -- Mapping scalar simply maps its value - Scalar v -> Scalar $ f v - -- Mapping complex tensor does mapping element by element - SimpleFinite index ts -> SimpleFinite index (f <$> ts) - FiniteTensor index ts -> FiniteTensor index $ fmap (fmap f) ts - InfiniteTensor index ts -> InfiniteTensor index $ fmap (fmap f) ts - -- Mapping errors changes nothing - Err msg -> Err msg + _ -> "[" ++ tail (Boxed.foldl' (\string e -> string ++ "," ++ show e) "" ts) ++ "]" -- Tensors can be compared lexigographically -- Allowes to put tensors in typical ordered containers instance ( - Ord a + Ord a, Unboxed.Unbox a ) => Ord (Tensor a) where {-# INLINE (<=) #-} - -- Error is smaller by other tensors, so when printing ordered containers, all erorrs will be printed first - -- Two errors are compared by they messages lexigographically - Err msg1 <= Err msg2 = msg1 <= msg2 - Err _ <= _ = True - _ <= Err _ = False -- Scalar is smaller than any complex tensor -- Two scalars are compared by they values Scalar x1 <= Scalar x2 = x1 <= x2 @@ -258,71 +163,47 @@ -- Complex tensors are compared lexigographically SimpleFinite _ ts1 <= SimpleFinite _ ts2 = ts1 <= ts2 FiniteTensor _ ts1 <= FiniteTensor _ ts2 = ts1 <= ts2 - InfiniteTensor _ ts1 <= InfiniteTensor _ ts2 = ts1 <= ts2 FiniteTensor _ _ <= SimpleFinite _ _ = False SimpleFinite _ _ <= FiniteTensor _ _ = True - InfiniteTensor _ _ <= FiniteTensor _ _ = False - FiniteTensor _ _ <= InfiniteTensor _ _ = True - InfiniteTensor _ _ <= SimpleFinite _ _ = False - SimpleFinite _ _ <= InfiniteTensor _ _ = True {-| Merge FiniteTensor of Scalars to SimpleFinite tensor for performance improvement -} -{-# INLINE mergeScalars #-} -mergeScalars :: Tensor a -> Tensor a -mergeScalars x = case x of +{-# INLINE _mergeScalars #-} +_mergeScalars :: Unboxed.Unbox a => Tensor a -> Tensor a +_mergeScalars x = case x of (FiniteTensor index1 ts1) -> case ts1 Boxed.! 0 of - Scalar _ -> SimpleFinite index1 (scalarVal <$> ts1) - _ -> FiniteTensor index1 $ mergeScalars <$> ts1 + Scalar _ -> SimpleFinite index1 $ Unboxed.generate (Boxed.length ts1) (\i -> scalarVal (ts1 Boxed.! i)) + _ -> FiniteTensor index1 $ _mergeScalars <$> ts1 _ -> x {-| Apply a tensor operator (here denoted by (+) ) elem by elem, trying to connect as many common indices as possible -} {-# INLINE _elemByElem' #-} -_elemByElem' :: Num a +_elemByElem' :: (Num a, Unboxed.Unbox a, NFData a) => Tensor a -- ^ First argument of operator -> Tensor a -- ^ Second argument of operator - -> (a -> a -> a) -- ^ Operator on tensor elements if indices are different + -> (a -> a -> a) -- ^ Function on tensor elements if indices are different -> (Tensor a -> Tensor a -> Tensor a) -- ^ Tensor operator called if indices are the same -> Tensor a -- ^ Result tensor -- @Scalar x + Scalar y = Scalar x + y@ _elemByElem' (Scalar x1) (Scalar x2) f _ = Scalar $ f x1 x2 -- @Scalar x + Tensor t[i] = Tensor r[i] | r[i] = x + t[i]@ -_elemByElem' (Scalar x) t f _ = (x `f`) <$> t +_elemByElem' (Scalar x) t f _ = (x `f`) `Multilinear.map` t -- @Tensor t[i] + Scalar x = Tensor r[i] | r[i] = t[i] + x@ -_elemByElem' t (Scalar x) f _ = (`f` x) <$> t - --- Two finite tensors case -_elemByElem' t1@(FiniteTensor index1 v1) t2@(FiniteTensor index2 v2) f op - | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | Index.indexName index1 `Data.List.elem` indicesNames t2 = - FiniteTensor index2 $ (\x -> _elemByElem' t1 x f op) <$> v2 - | otherwise = FiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 - --- Two infinite tensors case -_elemByElem' t1@(InfiniteTensor index1 v1) t2@(InfiniteTensor index2 v2) f op - | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | Index.indexName index1 `Data.List.elem` indicesNames t2 = - InfiniteTensor index2 $ (\x -> _elemByElem' t1 x f op) <$> v2 - | otherwise = InfiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 +_elemByElem' t (Scalar x) f _ = (`f` x) `Multilinear.map` t -- Two simple tensors case _elemByElem' t1@(SimpleFinite index1 v1) t2@(SimpleFinite index2 _) f op | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | otherwise = FiniteTensor index1 $ (\x -> f x <$> t2) <$> v1 - --- Finite and infinite tensor case -_elemByElem' t1@(FiniteTensor index1 v1) t2@(InfiniteTensor index2 v2) f op - | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | Index.indexName index1 `Data.List.elem` indicesNames t2 = - InfiniteTensor index2 $ (\x -> _elemByElem' t1 x f op) <$> v2 - | otherwise = FiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 + | otherwise = FiniteTensor index1 $ + Boxed.generate (Unboxed.length v1) + (\i -> (\x -> f x `Multilinear.map` t2) (v1 Unboxed.! i)) --- Infinite and finite tensor case -_elemByElem' t1@(InfiniteTensor index1 v1) t2@(FiniteTensor index2 v2) f op +-- Two finite tensors case +_elemByElem' t1@(FiniteTensor index1 v1) t2@(FiniteTensor index2 v2) f op | Index.indexName index1 == Index.indexName index2 = op t1 t2 | Index.indexName index1 `Data.List.elem` indicesNames t2 = FiniteTensor index2 $ (\x -> _elemByElem' t1 x f op) <$> v2 - | otherwise = InfiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 + | otherwise = FiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 -- Simple and finite tensor case _elemByElem' t1@(SimpleFinite index1 _) t2@(FiniteTensor index2 v2) f op @@ -334,23 +215,9 @@ | Index.indexName index1 == Index.indexName index2 = op t1 t2 | otherwise = FiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 --- Simple and infinite tensor case -_elemByElem' t1@(SimpleFinite index1 _) t2@(InfiniteTensor index2 v2) f op - | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | otherwise = InfiniteTensor index2 $ (\x -> _elemByElem' t1 x f op) <$> v2 - --- Infinite and simple tensor case -_elemByElem' t1@(InfiniteTensor index1 v1) t2@(SimpleFinite index2 _) f op - | Index.indexName index1 == Index.indexName index2 = op t1 t2 - | otherwise = InfiniteTensor index1 $ (\x -> _elemByElem' x t2 f op) <$> v1 - --- Appying operator to error tensor simply pushes this error further -_elemByElem' (Err msg) _ _ _ = Err msg -_elemByElem' _ (Err msg) _ _ = Err msg - -{-| Apply a tensor operator elem by elem and merge scalars to simple tensor at the and -} +{-| Apply a tensor operator elem by elem -} {-# INLINE _elemByElem #-} -_elemByElem :: Num a +_elemByElem :: (Num a, Unboxed.Unbox a, NFData a) => Tensor a -- ^ First argument of operator -> Tensor a -- ^ Second argument of operator -> (a -> a -> a) -- ^ Operator on tensor elements if indices are different @@ -360,11 +227,11 @@ let commonIndices = filter (`Data.List.elem` indicesNames t2) $ indicesNames t1 t1' = foldl' (|>>>) t1 commonIndices t2' = foldl' (|>>>) t2 commonIndices - in mergeScalars $ _elemByElem' t1' t2' f op + in t1' `deepseq` t2' `deepseq` _mergeScalars $ _elemByElem' t1' t2' f op -- Zipping two tensors with a combinator, assuming they have the same indices {-# INLINE zipT #-} -zipT :: Num a +zipT :: (Num a, Unboxed.Unbox a, NFData a) => (Tensor a -> Tensor a -> Tensor a) -- ^ Two tensors combinator -> (Tensor a -> a -> Tensor a) -- ^ Tensor and scalar combinator -> (a -> Tensor a -> Tensor a) -- ^ Scalar and tensor combinator @@ -375,50 +242,36 @@ -- Two simple tensors case zipT _ _ _ f (SimpleFinite index1 v1) (SimpleFinite index2 v2) = - if index1 == index2 then SimpleFinite index1 $ Boxed.zipWith f v1 v2 else Err incompatibleTypes + if index1 == index2 then + SimpleFinite index1 $ Unboxed.zipWith f v1 v2 + else error incompatibleTypes --Two finite tensors case -zipT f _ _ _ (FiniteTensor index1 v1) (FiniteTensor index2 v2) = - if index1 == index2 then FiniteTensor index1 $ Boxed.zipWith f v1 v2 else Err incompatibleTypes - --- Two infinte tensors case -zipT f _ _ _ (InfiniteTensor index1 v1) (InfiniteTensor index2 v2) = - if index1 == index2 then InfiniteTensor index1 $ Data.List.zipWith f v1 v2 else Err incompatibleTypes - --- Infinite and finite tensor case -zipT f _ _ _ (InfiniteTensor _ v1) (FiniteTensor index2 v2) = - FiniteTensor index2 $ Boxed.zipWith f (Boxed.fromList $ take (Boxed.length v2) v1) v2 - --- Finite and infinite tensor case -zipT f _ _ _ (FiniteTensor index1 v1) (InfiniteTensor _ v2) = - FiniteTensor index1 $ Boxed.zipWith f v1 (Boxed.fromList $ take (Boxed.length v1) v2) +zipT f _ _ _ (FiniteTensor index1 v1) (FiniteTensor index2 v2) = + if index1 == index2 then + FiniteTensor index1 $ Boxed.zipWith f v1 v2 + else error incompatibleTypes -- Finite and simple tensor case -zipT _ f _ _ (FiniteTensor index1 v1) (SimpleFinite index2 v2) = - if index1 == index2 then FiniteTensor index1 $ Boxed.zipWith f v1 v2 else Err incompatibleTypes +zipT _ f _ _ (FiniteTensor index1 v1) (SimpleFinite index2 v2) = + if index1 == index2 then + FiniteTensor index1 $ + Boxed.generate (Boxed.length v1) (\i -> f (v1 Boxed.! i) (v2 Unboxed.! i)) + else error incompatibleTypes -- Simple and finite tensor case -zipT _ _ f _ (SimpleFinite index1 v1) (FiniteTensor index2 v2) = - if index1 == index2 then FiniteTensor index1 $ Boxed.zipWith f v1 v2 else Err incompatibleTypes - --- Infinite and simple tensor case -zipT _ f _ _ (InfiniteTensor _ v1) (SimpleFinite index2 v2) = - FiniteTensor index2 $ Boxed.zipWith f (Boxed.fromList $ take (Boxed.length v2) v1) v2 - --- Simple and infinite tensor case -zipT _ _ f _ (SimpleFinite index1 v1) (InfiniteTensor _ v2) = - FiniteTensor index1 $ Boxed.zipWith f v1 (Boxed.fromList $ take (Boxed.length v1) v2) - --- Zipping error tensor simply pushes this erorr further -zipT _ _ _ _ (Err msg) _ = Err msg -zipT _ _ _ _ _ (Err msg) = Err msg +zipT _ _ f _ (SimpleFinite index1 v1) (FiniteTensor index2 v2) = + if index1 == index2 then + FiniteTensor index1 $ + Boxed.generate (Unboxed.length v1) (\i -> f (v1 Unboxed.! i) (v2 Boxed.! i)) + else error incompatibleTypes -- Zipping something with scalar is impossible -zipT _ _ _ _ _ _ = Err scalarIndices +zipT _ _ _ _ _ _ = error scalarIndices -- dot product of two tensors {-# INLINE dot #-} -dot :: Num a +dot :: (Num a, Unboxed.Unbox a, NFData a) => Tensor a -- ^ First dot product argument -> Tensor a -- ^ Second dot product argument -> Tensor a -- ^ Resulting dot product @@ -426,148 +279,30 @@ -- Two simple tensors product dot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') | count1 == count2 = - Scalar $ Boxed.sum $ Boxed.zipWith (*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (SimpleFinite i1@(Finite.Contravariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = - SimpleFinite i1 $ Boxed.zipWith (*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Covariant count2 _) ts2') - | count1 == count2 = - SimpleFinite i1 $ Boxed.zipWith (*) ts1' ts2' + Scalar $ Unboxed.sum $ Unboxed.zipWith (*) ts1' ts2' | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -- Two finite tensors product dot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') | count1 == count2 = Boxed.sum $ Boxed.zipWith (*) ts1' ts2' | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (FiniteTensor i1@(Finite.Contravariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Covariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Simple tensor and finite tensor product -dot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = Boxed.sum $ Boxed.zipWith (*.) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (SimpleFinite i1@(Finite.Contravariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (*.) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Covariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (*.) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Finite tensor and simple tensor product -dot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = Boxed.sum $ Boxed.zipWith (.*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (FiniteTensor i1@(Finite.Contravariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (.*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) -dot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Covariant count2 _) ts2') - | count1 == count2 = FiniteTensor i1 $ Boxed.zipWith (.*) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Simple tensor and infinite tensor product -dot (SimpleFinite (Finite.Covariant count1 _) ts1') (InfiniteTensor (Infinite.Contravariant _) ts2') = - Boxed.sum $ Boxed.zipWith (*.) ts1' (Boxed.fromList $ take count1 ts2') -dot (SimpleFinite (Finite.Contravariant count1 _) ts1') (InfiniteTensor i2@(Infinite.Contravariant _) ts2') = - InfiniteTensor i2 $ Boxed.toList $ Boxed.zipWith (*.) ts1' (Boxed.fromList $ take count1 ts2') -dot (SimpleFinite (Finite.Covariant count1 _) ts1') (InfiniteTensor i2@(Infinite.Covariant _) ts2') = - InfiniteTensor i2 $ Boxed.toList $ Boxed.zipWith (*.) ts1' (Boxed.fromList $ take count1 ts2') - --- Infinite tensor and simple tensor product -dot (InfiniteTensor (Infinite.Covariant _) ts1') (SimpleFinite (Finite.Contravariant count2 _) ts2') = - Boxed.sum $ Boxed.zipWith (.*) (Boxed.fromList $ take count2 ts1') ts2' -dot (InfiniteTensor i1@(Infinite.Contravariant _) ts1') (SimpleFinite (Finite.Contravariant count2 _) ts2') = - InfiniteTensor i1 $ Boxed.toList $ Boxed.zipWith (.*) (Boxed.fromList $ take count2 ts1') ts2' -dot (InfiniteTensor i1@(Infinite.Covariant _) ts1') (SimpleFinite (Finite.Covariant count2 _) ts2') = - InfiniteTensor i1 $ Boxed.toList $ Boxed.zipWith (.*) (Boxed.fromList $ take count2 ts1') ts2' - --- Finite tensor and infinite tensor product -dot (FiniteTensor (Finite.Covariant count1 _) ts1') (InfiniteTensor (Infinite.Contravariant _) ts2') = - Boxed.sum $ Boxed.zipWith (*) ts1' (Boxed.fromList $ take count1 ts2') -dot (FiniteTensor (Finite.Contravariant count1 _) ts1') (InfiniteTensor i2@(Infinite.Contravariant _) ts2') = - InfiniteTensor i2 $ Boxed.toList $ Boxed.zipWith (*) ts1' (Boxed.fromList $ take count1 ts2') -dot (FiniteTensor (Finite.Covariant count1 _) ts1') (InfiniteTensor i2@(Infinite.Covariant _) ts2') = - InfiniteTensor i2 $ Boxed.toList $ Boxed.zipWith (*) ts1' (Boxed.fromList $ take count1 ts2') - --- Infinite tensor and finite tensor product -dot (InfiniteTensor (Infinite.Covariant _) ts1') (FiniteTensor (Finite.Contravariant count2 _) ts2') = - Boxed.sum $ Boxed.zipWith (*) (Boxed.fromList $ take count2 ts1') ts2' -dot (InfiniteTensor i1@(Infinite.Contravariant _) ts1') (FiniteTensor (Finite.Contravariant count2 _) ts2') = - InfiniteTensor i1 $ Boxed.toList $ Boxed.zipWith (*) (Boxed.fromList $ take count2 ts1') ts2' -dot (InfiniteTensor i1@(Infinite.Covariant _) ts1') (FiniteTensor (Finite.Covariant count2 _) ts2') = - InfiniteTensor i1 $ Boxed.toList $ Boxed.zipWith (*) (Boxed.fromList $ take count2 ts1') ts2' - --- In other cases cannot happen! +-- Other cases cannot happen! dot t1' t2' = contractionErr (tensorIndex t1') (tensorIndex t2') --- bit dot product of two tensors -{-# INLINE bitDot #-} -bitDot :: ( - Num a, Bits a - ) => Tensor a -- ^ First dot product argument - -> Tensor a -- ^ Second dot product argument - -> Tensor a -- ^ Resulting dot product - --- Two finite tensors product -bitDot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (.&.) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Two simple tensors product -bitDot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = - let dotProduct v1 v2 = Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (.&.) v1 v2 - in Scalar $ dotProduct ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Simple tensor and finite tensor product -bitDot (SimpleFinite i1@(Finite.Covariant count1 _) ts1') (FiniteTensor i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (\e t -> (e .&.) <$> t) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Finite tensor and simple tensor product -bitDot (FiniteTensor i1@(Finite.Covariant count1 _) ts1') (SimpleFinite i2@(Finite.Contravariant count2 _) ts2') - | count1 == count2 = Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (\t e -> (.&. e) <$> t) ts1' ts2' - | otherwise = contractionErr (Index.toTIndex i1) (Index.toTIndex i2) - --- Simple tensor and infinite tensor product -bitDot (SimpleFinite (Finite.Covariant count1 _) ts1') (InfiniteTensor (Infinite.Contravariant _) ts2') = - Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (\e t -> (e .&.) <$> t) ts1' (Boxed.fromList $ take count1 ts2') - --- Infinite tensor and simple tensor product -bitDot (InfiniteTensor (Infinite.Covariant _) ts1') (SimpleFinite (Finite.Contravariant count2 _) ts2') = - Data.Foldable.foldl' (.|.) 0 $ Boxed.zipWith (\t e -> (.&. e) <$> t) (Boxed.fromList $ take count2 ts1') ts2' - --- Finite tensor and infinite tensor product -bitDot (FiniteTensor (Finite.Covariant count1 _) ts1') (InfiniteTensor (Infinite.Contravariant _) ts2') = - Boxed.sum $ Boxed.zipWith (*) ts1' (Boxed.fromList $ take count1 ts2') - --- Infinite tensor and finite tensor product -bitDot (InfiniteTensor (Infinite.Covariant _) ts1') (FiniteTensor (Finite.Contravariant count2 _) ts2') = - Boxed.sum $ Boxed.zipWith (*) (Boxed.fromList $ take count2 ts1') ts2' - --- In other cases cannot happen! -bitDot t1' t2' = contractionErr (tensorIndex t1') (tensorIndex t2') - -- contraction error -{-# INLINE contractionErr #-} +--{-# INLINE contractionErr #-} contractionErr :: Index.TIndex -- ^ Index of first dot product parameter -> Index.TIndex -- ^ Index of second dot product parameter -> Tensor a -- ^ Erorr message -contractionErr i1' i2' = Err $ +contractionErr i1' i2' = error $ "Tensor product: " ++ incompatibleTypes ++ " - index1 is " ++ show i1' ++ " and index2 is " ++ show i2' -- Tensors can be added, subtracted and multiplicated -instance Num a => Num (Tensor a) where +instance (Unboxed.Unbox a, Num a, NFData a) => Num (Tensor a) where -- Adding - element by element {-# INLINE (+) #-} @@ -584,101 +319,28 @@ -- Absolute value - element by element {-# INLINE abs #-} - abs t = abs <$> t + abs t = abs `Multilinear.map` t -- Signum operation - element by element {-# INLINE signum #-} - signum t = signum <$> t + signum t = signum `Multilinear.map` t -- Simple integer can be conveted to Scalar {-# INLINE fromInteger #-} fromInteger x = Scalar $ fromInteger x --- Bit operations on tensors -instance ( - Num a, Bits a - ) => Bits (Tensor a) where - - -- Bit sum - elem by elem - {-# INLINE (.|.) #-} - t1 .|. t2 = _elemByElem t1 t2 (.|.) $ zipT (.|.) (\t e -> (.|. e) <$> t) (\e t -> (e .|.) <$> t) (.|.) - - -- Bit tensor product - -- Summation and multiplication are replaced by its bit equivalents - -- Two scalars are multiplicated by their values - {-# INLINE (.&.) #-} - t1 .&. t2 = _elemByElem t1 t2 (.&.) bitDot - - -- Bit exclusive sum (XOR) - elem by elem - {-# INLINE xor #-} - t1 `xor` t2 = _elemByElem t1 t2 xor $ zipT xor (\t e -> (`xor` e) <$> t) (\e t -> (e `xor`) <$> t) xor - - -- Bit complement - {-# INLINE complement #-} - complement = Multilinear.map complement - - -- Bit shift of all elements - {-# INLINE shift #-} - shift t n = Multilinear.map (`shift` n) t - - -- Bit rotating of all elements - {-# INLINE rotate #-} - rotate t n = Multilinear.map (`rotate` n) t - - -- Returns number of bits of elements of tensor, -1 for elements of undefined size - {-# INLINE bitSize #-} - bitSize (Scalar x) = fromMaybe (-1) $ bitSizeMaybe x - bitSize (Err _) = -1 - bitSize t = - if isEmptyTensor t - then (-1) - else fromMaybe (-1) $ bitSizeMaybe $ firstElem t - - -- Returns number of bits of elements of tensor - {-# INLINE bitSizeMaybe #-} - bitSizeMaybe (Scalar x) = bitSizeMaybe x - bitSizeMaybe (Err _) = Nothing - bitSizeMaybe t = - if isEmptyTensor t - then Nothing - else bitSizeMaybe $ firstElem t - - -- Returns true if tensors element are signed - {-# INLINE isSigned #-} - isSigned (Scalar x) = isSigned x - isSigned (Err _) = False - isSigned t = - not (isEmptyTensor t) && - isSigned (firstElem t) - - -- bit i is a scalar value with the ith bit set and all other bits clear. - {-# INLINE bit #-} - bit i = Scalar (bit i) - - -- Test bit - shoud retur True, if bit n if equal to 1. - -- Tensors are entities with many elements, so this function always returns False. - -- Do not use it, it is implemented only for legacy purposes. - {-# INLINE testBit #-} - testBit _ _ = False - - -- Return the number of set bits in the argument. This number is known as the population count or the Hamming weight. - {-# INLINE popCount #-} - popCount = popCountDefault - -- Tensors can be divided by each other -instance Fractional a => Fractional (Tensor a) where +instance (Unboxed.Unbox a, Fractional a, NFData a) => Fractional (Tensor a) where {-# INLINE (/) #-} -- Scalar division return result of division of its values Scalar x1 / Scalar x2 = Scalar $ x1 / x2 -- Tensor and scalar are divided value by value - Scalar x1 / t2 = (x1 /) <$> t2 - t1 / Scalar x2 = (/ x2) <$> t1 - Err msg / _ = Err msg - _ / Err msg = Err msg + Scalar x1 / t2 = (x1 /) `Multilinear.map` t2 + t1 / Scalar x2 = (/ x2) `Multilinear.map` t1 -- Two complex tensors cannot be (for now) simply divided -- // TODO - tensor division and inversion - _ / _ = Err "TODO" + _ / _ = error "TODO" -- A scalar can be generated from rational number {-# INLINE fromRational #-} @@ -687,7 +349,7 @@ -- Real-number functions on tensors. -- Function of tensor is tensor of function of its elements -- E.g. exp [1,2,3,4] = [exp 1, exp2, exp3, exp4] -instance Floating a => Floating (Tensor a) where +instance (Unboxed.Unbox a, Floating a, NFData a) => Floating (Tensor a) where {-| PI number -} {-# INLINE pi #-} @@ -695,78 +357,78 @@ {-| Exponential function. (exp t)[i] = exp( t[i] ) -} {-# INLINE exp #-} - exp t = exp <$> t + exp t = exp `Multilinear.map` t {-| Natural logarithm. (log t)[i] = log( t[i] ) -} {-# INLINE log #-} - log t = log <$> t + log t = log `Multilinear.map` t {-| Sinus. (sin t)[i] = sin( t[i] ) -} {-# INLINE sin #-} - sin t = sin <$> t + sin t = sin `Multilinear.map` t {-| Cosinus. (cos t)[i] = cos( t[i] ) -} {-# INLINE cos #-} - cos t = cos <$> t + cos t = cos `Multilinear.map` t {-| Inverse sinus. (asin t)[i] = asin( t[i] ) -} {-# INLINE asin #-} - asin t = asin <$> t + asin t = asin `Multilinear.map` t {-| Inverse cosinus. (acos t)[i] = acos( t[i] ) -} {-# INLINE acos #-} - acos t = acos <$> t + acos t = acos `Multilinear.map` t {-| Inverse tangent. (atan t)[i] = atan( t[i] ) -} {-# INLINE atan #-} - atan t = atan <$> t + atan t = atan `Multilinear.map` t {-| Hyperbolic sinus. (sinh t)[i] = sinh( t[i] ) -} {-# INLINE sinh #-} - sinh t = sinh <$> t + sinh t = sinh `Multilinear.map` t {-| Hyperbolic cosinus. (cosh t)[i] = cosh( t[i] ) -} {-# INLINE cosh #-} - cosh t = cosh <$> t + cosh t = cosh `Multilinear.map` t {-| Inverse hyperbolic sinus. (asinh t)[i] = asinh( t[i] ) -} {-# INLINE asinh #-} - asinh t = acosh <$> t + asinh t = acosh `Multilinear.map` t {-| Inverse hyperbolic cosinus. (acosh t)[i] = acosh (t[i] ) -} {-# INLINE acosh #-} - acosh t = acosh <$> t + acosh t = acosh `Multilinear.map` t {-| Inverse hyperbolic tangent. (atanh t)[i] = atanh( t[i] ) -} {-# INLINE atanh #-} - atanh t = atanh <$> t + atanh t = atanh `Multilinear.map` t -- Multilinear operations -instance Num a => Multilinear Tensor a where +instance (Unboxed.Unbox a, Num a, NFData a) => Multilinear Tensor a where -- Add scalar right {-# INLINE (.+) #-} - t .+ x = (+x) <$> t + t .+ x = (+x) `Multilinear.map` t -- Subtract scalar right {-# INLINE (.-) #-} - t .- x = (\p -> p - x) <$> t + t .- x = (\p -> p - x) `Multilinear.map` t -- Multiplicate by scalar right {-# INLINE (.*) #-} - t .* x = (*x) <$> t + t .* x = (*x) `Multilinear.map` t -- Add scalar left {-# INLINE (+.) #-} - x +. t = (x+) <$> t + x +. t = (x+) `Multilinear.map` t -- Subtract scalar left {-# INLINE (-.) #-} - x -. t = (x-) <$> t + x -. t = (x-) `Multilinear.map` t -- Multiplicate by scalar left {-# INLINE (*.) #-} - x *. t = (x*) <$> t + x *. t = (x*) `Multilinear.map` t -- Two tensors sum {-# INLINE (.+.) #-} @@ -785,9 +447,7 @@ indices x = case x of Scalar _ -> [] FiniteTensor i ts -> Index.toTIndex i : indices (head $ toList ts) - InfiniteTensor i ts -> Index.toTIndex i : indices (head ts) SimpleFinite i _ -> [Index.toTIndex i] - Err _ -> [] -- Get tensor order [ (contravariant,covariant)-type ] {-# INLINE order #-} @@ -797,7 +457,6 @@ Finite.Contravariant _ _ -> (1,0) Finite.Covariant _ _ -> (0,1) Finite.Indifferent _ _ -> (0,0) - Err _ -> (-1,-1) _ -> let (cnvr, covr) = order $ firstTensor x in case tensorIndex x of Index.Contravariant _ _ -> (cnvr+1,covr) @@ -816,8 +475,6 @@ if Index.indexName index == iname then Finite.indexSize index else size (firstTensor t) iname - InfiniteTensor _ _ -> error infiniteIndex - Err msg -> error msg -- Rename tensor indices {-# INLINE ($|) #-} @@ -827,9 +484,6 @@ SimpleFinite (Finite.Covariant isize _) ts $| (_, d:_) = SimpleFinite (Finite.Covariant isize [d]) ts FiniteTensor (Finite.Contravariant isize _) ts $| (u:us, ds) = FiniteTensor (Finite.Contravariant isize [u]) $ ($| (us,ds)) <$> ts FiniteTensor (Finite.Covariant isize _) ts $| (us, d:ds) = FiniteTensor (Finite.Covariant isize [d]) $ ($| (us,ds)) <$> ts - InfiniteTensor (Infinite.Contravariant _) ts $| (u:us, ds) = InfiniteTensor (Infinite.Contravariant [u]) $ ($| (us,ds)) <$> ts - InfiniteTensor (Infinite.Covariant _) ts $| (us, d:ds) = InfiniteTensor (Infinite.Covariant [d]) $ ($| (us,ds)) <$> ts - Err msg $| _ = Err msg t $| _ = t -- Raise an index @@ -840,16 +494,10 @@ FiniteTensor (Finite.Contravariant (Finite.indexSize index) n) $ (/\ n) <$> ts | otherwise = FiniteTensor index $ (/\ n) <$> ts - InfiniteTensor index ts /\ n - | Index.indexName index == n = - InfiniteTensor (Infinite.Contravariant n) $ (/\ n) <$> ts - | otherwise = - InfiniteTensor index $ (/\ n) <$> ts t1@(SimpleFinite index ts) /\ n | Index.indexName index == n = SimpleFinite (Finite.Contravariant (Finite.indexSize index) n) ts | otherwise = t1 - Err msg /\ _ = Err msg -- Lower an index {-# INLINE (\/) #-} @@ -859,16 +507,10 @@ FiniteTensor (Finite.Covariant (Finite.indexSize index) n) $ (\/ n) <$> ts | otherwise = FiniteTensor index $ (\/ n) <$> ts - InfiniteTensor index ts \/ n - | Index.indexName index == n = - InfiniteTensor (Infinite.Covariant n) $ (\/ n) <$> ts - | otherwise = - InfiniteTensor index $ (\/ n) <$> ts t1@(SimpleFinite index ts) \/ n | Index.indexName index == n = SimpleFinite (Finite.Covariant (Finite.indexSize index) n) ts | otherwise = t1 - Err msg \/ _ = Err msg {-| Transpose a tensor (switch all indices types) -} {-# INLINE transpose #-} @@ -881,13 +523,6 @@ transpose (FiniteTensor (Finite.Indifferent count name) ts) = FiniteTensor (Finite.Indifferent count name) (Multilinear.transpose <$> ts) - transpose (InfiniteTensor (Infinite.Covariant name) ts) = - InfiniteTensor (Infinite.Contravariant name) (Multilinear.transpose <$> ts) - transpose (InfiniteTensor (Infinite.Contravariant name) ts) = - InfiniteTensor (Infinite.Covariant name) (Multilinear.transpose <$> ts) - transpose (InfiniteTensor (Infinite.Indifferent name) ts) = - InfiniteTensor (Infinite.Indifferent name) (Multilinear.transpose <$> ts) - transpose (SimpleFinite (Finite.Covariant count name) ts) = SimpleFinite (Finite.Contravariant count name) ts transpose (SimpleFinite (Finite.Contravariant count name) ts) = @@ -895,13 +530,9 @@ transpose (SimpleFinite (Finite.Indifferent count name) ts) = SimpleFinite (Finite.Indifferent count name) ts - transpose (Err msg) = Err msg {-| Shift tensor index right -} {-| Moves given index one level deeper in recursion -} - {-# INLINE shiftRight #-} - -- Error tensor has no indices to shift - Err msg `shiftRight` _ = Err msg -- Scalar has no indices to shift Scalar x `shiftRight` _ = Scalar x -- Simple tensor has only one index which cannot be shifted @@ -917,7 +548,8 @@ let index2 = tensorFiniteIndex (ts1 Boxed.! 0) -- Elements to transpose dane = if isSimple (ts1 Boxed.! 0) - then (Scalar <$>) <$> (tensorScalars <$> ts1) + then (\un -> Boxed.generate (Unboxed.length un) (\i -> Scalar $ un Unboxed.! i)) <$> + (tensorScalars <$> ts1) else tensorsFinite <$> ts1 -- Convert to list daneList = Boxed.toList <$> Boxed.toList dane @@ -926,31 +558,21 @@ -- then reconvert to vector again transposed = Boxed.fromList <$> Boxed.fromList transposedList -- and reconstruct tensor with transposed elements - in mergeScalars $ FiniteTensor index2 $ FiniteTensor index1 <$> transposed - | otherwise = t1 - - -- Infinite tensor is shifted by transposing nested lists - t1@(InfiniteTensor index1 ts1) `shiftRight` ind - -- We don't shift this index - | Data.List.length (indicesNames t1) > 1 && Index.indexName index1 /= ind = - InfiniteTensor index1 $ (|>> ind) <$> ts1 - -- We found index to shift - | Data.List.length (indicesNames t1) > 1 && Index.indexName index1 == ind = - -- Next index - let index2 = tensorInfiniteIndex (head ts1) - -- Elements to transpose - dane = if isSimple (head ts1) - then (Scalar <$>) <$> (Boxed.toList . tensorScalars <$> ts1) - else tensorsInfinite <$> ts1 - -- transpose tensor data (standard function available only for list) - transposed = Data.List.transpose dane - -- and reconstruct tensor with transposed elements - in mergeScalars $ InfiniteTensor index2 $ InfiniteTensor index1 <$> transposed + in _mergeScalars $ FiniteTensor index2 $ FiniteTensor index1 <$> transposed + -- there is only one index and therefore it cannot be shifted | otherwise = t1 - + + {-| Map function, as in Functor typeclass -} + {-# INLINE map #-} + map f x = case x of + -- Mapping scalar simply maps its value + Scalar v -> Scalar $ f v + -- Mapping complex tensor does mapping element by element + SimpleFinite index ts -> SimpleFinite index (f `Unboxed.map` ts) + FiniteTensor index ts -> FiniteTensor index $ Multilinear.map f <$> ts {-| List allows for random access to tensor elements -} -instance Num a => Accessible Tensor a where +instance (Unboxed.Unbox a, Num a, NFData a) => Accessible Tensor a where {-| Accessing tensor elements -} {-# INLINE el #-} @@ -989,42 +611,3 @@ then el (t1 ! snd (fromJust val)) (inds1,vals1) -- otherwise recursively access elements of all child tensors else FiniteTensor index1 $ (\t -> el t (inds,vals)) <$> v1 - - -- infinite tensor case - el t1@(InfiniteTensor index1 v1) (inds,vals) = - -- zip indices with their given values - let indvals = zip inds vals - -- find value for current index if given - val = Data.List.find (\(n,_) -> [n] == Index.indexName index1) indvals - -- and remove used index from indices list - indvals1 = Data.List.filter (\(n,_) -> [n] /= Index.indexName index1) indvals - -- indices unused so far - inds1 = Data.List.map fst indvals1 - -- and its corresponding values - vals1 = Data.List.map snd indvals1 - -- if value for current index was given - in if isJust val - -- then get it from current tensor and recursively process other indices - then el (t1 ! snd (fromJust val)) (inds1,vals1) - -- otherwise recursively access elements of all child tensors - else InfiniteTensor index1 $ (\t -> el t (inds,vals)) <$> v1 - - -- accessing elements of erorr tensor simply pushes this error further - el (Err msg) _ = Err msg - - {-| Mapping with indices. -} - {-# INLINE iMap #-} - iMap f t = iMap' t zeroList - where - zeroList = 0:zeroList - - iMap' (Scalar x) inds = - Scalar $ f inds x - iMap' (SimpleFinite index ts) inds = - SimpleFinite index $ Boxed.imap (\i e -> f (inds ++ [i]) e) ts - iMap' (FiniteTensor index ts) inds = - FiniteTensor index $ Boxed.imap (\i e -> iMap' e (inds ++ [i])) ts - iMap' (InfiniteTensor index ts) inds = - InfiniteTensor index $ (\tind -> iMap' (fst tind) $ inds ++ [snd tind]) <$> zip ts [0..] - iMap' (Err msg) _ = - Err msg
src/Multilinear/Matrix.hs view
@@ -23,6 +23,7 @@ ) where import Control.Monad.Primitive +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic import qualified Multilinear.Tensor as Tensor import Statistics.Distribution @@ -31,9 +32,9 @@ invalidIndices = "Indices and its sizes not compatible with structure of matrix!" {-| Generate matrix as function of its indices -} -{-# INLINE fromIndices #-} + fromIndices :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) -> Int -- ^ Number of matrix rows -> Int -- ^ Number of matrix columns @@ -41,12 +42,12 @@ -> Tensor a -- ^ Generated matrix fromIndices [u,d] us ds f = Tensor.fromIndices ([u],[us]) ([d],[ds]) $ \[ui] [di] -> f ui di -fromIndices _ _ _ _ = Err invalidIndices +fromIndices _ _ _ _ = error invalidIndices {-| Generate matrix with all components equal to @v@ -} -{-# INLINE Multilinear.Matrix.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) -> Int -- ^ Number of matrix rows -> Int -- ^ Number of matrix columns @@ -54,7 +55,7 @@ -> Tensor a -- ^ Generated matrix const [u,d] us ds = Tensor.const ([u],[us]) ([d],[ds]) -const _ _ _ = \_ -> Err invalidIndices +const _ _ _ = \_ -> error invalidIndices {-| Generate matrix with random real components with given probability distribution. The matrix is wrapped in the IO monad. -} @@ -69,7 +70,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDouble #-} + randomDouble :: ( ContGen d ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) @@ -79,7 +80,7 @@ -> IO (Tensor Double) -- ^ Generated matrix randomDouble [u,d] us ds = Tensor.randomDouble ([u],[us]) ([d],[ds]) -randomDouble _ _ _ = \_ -> return $ Err invalidIndices +randomDouble _ _ _ = \_ -> return $ error invalidIndices {-| Generate matrix with random integer components with given probability distribution. The matrix is wrapped in the IO monad. -} @@ -88,7 +89,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomInt #-} + randomInt :: ( DiscreteGen d ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) @@ -98,7 +99,7 @@ -> IO (Tensor Int) -- ^ Generated matrix randomInt [u,d] us ds = Tensor.randomInt ([u],[us]) ([d],[ds]) -randomInt _ _ _ = \_ -> return $ Err invalidIndices +randomInt _ _ _ = \_ -> return $ error invalidIndices {-| Generate matrix with random real components with given probability distribution and given seed. The matrix is wrapped in the a monad. -} @@ -113,7 +114,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDoubleSeed #-} + randomDoubleSeed :: ( ContGen d, PrimMonad m ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) @@ -124,7 +125,7 @@ -> m (Tensor Double) -- ^ Generated matrix randomDoubleSeed [u,d] us ds = Tensor.randomDoubleSeed ([u],[us]) ([d],[ds]) -randomDoubleSeed _ _ _ = \_ _ -> return $ Err invalidIndices +randomDoubleSeed _ _ _ = \_ _ -> return $ error invalidIndices {-| Generate matrix with random integer components with given probability distribution. and given seed. The matrix is wrapped in a monad. -} @@ -133,7 +134,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomIntSeed #-} + randomIntSeed :: ( DiscreteGen d, PrimMonad m ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index) @@ -144,4 +145,4 @@ -> m (Tensor Int) -- ^ Generated matrix randomIntSeed [u,d] us ds = Tensor.randomIntSeed ([u],[us]) ([d],[ds]) -randomIntSeed _ _ _ = \_ _ -> return $ Err invalidIndices +randomIntSeed _ _ _ = \_ _ -> return $ error invalidIndices
src/Multilinear/NForm.hs view
@@ -26,6 +26,7 @@ ) where import Control.Monad.Primitive +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic import qualified Multilinear.Tensor as Tensor import Statistics.Distribution @@ -37,9 +38,9 @@ invalidCrossProductIndices = "Indices and its sizes incompatible with cross product structure!" {-| Generate N-form as function of its indices -} -{-# INLINE fromIndices #-} + fromIndices :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> [Int] -- ^ Indices sizes -> ([Int] -> a) -- ^ Generator function @@ -48,9 +49,9 @@ fromIndices d ds f = Tensor.fromIndices ([],[]) (d,ds) $ \[] -> f {-| Generate N-form with all components equal to @v@ -} -{-# INLINE Multilinear.NForm.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> [Int] -- ^ Indices sizes -> a -- ^ N-form elements value @@ -72,7 +73,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDouble #-} + randomDouble :: ( ContGen d ) => String -- ^ Indices names (one character per index) @@ -89,7 +90,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomInt #-} + randomInt :: ( DiscreteGen d ) => String -- ^ Indices names (one character per index) @@ -113,7 +114,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDoubleSeed #-} + randomDoubleSeed :: ( ContGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -131,7 +132,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomIntSeed #-} + randomIntSeed :: ( DiscreteGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -143,21 +144,21 @@ randomIntSeed d ds = Tensor.randomIntSeed ([],[]) (d,ds) {-| 2-form representing a dot product -} -{-# INLINE dot #-} + dot :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> Int -- ^ Size of tensor (dot product is a square tensor) -> Tensor a -- ^ Generated dot product dot [i1,i2] size = fromIndices [i1,i2] [size,size] (\[i,j] -> if i == j then 1 else 0) -dot _ _ = Err invalidIndices +dot _ _ = error invalidIndices {-| Tensor representing a cross product (Levi - Civita symbol). It also allows to compute a determinant of square matrix - determinant of matrix @M@ is a equal to length of cross product of all columns of @M@ -} -- // TODO -{-# INLINE cross #-} + cross :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> Int -- ^ Size of tensor (dot product is a square tensor) -> Tensor a -- ^ Generated dot product @@ -165,4 +166,4 @@ cross [i,j,k] size = Tensor.fromIndices ([i],[size]) ([j,k],[size,size]) (\[_] [_,_] -> 0) -cross _ _ = Err invalidCrossProductIndices +cross _ _ = error invalidCrossProductIndices
src/Multilinear/NVector.hs view
@@ -23,14 +23,15 @@ ) where import Control.Monad.Primitive +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic import Multilinear.Tensor as Tensor import Statistics.Distribution {-| Generate n-vector as function of its indices -} -{-# INLINE fromIndices #-} + fromIndices :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> [Int] -- ^ Indices sizes -> ([Int] -> a) -- ^ Generator function @@ -39,9 +40,9 @@ fromIndices u us f = Tensor.fromIndices (u,us) ([],[]) $ \uis [] -> f uis {-| Generate n-vector with all components equal to @v@ -} -{-# INLINE Multilinear.NForm.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Indices names (one characted per index) -> [Int] -- ^ Indices sizes -> a -- ^ n-vector elements value @@ -63,7 +64,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDouble #-} + randomDouble :: ( ContGen d ) => String -- ^ Indices names (one character per index) @@ -80,7 +81,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomInt #-} + randomInt :: ( DiscreteGen d ) => String -- ^ Indices names (one character per index) @@ -104,7 +105,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDoubleSeed #-} + randomDoubleSeed :: ( ContGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -122,7 +123,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomIntSeed #-} + randomIntSeed :: ( DiscreteGen d, PrimMonad m ) => String -- ^ Index name (one character)
src/Multilinear/Tensor.hs view
@@ -25,6 +25,7 @@ import Control.Monad.Primitive import qualified Data.Vector as Boxed +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic import Multilinear.Index.Finite as Finite import Statistics.Distribution @@ -34,9 +35,9 @@ 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 + Num a, Unboxed.Unbox 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,...]) @@ -44,11 +45,11 @@ -- 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] [] + SimpleFinite (Contravariant s [u]) $ Unboxed.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] + SimpleFinite (Covariant s [d]) $ Unboxed.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 = @@ -59,12 +60,12 @@ 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 +fromIndices us ds _ = error $ invalidIndices us ds {-| Generate tensor composed of other tensors -} -{-# INLINE generate #-} + generate :: ( - Num a + Num a, Unboxed.Unbox 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,...]) @@ -82,12 +83,12 @@ 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 us ds _ = error $ invalidIndices us ds {-| Generate tensor with all components equal to @v@ -} -{-# INLINE Multilinear.Tensor.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox 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 @@ -95,11 +96,11 @@ -- 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 + SimpleFinite (Contravariant s [u]) $ Unboxed.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 + SimpleFinite (Covariant s [d]) $ Unboxed.replicate s v -- If many indices are given, first generate upper indices recursively from indices list const (u:us,s:size) d v = @@ -110,7 +111,7 @@ 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 +const us ds _ = error $ invalidIndices us ds {-| Generate tensor with random real components with given probability distribution. The tensor is wrapped in the IO monad. -} @@ -126,7 +127,7 @@ {-| - 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 @@ -137,13 +138,15 @@ -- 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 + component' <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i 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 + component' <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i return $ SimpleFinite (Covariant s [d]) component -- If many indices are given, first generate upper indices recursively from indices list @@ -157,7 +160,7 @@ 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 +randomDouble us ds _ = return $ error $ invalidIndices us ds {-| Generate tensor with random integer components with given probability distribution. The tensor is wrapped in the IO monad. -} @@ -166,7 +169,7 @@ {-| - 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 @@ -177,13 +180,15 @@ -- 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 + component' <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i 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 + component' <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i return $ SimpleFinite (Covariant s [d]) component -- If many indices are given, first generate upper indices recursively from indices list @@ -197,7 +202,7 @@ 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 +randomInt us ds _ = return $ error $ invalidIndices us ds {-| Generate tensor with random real components with given probability distribution and given seed. The tensor is wrapped in a monad. -} @@ -213,7 +218,7 @@ {-| - 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 @@ -225,13 +230,15 @@ -- 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 + component' <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i 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 + component' <- sequence $ Boxed.generate s $ \_ -> genContVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i return $ SimpleFinite (Covariant s [d]) component -- If many indices are given, first generate upper indices recursively from indices list @@ -245,7 +252,7 @@ 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 +randomDoubleSeed us ds _ _ = return $ error $ invalidIndices us ds {-| Generate tensor with random integer components with given probability distribution and given seed. The tensor is wrapped in a monad. -} @@ -254,7 +261,7 @@ {-| - 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) @@ -266,13 +273,15 @@ -- 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 + component' <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i 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 + component' <- sequence $ Boxed.generate s $ \_ -> genDiscreteVar distr gen + let component = Unboxed.generate s $ \i -> component' Boxed.! i return $ SimpleFinite (Covariant s [d]) component -- If many indices are given, first generate upper indices recursively from indices list @@ -286,4 +295,4 @@ 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 +randomIntSeed us ds _ _ = return $ error $ invalidIndices us ds
src/Multilinear/Vector.hs view
@@ -23,6 +23,7 @@ ) where import Control.Monad.Primitive +import qualified Data.Vector.Unboxed as Unboxed import Multilinear.Generic import Multilinear.Tensor as Tensor import Statistics.Distribution @@ -31,28 +32,28 @@ invalidIndices = "Indices and its sizes not compatible with structure of vector!" {-| Generate vector as function of indices -} -{-# INLINE fromIndices #-} + fromIndices :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Index name (one character) -> Int -- ^ Number of elements -> (Int -> a) -- ^ Generator function - returns a vector component at index @i@ -> Tensor a -- ^ Generated vector fromIndices [i] s f = Tensor.fromIndices ([i],[s]) ([],[]) $ \[x] [] -> f x -fromIndices _ _ _ = Err invalidIndices +fromIndices _ _ _ = error invalidIndices {-| Generate vector with all components equal to some @v@ -} -{-# INLINE Multilinear.Vector.const #-} + const :: ( - Num a + Num a, Unboxed.Unbox a ) => String -- ^ Index name (one character) -> Int -- ^ Number of elements -> a -- ^ Value of each element -> Tensor a -- ^ Generated vector const [i] s = Tensor.const ([i],[s]) ([],[]) -const _ _ = \_ -> Err invalidIndices +const _ _ = \_ -> error invalidIndices {-| Generate vector with random real components with given probability distribution. The vector is wrapped in the IO monad. -} @@ -67,7 +68,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDouble #-} + randomDouble :: ( ContGen d ) => String -- ^ Index name (one character) @@ -76,7 +77,7 @@ -> IO (Tensor Double) -- ^ Generated vector randomDouble [i] s = Tensor.randomDouble ([i],[s]) ([],[]) -randomDouble _ _ = \_ -> return $ Err invalidIndices +randomDouble _ _ = \_ -> return $ error invalidIndices {-| Generate vector with random integer components with given probability distribution. The vector is wrapped in the IO monad. -} @@ -85,7 +86,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomInt #-} + randomInt :: ( DiscreteGen d ) => String -- ^ Index name (one character) @@ -94,7 +95,7 @@ -> IO (Tensor Int) -- ^ Generated vector randomInt [i] s = Tensor.randomInt ([i],[s]) ([],[]) -randomInt _ _ = \_ -> return $ Err invalidIndices +randomInt _ _ = \_ -> return $ error invalidIndices {-| Generate vector with random real components with given probability distribution and given seed. The vector is wrapped in a monad. -} @@ -109,7 +110,7 @@ {-| - Uniform : "Statistics.Distribution.Uniform" -} {-| - F : "Statistics.Distribution.FDistribution" -} {-| - Laplace : "Statistics.Distribution.Laplace" -} -{-# INLINE randomDoubleSeed #-} + randomDoubleSeed :: ( ContGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -119,7 +120,7 @@ -> m (Tensor Double) -- ^ Generated vector randomDoubleSeed [i] s = Tensor.randomDoubleSeed ([i],[s]) ([],[]) -randomDoubleSeed _ _ = \_ _ -> return $ Err invalidIndices +randomDoubleSeed _ _ = \_ _ -> return $ error invalidIndices {-| Generate vector with random integer components with given probability distribution and given seed. The vector is wrapped in a monad. -} @@ -128,7 +129,7 @@ {-| - Poisson : "Statistics.Distribution.Poisson" -} {-| - Geometric : "Statistics.Distribution.Geometric" -} {-| - Hypergeometric: "Statistics.Distribution.Hypergeometric" -} -{-# INLINE randomIntSeed #-} + randomIntSeed :: ( DiscreteGen d, PrimMonad m ) => String -- ^ Index name (one character) @@ -138,5 +139,5 @@ -> m (Tensor Int) -- ^ Generated vector randomIntSeed [i] s = Tensor.randomIntSeed ([i],[s]) ([],[]) -randomIntSeed _ _ = \_ _ -> return $ Err invalidIndices +randomIntSeed _ _ = \_ _ -> return $ error invalidIndices
test/Spec.hs view
@@ -13,5 +13,44 @@ main ) where +import Control.DeepSeq +import Multilinear.Generic +import Multilinear.Class +import qualified Multilinear.Matrix as Matrix +import qualified Multilinear.Vector as Vector + +v_i :: Tensor Double +v_i = Vector.fromIndices "i" 10 fromIntegral + +v_j :: Tensor Double +v_j = Vector.fromIndices "j" 5 (\x -> fromIntegral x + 5.0) + +v_k :: Tensor Double +v_k = Vector.fromIndices "k" 10 fromIntegral + +m_ik :: Tensor Double +m_ik = Matrix.fromIndices "ik" 10 10 (\i j -> fromIntegral i + fromIntegral j) + main :: IO () -main = putStrLn "Test to do..."+main = do + putStr "v^i = " + print v_i + putStr "v^j = " + print v_j + putStr "v^k = " + print v_k + putStr "Matrix m_ji = v^j + v_i = " + let m = v_j + (v_i \/ "i") + print m + putStr "m_ji * v^i = " + print $ m * v_i + putStr "m_ji * v^k = " + print $ m * v_k + putStr "Matrix m_ik = " + print m_ik + putStr "m_ik * v^k = " + print $ m_ik * v_k + putStr "m_ik |>>> i = " + print $ m_ik |>>> "i" + putStr "m_ji * m_ik" + print $ m * m_ik