module Multilinear.Generic.Serialize (
toBinary, toBinaryFile,
fromBinary, fromBinaryFile,
Multilinear.Generic.Serialize.toJSON,
Multilinear.Generic.Serialize.toJSONFile,
Multilinear.Generic.Serialize.fromJSON,
Multilinear.Generic.Serialize.fromJSONFile,
fromCSVFile, toCSVFile
) where
import Codec.Compression.GZip
import Data.Conduit
import Data.Conduit.Combinators
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Data.Aeson
import qualified Data.ByteString.Internal as BS
import qualified Data.ByteString.Lazy as ByteString
import Data.Csv
import Data.Either
import Data.Serialize
import qualified Data.Vector as Boxed
import Data.Vector.Serialize ()
import Multilinear.Generic
import qualified Multilinear.Index.Finite as Finite
import Multilinear.Index.Finite.Serialize ()
import Multilinear.Index.Infinite.Serialize ()
instance Serialize a => Serialize (Tensor a)
instance ToJSON a => ToJSON (Tensor a)
instance FromJSON a => FromJSON (Tensor a)
instance ToField a => ToRecord (Tensor a) where
toRecord (Scalar x) = record [toField x]
toRecord (SimpleFinite _ scalars) = toRecord scalars
toRecord _ = error "Only 1-order tensor may be converted to record!"
instance FromField a => FromRecord (Tensor a) where
parseRecord v =
if Boxed.length v == 1 then
Scalar <$> v .! 0
else
error "non-scalar"
toBinary :: (
Serialize a
) => Tensor a
-> ByteString.ByteString
toBinary = compress . Data.Serialize.encodeLazy
toBinaryFile :: (
Serialize a
) => Tensor a
-> String
-> IO ()
toBinaryFile t fileName = do
let bs = toBinary t
runConduitRes $
sourceLazy bs .| sinkFile fileName
fromBinary :: (
Serialize a
) => ByteString.ByteString
-> Either String (Tensor a)
fromBinary = Data.Serialize.decodeLazy . decompress
fromBinaryFile :: (
Serialize a
) => String
-> ExceptT String IO (Tensor a)
fromBinaryFile fileName = do
contents <- lift $ runConduitRes $
sourceFile fileName .| sinkLazy
ExceptT $ return $ fromBinary contents
toJSON :: (
ToJSON a
) => Tensor a
-> ByteString.ByteString
toJSON = Data.Aeson.encode
toJSONFile :: (
ToJSON a
) => Tensor a
-> String
-> IO ()
toJSONFile t fileName = do
let bs = Multilinear.Generic.Serialize.toJSON t
runConduitRes $
sourceLazy bs .| sinkFile fileName
fromJSON :: (
FromJSON a
) => ByteString.ByteString
-> Maybe (Tensor a)
fromJSON = Data.Aeson.decode
fromJSONFile :: (
FromJSON a
) => String
-> MaybeT IO (Tensor a)
fromJSONFile fileName = do
contents <- lift $ runConduitRes $
sourceFile fileName .| sinkLazy
MaybeT $ return $ Multilinear.Generic.Serialize.fromJSON contents
toCSVFile :: (
ToField a
) => Tensor a
-> String
-> IO ()
toCSVFile (FiniteTensor _ vrows) fileName = do
let rows = Boxed.toList vrows
let bs = Data.Csv.encode rows
runConduitRes $
sourceLazy bs .| sinkFile fileName
toCSVFile (SimpleFinite _ vs) fileName = do
let rows = [vs]
let bs = Data.Csv.encode rows
runConduitRes $
sourceLazy bs .| sinkFile fileName
fromCSVFile :: (
FromField a
) => String
-> Char
-> String
-> ExceptT String IO (Tensor a)
fromCSVFile fileName separator [conI,covI] = do
contents <- lift $ runConduitRes $
sourceFile fileName .| sinkLazy
let decodedData = Data.Csv.decodeWith (DecodeOptions (BS.c2w separator)) NoHeader contents :: FromField a => Either String (Boxed.Vector (Boxed.Vector a))
if isLeft decodedData
then do
let msg = fromLeft "" decodedData
ExceptT $ return $ Left msg
else do
let components = fromRight (Boxed.empty) decodedData
let rows = (\r -> SimpleFinite (Finite.Covariant (Boxed.length r) [covI]) r) <$> components
ExceptT $ return $ Right $ FiniteTensor (Finite.Contravariant (Boxed.length rows) [conI]) rows
fromCSVFile _ _ _ = error "You must provide exactly two indices names!"