packages feed

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 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+