hmatrix-backprop 0.1.0.0 → 0.1.1.0
raw patch · 3 files changed
+132/−103 lines, 3 filesdep ~backpropdep ~hmatrix-vector-sizedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: backprop, hmatrix-vector-sized
API changes (from Hackage documentation)
- Numeric.LinearAlgebra.Static.Backprop: dmmap' :: (Reifies s W, KnownNat n, KnownNat m, Domain field vec mat, Num (mat n m), Num field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)
- Numeric.LinearAlgebra.Static.Backprop: dvmap' :: (Reifies s W, KnownNat n, Domain field vec mat, Num (vec n), Num field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)
+ Numeric.LinearAlgebra.Static.Backprop: class Reifies k (s :: k) a | s -> a
+ Numeric.LinearAlgebra.Static.Backprop: data BVar s a :: Type -> * -> *
+ Numeric.LinearAlgebra.Static.Backprop: data W :: *
+ Numeric.LinearAlgebra.Static.Backprop: mmap :: (Reifies s W, KnownNat n, KnownNat m) => (BVar s Double -> BVar s Double) -> BVar s (L n m) -> BVar s (L n m)
+ Numeric.LinearAlgebra.Static.Backprop: mmap' :: forall n m mat field s. (Reifies s W, KnownNat m, Num (mat n m), Sized field (mat n m) Matrix, Element field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)
+ Numeric.LinearAlgebra.Static.Backprop: vmap :: (Reifies s W, KnownNat n) => (BVar s Double -> BVar s Double) -> BVar s (R n) -> BVar s (R n)
+ Numeric.LinearAlgebra.Static.Backprop: vmap' :: (Reifies s W, Num (vec n), Storable field, Sized field (vec n) Vector) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)
- Numeric.LinearAlgebra.Static.Backprop: dmmap :: forall n m mat field s. (Reifies s W, KnownNat m, Num (mat n m), Storable (field, field), Sized field (mat n m) Matrix, Element field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)
+ Numeric.LinearAlgebra.Static.Backprop: dmmap :: (Reifies s W, KnownNat n, KnownNat m, Domain field vec mat, Num (mat n m), Num field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)
- Numeric.LinearAlgebra.Static.Backprop: dvmap :: (Reifies s W, Num (vec n), Storable field, Storable (field, field), Sized field (vec n) Vector) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)
+ Numeric.LinearAlgebra.Static.Backprop: dvmap :: (Reifies s W, KnownNat n, Domain field vec mat, Num (vec n), Num field) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)
Files
- CHANGELOG.md +18/−0
- hmatrix-backprop.cabal +7/−6
- src/Numeric/LinearAlgebra/Static/Backprop.hs +107/−97
CHANGELOG.md view
@@ -1,6 +1,24 @@ Changelog ========= +Version 0.1.1.0+---------------++*Mar 25, 2018*++<https://github.com/mstksg/hmatrix-backprop/releases/tag/v0.1.1.0>++* `dvmap`/`dmmap` family rewritten, which is a breaking API change. Previous+ version of `dvmap`/`dmmap` would not even compile at all if used, though,+ because of nonsensical constraints, so it is likely that no code that+ previously worked is now breaking.+* *backprop* types re-exported for convenience++*Internal*++* Rewrote much code to use `isoVar` instead of `opIso`, for increased clarity+ and cleanliness. Requires *backprop-0.1.4.0*.+ Version 0.1.0.0 ---------------
hmatrix-backprop.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 0d60d828b601d4eb24c4978c049e961589bc85fcabfd9c4c71ee0103caf1477e+-- hash: 7bd7d72297fd6caf75a31bbc3856f764f21999255f99a4f6bcad71e4739acf2a name: hmatrix-backprop-version: 0.1.0.0+version: 0.1.1.0 synopsis: hmatrix operations lifted for backprop description: hmatrix operations lifted for backprop. .@@ -39,12 +39,13 @@ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints build-depends: ANum >=0.2- , backprop >=0.1.2+ , backprop >=0.1.4 , base >=4.7 && <5+ , finite-typelits , ghc-typelits-knownnat , ghc-typelits-natnormalise , hmatrix >=0.18- , hmatrix-vector-sized >=0.1+ , hmatrix-vector-sized >=0.1.1 , microlens , vector , vector-sized >=0.6@@ -61,13 +62,13 @@ test ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends:- backprop >=0.1.2+ backprop >=0.1.4 , base >=4.7 && <5 , finite-typelits , hedgehog , hmatrix >=0.18 , hmatrix-backprop- , hmatrix-vector-sized >=0.1+ , hmatrix-vector-sized >=0.1.1 , microlens , microlens-platform , vector-sized >=0.6
src/Numeric/LinearAlgebra/Static/Backprop.hs view
@@ -43,6 +43,8 @@ -- functions are automatically backpropagatable. Useful types are all -- re-exported. --+-- Also contains 'sumElements' 'BVar' operation.+-- -- Formulas for gradients come from the following papers: -- -- * https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf@@ -77,10 +79,6 @@ -- to handle these are at the moment. -- * 'H.withRows' and 'H.withColumns' made "type-safe", without -- existential types, with 'fromRows' and 'fromColumns'.------ Some other notes:------ * Added 'sumElements', as well, for convenience. module Numeric.LinearAlgebra.Static.Backprop ( -- * Vector@@ -150,10 +148,12 @@ , dot , cross , diagR+ , vmap+ , vmap' , dvmap- , dvmap'+ , mmap+ , mmap' , dmmap- , dmmap' , outer , zipWithVector , zipWithVector'@@ -179,26 +179,34 @@ , mTm , unSym , (<·>)+ -- * Backprop types re-exported+ -- | Re-exported for convenience.+ --+ -- @since 0.1.1.0+ , BVar+ , Reifies+ , W ) where import Data.ANum+import Data.Bifunctor import Data.Maybe import Data.Proxy import Foreign.Storable import GHC.TypeLits import Lens.Micro hiding ((&)) import Numeric.Backprop-import Numeric.Backprop.Op import Numeric.Backprop.Tuple import Unsafe.Coerce+import qualified Data.Vector as V import qualified Data.Vector.Generic as VG import qualified Data.Vector.Generic.Sized as SVG import qualified Data.Vector.Sized as SV import qualified Data.Vector.Storable.Sized as SVS import qualified Numeric.LinearAlgebra as HU-import qualified Numeric.LinearAlgebra.Devel as HU import qualified Numeric.LinearAlgebra.Static as H import qualified Numeric.LinearAlgebra.Static.Vector as H+import qualified Prelude.Backprop as B #if MIN_VERSION_base(4,11,0) import Prelude hiding ((<>))@@ -209,9 +217,7 @@ => BVar s H.ℝ -> BVar s H.ℝ -> BVar s (H.R 2)-vec2 = liftOp2 $ opIsoN- (\(x ::< y ::< Ø) -> H.vec2 x y )- (\(HU.toList.H.extract->[dx,dy]) -> dx ::< dy ::< Ø)+vec2 = isoVar2 H.vec2 (\(H.rVec->v) -> (SVS.index v 0, SVS.index v 1)) {-# INLINE vec2 #-} vec3@@ -220,9 +226,7 @@ -> BVar s H.ℝ -> BVar s H.ℝ -> BVar s (H.R 3)-vec3 = liftOp3 $ opIsoN- (\(x ::< y ::< z ::< Ø) -> H.vec3 x y z )- (\(HU.toList.H.extract->[dx,dy,dz]) -> dx ::< dy ::< dz ::< Ø)+vec3 = isoVar3 H.vec3 (\(H.rVec->v) -> (SVS.index v 0, SVS.index v 1, SVS.index v 2)) {-# INLINE vec3 #-} vec4@@ -232,22 +236,17 @@ -> BVar s H.ℝ -> BVar s H.ℝ -> BVar s (H.R 4)-vec4 vX vY vZ vW = liftOp o (vX :< vY :< vZ :< vW :< Ø)- where- o :: Op '[H.ℝ, H.ℝ, H.ℝ, H.ℝ] (H.R 4)- o = opIsoN- (\(x ::< y ::< z ::< w ::< Ø) -> H.vec4 x y z w )- (\(HU.toList.H.extract->[dx,dy,dz,dw]) -> dx ::< dy ::< dz ::< dw ::< Ø)- {-# INLINE o #-}+vec4 vX vY vZ vW = isoVarN+ (\(x ::< y ::< z ::< w ::< Ø) -> H.vec4 x y z w)+ (\(H.rVec->v) -> SVS.index v 0 ::< SVS.index v 1 ::< SVS.index v 2 ::< SVS.index v 3 ::< Ø)+ (vX :< vY :< vZ :< vW :< Ø) {-# INLINE vec4 #-} (&) :: (Reifies s W, KnownNat n, 1 <= n, KnownNat (n + 1)) => BVar s (H.R n) -> BVar s H.ℝ -> BVar s (H.R (n + 1))-(&) = liftOp2 $ opIsoN- (\(xs ::< y ::< Ø) -> xs H.& y )- (\(H.split->(dxs,dy)) -> dxs ::< fst (H.headTail dy) ::< Ø)+(&) = isoVar2 (H.&) (\(H.split->(dxs,dy)) -> (dxs, fst (H.headTail dy))) infixl 4 & {-# INLINE (&) #-} @@ -255,9 +254,7 @@ => BVar s (H.R n) -> BVar s (H.R m) -> BVar s (H.R (n + m))-(#) = liftOp2 $ opIsoN- (\(x ::< y ::< Ø) -> x H.# y )- (\(H.split->(dX,dY)) -> dX ::< dY ::< Ø)+(#) = isoVar2 (H.#) H.split infixl 4 # {-# INLINE (#) #-} @@ -267,9 +264,7 @@ -> (BVar s (H.R p), BVar s (H.R (n - p))) split v = (t ^^. _1, t ^^. _2) -- should we just return the T2 ? where- t = liftOp1 (opIso (tupT2 . H.split)- (uncurryT2 (H.#))- ) v+ t = isoVar (tupT2 . H.split) (uncurryT2 (H.#)) v {-# NOINLINE t #-} {-# INLINE split #-} @@ -279,9 +274,9 @@ -> (BVar s H.ℝ, BVar s (H.R (n - 1))) headTail v = (t ^^. _1, t ^^. _2) where- t = liftOp1 (opIso (tupT2 . H.headTail)- (\(T2 d dx) -> (H.konst d :: H.R 1) H.# dx)- ) v+ t = isoVar (tupT2 . H.headTail)+ (\(T2 d dx) -> (H.konst d :: H.R 1) H.# dx)+ v {-# NOINLINE t #-} {-# INLINE headTail #-} @@ -290,9 +285,8 @@ :: forall n s. (Reifies s W, KnownNat n) => SV.Vector n (BVar s H.ℝ) -> BVar s (H.R n)-vector vs =- liftOp1 (opIso (H.vecR . SVG.convert) (SVG.convert . H.rVec))- (collectVar vs)+vector = isoVar (H.vecR . SVG.convert) (SVG.convert . H.rVec)+ . collectVar {-# INLINE vector #-} linspace@@ -312,22 +306,20 @@ row :: (Reifies s W, KnownNat n) => BVar s (H.R n) -> BVar s (H.L 1 n)-row = liftOp1 $ opIso H.row H.unrow+row = isoVar H.row H.unrow {-# INLINE row #-} col :: (Reifies s W, KnownNat n) => BVar s (H.R n) -> BVar s (H.L n 1)-col = liftOp1 $ opIso H.col H.uncol+col = isoVar H.col H.uncol {-# INLINE col #-} (|||) :: (Reifies s W, KnownNat c, KnownNat r1, KnownNat (r1 + r2)) => BVar s (H.L c r1) -> BVar s (H.L c r2) -> BVar s (H.L c (r1 + r2))-(|||) = liftOp2 $ opIsoN- (\(x ::< y ::< Ø) -> x H.||| y )- (\(H.splitCols->(dX,dY)) -> dX ::< dY ::< Ø)+(|||) = isoVar2 (H.|||) H.splitCols infixl 3 ||| {-# INLINE (|||) #-} @@ -335,9 +327,7 @@ => BVar s (H.L r1 c) -> BVar s (H.L r2 c) -> BVar s (H.L (r1 + r2) c)-(===) = liftOp2 $ opIsoN- (\(x ::< y ::< Ø) -> x H.=== y )- (\(H.splitRows->(dX,dY)) -> dX ::< dY ::< Ø)+(===) = isoVar2 (H.===) H.splitRows infixl 2 === {-# INLINE (===) #-} @@ -347,9 +337,7 @@ -> (BVar s (H.L p n), BVar s (H.L (m - p) n)) splitRows v = (t ^^. _1, t ^^. _2) where- t = liftOp1 (opIso (tupT2 . H.splitRows)- (\(T2 dx dy) -> dx H.=== dy)- ) v+ t = isoVar (tupT2 . H.splitRows) (uncurryT2 (H.===)) v {-# NOINLINE t #-} {-# INLINE splitRows #-} @@ -359,9 +347,7 @@ -> (BVar s (H.L m p), BVar s (H.L m (n - p))) splitCols v = (t ^^. _1, t ^^. _2) where- t = liftOp1 (opIso (tupT2 . H.splitCols)- (uncurryT2 (H.|||))- ) v+ t = isoVar (tupT2 . H.splitCols) (uncurryT2 (H.|||)) v {-# NOINLINE t #-} {-# INLINE splitCols #-} @@ -369,20 +355,20 @@ :: (Reifies s W, KnownNat n) => BVar s (H.L 1 n) -> BVar s (H.R n)-unrow = liftOp1 $ opIso H.unrow H.row+unrow = isoVar H.unrow H.row {-# INLINE unrow #-} uncol :: (Reifies s W, KnownNat n) => BVar s (H.L n 1) -> BVar s (H.R n)-uncol = liftOp1 $ opIso H.uncol H.col+uncol = isoVar H.uncol H.col {-# INLINE uncol #-} tr :: (Reifies s W, HU.Transposable m mt, HU.Transposable mt m, Num m, Num mt) => BVar s m -> BVar s mt-tr = liftOp1 $ opIso H.tr H.tr+tr = isoVar H.tr H.tr {-# INLINE tr #-} diag@@ -397,15 +383,9 @@ :: forall m n s. (Reifies s W, KnownNat m, KnownNat n) => [BVar s H.ℝ] -> BVar s (H.L m n)-matrix vs = case SV.fromList @(m * n) vs of- Nothing -> error "matrix: invalid number of elements"- Just vs' ->- liftOp1 (opIso (fromJust . H.create . HU.reshape n . VG.convert . SV.fromSized)- (SV.concatMap (SVG.convert . H.rVec) . H.lRows)- )- (collectVar vs')- where- n = fromInteger $ natVal (Proxy @n)+matrix = maybe (error "matrix: invalid number of elements")+ (isoVar (H.vecL . SVG.convert) (SVG.convert . H.lVec) . collectVar)+ . SV.fromList @(m * n) {-# INLINE matrix #-} -- | Matrix product@@ -808,29 +788,43 @@ ) {-# INLINE diagR #-} -dvmap+-- | Note: if possible, use the potentially much more performant 'vmap''.+vmap :: ( Reifies s W+ , KnownNat n+ )+ => (BVar s Double -> BVar s Double)+ -> BVar s (H.R n)+ -> BVar s (H.R n)+vmap f = isoVar (H.vecR . SVG.convert @V.Vector) (SVG.convert . H.rVec)+ . B.fmap f+ . isoVar (SVG.convert . H.rVec) (H.vecR . SVG.convert)+{-# INLINE vmap #-}++-- | 'vmap', but potentially more performant. Only usable if the mapped+-- function does not depend on any external 'BVar's.+vmap'+ :: ( Reifies s W , Num (vec n) , Storable field- , Storable (field, field) , H.Sized field (vec n) HU.Vector ) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)-dvmap f = liftOp1 . op1 $ \x ->- let (y, dx) = HU.unzipVector $ VG.map (backprop f) (H.extract x)- in ( fromJust (H.create y)- , \d -> d * fromJust (H.create dx)- )-{-# INLINE dvmap #-}+vmap' f = liftOp1 . op1 $ bimap (fromJust . H.create . VG.convert)+ ((*) . fromJust . H.create . VG.convert)+ . V.unzip+ . V.map (backprop f)+ . VG.convert+ . H.extract+{-# INLINE vmap' #-} -- TODO: Can be made more efficient if backprop exports -- a custom-total-derivative version --- | A version of 'dvmap' that is less performant but is based on--- 'H.zipWithVector' from 'H.Domain'.-dvmap'+-- | Note: Potentially less performant than 'vmap''.+dvmap :: ( Reifies s W , KnownNat n , H.Domain field vec mat@@ -840,38 +834,53 @@ => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (vec n) -> BVar s (vec n)-dvmap' f = liftOp1 . op1 $ \x ->+dvmap f = liftOp1 . op1 $ \x -> ( H.dvmap (evalBP f) x , (H.dvmap (gradBP f) x *) )-{-# INLINE dvmap' #-}+{-# INLINE dvmap #-} -dmmap+-- | Note: if possible, use the potentially much more performant 'mmap''.+mmap+ :: ( Reifies s W+ , KnownNat n+ , KnownNat m+ )+ => (BVar s Double -> BVar s Double)+ -> BVar s (H.L n m)+ -> BVar s (H.L n m)+mmap f = isoVar (H.vecL . SVG.convert @V.Vector) (SVG.convert . H.lVec)+ . B.fmap f+ . isoVar (SVG.convert . H.lVec) (H.vecL . SVG.convert)+{-# INLINE mmap #-}++-- | 'mmap', but potentially more performant. Only usable if the mapped+-- function does not depend on any external 'BVar's.+mmap' :: forall n m mat field s. ( Reifies s W , KnownNat m , Num (mat n m)- , Storable (field, field) , H.Sized field (mat n m) HU.Matrix , HU.Element field ) => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)-dmmap f = liftOp1 . op1 $ \x ->- let (y', dx') = HU.unzipVector- . VG.map (backprop f)- . HU.flatten- $ H.extract x- in ( fromJust . H.create . HU.reshape m $ y'- , \d -> (* d) . fromJust . H.create . HU.reshape m $ dx'- )+mmap' f = liftOp1 . op1 $ bimap (fromJust . H.create . HU.reshape m . VG.convert)+ ((*) . fromJust . H.create . HU.reshape m . VG.convert)+ . V.unzip+ . V.map (backprop f)+ . VG.convert+ . HU.flatten+ . H.extract where m :: Int m = fromInteger $ natVal (Proxy @m)-{-# INLINE dmmap #-}+{-# INLINE mmap' #-} -dmmap'+-- | Note: Potentially less performant than 'mmap''.+dmmap :: ( Reifies s W , KnownNat n , KnownNat m@@ -882,11 +891,11 @@ => (forall s'. Reifies s' W => BVar s' field -> BVar s' field) -> BVar s (mat n m) -> BVar s (mat n m)-dmmap' f = liftOp1 . op1 $ \x ->+dmmap f = liftOp1 . op1 $ \x -> ( H.dmmap (evalBP f) x , (H.dmmap (gradBP f) x *) )-{-# INLINE dmmap' #-}+{-# INLINE dmmap #-} outer :: ( Reifies s W@@ -1031,28 +1040,28 @@ :: forall m n s. (Reifies s W, KnownNat m, KnownNat n) => BVar s (H.L m n) -> SV.Vector m (BVar s (H.R n))-toRows = sequenceVar . liftOp1 (opIso H.lRows H.rowsL)+toRows = sequenceVar . isoVar H.lRows H.rowsL {-# INLINE toRows #-} toColumns :: forall m n s. (Reifies s W, KnownNat m, KnownNat n) => BVar s (H.L m n) -> SV.Vector n (BVar s (H.R m))-toColumns = sequenceVar . liftOp1 (opIso H.lCols H.colsL)+toColumns = sequenceVar . isoVar H.lCols H.colsL {-# INLINE toColumns #-} fromRows :: forall m n s. (Reifies s W, KnownNat m, KnownNat n) => SV.Vector m (BVar s (H.R n)) -> BVar s (H.L m n)-fromRows = liftOp1 (opIso H.rowsL H.lRows) . collectVar+fromRows = isoVar H.rowsL H.lRows . collectVar {-# INLINE fromRows #-} fromColumns :: forall m n s. (Reifies s W, KnownNat m, KnownNat n) => SV.Vector n (BVar s (H.R m)) -> BVar s (H.L m n)-fromColumns = liftOp1 (opIso H.colsL H.lCols) . collectVar+fromColumns = isoVar H.colsL H.lCols . collectVar {-# INLINE fromColumns #-} konst@@ -1142,9 +1151,10 @@ :: forall t s d q. (Reifies q W, H.Sized t s d, Num s, Num (d t)) => BVar q (d t) -> Maybe (BVar q s)-create = fmap (unANum . sequenceVar) . liftOp1 $- opIso (ANum . H.create)- (maybe 0 H.extract . unANum )+create = unANum+ . sequenceVar+ . isoVar (ANum . H.create)+ (maybe 0 H.extract . unANum ) {-# INLINE create #-} @@ -1198,7 +1208,7 @@ :: (Reifies s W, KnownNat n) => BVar s (H.Sym n) -> BVar s (H.Sq n)-unSym = liftOp1 (opIso H.unSym unsafeCoerce)+unSym = isoVar H.unSym unsafeCoerce {-# INLINE unSym #-} -- | Unicode synonym for '<.>>'