matrix-sized 0.1.0 → 0.1.1
raw patch · 8 files changed
+178/−99 lines, 8 filesdep +double-conversiondep −conduit-extra
Dependencies added: double-conversion
Dependencies removed: conduit-extra
Files
- matrix-sized.cabal +5/−4
- src/Data/Matrix/Dynamic.hs +93/−0
- src/Data/Matrix/Static/Dense.hs +2/−1
- src/Data/Matrix/Static/Generic.hs +5/−33
- src/Data/Matrix/Static/IO.hs +55/−38
- src/Data/Matrix/Static/Sparse.hs +6/−17
- tests/Test/Base.hs +9/−4
- tests/Test/Utils.hs +3/−2
matrix-sized.cabal view
@@ -1,13 +1,13 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: 2b1169a59b262a2ee39daffd72cfe59b2b4f716e82ef7df496b186c155e1b003+-- hash: 856dabd5492c97e6a433e61a9b3b39337897ded09cf66a9702ba3010a9ee0a83 name: matrix-sized-version: 0.1.0+version: 0.1.1 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@@ -391,6 +391,7 @@ Data.Matrix.Static.Generic Data.Matrix.Static.Generic.Mutable Data.Matrix.Static.IO+ Data.Matrix.Dynamic other-modules: Data.Matrix.Static.Internal Data.Matrix.Static.LinearAlgebra.Internal@@ -413,7 +414,7 @@ , bytestring , bytestring-lexing , conduit- , conduit-extra+ , double-conversion , primitive >=0.6.4.0 , singletons , store
+ src/Data/Matrix/Dynamic.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+module Data.Matrix.Dynamic+ ( Dynamic(..)+ , withDyn+ , matrix+ , fromList+ , fromVector+ , fromColumns+ , fromRows+ , fromTriplet+ , decodeSparse+ )where++import Data.ByteString (ByteString)+import qualified Data.Matrix.Static.Sparse as S+import qualified Data.Matrix.Static.Generic as C+import qualified Data.Vector.Generic as G+import Data.Kind (Type)+import Data.Singletons+import Data.Singletons.TypeLits+import Data.Store (Store(..), decodeExWith)++data Dynamic (m :: C.MatrixKind) (v :: Type -> Type) a where+ Dynamic :: m r c v a -> Dynamic m v a++withDyn :: Dynamic m v a -> (forall r c. m r c v a -> b) -> b+withDyn (Dynamic x) f = f x+{-# INLINE withDyn #-}++matrix :: forall m v a. C.Matrix m v a => [[a]] -> Dynamic m v a+matrix lists = withSomeSing (fromIntegral r) $ \(SNat :: Sing r) ->+ withSomeSing (fromIntegral c) $ \(SNat :: Sing c) ->+ Dynamic (C.matrix lists :: m r c v a)+ where+ r = length lists+ c = length $ head lists+{-# INLINE matrix #-}++-- | Construct matrix from a list containg columns.+fromList :: forall v a m. C.Matrix m v a+ => (Int, Int) -> [a] -> Dynamic m v a+fromList d = fromVector d . G.fromList+{-# INLINE fromList #-}++-- | Construct matrix from a vector containg columns.+fromVector :: forall v a m. C.Matrix m v a+ => (Int, Int) -> v a -> Dynamic m v a+fromVector (r, c) vec = withSomeSing (fromIntegral r) $ \(SNat :: Sing r) ->+ withSomeSing (fromIntegral c) $ \(SNat :: Sing c) ->+ Dynamic (C.fromVector vec :: m r c v a)+{-# INLINE fromVector #-}++-- | O(m*n) Create matrix from rows+fromRows :: forall m v a. C.Matrix m v a+ => [v a] -> Dynamic m v a+fromRows xs = withSomeSing r $ \(SNat :: Sing r) -> + withSomeSing c $ \(SNat :: Sing c) -> Dynamic (C.fromRows xs :: m r c v a)+ where+ r = fromIntegral $ length xs+ c = fromIntegral $ G.length $ head xs+{-# INLINE fromRows #-}++fromColumns :: forall m v a. C.Matrix m v a+ => [v a] -> Dynamic m v a+fromColumns xs = withSomeSing r $ \(SNat :: Sing r) -> + withSomeSing c $ \(SNat :: Sing c) -> Dynamic (C.fromColumns xs :: m r c v a)+ where+ c = fromIntegral $ length xs+ r = fromIntegral $ G.length $ head xs+{-# INLINE fromColumns #-}++fromTriplet :: forall u v a. (G.Vector u (Int, Int, a), G.Vector v a)+ => (Int, Int) -> u (Int, Int, a) -> Dynamic S.SparseMatrix v a+fromTriplet (r, c) triplets = withSomeSing (fromIntegral r) $ \(SNat :: Sing r) -> + withSomeSing (fromIntegral c) $ \(SNat :: Sing c) ->+ Dynamic (S.fromTriplet triplets :: S.SparseMatrix r c v a)+{-# INLINE fromTriplet #-}++decodeSparse :: forall v a. (Store (v a), G.Vector v a)+ => ByteString -> Dynamic S.SparseMatrix v a+decodeSparse bs = withSomeSing (fromIntegral (r :: Int)) $ \(SNat :: Sing r) ->+ withSomeSing (fromIntegral (c :: Int)) $ \(SNat :: Sing c) ->+ Dynamic (S.SparseMatrix nnz inner outer :: S.SparseMatrix r c v a)+ where+ (r,c,nnz,inner,outer) = decodeExWith + ((,,,,) <$> peek <*> peek <*> peek <*> peek <*> peek)+ bs+{-# INLINE decodeSparse #-}
src/Data/Matrix/Static/Dense.hs view
@@ -105,7 +105,7 @@ import Data.Tuple (swap) import qualified Data.List as L import Text.Printf (printf)-import Data.Store (Store(..), Size(..), decodeExWith)+import Data.Store (Store(..), Size(..)) import Foreign.Storable (sizeOf) import Data.Matrix.Static.Dense.Mutable (MMatrix (..))@@ -123,6 +123,7 @@ size = VarSize $ \(Matrix vec) -> case size of VarSize f -> 2 * sizeOf (0 :: Int) + f vec+ _ -> undefined poke mat@(Matrix vec) = poke r >> poke c >> poke vec where
src/Data/Matrix/Static/Generic.hs view
@@ -11,7 +11,7 @@ , Matrix(..) , MatrixKind - -- * Derived mothods+ -- * Matrix query , rows , cols , (!)@@ -19,15 +19,15 @@ , takeRow , toRows , toColumns++ -- * Matrix Construction , empty , matrix- , withMatrix , fromRows- , withRows , fromColumns- , withColumns , fromVector , fromList+ , toList , create , convertAny@@ -44,8 +44,7 @@ import Data.Tuple (swap) import Data.Kind (Type) import GHC.TypeLits (Nat, type (<=))-import Data.Singletons (SingI, Sing, fromSing, sing, withSomeSing)-import Data.Singletons.TypeLits+import Data.Singletons (SingI, Sing, fromSing, sing) import Data.Matrix.Static.Generic.Mutable (MMatrix, MMatrixKind) @@ -158,15 +157,6 @@ matrix = fromList . concat . L.transpose {-# INLINE matrix #-} -withMatrix :: forall mat v a b. Matrix mat v a- => [[a]] -> (forall r c. mat r c v a -> b) -> b-withMatrix xs f = withSomeSing n $ \(SNat :: Sing n) -> - withSomeSing m $ \(SNat :: Sing m) -> f (matrix xs :: mat n m v a)- where- n = fromIntegral $ length xs- m = fromIntegral $ length $ head xs-{-# INLINE withMatrix #-}- -- | Construct matrix from a list containg columns. fromList :: (SingI r, SingI c, Matrix m v a) => [a] -> m r c v a@@ -178,29 +168,11 @@ fromRows = transpose . fromColumns {-# INLINE fromRows #-} -withRows :: forall mat v a b. Matrix mat v a- => [v a] -> (forall r c. mat r c v a -> b) -> b-withRows xs f = withSomeSing n $ \(SNat :: Sing n) -> - withSomeSing m $ \(SNat :: Sing m) -> f (fromRows xs :: mat n m v a)- where- n = fromIntegral $ length xs- m = fromIntegral $ G.length $ head xs-{-# INLINE withRows #-}- -- | O(m*n) Create matrix from columns fromColumns :: (Matrix m v a, SingI r, SingI c) => [v a] -> m r c v a fromColumns = fromVector . G.concat {-# INLINE fromColumns #-}--withColumns :: forall mat v a b. Matrix mat v a- => [v a] -> (forall r c. mat r c v a -> b) -> b-withColumns xs f = withSomeSing n $ \(SNat :: Sing n) -> - withSomeSing m $ \(SNat :: Sing m) -> f (fromRows xs :: mat n m v a)- where- m = fromIntegral $ length xs- n = fromIntegral $ G.length $ head xs-{-# INLINE withColumns #-} -- | O(m*n) Create a list by concatenating columns toList :: Matrix m v a => m r c v a -> [a]
src/Data/Matrix/Static/IO.hs view
@@ -15,18 +15,24 @@ module Data.Matrix.Static.IO ( fromMM- , withMM+ , fromMM'+ , toMM+ , IOElement(..) ) where import qualified Data.ByteString.Char8 as B import Conduit import Control.Monad (when) import qualified Data.Vector.Generic as G+import Data.Matrix.Dynamic (Dynamic(..))+import qualified Data.Vector.Unboxed as U import Data.ByteString.Lex.Fractional (readExponential, readSigned) import Data.ByteString.Lex.Integral (readDecimal, readDecimal_) import Data.Singletons import Data.Maybe import Data.Singletons.TypeLits+import Data.Double.Conversion.ByteString (toShortest)+import Text.Printf (printf) import qualified Data.Matrix.Static.Sparse as S @@ -36,63 +42,84 @@ | MMPattern deriving (Eq) -class IOElement a where+class U.Unbox a => IOElement a where decodeElem :: B.ByteString -> a+ encodeElem :: a -> B.ByteString elemType :: Proxy a -> MMElem instance IOElement Int where decodeElem x = fst . fromMaybe errMsg . readSigned readDecimal $ x where errMsg = error $ "readInt: Fail to cast ByteString to Int:" ++ show x+ encodeElem = B.pack . show elemType _ = MMInteger instance IOElement Double where decodeElem x = fst . fromMaybe errMsg . readSigned readExponential $ x where errMsg = error $ "readDouble: Fail to cast ByteString to Double:" ++ show x+ encodeElem = toShortest elemType _ = MMReal -withMM :: forall m v a b. (Monad m, G.Vector v a, IOElement a)- => ConduitT () B.ByteString m ()- -> (forall r c. S.SparseMatrix r c v a -> m b)- -> m b-withMM conduit f = do- (ty, (r,c,nnz)) <- parseHeader conduit+fromMM' :: forall o m v a. (PrimMonad m, G.Vector v a, IOElement a)+ => ConduitT B.ByteString o m (Dynamic S.SparseMatrix v a)+fromMM' = linesUnboundedAsciiC .| do+ (ty, (r,c,nnz)) <- parseHeader when (elemType (Proxy :: Proxy a) /= ty) $ error "Element types do not match"+ vec <- streamTriplet .| sinkVector + when (U.length vec /= nnz) $ error $+ "number of non-zeros do not match: " <> show nnz <> "/=" <> show (U.length vec) withSomeSing (fromIntegral (r :: Int)) $ \(SNat :: Sing r) ->- withSomeSing (fromIntegral (c :: Int)) $ \(SNat :: Sing c) -> do- mat@(S.SparseMatrix v _ _) <- S.fromTripletC triplets- let n = G.length v- when (n /= nnz) $ error $- "number of non-zeros do not match: " <> show nnz <> "/=" <> show n- f (mat :: S.SparseMatrix r c v a)- where- triplets = conduit .| linesUnboundedAsciiC .| filterC (not . (=='%') . B.head) .|- (dropC 1 >> streamTriplet)+ withSomeSing (fromIntegral (c :: Int)) $ \(SNat :: Sing c) ->+ return $ Dynamic (S.fromTriplet vec :: S.SparseMatrix r c v a) -fromMM :: forall m r c v a. (Monad m, SingI r, SingI c, G.Vector v a, IOElement a)- => ConduitT () B.ByteString m () -> m (S.SparseMatrix r c v a)-fromMM conduit = do- (ty, (r,c,nnz)) <- parseHeader conduit+fromMM :: forall o m r c v a. (PrimMonad m, SingI r, SingI c, G.Vector v a, IOElement a)+ => ConduitT B.ByteString o m (S.SparseMatrix r c v a)+fromMM = linesUnboundedAsciiC .| do+ (ty, (r,c,nnz)) <- parseHeader mat@(S.SparseMatrix v _ _) <- case () of _ | elemType (Proxy :: Proxy a) /= ty -> error "Element types do not match" | (r, c) /= (nrow, ncol) -> error $ "Dimensions do not match: " <> show (r,c) <> "/=" <> show (nrow,ncol)- | otherwise -> S.fromTripletC triplets+ | otherwise -> do+ vec <- streamTriplet .| sinkVector + return $ S.fromTriplet (vec :: U.Vector (Int, Int, a)) let n = G.length v if n /= nnz then error $ "number of non-zeros do not match: " <> show nnz <> "/=" <> show n else return mat where- triplets = conduit .| linesUnboundedAsciiC .| filterC (not . (=='%') . B.head) .|- (dropC 1 >> streamTriplet) nrow = fromIntegral $ fromSing (sing :: Sing r) :: Int ncol = fromIntegral $ fromSing (sing :: Sing c) :: Int -parseHeader :: Monad m => ConduitT () B.ByteString m () -> m (MMElem, (Int, Int, Int))-parseHeader conduit = runConduit $- conduit .| linesUnboundedAsciiC .| headerParser+toMM :: forall m r c v a i. (Monad m, S.Zero a, IOElement a, G.Vector v a)+ => S.SparseMatrix r c v a -> ConduitT i B.ByteString m ()+toMM mat@(S.SparseMatrix vec _ _) = ( do+ yield header+ yield "%"+ yield $ B.pack $ printf "%d %d %d" r c n+ S.toTriplet mat .| mapC f ) .| unlinesAsciiC where+ f (i, j, x) = B.unwords [B.pack $ show (i+1), B.pack $ show (j+1), encodeElem x]+ header = case elemType (Proxy :: Proxy a) of+ MMReal -> "%%MatrixMarket matrix coordinate real general"+ MMInteger -> "%%MatrixMarket matrix coordinate integer general"+ _ -> undefined+ (r, c) = S.dim mat+ n = G.length vec++parseHeader :: Monad m => ConduitT B.ByteString o m (MMElem, (Int, Int, Int))+parseHeader = do+ ty <- headC >>= \case+ Nothing -> error "Empty file"+ Just header -> return $ parse header+ dropWhileC $ (=='%') . B.head + headC >>= \case+ Nothing -> error "Empty file"+ Just x ->+ let [r, c, nnz] = map decodeElem $ B.words x+ in return (ty, (r, c, nnz))+ where parse x | "%%MatrixMarket" `B.isPrefixOf` x = case B.words x of [_, _, format, ty, form] -> @@ -105,21 +132,11 @@ in ty' _ -> error $ "Cannot parse header: " <> show x | otherwise = error $ "Cannot parse header: " <> show x- headerParser = do- ty <- headC >>= \case- Nothing -> error "Empty file"- Just header -> return $ parse header- line <- filterC (not . (=='%') . B.head) .| headC- case line of- Nothing -> error "Empty file"- Just x ->- let [r, c, nnz] = map decodeElem $ B.words x- in return (ty, (r, c, nnz)) {-# INLINE parseHeader #-} streamTriplet :: (Monad m, IOElement a) => ConduitT B.ByteString (Int, Int, a) m () streamTriplet = mapC (f . B.words) where- f [i,j,x] = (readDecimal_ i, readDecimal_ j, decodeElem x)+ f [i,j,x] = (readDecimal_ i - 1, readDecimal_ j - 1, decodeElem x) f x = error $ "Formatting error: " <> show x {-# INLINE streamTriplet #-}
src/Data/Matrix/Static/Sparse.hs view
@@ -38,7 +38,6 @@ , fromTriplet , fromTripletC , toTriplet- , withDecodedMatrix , C.fromVector , C.fromList , C.unsafeFromVector@@ -69,10 +68,8 @@ import GHC.TypeLits (type (<=)) import Foreign.C.Types import Data.Complex-import Data.Store (Store(..), Size(..), decodeExWith)+import Data.Store (Store(..), Size(..)) import Foreign.Storable (sizeOf)-import Data.ByteString (ByteString)-import Data.Singletons.TypeLits import qualified Data.Matrix.Static.Dense as D import qualified Data.Matrix.Static.Dense.Mutable as DM@@ -123,6 +120,7 @@ case (size, size) of (VarSize f, VarSize g) -> 2 * sizeOf (0 :: Int) + f nnz + g inner + g outer+ _ -> undefined poke mat@(SparseMatrix nnz inner outer) = poke r >> poke c >> poke nnz >> poke inner >> poke outer@@ -138,17 +136,6 @@ r = fromIntegral $ fromSing (sing :: Sing r) :: Int c = fromIntegral $ fromSing (sing :: Sing c) :: Int -withDecodedMatrix :: forall v a b. (G.Vector v a, Store (v a))- => ByteString -> (forall r c. SparseMatrix r c v a -> b) -> b-withDecodedMatrix bs f = withSomeSing (fromIntegral (r :: Int)) $ \(SNat :: Sing r) ->- withSomeSing (fromIntegral (c :: Int)) $ \(SNat :: Sing c) ->- f (SparseMatrix nnz inner outer :: SparseMatrix r c v a)- where- (r,c,nnz,inner,outer) = decodeExWith - ((,,,,) <$> peek <*> peek <*> peek <*> peek <*> peek)- bs-{-# INLINE withDecodedMatrix #-}- 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'@@ -261,8 +248,9 @@ where outer = S.scanl (+) 0 $ S.create $ do vec <- SM.replicate c 0- G.forM_ triplets $ \(_, j, _) -> - SM.unsafeModify vec (+1) j+ G.forM_ triplets $ \(i, j, _) -> if i < r && j < c+ then SM.unsafeModify vec (+1) j+ else error $ printf "Index out of bound: (%d, %d) >= (%d, %d)" i j r c return vec (val, inner) = runST $ do outer' <- S.thaw outer@@ -275,6 +263,7 @@ SM.unsafeModify outer' (+1) j (,) <$> G.unsafeFreeze val' <*> S.unsafeFreeze inner' nnz = G.length triplets+ r = fromIntegral $ fromSing (sing :: Sing r) c = fromIntegral $ fromSing (sing :: Sing c) {-# INLINE fromTriplet #-}
tests/Test/Base.hs view
@@ -13,11 +13,12 @@ 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 qualified Data.Matrix.Dynamic as Dyn+import Control.Monad.ST (runST)+import Data.Matrix.Static.IO import Data.Singletons hiding ((@@))-import Data.Singletons.Prelude (Min) import Data.Vector (Vector) import qualified Data.Vector as V-import Control.Monad.IO.Class (liftIO) import Test.Tasty.QuickCheck import Conduit import Data.Store@@ -35,14 +36,18 @@ pSerialization = testGroup "Serialization" [ testProperty "Dense: id == decode . encode" tStoreD , testProperty "Sparse: id == decode . encode" tStoreS- , testProperty "Sparse: id == decode . encode" tStoreS' ]+ , testProperty "Sparse: id == decode . encode" tStoreS'+ , testProperty "Sparse: id ~= fromMM . toMM" tMM ] where tStoreD :: D.Matrix 80 60 Vector Double -> Bool tStoreD mat = mat == decodeEx (encode mat) tStoreS :: S.SparseMatrix 80 60 Vector Double -> Bool tStoreS mat = mat == decodeEx (encode mat) tStoreS' :: S.SparseMatrix 80 60 Vector Double -> Bool- tStoreS' mat = G.flatten mat == S.withDecodedMatrix (encode mat) G.flatten+ tStoreS' mat = G.flatten mat ==+ Dyn.withDyn (Dyn.decodeSparse $ encode mat) G.flatten+ tMM :: S.SparseMatrix 80 60 Vector Int -> Bool+ tMM mat = runST (runConduit $ toMM mat .| fromMM) == mat pConversion :: TestTree pConversion = testGroup "Conversion"
tests/Test/Utils.hs view
@@ -39,8 +39,9 @@ (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 (SingI m, SingI n, G.Vector v Bool, G.Vector v a, Approx a) =>+ Approx (D.Matrix m n v 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