diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# multilinear-io
+Input/output capability in various formats (binary, CSV, JSON) for Multilinear package in Haskell. 
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/benchmark/Bench.hs b/benchmark/Bench.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/Bench.hs
@@ -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 ()
+
diff --git a/multilinear-io.cabal b/multilinear-io.cabal
new file mode 100644
--- /dev/null
+++ b/multilinear-io.cabal
@@ -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
diff --git a/src/Multilinear/Generic/Serialize.hs b/src/Multilinear/Generic/Serialize.hs
new file mode 100644
--- /dev/null
+++ b/src/Multilinear/Generic/Serialize.hs
@@ -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
diff --git a/src/Multilinear/Index/Finite/Serialize.hs b/src/Multilinear/Index/Finite/Serialize.hs
new file mode 100644
--- /dev/null
+++ b/src/Multilinear/Index/Finite/Serialize.hs
@@ -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
diff --git a/src/Multilinear/Index/Infinite/Serialize.hs b/src/Multilinear/Index/Infinite/Serialize.hs
new file mode 100644
--- /dev/null
+++ b/src/Multilinear/Index/Infinite/Serialize.hs
@@ -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
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -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
+  
