matrix-sized 0.0.3 → 0.0.4
raw patch · 15 files changed
+797/−298 lines, 15 filesdep +conduitdep +data-ordlistdep +ieee754
Dependencies added: conduit, data-ordlist, ieee754
Files
- README.md +1/−1
- cbits/eigen-runtime.h +12/−0
- cbits/eigen-solver.cpp +80/−9
- matrix-sized.cabal +11/−2
- src/Data/Matrix/Static/Dense.hs +33/−2
- src/Data/Matrix/Static/Internal.hs +20/−0
- src/Data/Matrix/Static/LinearAlgebra.hs +142/−37
- src/Data/Matrix/Static/LinearAlgebra/Internal.hs +189/−0
- src/Data/Matrix/Static/LinearAlgebra/Types.hs +1/−172
- src/Data/Matrix/Static/Sparse.hs +89/−45
- src/Data/Matrix/Static/Sparse/Mutable.hs +6/−3
- tests/Main.hs +4/−27
- tests/Test/Base.hs +61/−0
- tests/Test/LinearAlgebra.hs +83/−0
- tests/Test/Utils.hs +65/−0
README.md view
@@ -37,7 +37,7 @@ f :: (SingI n, (2 <= n - 2) ~ 'True) => Matrix n n Double -> Matrix n n (Complex Double)-f m = let (d, v) = eigs (sing :: Sing 2) m+f m = let (d, v) = eigS (sing :: Sing 2) m in v @@ S.diag d @@ G.transpose v main :: IO ()
cbits/eigen-runtime.h view
@@ -39,6 +39,18 @@ GUARD_END\ } +#define API2(name,args,call) \+extern "C" RET eigen_##name args {\+ GUARD_START\+ switch (code) {\+ case 0: return name<T0, T0>call;\+ case 1: return name<T1, T1>call;\+ case 2: return name<T2, T0>call;\+ case 3: return name<T3, T1>call;\+ }\+ GUARD_END\+}+ typedef float T0; typedef double T1; typedef std::complex<float> T2;
cbits/eigen-solver.cpp view
@@ -1,4 +1,5 @@ #include <Spectra/GenEigsSolver.h>+#include <Spectra/SymEigsSolver.h> #include "eigen-runtime.h" #include <Eigen/Sparse> #include <Spectra/MatOp/SparseGenMatProd.h>@@ -6,6 +7,20 @@ using namespace Spectra; using namespace Eigen; +extern "C" RET eigen_eig( + void* d, void* v, + const void* p, int n)+{+ typedef Map< Matrix<T1,Dynamic,Dynamic> > MapMatrix;+ typedef Map< Matrix<T3,Dynamic,Dynamic> > MapComplexMatrix;+ MapMatrix M((T1*)p, n, n);+ MapComplexMatrix D((T3*)d, n, 1);+ MapComplexMatrix V((T3*)v, n, n);+ EigenSolver<MatrixXd> es(M);+ D = es.eigenvalues();+ V = es.eigenvectors();+}+ extern "C" RET spectral_eigs( int k, void* d, void* v, @@ -29,6 +44,28 @@ return 0; } +extern "C" RET spectral_eigsh( + int k,+ void* d, void* v, + const void* p, int n)+{+ typedef Map< Matrix<T1,Dynamic,Dynamic> > MapMatrix;+ MapMatrix M((T1*)p, n, n);+ MapMatrix D((T1*)d, k, 1);+ MapMatrix V((T1*)v, n, k);++ DenseGenMatProd<double> op(M);+ int ncv = 2 * k;+ ncv = (ncv <= n) ? ncv : n;+ SymEigsSolver< double, LARGEST_MAGN, DenseGenMatProd<double> > eigsh(&op, k, ncv);+ eigsh.init();+ int nconv = eigsh.compute();+ if(eigsh.info() == 0)+ D = eigsh.eigenvalues();+ V = eigsh.eigenvectors();+ return 0;+}+ extern "C" RET spectral_seigs( int k, void* d, void* v,@@ -55,6 +92,33 @@ return 0; } +extern "C" RET spectral_seigsh( + int k,+ void* d, void* v,+ const void* values,+ const void* outerIndexPtr,+ const void* innerIndices,+ int n, int s)+{+ typedef Map< Matrix<T1,Dynamic,Dynamic> > MapMatrix;+ typedef Map<const SparseMatrix<T1> > MapSparseMatrix;+ MapSparseMatrix M(n, n, s, (int*)outerIndexPtr, (int*)innerIndices, (T1*)values);+ MapMatrix D((T1*)d, k, 1);+ MapMatrix V((T1*)v, n, k);++ SparseGenMatProd<double> op(M);+ int ncv = 2 * k;+ ncv = (ncv <= n) ? ncv : n;+ SymEigsSolver< double, LARGEST_MAGN, SparseGenMatProd<double> > eigsh(&op, k, ncv);+ eigsh.init();+ int nconv = eigsh.compute();+ if(eigsh.info() == 0)+ D = eigsh.eigenvalues();+ V = eigsh.eigenvectors();+ return 0;+}++ template <class T> RET cholesky(void* px, const void* pa, int n) {@@ -68,16 +132,23 @@ void* px, const void* pa, int n), (px,pa,n)); -/*-template <class T>-RET bdcsvd(void* px, int r, int c+template <class T, class TT>+RET bdcsvd(+ void* pu, void* ps, void* pv, + const void* px, int r, int c) {+ int m = r < c ? r : c; typedef Map< Matrix<T,Dynamic,Dynamic> > MapMatrix;- MapMatrix x((T*)px, n, n);- MapMatrix A((T*)pa, n, n);- x = A.llt().matrixL();+ typedef Map< Matrix<TT,Dynamic,Dynamic> > MapMatrix2;+ MapMatrix A((T*)px, r, c);+ MapMatrix U((T*)pu, r, m);+ MapMatrix2 s((TT*)ps, m, 1);+ MapMatrix V((T*)pv, c, m);+ BDCSVD< Matrix<T,Dynamic,Dynamic>> svd(A, ComputeThinU|ComputeThinV);+ U = svd.matrixU();+ V = svd.matrixV();+ s = svd.singularValues(); return 0; }-API(cholesky, (int code,- void* px, const void* pa, int n), (px,pa,n));-*/+API2(bdcsvd, (int code,+ void* pu, void* ps, void* pv, const void* px, int r, int c), (pu,ps,pv,px,r,c));
matrix-sized.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ef7b97a49f99defe3cd58f2c5a3411fe8c958f0ad40f4b3f0bd763720b643db8+-- hash: a94165a01f28a1a01026b95b46341900f92789d9a5b17aec1fe9ab414b560a26 name: matrix-sized-version: 0.0.3+version: 0.0.4 synopsis: Haskell matrix library with interface to C++ linear algebra libraries. description: A Haskell implementation of matrices with statically known sizes. The library also comes with the bindings to high performance C++ linear algebra libraries such as Eigen and Spectra. category: Math@@ -392,6 +392,7 @@ Data.Matrix.Static.Generic.Mutable other-modules: Data.Matrix.Static.Internal+ Data.Matrix.Static.LinearAlgebra.Internal hs-source-dirs: src ghc-options: -Wall@@ -408,6 +409,7 @@ stdc++ build-depends: base >=4.10 && <5+ , conduit , primitive >=0.6.4.0 , singletons , vector >=0.11@@ -420,11 +422,18 @@ test-suite test type: exitcode-stdio-1.0 main-is: Main.hs+ other-modules:+ Test.Base+ Test.LinearAlgebra+ Test.Utils hs-source-dirs: tests ghc-options: -Wall -threaded build-depends: base+ , conduit+ , data-ordlist+ , ieee754 , matrix-sized , primitive >=0.6.4.0 , singletons
src/Data/Matrix/Static/Dense.hs view
@@ -42,6 +42,7 @@ , C.fromColumns , C.unsafeFromVector , replicate+ , diag , diagRect -- * Conversions@@ -88,11 +89,17 @@ , C.freeze , C.unsafeFreeze , C.create++ , sum+ , all+ , any ) where import Control.Monad (liftM) import qualified Data.Vector.Generic as G-import Prelude hiding (replicate, mapM, mapM_, zipWith, map, sequence, sequence_, zip, unzip, zipWith3, zip3, unzip3)+import Prelude hiding ( replicate, mapM, mapM_, zipWith, map+ , sequence, sequence_, zip, unzip, zipWith3+ , zip3, unzip3, sum, all, any ) import GHC.TypeLits (type (<=)) import Data.Singletons import Data.Tuple (swap)@@ -203,6 +210,17 @@ c = fromIntegral $ fromSing (sing :: Sing c) {-# INLINE replicate #-} +-- | O(m*n) Create a square matrix with default values and given diagonal+diag :: (G.Vector v a, SingI n)+ => a -- ^ default value+ -> Matrix n 1 v a -- ^ diagonal+ -> Matrix n n v a+diag z0 d = C.create $ do+ mat <- DM.replicate z0+ C.imapM_ (DM.unsafeWrite mat) d+ return mat+{-# INLINE diag #-}+ -- | O(m*n) Create a rectangular matrix with default values and given diagonal diagRect :: (G.Vector v a, SingI r, SingI c, n <= r, n <= c) => a -- ^ default value@@ -352,8 +370,21 @@ convert (Matrix vec) = Matrix $ G.convert vec {-# INLINE convert #-} +sum :: (Num a, G.Vector v a) => Matrix r c v a -> a+sum (Matrix vec) = G.sum vec+{-# INLINE sum #-} +all :: G.Vector v a => (a -> Bool) -> Matrix r c v a -> Bool+all f (Matrix vec) = G.all f vec+{-# INLINE all #-}++any :: G.Vector v a => (a -> Bool) -> Matrix r c v a -> Bool+any f (Matrix vec) = G.any f vec+{-# INLINE any #-}+ -- Helper-toIndex :: Int -> Int -> (Int, Int)+toIndex :: Int -- ^ Number of rows+ -> Int -- ^ 1-d index+ -> (Int, Int) -- ^ 2-d index toIndex r i = swap $ i `divMod` r {-# INLINE toIndex #-}
src/Data/Matrix/Static/Internal.hs view
@@ -9,8 +9,12 @@ , c_ss_plus , c_inverse , c_cholesky+ , c_eig , c_eigs+ , c_eigsh , c_seigs+ , c_seigsh+ , c_bdcsvd ) where import Data.Complex (Complex)@@ -80,11 +84,27 @@ c_cholesky :: CInt -> Ptr a -> Ptr a -> CInt -> IO CString +foreign import ccall "eigen_eig"+ c_eig :: Ptr (Complex Double) -> Ptr (Complex Double)+ -> Ptr Double -> CInt -> IO CString+ foreign import ccall "spectral_eigs" c_eigs :: CInt -> Ptr (Complex Double) -> Ptr (Complex Double) -> Ptr Double -> CInt -> IO CString +foreign import ccall "spectral_eigsh"+ c_eigsh :: CInt -> Ptr Double -> Ptr Double -> Ptr Double -> CInt -> IO CString+ foreign import ccall "spectral_seigs" c_seigs :: CInt -> Ptr (Complex Double) -> Ptr (Complex Double) -> Ptr Double -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> IO CString++foreign import ccall "spectral_seigsh"+ c_seigsh :: CInt -> Ptr Double -> Ptr Double+ -> Ptr Double -> Ptr CInt -> Ptr CInt+ -> CInt -> CInt -> IO CString++foreign import ccall "eigen_bdcsvd"+ c_bdcsvd :: CInt -> Ptr a -> Ptr b -> Ptr a+ -> Ptr a -> CInt -> CInt -> IO CString
src/Data/Matrix/Static/LinearAlgebra.hs view
@@ -6,15 +6,26 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE InstanceSigs #-} module Data.Matrix.Static.LinearAlgebra- ( Arithmetic(..)+ ( module Data.Matrix.Static.LinearAlgebra.Types+ , Arithmetic(..) , Factorization(..)- , module Data.Matrix.Static.LinearAlgebra.Types+ , LinearAlgebra(..)++ -- * Dense matrix operation+ , inverse+ , eig+ , svd+ , cond ) where import qualified Data.Vector.Storable as VS+import Data.Vector.Storable (Vector) import System.IO.Unsafe (unsafePerformIO) import Data.Complex (Complex) import Data.Singletons.Prelude hiding ((@@), type (==))@@ -27,35 +38,39 @@ import qualified Data.Matrix.Static.Generic as C import qualified Data.Matrix.Static.Internal as Internal import Data.Matrix.Static.LinearAlgebra.Types+import Data.Matrix.Static.LinearAlgebra.Internal class Arithmetic (mat1 :: C.MatrixKind) (mat2 :: C.MatrixKind) where- -- | Matrix multiplication+ -- | Matrix multiplication between different types of matrices. (@@) :: ( Numeric a, SingI n, SingI m , If (mat1 == mat2) mat1 D.Matrix ~ mat3 )- => mat1 n p VS.Vector a- -> mat2 p m VS.Vector a- -> mat3 n m VS.Vector a+ => mat1 n p Vector a+ -> mat2 p m Vector a+ -> mat3 n m Vector a infixr 8 @@ + -- | Element-wise addition between different types of matrices. (%+%) :: ( Numeric a, SingI n, SingI m , If (mat1 == mat2) mat1 D.Matrix ~ mat3 )- => mat1 n m VS.Vector a- -> mat2 n m VS.Vector a- -> mat3 n m VS.Vector a+ => mat1 n m Vector a+ -> mat2 n m Vector a+ -> mat3 n m Vector a infixr 8 %+% + -- | Element-wise substraction between different types of matrices. (%-%) :: ( Numeric a, SingI n, SingI m , If (mat1 == mat2) mat1 D.Matrix ~ mat3 )- => mat1 n m VS.Vector a- -> mat2 n m VS.Vector a- -> mat3 n m VS.Vector a+ => mat1 n m Vector a+ -> mat2 n m Vector a+ -> mat3 n m Vector a infixr 8 %-% + -- | Element-wise multiplication between different types of matrices. (%*%) :: ( Numeric a, SingI n, SingI m , If (mat1 == mat2) mat1 S.SparseMatrix ~ mat3 )- => mat1 n m VS.Vector a- -> mat2 n m VS.Vector a- -> mat3 n m VS.Vector a+ => mat1 n m Vector a+ -> mat2 n m Vector a+ -> mat3 n m Vector a infixr 8 %*% instance Arithmetic D.Matrix D.Matrix where@@ -82,45 +97,69 @@ (%-%) a b = a %+% C.map negate b (%*%) = withSS Internal.c_ss_cmul +class LinearAlgebra (mat :: C.MatrixKind) where+ ident :: (Numeric a, SingI n) => mat n n Vector a -class Factorization mat where- -- | Matrix inverse- inverse :: (SingI n, Numeric a) => mat n n VS.Vector a -> mat n n VS.Vector a+instance LinearAlgebra D.Matrix where+ ident = D.diag 0 $ D.replicate 1 - -- | Eigenvalues (not ordered) and+instance LinearAlgebra S.SparseMatrix where+ ident = S.diag $ D.replicate 1++class Factorization mat where+ -- | Eigenvalues (from largest to smallest) and -- eigenvectors (as columns) of a general square matrix.- eigs :: (SingI k, SingI n, (k <= n - 2) ~ 'True)+ eigS :: (SingI k, SingI n, (k <= n - 2) ~ 'True) => Sing k- -> mat n n VS.Vector Double+ -> mat n n Vector Double -> (Matrix k 1 (Complex Double), Matrix n k (Complex Double)) + -- | Eigenvalues (from largest to smallest) and+ -- eigenvectors (as columns) of a symmetric square matrix.+ eigSH :: (SingI k, SingI n, (k <= n - 1) ~ 'True)+ => Sing k+ -> mat n n Vector Double+ -> (Matrix k 1 Double, Matrix n k Double)+ -- | Cholesky decomposition- cholesky :: (Numeric a, SingI n) => mat n n VS.Vector a -> mat n n VS.Vector a+ cholesky :: (Numeric a, SingI n) => mat n n Vector a -> mat n n Vector a -instance Factorization D.Matrix where - inverse = withFun1 Internal.c_inverse+instance Factorization D.Matrix where+ eigS s mat+ | D.all (==0) mat = ( D.replicate 0, D.replicate 1)+ | otherwise = unsafePerformIO $ do+ m1 <- CM.new+ m2 <- CM.new+ _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ ->+ unsafeWith mat $ \v n _ -> Internal.c_eigs k v1 v2 v n+ m1' <- C.unsafeFreeze m1+ m2' <- C.unsafeFreeze m2+ return (m1', m2')+ where+ k = fromIntegral $ fromSing s+ {-# INLINE eigS #-} - eigs s mat = unsafePerformIO $ do- m1 <- CM.new- m2 <- CM.new- _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ -> do- unsafeWith mat $ \v n _ -> Internal.c_eigs k v1 v2 v n- m1' <- C.unsafeFreeze m1- m2' <- C.unsafeFreeze m2- return (m1', m2')+ eigSH s mat+ | D.all (==0) mat = (D.replicate 0, D.replicate 1)+ | otherwise = unsafePerformIO $ do+ m1 <- CM.new+ m2 <- CM.new+ _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ ->+ unsafeWith mat $ \v n _ -> Internal.c_eigsh k v1 v2 v n+ m1' <- C.unsafeFreeze m1+ m2' <- C.unsafeFreeze m2+ return (m1', m2') where k = fromIntegral $ fromSing s- {-# INLINE eigs #-}+ {-# INLINE eigSH #-} cholesky mat = flip withFun1 mat $ \code p1 c1 _ p2 _ _ -> Internal.c_cholesky code p1 p2 c1 {-# INLINE cholesky #-} instance Factorization S.SparseMatrix where- inverse = undefined-- eigs s mat = unsafePerformIO $ do+ eigS s mat = unsafePerformIO $ do m1 <- CM.new m2 <- CM.new _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ ->@@ -131,6 +170,72 @@ return (m1', m2') where k = fromIntegral $ fromSing s- {-# INLINE eigs #-}+ {-# INLINE eigS #-} + eigSH s mat = unsafePerformIO $ do+ m1 <- CM.new+ m2 <- CM.new+ _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ ->+ unsafeWithS mat $ \pv pin po n _ size ->+ Internal.c_seigsh k v1 v2 pv po pin n size+ m1' <- C.unsafeFreeze m1+ m2' <- C.unsafeFreeze m2+ return (m1', m2')+ where+ k = fromIntegral $ fromSing s+ {-# INLINE eigSH #-}+ cholesky = undefined++type family R a where+ R Float = Float+ R Double = Double+ R (Complex Double) = Double+ R (Complex Float) = Float+ +-- | The inverse of a dense matrix.+inverse :: (SingI n, Numeric a) => Matrix n n a -> Matrix n n a+inverse = withFun1 Internal.c_inverse+{-# INLINE inverse #-}++-- | Compute the full eigendecomposition for dense matrix.+eig :: forall n . SingI n+ => Matrix n n Double+ -> (Matrix n 1 (Complex Double), Matrix n n (Complex Double))+eig mat = unsafePerformIO $ do+ m1 <- CM.new+ m2 <- CM.new+ _ <- unsafeWith' m1 $ \v1 _ _ -> unsafeWith' m2 $ \v2 _ _ ->+ unsafeWith mat $ \v n _ -> Internal.c_eig v1 v2 v n+ m1' <- C.unsafeFreeze m1+ m2' <- C.unsafeFreeze m2+ return (m1', m2')+{-# INLINE eig #-}++-- | Compute the full singular value decomposition for dense matrix.+svd :: forall n p a m. (Numeric (R a), Numeric a, SingI n, SingI p, SingI m, m ~ Min n p)+ => Matrix n p a+ -> (Matrix n m a, Matrix m 1 (R a), Matrix p m a)+svd mat = unsafePerformIO $ do+ mu <- CM.new+ ms <- CM.new+ mv <- CM.new+ checkResult $ unsafeWith' mu $ \pu _ _ -> unsafeWith' ms $ \ps _ _ ->+ unsafeWith' mv $ \pv _ _ -> unsafeWith mat $ \px r c ->+ Internal.c_bdcsvd (foreignType (undefined :: a))+ pu ps pv px r c+ u <- C.unsafeFreeze mu+ s <- C.unsafeFreeze ms+ v <- C.unsafeFreeze mv+ return (u, s, v)+{-# INLINE svd #-}++-- | Condition number.+cond :: ( Numeric a, Numeric (R a), Ord (R a), Fractional (R a)+ , SingI n, SingI m, SingI (Min n m))+ => Matrix n m a -> R a+cond mat = VS.maximum val / VS.minimum val+ where+ val = VS.filter (/=0) $ D.flatten s+ (_,s,_) = svd mat+{-# INLINE cond #-}
+ src/Data/Matrix/Static/LinearAlgebra/Internal.hs view
@@ -0,0 +1,189 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DataKinds #-}+{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}+module Data.Matrix.Static.LinearAlgebra.Internal+ ( withFun1+ , withFun2+ , withDS+ , withSD+ , withSS+ , checkResult+ , unsafeWith+ , unsafeWith'+ , unsafeWithS+ ) where++import Data.Vector.Storable (Storable)+import qualified Data.Vector.Storable as VS+import qualified Data.Vector.Storable.Mutable as VSM+import System.IO.Unsafe (unsafePerformIO)+import Control.Monad (when)+import Control.Monad.ST (RealWorld)+import Data.Singletons+import Foreign+import Foreign.C.Types+import Foreign.C.String++import qualified Data.Matrix.Static.Dense as D+import qualified Data.Matrix.Static.Dense.Mutable as DM+import qualified Data.Matrix.Static.Sparse as S+import qualified Data.Matrix.Static.Generic.Mutable as CM+import qualified Data.Matrix.Static.Generic as C+import Data.Matrix.Static.LinearAlgebra.Types++withFun1 :: forall r1 c1 r2 c2 a. (SingI r2, SingI c2, Numeric a)+ => (CInt -> Ptr a -> CInt -> CInt -> Ptr a -> CInt -> CInt -> IO CString)+ -> Matrix r1 c1 a -> Matrix r2 c2 a+withFun1 f m1 = unsafePerformIO $ do+ m0 <- CM.new+ checkResult $ unsafeWith' m0 $ \vals0 rows0 cols0 ->+ unsafeWith m1 $ \vals1 rows1 cols1 -> f (foreignType (undefined :: a))+ vals0 rows0 cols0+ vals1 rows1 cols1+ C.unsafeFreeze m0+{-# INLINE withFun1 #-}++withFun2 :: forall r1 c1 r2 c2 r3 c3 a.+ (SingI r3, SingI c3, Numeric a)+ => ( CInt -> Ptr a -> CInt -> CInt -> Ptr a -> CInt -> CInt+ -> Ptr a -> CInt -> CInt -> IO CString )+ -> Matrix r1 c1 a+ -> Matrix r2 c2 a+ -> Matrix r3 c3 a+withFun2 f m1 m2 = unsafePerformIO $ do+ m0 <- CM.new+ checkResult $ unsafeWith' m0 $ \vals0 rows0 cols0 ->+ unsafeWith m1 $ \vals1 rows1 cols1 ->+ unsafeWith m2 $ \vals2 rows2 cols2 ->+ f (foreignType (undefined :: a))+ vals0 rows0 cols0+ vals1 rows1 cols1+ vals2 rows2 cols2+ C.unsafeFreeze m0+{-# INLINE withFun2 #-}++withDS :: forall r1 c1 r2 c2 r3 c3 a.+ (SingI r3, SingI c3, Numeric a)+ => ( CInt+ -> Ptr a -> CInt -> CInt+ -> Ptr a -> CInt -> CInt+ -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt+ -> IO CString )+ -> Matrix r1 c1 a+ -> SparseMatrix r2 c2 a+ -> Matrix r3 c3 a+withDS f m1 m2 = unsafePerformIO $ do+ m0 <- CM.new+ checkResult $ unsafeWith' m0 $ \v0 r0 c0 ->+ unsafeWith m1 $ \v1 r1 c1 ->+ unsafeWithS m2 $ \v2 inner outer r2 c2 s ->+ f (foreignType (undefined :: a))+ v0 r0 c0+ v1 r1 c1+ v2 outer inner r2 c2 s+ C.unsafeFreeze m0+{-# INLINE withDS #-}++withSD :: forall r1 c1 r2 c2 r3 c3 a.+ (SingI r3, SingI c3, Numeric a)+ => ( CInt+ -> Ptr a -> CInt -> CInt+ -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt+ -> Ptr a -> CInt -> CInt+ -> IO CString )+ -> SparseMatrix r2 c2 a+ -> Matrix r1 c1 a+ -> Matrix r3 c3 a+withSD f m2 m1 = unsafePerformIO $ do+ m0 <- CM.new+ checkResult $ unsafeWith' m0 $ \v0 r0 c0 ->+ unsafeWith m1 $ \v1 r1 c1 ->+ unsafeWithS m2 $ \v2 inner outer r2 c2 s ->+ f (foreignType (undefined :: a))+ v0 r0 c0+ v2 outer inner r2 c2 s+ v1 r1 c1+ C.unsafeFreeze m0+{-# INLINE withSD #-}++mkSparseMatrix :: forall r c a. (Storable a, SingI r, SingI c)+ => (Ptr (Ptr a) -> Ptr (Ptr CInt) -> Ptr CInt -> IO Int)+ -> IO (SparseMatrix r c a)+mkSparseMatrix f = do+ outer' <- VSM.new $ c + 1+ (n, pv, pinner) <- VSM.unsafeWith outer' $ \pouter -> alloca $ \ppv -> alloca $ \ppi -> do+ n <- f ppv ppi pouter+ pv <- peek ppv >>= newForeignPtr finalizerFree+ pinner <- peek ppi >>= newForeignPtr finalizerFree+ return (n, pv, pinner)+ outer <- VS.unsafeFreeze outer'+ return $ S.SparseMatrix (VS.unsafeFromForeignPtr0 pv n)+ (VS.unsafeFromForeignPtr0 pinner n)+ outer+ where+ c = fromIntegral $ fromSing (sing :: Sing c)+{-# INLINE mkSparseMatrix #-}++withSS :: forall r1 c1 r2 c2 r3 c3 a.+ (SingI r3, SingI c3, Numeric a)+ => ( CInt+ -> Ptr (Ptr a) -> Ptr CInt -> Ptr (Ptr CInt) -> CInt -> CInt -> Ptr CInt+ -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt+ -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt+ -> IO CString )+ -> SparseMatrix r1 c1 a+ -> SparseMatrix r2 c2 a+ -> SparseMatrix r3 c3 a+withSS f m1 m2 = unsafePerformIO $ mkSparseMatrix $ \v0 inner0 outer0 ->+ alloca $ \pn -> unsafeWithS m1 $ \v1 inner1 outer1 r1 c1 s1 ->+ unsafeWithS m2 $ \v2 inner2 outer2 r2 c2 s2 -> do+ checkResult $ f (foreignType (undefined :: a))+ v0 outer0 inner0 r c pn+ v1 outer1 inner1 r1 c1 s1+ v2 outer2 inner2 r2 c2 s2+ fromIntegral <$> peek pn+ where+ r = fromIntegral $ fromSing (sing :: Sing r3)+ c = fromIntegral $ fromSing (sing :: Sing c3)+{-# INLINE withSS #-}++checkResult :: IO CString -> IO ()+checkResult func = func >>= \c_str -> when (c_str /= nullPtr) $+ peekCString c_str >>= \str -> error str+{-# INLINE checkResult #-}++-------------------------------------------------------------------------------+-- Raw pointers+-------------------------------------------------------------------------------++-- | Pass a pointer to the matrix's data to the IO action.+-- The data may not be modified through the pointer.+unsafeWith :: Storable a => Matrix n m a -> (Ptr a -> CInt -> CInt -> IO b) -> IO b+unsafeWith mat@(D.Matrix vec) f = VS.unsafeWith vec $ \p ->+ f p (fromIntegral r) $ fromIntegral c + where+ (r,c) = C.dim mat+{-# INLINE unsafeWith #-}++unsafeWith' :: Storable a => MMatrix n m RealWorld a -> (Ptr a -> CInt -> CInt -> IO b) -> IO b+unsafeWith' mat@(DM.MMatrix vec) f = VSM.unsafeWith vec $ \p ->+ f p (fromIntegral r) $ fromIntegral c+ where+ (r,c) = CM.dim mat+{-# INLINE unsafeWith' #-}++-- | Pass a pointer to the matrix's data to the IO action.+-- The data may not be modified through the pointer.+unsafeWithS :: (Storable a, S.Zero a)+ => SparseMatrix n m a+ -> (Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt -> IO b)+ -> IO b+unsafeWithS mat@(S.SparseMatrix val inner outer) f = VS.unsafeWith val $ \pval ->+ VS.unsafeWith inner $ \pinner -> VS.unsafeWith outer $ \pouter ->+ f pval pinner pouter (fromIntegral r) (fromIntegral c) (fromIntegral $ VS.length val)+ where+ (r,c) = C.dim mat+{-# INLINE unsafeWithS #-}
src/Data/Matrix/Static/LinearAlgebra/Types.hs view
@@ -9,39 +9,22 @@ , Matrix , MMatrix , SparseMatrix- , withFun1- , withFun2- , withDS- , withSD- , withSS- , unsafeWith- , unsafeWith'- , unsafeWithS ) where import Data.Vector.Storable (Vector, Storable)-import qualified Data.Vector.Storable as VS-import qualified Data.Vector.Storable.Mutable as VSM import Data.Vector.Storable.Mutable (MVector)-import System.IO.Unsafe (unsafePerformIO)-import Control.Monad (when) import Data.Complex (Complex)-import Control.Monad.ST (RealWorld)-import Data.Singletons-import Foreign import Foreign.C.Types-import Foreign.C.String import qualified Data.Matrix.Static.Dense as D import qualified Data.Matrix.Static.Dense.Mutable as DM import qualified Data.Matrix.Static.Sparse as S-import qualified Data.Matrix.Static.Generic.Mutable as CM-import qualified Data.Matrix.Static.Generic as C class (S.Zero a, Storable a, Num a) => Numeric a where foreignType :: a -> CInt instance Numeric Float where foreignType _ = 0+instance Numeric CFloat where foreignType _ = 0 instance Numeric Double where foreignType _ =1 instance Numeric (Complex Float) where foreignType _ = 2 instance Numeric (Complex Double) where foreignType _ = 3@@ -50,157 +33,3 @@ type MMatrix r c s a = DM.MMatrix r c MVector s a type SparseMatrix r c a = S.SparseMatrix r c Vector a--withFun1 :: forall r1 c1 r2 c2 a. (SingI r2, SingI c2, Numeric a)- => (CInt -> Ptr a -> CInt -> CInt -> Ptr a -> CInt -> CInt -> IO CString)- -> Matrix r1 c1 a -> Matrix r2 c2 a-withFun1 f m1 = unsafePerformIO $ do- m0 <- CM.new- checkResult $ unsafeWith' m0 $ \vals0 rows0 cols0 ->- unsafeWith m1 $ \vals1 rows1 cols1 -> f (foreignType (undefined :: a))- vals0 rows0 cols0- vals1 rows1 cols1- C.unsafeFreeze m0-{-# INLINE withFun1 #-}--withFun2 :: forall r1 c1 r2 c2 r3 c3 a.- (SingI r3, SingI c3, Numeric a)- => ( CInt -> Ptr a -> CInt -> CInt -> Ptr a -> CInt -> CInt- -> Ptr a -> CInt -> CInt -> IO CString )- -> Matrix r1 c1 a- -> Matrix r2 c2 a- -> Matrix r3 c3 a-withFun2 f m1 m2 = unsafePerformIO $ do- m0 <- CM.new- checkResult $ unsafeWith' m0 $ \vals0 rows0 cols0 ->- unsafeWith m1 $ \vals1 rows1 cols1 ->- unsafeWith m2 $ \vals2 rows2 cols2 ->- f (foreignType (undefined :: a))- vals0 rows0 cols0- vals1 rows1 cols1- vals2 rows2 cols2- C.unsafeFreeze m0-{-# INLINE withFun2 #-}--withDS :: forall r1 c1 r2 c2 r3 c3 a.- (SingI r3, SingI c3, Numeric a)- => ( CInt- -> Ptr a -> CInt -> CInt- -> Ptr a -> CInt -> CInt- -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt- -> IO CString )- -> Matrix r1 c1 a- -> SparseMatrix r2 c2 a- -> Matrix r3 c3 a-withDS f m1 m2 = unsafePerformIO $ do- m0 <- CM.new- checkResult $ unsafeWith' m0 $ \v0 r0 c0 ->- unsafeWith m1 $ \v1 r1 c1 ->- unsafeWithS m2 $ \v2 inner outer r2 c2 s ->- f (foreignType (undefined :: a))- v0 r0 c0- v1 r1 c1- v2 outer inner r2 c2 s- C.unsafeFreeze m0-{-# INLINE withDS #-}--withSD :: forall r1 c1 r2 c2 r3 c3 a.- (SingI r3, SingI c3, Numeric a)- => ( CInt- -> Ptr a -> CInt -> CInt- -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt- -> Ptr a -> CInt -> CInt- -> IO CString )- -> SparseMatrix r2 c2 a- -> Matrix r1 c1 a- -> Matrix r3 c3 a-withSD f m2 m1 = unsafePerformIO $ do- m0 <- CM.new- checkResult $ unsafeWith' m0 $ \v0 r0 c0 ->- unsafeWith m1 $ \v1 r1 c1 ->- unsafeWithS m2 $ \v2 inner outer r2 c2 s ->- f (foreignType (undefined :: a))- v0 r0 c0- v2 outer inner r2 c2 s- v1 r1 c1- C.unsafeFreeze m0-{-# INLINE withSD #-}--mkSparseMatrix :: forall r c a. (Storable a, SingI r, SingI c)- => (Ptr (Ptr a) -> Ptr (Ptr CInt) -> Ptr CInt -> IO Int)- -> IO (SparseMatrix r c a)-mkSparseMatrix f = do- outer' <- VSM.new $ c + 1- (n, pv, pinner) <- VSM.unsafeWith outer' $ \pouter -> alloca $ \ppv -> alloca $ \ppi -> do- n <- f ppv ppi pouter- pv <- peek ppv >>= newForeignPtr finalizerFree- pinner <- peek ppi >>= newForeignPtr finalizerFree- return (n, pv, pinner)- outer <- VS.unsafeFreeze outer'- return $ S.SparseMatrix (VS.unsafeFromForeignPtr0 pv n)- (VS.unsafeFromForeignPtr0 pinner n)- outer- where- c = fromIntegral $ fromSing (sing :: Sing c)-{-# INLINE mkSparseMatrix #-}--withSS :: forall r1 c1 r2 c2 r3 c3 a.- (SingI r3, SingI c3, Numeric a)- => ( CInt- -> Ptr (Ptr a) -> Ptr CInt -> Ptr (Ptr CInt) -> CInt -> CInt -> Ptr CInt- -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt- -> Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt- -> IO CString )- -> SparseMatrix r1 c1 a- -> SparseMatrix r2 c2 a- -> SparseMatrix r3 c3 a-withSS f m1 m2 = unsafePerformIO $ mkSparseMatrix $ \v0 inner0 outer0 ->- alloca $ \pn -> unsafeWithS m1 $ \v1 inner1 outer1 r1 c1 s1 ->- unsafeWithS m2 $ \v2 inner2 outer2 r2 c2 s2 -> do- checkResult $ f (foreignType (undefined :: a))- v0 outer0 inner0 r c pn- v1 outer1 inner1 r1 c1 s1- v2 outer2 inner2 r2 c2 s2- fromIntegral <$> peek pn- where- r = fromIntegral $ fromSing (sing :: Sing r3)- c = fromIntegral $ fromSing (sing :: Sing c3)-{-# INLINE withSS #-}--checkResult :: IO CString -> IO ()-checkResult func = func >>= \c_str -> when (c_str /= nullPtr) $- peekCString c_str >>= \str -> error str-{-# INLINE checkResult #-}------------------------------------------------------------------------------------ Raw pointers------------------------------------------------------------------------------------ | Pass a pointer to the matrix's data to the IO action.--- The data may not be modified through the pointer.-unsafeWith :: Storable a => Matrix n m a -> (Ptr a -> CInt -> CInt -> IO b) -> IO b-unsafeWith mat@(D.Matrix vec) f = VS.unsafeWith vec $ \p ->- f p (fromIntegral r) $ fromIntegral c - where- (r,c) = C.dim mat-{-# INLINE unsafeWith #-}--unsafeWith' :: Storable a => MMatrix n m RealWorld a -> (Ptr a -> CInt -> CInt -> IO b) -> IO b-unsafeWith' mat@(DM.MMatrix vec) f = VSM.unsafeWith vec $ \p ->- f p (fromIntegral r) $ fromIntegral c- where- (r,c) = CM.dim mat-{-# INLINE unsafeWith' #-}---- | Pass a pointer to the matrix's data to the IO action.--- The data may not be modified through the pointer.-unsafeWithS :: (Storable a, S.Zero a)- => SparseMatrix n m a- -> (Ptr a -> Ptr CInt -> Ptr CInt -> CInt -> CInt -> CInt -> IO b)- -> IO b-unsafeWithS mat@(S.SparseMatrix val inner outer) f = VS.unsafeWith val $ \pval ->- VS.unsafeWith inner $ \pinner -> VS.unsafeWith outer $ \pouter ->- f pval pinner pouter (fromIntegral r) (fromIntegral c) (fromIntegral $ VS.length val)- where- (r,c) = C.dim mat-{-# INLINE unsafeWithS #-}
src/Data/Matrix/Static/Sparse.hs view
@@ -35,6 +35,8 @@ -- * Construction , C.empty , fromTriplet+ , fromTripletC+ , toTriplet , C.fromVector , C.fromList , C.unsafeFromVector@@ -54,10 +56,12 @@ import qualified Data.Vector.Storable as S import qualified Data.Vector.Storable.Mutable as SM import Data.Singletons-import Control.Monad import Control.Monad.ST (runST) import Data.Bits (shiftR) import Text.Printf (printf)+import Conduit+import Data.Conduit.Internal (zipSinks)+import Data.Tuple (swap) import GHC.TypeLits (type (<=)) import Foreign.C.Types import Data.Complex@@ -77,6 +81,9 @@ instance Zero Float where zero = 0.0 +instance Zero CFloat where+ zero = 0.0+ instance Zero Double where zero = 0.0 @@ -101,28 +108,16 @@ -- non-zero in the previous two arrays. -> SparseMatrix r c v a +instance (G.Vector v a, Eq (v a)) => Eq (SparseMatrix r c v a) where+ (==) (SparseMatrix a b c) (SparseMatrix a' b' c') =+ a == a' && b == b' && c == c'+ instance (G.Vector v a, Zero a, Show a) => Show (SparseMatrix r c v a) where show mat = printf "(%d x %d)\n%s" r c vals where (r,c) = C.dim mat vals = unlines $ map (unwords . map show . G.toList) $ C.toRows mat -instance (SingI r, SingI c, G.Vector v a, Zero a, Num a) =>- Num (SparseMatrix r c v a) where- m1 + m2 = undefined- m1 - m2 = undefined- m1 * m2 = undefined- negate = C.map negate- abs = C.map abs- signum = undefined- fromInteger = undefined--instance (SingI r, SingI c, G.Vector v a, Zero a, Fractional a) =>- Fractional (SparseMatrix r c v a) where- m1 / m2 = undefined- recip = C.map recip- fromRational = undefined- instance (G.Vector v a, Zero a) => C.Matrix SparseMatrix v a where -- | O(1) Return the size of matrix. dim :: forall r c. SparseMatrix r c v a -> (Int, Int)@@ -145,27 +140,18 @@ -- | O(1) Create matrix from vector containing columns. unsafeFromVector :: forall r c. (G.Vector v a, SingI r, SingI c) => v a -> SparseMatrix r c v a- unsafeFromVector vec = SparseMatrix- (G.generate n (G.unsafeIndex vec . S.unsafeIndex nz))- inner outer+ unsafeFromVector vec = fromTriplet vec' where- inner = S.map fromIntegral $ S.map (`mod` c) nz- outer = S.create $ do- v <- SM.replicate (c+1) 0- S.forM_ nz $ \x -> do- let i = x `div` r- SM.unsafeModify v succ (i+1)- forM_ [1..c] $ \i -> do- x <- SM.unsafeRead v (i-1)- SM.unsafeModify v (+x) i- return v- nz = S.filter (\i -> vec `G.unsafeIndex` i /= zero) $ S.enumFromN 0 (r*c)- n = S.length nz+ vec' = map (\((a,b),c) -> (a,b,c)) $ filter ((/=zero) . snd) $+ zipWith (\i x -> (toIndex i, x)) [0..] $ G.toList vec+ toIndex i = swap $ i `divMod` r r = fromIntegral $ fromSing (sing :: Sing r)- c = fromIntegral $ fromSing (sing :: Sing c) {-# INLINE unsafeFromVector #-} - transpose (SparseMatrix val inner outer) = undefined + transpose mat = runIdentity $ fromTripletC source+ where+ source = toTriplet mat .| mapC (\(i,j,x) -> (j,i,x))+ {-# INLINE transpose #-} thaw = undefined {-# INLINE thaw #-}@@ -180,9 +166,15 @@ {-# INLINE unsafeFreeze #-} map f (SparseMatrix vec inner outer) = SparseMatrix (G.map f vec) inner outer- imap = undefined {-# INLINE map #-} + imap f mat@(SparseMatrix _ inner outer) = SparseMatrix vec' inner outer+ where+ vec' = runST $ runConduit $ toTriplet mat .| mapC g .| sinkVector+ g (i,j,x) = f (i,j) x+ {-# INLINE imap #-}+ + -- | O(n) Create matrix from triplet. row and column indices *are not* assumed to be ordered -- duplicate entries are carried over to the CSR represention fromTriplet :: forall t r c v a. (Traversable t, G.Vector v a, SingI r, SingI c)@@ -192,28 +184,71 @@ outer = S.scanl (+) 0 $ S.create $ do vec <- SM.replicate c 0 _ <- flip mapM triplets $ \(_, j, _) -> - SM.modify vec (+1) j+ SM.unsafeModify vec (+1) j return vec (val, inner) = runST $ do outer' <- S.thaw outer val' <- GM.new nnz inner' <- SM.new nnz _ <- flip mapM triplets $ \(i, j, v) -> do- idx <- fromIntegral <$> SM.read outer' j- GM.write val' idx v- SM.write inner' idx $ fromIntegral i- SM.modify outer' (+1) j+ idx <- fromIntegral <$> SM.unsafeRead outer' j+ GM.unsafeWrite val' idx v+ SM.unsafeWrite inner' idx $ fromIntegral i+ SM.unsafeModify outer' (+1) j (,) <$> G.unsafeFreeze val' <*> S.unsafeFreeze inner' nnz = length triplets c = fromIntegral $ fromSing (sing :: Sing c) {-# INLINE fromTriplet #-} -{--toTriplet :: (G.Vector v1 (Int, Int, a), G.Vector v2 a, SingI r, SingI c)- => SparseMatrix r c v2 a -> v1 (Int, Int, a)-toTriplet mat = --}+-- | O(n) Create matrix from triplet. row and column indices *are not* assumed to be ordered+-- duplicate entries are carried over to the CSR represention+fromTripletC :: forall m r c v a. (Monad m, G.Vector v a, SingI r, SingI c)+ => ConduitT () (Int, Int, a) m ()+ -> m (SparseMatrix r c v a)+fromTripletC triplets = do+ (nnz, outer) <- runConduit $ triplets .| zipSinks lengthC sinkOuter+ (val, inner, _) <- runConduit $ triplets .| sinkValInner nnz (clone outer)+ return $ SparseMatrix val inner outer+ where+ sinkOuter = S.scanl (+) 0 <$> foldlC f (S.replicate c 0)+ where+ f vec (_, j, _) = S.modify (\v -> SM.unsafeModify v (+1) j) vec+ sinkValInner nnz outer0 = foldlC f (val0, inner0, outer0)+ where+ val0 = G.create $ GM.new nnz+ inner0 = S.create $ SM.new nnz+ f (val, inner, outer) (i, j, v) = (val', inner', outer')+ where+ idx = fromIntegral $ outer `S.unsafeIndex` j+ val' = G.create $ do+ vec <- G.unsafeThaw val+ GM.unsafeWrite vec idx v+ return vec+ inner' = S.create $ do+ vec <- S.unsafeThaw inner+ SM.unsafeWrite vec idx $ fromIntegral i+ return vec+ outer' = S.create $ do+ vec <- S.unsafeThaw outer+ SM.unsafeModify vec (+1) j+ return vec+ c = fromIntegral $ fromSing (sing :: Sing c)+ clone x = S.create $ S.thaw x+{-# INLINE fromTripletC #-} +toTriplet :: (Monad m, G.Vector v a, SingI r, SingI c)+ => SparseMatrix r c v a -> ConduitT i (Int, Int, a) m ()+toTriplet (SparseMatrix val inner outer) =+ G.ifoldM_ go (fromIntegral $ G.head outer) outer+ where+ go start curC end = do+ enumFromToC start (end'-1) .| mapC f+ return end'+ where+ end' = fromIntegral end+ f i = (fromIntegral $ inner `G.unsafeIndex` i, fromIntegral curC - 1, val `G.unsafeIndex` i)+{-# INLINE toTriplet #-}+ -- | O(m*n) Create a rectangular matrix with default values and given diagonal diag :: (G.Vector v a, Zero a, SingI n) => D.Matrix n 1 v a -- ^ diagonal@@ -242,3 +277,12 @@ k = (u+l) `shiftR` 1 x' = vec `S.unsafeIndex` k {-# INLINE binarySearchByBounds #-}+++-------------------------------------------------------------------------------+-- Helper+-------------------------------------------------------------------------------++--getIndex :: Int -> (Int, Int)+--getIndex = +--{-# INLINE getIndex #-}
src/Data/Matrix/Static/Sparse/Mutable.hs view
@@ -16,6 +16,7 @@ import qualified Data.Vector.Generic.Mutable as GM import qualified Data.Vector.Storable as S import Prelude hiding (read, replicate)+import Foreign.C.Types import Data.Singletons import qualified Data.Matrix.Static.Generic.Mutable as C@@ -25,16 +26,18 @@ MSparseMatrix :: (SingI r, SingI c) => (v s a) -- ^ Values: stores the coefficient values -- of the non-zeros.- -> (S.Vector Int) -- ^ InnerIndices: stores the row+ -> (S.Vector CInt) -- ^ InnerIndices: stores the row -- (resp. column) indices of the non-zeros.- -> (S.Vector Int) -- ^ OuterStarts: stores for each column+ -> (S.Vector CInt) -- ^ OuterStarts: stores for each column -- (resp. row) the index of the first -- non-zero in the previous two arrays.+ -> (S.Vector CInt) -- InnerNNZs: stores the number of non-zeros+ -- of each column (resp. row). -> MSparseMatrix r c v s a instance GM.MVector v a => C.MMatrix MSparseMatrix v a where dim :: forall r c s. MSparseMatrix r c v s a -> (Int, Int)- dim (MSparseMatrix _ _ _) = (r,c)+ dim (MSparseMatrix _ _ _ _) = (r,c) where r = fromIntegral $ fromSing (sing :: Sing r) c = fromIntegral $ fromSing (sing :: Sing c)
tests/Main.hs view
@@ -1,36 +1,13 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}- module Main where import Test.Tasty-import Data.Matrix.Static.LinearAlgebra-import qualified Data.Matrix.Static.Dense as D-import qualified Data.Matrix.Static.Sparse as S-import Data.Singletons-import Data.Vector.Storable (Storable)-import GHC.TypeNats (KnownNat)-import Control.Monad.IO.Class (liftIO) -import Test.Tasty.QuickCheck--instance (Arbitrary a, KnownNat m, KnownNat n) => Arbitrary (Matrix m n a) where- arbitrary = D.fromList <$> vector (m*n)- where- m = fromIntegral $ fromSing (sing :: Sing m)- n = fromIntegral $ fromSing (sing :: Sing n)- shrink _v = []--{--propTranspose :: Matrix 50 100 Double -> Bool-propTranspose m = D.transpose (D.transpose m) == m && - D.convertAny (S.transpose $ S.transpose (D.convertAny m)) == m--}+import Test.Base+import Test.LinearAlgebra main :: IO () main = defaultMain $ testGroup "Main"- [ testProperty "" square+ [ base+ , linearAlgebra ]
+ tests/Test/Base.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE IncoherentInstances #-}++module Test.Base (base) where++import Test.Tasty+import qualified Data.Matrix.Static.Generic as G+import qualified Data.Matrix.Static.Dense as D+import qualified Data.Matrix.Static.Sparse as S+import Data.Singletons hiding ((@@))+import Data.Singletons.Prelude (Min)+import Data.Vector (Vector)+import Control.Monad.IO.Class (liftIO)+import Test.Tasty.QuickCheck+import Conduit++import Test.Utils++base :: TestTree+base = testGroup "Base"+ [ pConversion+ , pTranspose+ ]++pConversion = testGroup "Conversion"+ [ testProperty "Dense: fromVector . flatten" t1+ , testProperty "Sparse: fromVector . flatten" t2+ , testProperty "Sparse -- fromTriplet . toTriplet" tTri+ , testProperty "Sparse -- fromTripletC . toTriplet" tTriC+ , testProperty "Sparse -- dense" t3 ]+ where+ t1 :: D.Matrix 80 60 Vector Int -> Bool+ t1 mat = (D.fromVector $ D.flatten mat) == mat+ t2 :: S.SparseMatrix 80 60 Vector Int -> Bool+ t2 mat = mat == S.fromVector (S.flatten mat)+ t3 :: D.Matrix 80 60 Vector Int -> Bool+ t3 mat = (D.fromVector $ S.flatten mat') == mat+ where+ mat' = S.fromVector $ D.flatten mat :: S.SparseMatrix 80 60 Vector Int+ tTri :: S.SparseMatrix 80 60 Vector Int -> Bool+ tTri mat = S.fromTriplet xs == mat+ where+ xs = runIdentity $ runConduit $ S.toTriplet mat .| sinkList+ tTriC :: S.SparseMatrix 80 60 Vector Int -> Bool+ tTriC mat = mat == runIdentity (S.fromTripletC (S.toTriplet mat))++pTranspose = testGroup "Transpose"+ [ testProperty "Dense" t1+ , testProperty "Sparse" tSp+ ]+ where+ t1 :: D.Matrix 80 40 Vector Int -> Bool+ t1 mat = D.transpose (D.transpose mat) == mat+ tSp :: S.SparseMatrix 80 40 Vector Int -> Bool+ tSp mat = G.transpose (G.transpose mat) == mat
+ tests/Test/LinearAlgebra.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE IncoherentInstances #-}++module Test.LinearAlgebra (linearAlgebra) where++import Test.Tasty+import Data.Matrix.Static.LinearAlgebra+import qualified Data.Matrix.Static.Dense as D+import qualified Data.Matrix.Static.Generic as G+import qualified Data.Matrix.Static.Sparse as S+import Data.Singletons hiding ((@@))+import Data.Singletons.Prelude (Min)+import Data.Complex+import Data.Vector.Storable (Storable)+import GHC.TypeNats (KnownNat)+import Control.Monad.IO.Class (liftIO)+import Test.Tasty.QuickCheck++import Test.Utils++linearAlgebra :: TestTree+linearAlgebra = testGroup "Linear algebra"+ [ svdTest+ , eigenTest+ ]++svdTest = testGroup "SVD"+ [ testProperty "SVD (Float)" svd1+ , testProperty "SVD (Double)" svd2+ ]+ where+ svd1 :: Matrix 50 30 Float -> Bool+ svd1 m = m' ~= m+ where+ m' = u @@ S.diag d @@ D.transpose v+ (u,d,v) = svd m+ svd2 :: Matrix 50 30 Double -> Bool+ svd2 m = m' ~= m+ where+ m' = u @@ S.diag d @@ D.transpose v+ (u,d,v) = svd m++eigenTest = testGroup "Eigendecomposition"+ [ testProperty "Full" eigen1+ , testProperty "Partial dense" eigen2+ , testProperty "Partial symmetric dense" eigen3+ , testProperty "Partial symmetric sparse" eigen4+ ]+ where+ eigen1 :: Matrix 100 100 Double -> Bool+ eigen1 m = (m' @@ v) ~= (v @@ S.diag d)+ where+ m' = D.map (\x -> mkPolar x 0) m+ (d, v)= eig m+ eigen2 :: Matrix 10 10 Double -> Bool+ eigen2 m = m' @@ v ~= v @@ S.diag d+ where+ m' = D.map (\x -> mkPolar x 0) m+ (d, v)= eigS (sing :: Sing 8) m+ eigen3 :: Matrix 100 100 Double -> Bool+ eigen3 raw = m @@ v ~= v @@ S.diag d+ where+ m = raw %+% D.transpose raw+ (d, v)= eigSH (sing :: Sing 99) m+ eigen4 :: SparseMatrix 100 100 Double -> Bool+ eigen4 raw = m @@ v ~= v @@ S.diag d+ where+ m = raw %+% D.transpose raw+ (d, v)= eigSH (sing :: Sing 99) m++++{-+propTranspose :: Matrix 50 100 Double -> Bool+propTranspose m = D.transpose (D.transpose m) == m && + D.convertAny (S.transpose $ S.transpose (D.convertAny m)) == m+-}
+ tests/Test/Utils.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE IncoherentInstances #-}++module Test.Utils where++import Test.Tasty.QuickCheck+import Data.Complex+import Data.Matrix.Static.LinearAlgebra+import Data.Singletons+import qualified Data.Matrix.Static.Dense as D+import qualified Data.Matrix.Static.Sparse as S+import Data.Vector.Storable (Storable)+import Data.List.Ordered+import Data.Ord+import qualified Data.Vector.Generic as G+import Data.AEq+import Control.Monad++class Approx a where+ (~=) :: a -> a -> Bool+ infixl 4 ~=++instance Approx Double where+ a ~= b = abs (a - b) < 1e-10++instance Approx Float where+ a ~= b = abs (a - b) < 1e-3++instance Approx (Complex Double) where+ a ~= b = r1 ~= r2 && i1 ~= i2+ where+ (r1, i1) = polar a+ (r2, i2) = polar b++instance (SingI m, SingI n, Storable a, Approx a) => Approx (Matrix m n a) where+ m1 ~= m2 = D.all id $ D.zipWith (~=) m1 m2++instance (G.Vector v a, Arbitrary a, SingI m, SingI n)+ => Arbitrary (D.Matrix m n v a) where+ arbitrary = D.fromList <$> vector (m*n)+ where+ m = fromIntegral $ fromSing (sing :: Sing m)+ n = fromIntegral $ fromSing (sing :: Sing n)+ shrink _v = []++instance (G.Vector v a, Arbitrary a, SingI m, SingI n, S.Zero a)+ => Arbitrary (S.SparseMatrix m n v a) where+ arbitrary = do+ vals <- filter (/=S.zero) <$> vector p+ xs <- fmap (nubSortBy (comparing fst)) $ forM vals $ \v -> do+ i <- choose (0, m-1)+ j <- choose (0, n-1)+ return ((i,j),v)+ return $ S.fromTriplet $ map (\((a,b),c) -> (a,b,c)) xs+ where+ p = (m * n) `div` 10+ m = fromIntegral $ fromSing (sing :: Sing m)+ n = fromIntegral $ fromSing (sing :: Sing n)+ shrink _v = []