packages feed

record-encode (empty) → 0.1.0.0

raw patch · 8 files changed

+418/−0 lines, 8 filesdep +QuickCheckdep +basedep +generics-sopsetup-changed

Dependencies added: QuickCheck, base, generics-sop, hspec, record-encode, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Marco Zocca (c) 2018++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 Marco Zocca nor the names of other+      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+OWNER 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,32 @@+# record-encode++[![Build Status](https://travis-ci.org/ocramz/record-encode.png)](https://travis-ci.org/ocramz/record-encode)++This library provides generic machinery to encode values of some algebraic type as points in a vector space.++Analyzing datasets that have one or more categorical variables (== values having a sum type) typically requires a series of boilerplate transformations, and the `encodeOneHot` function provided here does precisely that.++# Usage example++```+    {-# language DeriveGeneric -#}++    import qualified GHC.Generics as G+    import qualified Generics.SOP as SOP+    +    import Data.Record.Encode++    data X = A | B | C deriving (G.Generic)+    instance SOP.Generic X+```++```+    > encodeOneHot B+    [0,1,0]+```++++# Acknowledgements++Gagandeep Bhatia (@gagandeepb) for his GSoC '18 work on `Frames-beam`, Mark Karpov (@mrkkrp) for his Template Haskell tutorial, Anthony Cowley (@acowley) for `vinyl` and `Frames`, @mniip on Freenode #haskell for helping me better understand what can be done with generic programming.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ record-encode.cabal view
@@ -0,0 +1,44 @@+name:                record-encode+version:             0.1.0.0+synopsis:            Generic encoding of records+description:         Generic encoding of records. Provides a single function and two typeclasses to encode values of sum types as one-hot vectors.+homepage:            https://github.com/ocramz/record-encode+license:             BSD3+license-file:        LICENSE+author:              Marco Zocca+maintainer:          ocramz fripost org+copyright:           2018 Marco Zocca+category:            Data+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10+tested-with:         GHC == 8.4.3++library+  default-language:    Haskell2010+  ghc-options:         -Wall +  hs-source-dirs:      src+  exposed-modules:     Data.Record.Encode+  other-modules:       Data.Record.Encode.Generics+  build-depends:       base >= 4.7 && < 5+                     , generics-sop+                     , vector+++test-suite spec+  default-language:    Haskell2010+  ghc-options:         -Wall+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:     LibSpec+  build-depends:       base+                     , record-encode+                     , generics-sop+                     , hspec+                     , QuickCheck+                     , vector++source-repository head+  type:     git+  location: https://github.com/ocramz/record-encode
+ src/Data/Record/Encode.hs view
@@ -0,0 +1,96 @@+{-# language FlexibleContexts #-}+{-# language ScopedTypeVariables #-}+{-# language DeriveGeneric #-}+{-# language ConstraintKinds #-}++{-|+This library provides generic machinery (via GHC.Generics and `generics-sop`) to encode values of some algebraic type as points in a vector space.++Processing datasets that have one or more categorical variables (which in other words are values of a sum type) typically requires a series of boilerplate transformations, and the 'encodeOneHot' function provided here does precisely that.+++== Internals++This library makes use of generic programming to analyze both values and types (see the internal Data.Record.Encode.Generics module).++Initially, it was relying on Template Haskell to analyze /types/, using the the instance generation machinery explained here: <https://markkarpov.com/tutorial/th.html#example-1-instance-generation>+++-}+module Data.Record.Encode (+  -- * One-hot encoding+    encodeOneHot+  , G+  -- -- ** Typeclasses+  -- , GVariants(..)+  ) where++import qualified GHC.Generics as G+import Generics.SOP hiding (Proxy)+import Data.Proxy++import qualified Data.Vector as V+import qualified Data.Vector.Mutable as VM++import Data.Record.Encode.Generics++++data X a = A | B a | C | D | E | F deriving G.Generic+instance Generic (X a)++-- | Constraints necessary to 'encodeOneHot' a value.+--+-- NB: 'GVariants' is an internal typeclass, and this constraint is automatically satisfied if the type is an instance of 'G.Generic'+type G a = (GVariants (G.Rep a), G.Generic a, Generic a)++-- | Computes the one-hot encoding of a value of a sum type.+--+-- The type of the input value must be an instance of 'Generic' (from GHC.Generics) /and/ of 'Generic' (from the `generics-sop` library).+--+-- >>> :set -XDeriveGeneric+--+-- >>> import qualified GHC.Generics as G+-- >>> import qualified Generics.SOP as SOP+-- >>> import Data.Record.Encode+--+-- >>> data X = A | B | C deriving (G.Generic)+-- >>> instance SOP.Generic X+--+-- >>> encodeOneHot B+-- [0,1,0]+encodeOneHot :: forall a . G a => a -> V.Vector Int+encodeOneHot x = oneHot len i where+  len = fromIntegral $ gnconstructors (Proxy :: Proxy a)+  i = gindex $ from x++-- | Create a one-hot vector+oneHot :: Num a =>+          Int  -- ^ Vector length+       -> Int  -- ^ Index of "1" entry+       -> V.Vector a+oneHot n i = V.create $ do+  vm <- VM.replicate n 0+  VM.write vm i 1+  return vm++-- class Encode i d where+--   -- type ETy d :: *+--   encode :: d -> V.Vector i+--   -- type EIx d :: *  +--   -- encode :: d -> V.Vector (ETy d)+--   -- encodeSparse :: d -> V.Vector (EIx d, EIx d, ETy d)++-- -- | Some pointwise decision (e.g. maximum a posteriori) from a mixture of labels to a single value+-- class Decode i d where+--   decode :: V.Vector i -> d+  +  +++{- |++from A+  :: (C1 _ U1 :+: (C1 _ U1 +: C1 _ U1)) :+: (C1 _ U1 :+: (C1 _ U1 :+: C1 _ U1))++-}
+ src/Data/Record/Encode/Generics.hs view
@@ -0,0 +1,134 @@+{-# language TypeOperators #-}+{-# language DeriveGeneric #-}+-- {-# language GADTs, DataKinds, PolyKinds, FlexibleInstances, FlexibleContexts #-}+{-# language KindSignatures, FlexibleInstances, FlexibleContexts, ScopedTypeVariables #-}+module Data.Record.Encode.Generics (+    gindex+  , GVariants(..)+  , gnconstructors+  ) where++import qualified GHC.Generics as G+import Data.Proxy++import Generics.SOP hiding (Proxy)+import Generics.SOP.NS+++data X a = A | B a | C | D | E | F deriving G.Generic+instance Generic (X a)++++-- | Compute the structural index of a value of a sum type via its Generic representation+-- e.g.:+-- +-- >>> data S = Sa | Sb | Sc deriving (Eq, Show, G.Generic)+-- >>> instance Generic S+-- +-- >>> gindex $ from Sb+-- 1+gindex :: SOP f xs -> Int+gindex = index_SOP+++-- | Counts the number of outermost constructors ("variants" of a type)+class GVariants (f :: * -> *) where+  vars :: proxy f -> Int++instance GVariants (G.M1 G.C m f) where { vars _ = 1 }++instance GVariants G.V1 where { vars _ = 0 }++instance GVariants f => GVariants (G.M1 G.D m f) where { vars _ = vars (Proxy :: Proxy f) }++instance (GVariants f, GVariants g) => GVariants (f G.:+: g) where { vars _ = vars (Proxy :: Proxy f) + vars (Proxy :: Proxy g) }++-- | Counts the number of outermost constructors+gnconstructors :: forall a . (G.Generic a, GVariants (G.Rep a)) => Proxy a -> Int+gnconstructors _ = vars (Proxy :: Proxy (G.Rep a))+++-- [17:21] <mniip> @let class Variants f where { variants :: proxy f -> Int }; instance Variants (M1 C m f) where { variants _ = 1 }; instance Variants V1 where { variants _ = 0 }; instance Variants f => Variants (M1 D m f) where { variants _ = variants (Proxy :: Proxy f) }; instance (Variants f, Variants g) => Variants (f :+: g) where { variants _ = variants (Proxy :: Proxy f) + variants (Proxy :: Proxy g) }+-- [17:21] <lambdabot>  Defined.+-- [17:21] <mniip> @let variants' :: forall a. (Generic a, Variants (Rep a)) => Proxy a -> Int; variants' _ = variants (Proxy :: Proxy (Rep a))+-- [17:21] <lambdabot>  Defined.+-- [17:21] <mniip> > variants' (Proxy :: Proxy Bool)+-- [17:21] <lambdabot>  2+-- [17:22] <mniip> > variants' (Proxy :: Proxy (Maybe (Either Bool Bool)))+-- [17:22] <lambdabot>  2+-- [17:22] <mniip> as you can see this only focuses on the outer type constructor+-- [17:22] <mniip> > variants (Proxy :: Proxy Proxy)++++++++++++++++-- -- | ++-- class GEncode i o where+--   gencode :: i x -> Maybe (o x)++-- -- λ> :t fmap to . gencode . from+-- -- +-- -- fmap to . gencode . from+-- --   :: (GEncode (Rep a) (Rep b), Generic b, Generic a) =>+-- --      a -> Maybe b+++-- -- instance GEncode (V1 p) where+-- --   gencode _ = error "Cannot encode V1"++-- -- instance GEncode (U1 p) where+-- --   gencode U1 = error "Cannot encode U1"+++++++-- data OneHot = OH !Int !Int deriving (Eq, Show) ++-- class Encode d where+--   type ETy d :: *+--   type ETy d = OneHot -- (Int, Int)+--   -- encode :: d -> V.Vector (ETy d)+--   encode :: d x -> ETy d++-- -- instance Encode+++  ++++-- instance Encode (V1 p) where+--   encode _ = error "Cannot encode V1"++-- instance Encode (U1 p) where+--   encode U1 = error "Cannot encode U1"++-- instance Encode (K1 i c p) where++-- instance Encode (M1 i c f p) where++-- instance Encode ((f :*: g) p) where++-- instance Encode ((f :+: g) p) where  +  +++++-- class VG.Vector v x => GEncode' i v x where+--   gencode' :: i x -> Maybe (v Int)
+ test/LibSpec.hs view
@@ -0,0 +1,79 @@+{-# language DeriveGeneric #-}+module LibSpec where++import Test.Hspec+-- import Test.Hspec.QuickCheck++import qualified GHC.Generics as G+import Generics.SOP ++import qualified Data.Vector as V++import Data.Record.Encode+++data X = Xa | Xb | Xc deriving (Eq, Show, G.Generic)+instance Generic X+++++main :: IO ()+main = hspec spec++spec :: Spec+spec =+  describe "Data.Record.Encode" $ do+    it "creates a one-hot encoded vector from a sum type" $ do+      V.length (encodeOneHot Xb) `shouldBe` 3+      +      +    -- prop "ourAdd is commutative" $ \x y ->+    --   ourAdd x y `shouldBe` ourAdd y x+++++-- data P0 = P0 Bool Char deriving (Eq, Show, G.Generic)+-- -- instance Generic P0++-- deriveCountable ''Bool+-- deriveCountable ''Char+-- -- deriveCountable ''Integer+-- deriveCountable ''P0+++-- -- λ> hcmap (Proxy :: Proxy Show) (mapIK (const ())) $ from $ P0 42 'z'+-- -- SOP (Z (K () :* K () :* Nil))+++-- data Fx = Ax | Bx | Cx deriving (Eq, Show, Enum, G.Generic)+-- -- instance Generic Fx++-- data Fy a = Ay a | By | Cy deriving (Eq, Show, G.Generic)+-- -- instance Generic (Fy a)++-- data Gx = Ax' | Bx' | Cx' deriving (Eq, Show, Enum, G.Generic)+-- -- instance Generic Gx++-- -- | a Product-Of-Sums+-- data P1 a = P1 Fx (Fy a) deriving (Eq, Show, G.Generic)+-- -- instance Generic (P1 a)++-- p10 :: P1 Integer+-- p10 = P1 Ax (Ay 42)++-- -- gp10 :: SOP I '[ '[Fx, Fy Integer] ]+-- -- gp10 = from p10++-- -- λ> from $ P1 Ax (Ay 42)+-- -- SOP (Z (I Ax :* I (Ay 42) :* Nil))+++-- data P2 = P2 Fx Fx deriving (Eq, Show, G.Generic)+-- -- instance Generic P2++++-- -- data Y = Y (X, X) deriving (Eq, Show, G.Generic)+-- -- deriveCountable ''Y
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}