multilinear-io (empty) → 0.2.1
raw patch · 9 files changed
+474/−0 lines, 9 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, cereal, cereal-vector, criterion, csv-enumerator, deepseq, either, multilinear, multilinear-io, transformers, vector, zlib
Files
- LICENSE +29/−0
- README.md +2/−0
- Setup.hs +2/−0
- benchmark/Bench.hs +35/−0
- multilinear-io.cabal +87/−0
- src/Multilinear/Generic/Serialize.hs +154/−0
- src/Multilinear/Index/Finite/Serialize.hs +26/−0
- src/Multilinear/Index/Infinite/Serialize.hs +27/−0
- test/Spec.hs +112/−0
+ LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License + +Copyright (c) 2018, Artur M. Brodzki +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,2 @@+# multilinear-io +Input/output capability in various formats (binary, CSV, JSON) for Multilinear package in Haskell.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ benchmark/Bench.hs view
@@ -0,0 +1,35 @@+{-|+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 Control.DeepSeq+import Criterion.Main+import Criterion.Measurement as Meas+import Criterion.Types+import Multilinear.Generic+import qualified Multilinear.Matrix as Matrix++m1 :: Tensor Double+m1 = Matrix.fromIndices "ij" 1000 1000 $ \i j -> fromIntegral (2*i) - exp (fromIntegral j)++m2 :: Tensor Double+m2 = Matrix.fromIndices "jk" 1000 1000 $ \i j -> sin (fromIntegral i) + cos (fromIntegral j)++main :: IO ()+main = do+ putStrLn "Two matrices 1000x1000 multiplying..."+ (meas,_) <- Meas.measure ( nfIO $ (m1 * m2) `deepseq` putStrLn "End!" ) 1+ putStrLn $ "Measured time: " ++ show (measCpuTime meas) ++ " s."+ return ()+
+ multilinear-io.cabal view
@@ -0,0 +1,87 @@+-- This file has been generated from package.yaml by hpack version 0.28.2. +--+-- see: https://github.com/sol/hpack+--+-- hash: fc24bf1b7c659c968d9c62e939cea9cb5b96f56f414a6cedfa5c560678c2b83a++name: multilinear-io+version: 0.2.1+synopsis: Input/output capability for multilinear package.+description: Input/output capability for multilinear package <https://hackage.haskell.org/package/multilinear>. Supports various file formats: binary, CSV, JSON. More information available on GitHub: <https://github.com/ArturB/multilinear-io#readme>+category: Machine learning+homepage: https://github.com/ArturB/multilinear-io#readme+bug-reports: https://github.com/ArturB/multilinear-io/issues+author: Artur M. Brodzki+maintainer: artur@brodzki.org+copyright: 2018 Artur M. Brodzki+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10+extra-source-files:+ README.md++source-repository head+ type: git+ location: https://github.com/ArturB/multilinear-io++library+ exposed-modules:+ Multilinear.Generic.Serialize+ Multilinear.Index.Finite.Serialize+ Multilinear.Index.Infinite.Serialize+ other-modules:+ Paths_multilinear_io+ hs-source-dirs:+ src+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses+ ghc-options: -O2 -Wall+ build-depends:+ aeson+ , base >=4.7 && <5+ , bytestring+ , cereal+ , cereal-vector+ , csv-enumerator+ , either+ , multilinear >=0.2.0 && <0.3+ , transformers+ , vector+ , zlib+ default-language: Haskell2010++test-suite multilinear-io-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Paths_multilinear_io+ hs-source-dirs:+ test+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses+ ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.7 && <5+ , either+ , multilinear >=0.2.0 && <0.3+ , multilinear-io+ , transformers+ default-language: Haskell2010++benchmark multilinear-io-bench+ type: exitcode-stdio-1.0+ main-is: Bench.hs+ other-modules:+ Paths_multilinear_io+ hs-source-dirs:+ benchmark+ default-extensions: DeriveGeneric FlexibleContexts FlexibleInstances MultiParamTypeClasses+ ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.7 && <5+ , criterion+ , deepseq+ , either+ , multilinear >=0.2.0 && <0.3+ , multilinear-io+ , transformers+ default-language: Haskell2010
+ src/Multilinear/Generic/Serialize.hs view
@@ -0,0 +1,154 @@+{-|+Module : Multilinear.Generic.Serialize+Description : Generic array tensor serialization: binary, JSON, CSV. +Copyright : (c) Artur M. Brodzki, 2018+License : BSD3+Maintainer : artur@brodzki.org+Stability : experimental+Portability : Windows/POSIX++-}++module Multilinear.Generic.Serialize (+ toBinary, toBinaryFile,+ fromBinary, fromBinaryFile,+ Multilinear.Generic.Serialize.toJSON, toJSONFile,+ Multilinear.Generic.Serialize.fromJSON, fromJSONFile,+ fromCSV, toCSV+) where++import Codec.Compression.GZip+import Control.Exception+import Control.Monad.Trans.Class+import Control.Monad.Trans.Either+import Control.Monad.Trans.Maybe+import Data.Aeson+import qualified Data.ByteString.Lazy as ByteString+import Data.CSV.Enumerator+import Data.Either+import Data.Serialize+import qualified Data.Vector as Boxed+import Data.Vector.Serialize ()+import Multilinear.Class+import Multilinear.Generic+import qualified Multilinear.Index.Finite as Finite+import Multilinear.Index.Finite.Serialize ()+import Multilinear.Index.Infinite.Serialize ()++-- Binary serialization instance+instance Serialize a => Serialize (Tensor a)+-- JSON serialization instance+instance ToJSON a => ToJSON (Tensor a)+-- JSON deserialization instance+instance FromJSON a => FromJSON (Tensor a)++invalidIndices :: String -- ^ CSV error message+invalidIndices = "Indices and its sizes not compatible with structure of matrix!"++deserializationError :: String -- ^ CSV error message+deserializationError = "Components deserialization error!"++{-| Serialize tensor to binary string -}+toBinary :: (+ Serialize a + ) => Tensor a -- ^ Tensor to serialize+ -> ByteString.ByteString -- ^ Tensor serialized to lazy ByteString+toBinary = Data.Serialize.encodeLazy++{-| Write tensor to binary file. Uses compression with gzip -}+toBinaryFile :: (+ Serialize a + ) => String -- ^ File name+ -> Tensor a -- ^ Tensor to serialize+ -> IO ()+toBinaryFile name = ByteString.writeFile name . compress . toBinary++{-| Deserialize tensor from binary string -}+fromBinary :: (+ Serialize a+ ) => ByteString.ByteString -- ^ ByteString to deserialize+ -> Either String (Tensor a) -- ^ Deserialized tensor or an error message. +fromBinary = Data.Serialize.decodeLazy++{-| Read tensor from binary file -}+fromBinaryFile :: (+ Serialize a+ ) => String -- ^ File path. + -> EitherT String IO (Tensor a) -- ^ Deserialized tensor or an error message+fromBinaryFile name = do+ contents <- lift $ ByteString.readFile name+ EitherT $ return $ fromBinary $ decompress contents++{-| Serialize tensor to JSON string -}+toJSON :: (+ ToJSON a+ ) => Tensor a -- ^ Tensor to serialize. + -> ByteString.ByteString -- ^ Tensor serialized to lazy ByteString. +toJSON = Data.Aeson.encode++{-| Write tensor to JSON file -}+toJSONFile :: (+ ToJSON a+ ) => String -- ^ File path. + -> Tensor a -- ^ Tensor to serialize+ -> IO ()+toJSONFile name = ByteString.writeFile name . Multilinear.Generic.Serialize.toJSON++{-| Deserialize tensor from JSON string -}+fromJSON :: (+ FromJSON a+ ) => ByteString.ByteString -- ^ ByteString to deserialize+ -> Maybe (Tensor a) -- ^ Deserialized tensor or Nothing, if deserialization error occured. +fromJSON = Data.Aeson.decode++{-| Read tensor from JSON file -}+fromJSONFile :: (+ FromJSON a+ ) => String -- ^ File path. + -> MaybeT IO (Tensor a) -- ^ Deserialized tensor or Nothing, if error occured. +fromJSONFile name = do+ contents <- lift $ ByteString.readFile name+ MaybeT $ return $ Multilinear.Generic.Serialize.fromJSON contents++{-| Read tensor (matrix) components from CSV file. -}+{-# INLINE fromCSV #-}+fromCSV :: (+ Num a, Serialize a+ ) => String -- ^ Indices names (one character per index, first character: rows index, second character: columns index)+ -> String -- ^ CSV file name+ -> Char -- ^ Separator expected to be used in this CSV file+ -> EitherT SomeException IO (Tensor a) -- ^ Generated matrix or error message++fromCSV x = case x of+ [u,d] -> \fileName separator -> do+ csv <- EitherT $ readCSVFile (CSVS separator (Just '"') (Just '"') separator) fileName+ let components = (Data.Serialize.decode <$> ) <$> csv+ let rows = length components+ let columns = if rows > 0 then length $ rights (head components) else 0+ if rows > 0 && columns > 0+ then return $ + FiniteTensor (Finite.Contravariant rows [u]) $ (+ SimpleFinite (Finite.Covariant columns [d]) . Boxed.fromList . rights+ ) <$> Boxed.fromList components+ else EitherT $ return $ Left $ SomeException $ TypeError deserializationError++ _ -> \_ _ -> return $ Err invalidIndices+++{-| Write matrix to CSV file. -}+{-# INLINE toCSV #-}+toCSV :: (+ Num a, Serialize a+ ) => Tensor a -- ^ Matrix to serialize. If given tensor os not a matrix, an error occurs and no data (0 rows) are saved to file. + -> String -- ^ CSV file name+ -> Char -- ^ Separator expected to be used in this CSV file+ -> IO Int -- ^ Number of rows written++toCSV t = case order t of+ (1,1) -> \fileName separator ->+ let t' = _standardize t+ elems = Boxed.toList $ Boxed.toList . tensorScalars <$> tensorsFinite t'+ encodedElems = (Data.Serialize.encode <$>) <$> elems+ in writeCSVFile (CSVS separator (Just '"') (Just '"') separator) fileName encodedElems++ _ -> \_ _ -> return 0
+ src/Multilinear/Index/Finite/Serialize.hs view
@@ -0,0 +1,26 @@+{-| +Module : Multilinear.Index.Finite.Serialize +Description : Finite-dimensional tensor index serialization: binary, JSON. +Copyright : (c) Artur M. Brodzki, 2018 +License : BSD3 +Maintainer : artur@brodzki.org +Stability : experimental +Portability : Windows/POSIX + +Finite-dimensional tensor index. + +-} + +module Multilinear.Index.Finite.Serialize ( +) where + +import Data.Aeson +import Data.Serialize +import Multilinear.Index.Finite + +{-| Binary serialization and deserialization |-} +instance Serialize Index + +{-| Serialization to and from JSON |-} +instance FromJSON Index +instance ToJSON Index
+ src/Multilinear/Index/Infinite/Serialize.hs view
@@ -0,0 +1,27 @@+{-| +Module : Multilinear.Index.Infinite.Serialize +Description : Infinite-dimensional tensor index serialization: binary, JSON. +Copyright : (c) Artur M. Brodzki, 2018 +License : BSD3 +Maintainer : artur@brodzki.org +Stability : experimental +Portability : Windows/POSIX + +Infinite-dimensional tensor index. + +-} + +module Multilinear.Index.Infinite.Serialize ( + Index(..) +) where + +import Data.Aeson +import Data.Serialize +import Multilinear.Index.Infinite + +{-| Binary serialization and deserialization |-} +instance Serialize Index + +{-| Serialization to and from JSON |-} +instance FromJSON Index +instance ToJSON Index
+ test/Spec.hs view
@@ -0,0 +1,112 @@+module Main where++import Control.Exception.Base+import Control.Monad.Trans.Either+import Control.Monad.Trans.Class+import Multilinear.Class as Multilinear+import Multilinear.Generic+import Multilinear.Generic.Serialize+import qualified Multilinear.Matrix as Matrix+import qualified Multilinear.Tensor as Tensor+import qualified Multilinear.Vector as Vector++-- PARAMETRY SKRYPTU+fi = signum -- funkcja aktywacji perceptronu+layers = 10 -- liczba warstw perceptronu++mlp_input = "test/data/mlp_input.csv" -- dane uczące dla perceptronu+mlp_expected = "test/data/mlp_expected.csv" -- dane oczekiwane dla percepttronu+mlp_classify = "test/data/mlp_classify.csv" -- dane do klasyfikacji na nauczonym perceptronie+mlp_output = "test/data/mlp_output.csv" -- wyjście perceptronu+hopfield_input = "test/data/hopfield_input.csv" -- wzorce do zpamiętania dla sieci Hopfielda+hopfield_classify = "test/data/hopfield_classify.csv" -- dane do klasyfikacji dla sieci Hopfielda+hopfield_output = "test/data/hopfield_output.csv" -- wyjście sieci Hopfielda+++-- PERCEPTRON WIELOWARSTWOWY+perceptron :: Int -- ns: liczba neuronów w warstwie+ -> Int -- ks: liczba warstw + -> Int -- ps: liczba wektorów uczących+ -> Int -- cs: liczba wektorów do klasyfikacji+ -> (Int -> Tensor Double) -- x t: wejścia uczące w funkcji czasu+ -> (Int -> Tensor Double) -- e t: wyjścia oczekiwane w funkcji czasu+ -> (Int -> Tensor Double) -- c t: dane do klasyfikacji w funkcji czasu+ -> Tensor Double -- Tensor ("i","t"): zaklasyfikowane dane++perceptron ns ks ps cs x e c =+ let -- wagi startowe+ zero = Tensor.const ("ki",[ks,ns]) ("j",[ns]) 0+ -- wagi w następnym kroku uczącym+ nextWeights w x e =+ let ygen [k] [] = -- tensor wyjść+ if k == 0 then x $| ("j","") + else fi <$> w $$| ("k",[k - 1]) $| ("i","j") * ygen [k-1] [] $| ("j","")+ y = Tensor.generate ("k",[ks + 1]) ("",[]) $ \[k] [] -> ygen [k] []+ -- tensor wejścia-wyjścia omega+ om = Tensor.generate ("k",[ks]) ("",[]) $ + \[k] [] -> ygen [k + 1] [] $| ("i","") * ygen [k] [] $| ("j","") \/ "j"+ incWgen [k] [] = -- inkrementacyjna propagacja wsteczna+ if k == ks - 1 then x $| ("j","") \/ "j" * (y $$| ("k",[ks-1]) $| ("i","") - e $| ("i",""))+ else Multilinear.transpose (w $$| ("k",[k+1])) $| ("i","b") * + incWgen [k+1] [] $| ("b","c") * + om $$| ("k",[k]) $| ("c","j")+ incW = Tensor.generate ("k",[ks]) ("",[]) $ \[k] [] -> incWgen [k] []+ in w $| ("ki","j") + incW $| ("ki","j")+ xl = take 2 $ x <$> [0 .. ps - 1]+ el = take 2 $ e <$> [0 .. ps - 1]+ -- uczenie sieci+ learnedNetwork = foldr (\(x,e) w -> nextWeights w x e) zero $ zip xl el+ -- praca nauczonej sieci+ out t = fi <$> learnedNetwork $$| ("k",[ks-1]) $| ("i","j") * c t $| ("j","")+ in Tensor.generate ("",[]) ("t",[cs]) $ \[] [t] -> out t++-- SIEĆ HOPFIELDA+hopfield :: Int -- ns: liczba neuronów w sieci+ -> Int -- ps: liczba wzorców do zapamiętania+ -> Int -- cs: liczba wzorców do klasyfikacji+ -> Tensor Int -- x: macierz wektorów do zapamiętania+ -> Tensor Int -- c: macierz wektorów do sklasyfikowania+ -> Tensor Int -- macierz sklafyfikowanych wektorów+hopfield ns ps cs x c = + let -- 1 - deltaKroneckera+ delta = Matrix.fromIndices "ij" ns ns $ \i j -> if i == j then 0 else 1+ -- wagi sieci ze wzorców+ w = delta * x $| ("i","t") * (x $| ("j","t") \/ "j") * Vector.const "t" ps 1+ -- wyjście sieci: sieć działa rekurencyjnie aż do osiągnięcia stanu stabilnego+ y inp =+ let out = (\x -> if x > 0 then 1 else 0) <$> w $| ("i","j") * inp $| ("j","") + in if out $| ("i","") == inp $| ("i","") then out else y out+ -- klasyfikacja zadanych wektorów+ in Tensor.generate ("",[]) ("t",[cs]) $ \[] [t] -> y ((c $| ("i","t")) $$| ("t",[t]))++-- OPERACJE WEJŚCIA/WYJŚCIA+prog :: EitherT SomeException IO ()+prog = do+ -- wczytywanie danych+ mlpInput <- fromCSV "tj" mlp_input ';'+ mlpExp <- fromCSV "tj" mlp_expected ';'+ mlpClas <- fromCSV "tj" mlp_classify ';'+ hopInput <- fromCSV "tj" hopfield_input ';'+ hopClas <- fromCSV "tj" hopfield_classify ';'+ let mx t = Multilinear.transpose $ mlpInput $$| ("t",[t])+ let me t = Multilinear.transpose $ mlpExp $$| ("t",[t])+ let mc t = Multilinear.transpose $ mlpClas $$| ("t",[t])+ let hx = Multilinear.transpose $ hopInput $| ("it",[])+ let hc = Multilinear.transpose $ hopClas $| ("it",[])+ let (ns_mlp,ps_mlp,cs_mlp,ns_hop,ps_hop,cs_hop) = + (mlpInput `size` "j", mlpExp `size` "t", mlpClas `size` "t", + hopInput `size` "j", hopInput `size` "t", hopClas `size` "t")+ -- perceptron+ let mlp_net = perceptron ns_mlp layers ps_mlp cs_mlp mx me mc+ smlp <- lift $ toCSV mlp_net mlp_output ';'+ -- hopfield+ let hop_net = hopfield ns_hop ps_hop cs_hop hx hc+ shop <- lift $ toCSV hop_net hopfield_output ';'+ lift $ putStrLn $ "Perceptron: " ++ show smlp ++ " vectors saved to '" ++ mlp_output ++ "'."+ lift $ putStrLn $ "Hopfield: " ++ show shop ++ " vectors saved to '" ++ hopfield_output ++ "'."+ return ()++-- ENTRY POINT+main :: IO (Either SomeException ())+main = runEitherT prog+