backprop 0.1.2.0 → 0.1.3.0
raw patch · 8 files changed
+342/−49 lines, 8 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Numeric.Backprop: noGrad :: (Tuple as -> b) -> Op as b
+ Numeric.Backprop: noGrad1 :: (a -> b) -> Op '[a] b
+ Numeric.Backprop: opIsoN :: (Tuple as -> b) -> (b -> Tuple as) -> Op as b
+ Numeric.Backprop.Op: noGrad :: (Tuple as -> b) -> Op as b
+ Numeric.Backprop.Op: noGrad1 :: (a -> b) -> Op '[a] b
+ Prelude.Backprop: (<$>) :: forall f a b s. (Traversable f, Num a, Num b, Num (f b), Reifies s W) => (BVar s a -> BVar s b) -> BVar s (f a) -> BVar s (f b)
+ Prelude.Backprop: coerce :: forall a b s. (Coercible a b, Num a, Num b, Reifies s W) => BVar s a -> BVar s b
+ Prelude.Backprop: fmap :: forall f a b s. (Traversable f, Num a, Num b, Num (f b), Reifies s W) => (BVar s a -> BVar s b) -> BVar s (f a) -> BVar s (f b)
+ Prelude.Backprop: length :: forall t a b s. (Foldable t, Num (t a), Num b, Reifies s W) => BVar s (t a) -> BVar s b
+ Prelude.Backprop: liftA2 :: forall f a b c s. (Traversable f, Applicative f, Num a, Num b, Num c, Num (f c), Reifies s W) => (BVar s a -> BVar s b -> BVar s c) -> BVar s (f a) -> BVar s (f b) -> BVar s (f c)
+ Prelude.Backprop: liftA3 :: forall f a b c d s. (Traversable f, Applicative f, Num a, Num b, Num c, Num d, Num (f d), Reifies s W) => (BVar s a -> BVar s b -> BVar s c -> BVar s d) -> BVar s (f a) -> BVar s (f b) -> BVar s (f c) -> BVar s (f d)
+ Prelude.Backprop: maximum :: forall t a s. (Foldable t, Functor t, Num a, Ord a, Num (t a), Reifies s W) => BVar s (t a) -> BVar s a
+ Prelude.Backprop: minimum :: forall t a s. (Foldable t, Functor t, Num a, Ord a, Num (t a), Reifies s W) => BVar s (t a) -> BVar s a
+ Prelude.Backprop: product :: forall t a s. (Foldable t, Functor t, Num (t a), Fractional a, Reifies s W) => BVar s (t a) -> BVar s a
+ Prelude.Backprop: pure :: forall t a s. (Foldable t, Applicative t, Num (t a), Num a, Reifies s W) => BVar s a -> BVar s (t a)
+ Prelude.Backprop: sum :: forall t a s. (Foldable t, Functor t, Num (t a), Num a, Reifies s W) => BVar s (t a) -> BVar s a
+ Prelude.Backprop: traverse :: forall t f a b s. (Traversable t, Applicative f, Foldable f, Num a, Num b, Num (f (t b)), Num (t b), Reifies s W) => (BVar s a -> f (BVar s b)) -> BVar s (t a) -> BVar s (f (t b))
- Numeric.Backprop: collectVar :: forall a t s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a) => t (BVar s a) -> BVar s (t a)
+ Numeric.Backprop: collectVar :: forall t a s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a) => t (BVar s a) -> BVar s (t a)
- Numeric.Backprop: liftOp :: forall s as b. (Reifies s W, Num b, Every Num as) => Op as b -> Prod (BVar s) as -> BVar s b
+ Numeric.Backprop: liftOp :: forall as b s. (Reifies s W, Num b, Every Num as) => Op as b -> Prod (BVar s) as -> BVar s b
- Numeric.Backprop: liftOp1 :: forall s a b. (Reifies s W, Num a, Num b) => Op '[a] b -> BVar s a -> BVar s b
+ Numeric.Backprop: liftOp1 :: forall a b s. (Reifies s W, Num a, Num b) => Op '[a] b -> BVar s a -> BVar s b
- Numeric.Backprop: liftOp2 :: forall s a b c. (Reifies s W, Num a, Num b, Num c) => Op '[a, b] c -> BVar s a -> BVar s b -> BVar s c
+ Numeric.Backprop: liftOp2 :: forall a b c s. (Reifies s W, Num a, Num b, Num c) => Op '[a, b] c -> BVar s a -> BVar s b -> BVar s c
- Numeric.Backprop: liftOp3 :: forall s a b c d. (Reifies s W, Num a, Num b, Num c, Num d) => Op '[a, b, c] d -> BVar s a -> BVar s b -> BVar s c -> BVar s d
+ Numeric.Backprop: liftOp3 :: forall a b c d s. (Reifies s W, Num a, Num b, Num c, Num d) => Op '[a, b, c] d -> BVar s a -> BVar s b -> BVar s c -> BVar s d
Files
- Build.hs +2/−0
- CHANGELOG.md +23/−0
- README.md +21/−26
- backprop.cabal +6/−4
- src/Numeric/Backprop.hs +6/−1
- src/Numeric/Backprop/Internal.hs +30/−17
- src/Numeric/Backprop/Op.hs +40/−1
- src/Prelude/Backprop.hs +214/−0
Build.hs view
@@ -78,6 +78,8 @@ "--package mnist-idx" "--package one-liner-instances" "--package split"+ "--package singletons"+ "--package mwc-random" "--" ("samples" </> src) "-o" f
CHANGELOG.md view
@@ -1,6 +1,26 @@ Changelog ========= +Version 0.1.3.0+---------------++*Feb 12, 2018*++<https://github.com/mstksg/backprop/releases/tag/v0.1.3.0>++* *Preulude.Backprop* module added with lifted versions of several *Prelude*+ and base functions.+* `liftOpX` family of operators now have a more logical ordering for type+ variables. This change breaks backwards-compatibility.+* `opIsoN` added to export list of *Numeric.Backprop*+* `noGrad` and `noGrad1` added to *Numeric.Backprop.Op*, for functions with+ no defined gradient.++*Internal*++* Completely decoupled the internal implementation from `Num`, which showed+ some performance benefits.+ Version 0.1.2.0 --------------- @@ -25,6 +45,9 @@ *Numeric.Backprop.Tuple*. This is meant to be a band-aid for the problem of orphan instances and potential mismatched tuple types. * Fixed bug in `collectVar` that occurs if container sizes change++*Internal*+ * Internal tweaks to the underlying automatic differentiation types that decouple backpropagation from `Num`, internally. `Num` is now just used externally as a part of the API, which might someday be made optional.
README.md view
@@ -4,9 +4,10 @@ [](https://hackage.haskell.org/package/backprop) [](https://travis-ci.org/mstksg/backprop) -[**Literate Haskell Tutorial/Demo on MNIST data set**][mnist-lhs] (and [PDF-rendering][mnist-pdf])+[**Introductory blog post**][blog] +[blog]: https://blog.jle.im/entry/introducing-the-backprop-library.html+ Automatic *heterogeneous* back-propagation. Write your functions to compute your result, and the library will automatically@@ -27,29 +28,24 @@ MNIST Digit Classifier Example ------------------------------ -Tutorial and example on training on the MNIST data set [available here as a-literate haskell file][mnist-lhs], or [rendered here as a PDF][mnist-pdf]!-**Read this first!**+My [blog post][blog] introduces the concepts in this library in the context of+training a handwritten digit classifier. I recommend reading that first. +There are some [literate haskell examples][mnist-lhs] in the source, though+([rendered as pdf here][mnist-pdf]), which can be built (if [stack][] is+installed) using:+ [mnist-lhs]: https://github.com/mstksg/backprop/blob/master/samples/backprop-mnist.lhs [mnist-pdf]: https://github.com/mstksg/backprop/blob/master/renders/backprop-mnist.pdf--The [literate haskell file][mnist-lhs] is a standalone haskell file that you-can compile (preferably with `-O2`) on its own with stack or some other-dependency manager. It can also be compiled with the build script in the-project directory (if [stack][] is installed, and appropriate dependencies are-installed), using- [stack]: http://haskellstack.org/ ```bash $ ./Build.hs exe ``` -After the MNIST tutorial, there is a follow-up tutorial on using the library-with more advanced types, with extensible neural networks a la [this blog-post][blog], [available as literate haskell][neural-lhs] and also [rendered as-a PDF][neural-pdf].+There is a follow-up tutorial on using the library with more advanced types,+with extensible neural networks a la [this blog post][blog], [available as+literate haskell][neural-lhs] and also [rendered as a PDF][neural-pdf]. [blog]: https://blog.jle.im/entries/series/+practical-dependent-types-in-haskell.html [neural-lhs]: https://github.com/mstksg/backprop/blob/master/samples/extensible-neural.lhs@@ -58,6 +54,8 @@ Brief example ------------- +(This is a really brief version of my [blog post][blog])+ The quick example below describes the running of a neural network with one hidden layer to calculate its squared error with respect to target `targ`, which is parameterized by two weight matrices and two bias vectors.@@ -110,8 +108,8 @@ -> BVar s (R o) neuralNet x n = z where- y = logistic $ (n ^^. weight1) #>! x + (n ^^. bias1)- z = logistic $ (n ^^. weight2) #>! y + (n ^^. bias2)+ y = logistic $ (n ^^. weight1) #> x + (n ^^. bias1)+ z = logistic $ (n ^^. weight2) #> y + (n ^^. bias2) logistic :: Floating a => a -> a logistic x = 1 / (1 + exp (-x))@@ -143,13 +141,10 @@ -> BVar s (R o) -> BVar s (Network i h o) -> BVar s Double-netError x targ n = sum' (err <.>! err)- where- err = neuralNet x - t+netError x targ n = norm_2 (neuralNet x - t) ``` -(`sum'` is a backprop-aware vector sum, and `<.>!` is a backprop-aware dot-product)+(`norm_2` is a backprop-aware euclidean norm) Now, we can perform gradient descent! @@ -167,8 +162,8 @@ Ta dah! We were able to compute the gradient of our error function, just by only saying how to compute *the error itself*. -For a more fleshed out example, see the [MNIST tutorial][mnist-lhs] (also-[rendered as a pdf][mnist-pdf])+For a more fleshed out example, see my [blog post][blog] and the [MNIST+tutorial][mnist-lhs] (also [rendered as a pdf][mnist-pdf]) Lens Access -----------@@ -292,7 +287,7 @@ However, it is worth noting that it wouldn't be too hard to add "Additive Typeclass" instances for any custom types -- one would just need to define- `(<+>) = (+)`, `zero = fromInteger 1`, and `one = fromInteger 1` (a+ `(<+>) = (+)`, `zero = fromInteger 0`, and `one = fromInteger 1` (a three-liner), so it might not be too bad. But really, a lot of this would all resolve itself if we got `Num`
backprop.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: d40da486697c0035648b98d75f36ea36bb66bdb648bb3406bf9eed465f497bb2+-- hash: a179c83822a97b40195355e48510d1b31057e23a68d04abb6966e24bd4b9d6f0 name: backprop-version: 0.1.2.0+version: 0.1.3.0 synopsis: Heterogeneous automatic differentation (backpropagation) description: Write your functions to compute your result, and the library will automatically generate functions to compute your gradient.@@ -22,6 +22,7 @@ copyright: (c) Justin Le 2018 license: BSD3 license-file: LICENSE+tested-with: GHC >= 8.0 build-type: Simple cabal-version: >= 1.10 @@ -43,7 +44,7 @@ library hs-source-dirs: src- ghc-options: -Wall -fprint-explicit-kinds -fwarn-redundant-constraints+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints -fprint-explicit-kinds build-depends: base >=4.7 && <5 , deepseq@@ -57,6 +58,7 @@ Numeric.Backprop Numeric.Backprop.Op Numeric.Backprop.Tuple+ Prelude.Backprop other-modules: Numeric.Backprop.Internal Data.Type.Util@@ -67,7 +69,7 @@ main-is: MNISTBench.hs hs-source-dirs: bench- ghc-options: -Wall -fprint-explicit-kinds -fwarn-redundant-constraints -threaded -rtsopts -with-rtsopts=-N -O2+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints -fprint-explicit-kinds -threaded -rtsopts -with-rtsopts=-N -O2 build-depends: backprop , base >=4.7 && <5
src/Numeric/Backprop.hs view
@@ -73,7 +73,9 @@ -- *** Giving gradients directly , op1, op2, op3 -- *** From Isomorphisms- , opCoerce, opTup, opIso, opLens+ , opCoerce, opTup, opIso, opIsoN, opLens+ -- *** No gradients+ , noGrad1, noGrad -- * Utility -- ** Inductive tuples/heterogeneous lists , Prod(..), pattern (:>), only, head'@@ -279,6 +281,9 @@ -- s a@ (a 'BVar' holding a @a@) -- -- This is the main way to pull out values from 'BVar' of container types.+--+-- __WARNING__: Do not use with any lenses that operate "numerically" on+-- the contents (like 'multiplying'). -- (^^.) :: forall a b s. (Reifies s W, Num a)
src/Numeric/Backprop/Internal.hs view
@@ -153,17 +153,18 @@ {-# INLINE forceTapeNode #-} data SomeTapeNode :: Type where- STN :: forall a. Num a- => !(TapeNode a)+ STN :: { _stnZero :: a+ , _stnNode :: !(TapeNode a)+ } -> SomeTapeNode forceSomeTapeNode :: SomeTapeNode -> ()-forceSomeTapeNode (STN tn) = forceTapeNode tn `seq` ()+forceSomeTapeNode (STN !_ tn) = forceTapeNode tn `seq` () {-# INLINE forceSomeTapeNode #-} -- | Debugging string for a 'SomeTapeMode'. debugSTN :: SomeTapeNode -> String-debugSTN (STN TN{..}) = show . foldMap1 ((:[]) . debugIR) $ _tnInputs+debugSTN (STN _ TN{..}) = show . foldMap1 ((:[]) . debugIR) $ _tnInputs -- | An ephemeral Wengert Tape in the environment. Used internally to -- track of the computational graph of variables.@@ -184,7 +185,7 @@ -> IO (BVar s a) insertNode tn !x !w = fmap ((`BV` x) . BRIx) . atomicModifyIORef' (wRef w) $ \(!n,!t) -> let n' = n + 1- t' = STN tn:t+ t' = STN 0 tn:t in forceTapeNode tn `seq` n' `seq` t' `seq` ((n', t'), n) {-# INLINE insertNode #-} @@ -224,7 +225,7 @@ -- information, and "Numeric.Backprop.Op#prod" for a mini-tutorial on using -- 'Prod' and 'Tuple'. liftOp- :: forall s as b. (Reifies s W, Num b, Every Num as)+ :: forall as b s. (Reifies s W, Num b, Every Num as) => Op as b -> Prod (BVar s) as -> BVar s b@@ -232,7 +233,7 @@ {-# INLINE liftOp #-} liftOp1_- :: forall s a b. (Reifies s W, Num a, Num b)+ :: forall a b s. (Reifies s W, Num a, Num b) => Op '[a] b -> BVar s a -> IO (BVar s b)@@ -253,7 +254,7 @@ -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp1- :: forall s a b. (Reifies s W, Num a, Num b)+ :: forall a b s. (Reifies s W, Num a, Num b) => Op '[a] b -> BVar s a -> BVar s b@@ -261,7 +262,7 @@ {-# INLINE liftOp1 #-} liftOp2_- :: forall s a b c. (Reifies s W, Num a, Num b, Num c)+ :: forall a b c s. (Reifies s W, Num a, Num b, Num c) => Op '[a,b] c -> BVar s a -> BVar s b@@ -285,7 +286,7 @@ -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp2- :: forall s a b c. (Reifies s W, Num a, Num b, Num c)+ :: forall a b c s. (Reifies s W, Num a, Num b, Num c) => Op '[a,b] c -> BVar s a -> BVar s b@@ -294,7 +295,7 @@ {-# INLINE liftOp2 #-} liftOp3_- :: forall s a b c d. (Reifies s W, Num a, Num b, Num c, Num d)+ :: forall a b c d s. (Reifies s W, Num a, Num b, Num c, Num d) => Op '[a,b,c] d -> BVar s a -> BVar s b@@ -321,7 +322,7 @@ -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp3- :: forall s a b c d. (Reifies s W, Num a, Num b, Num c, Num d)+ :: forall a b c d s. (Reifies s W, Num a, Num b, Num c, Num d) => Op '[a,b,c] d -> BVar s a -> BVar s b@@ -387,6 +388,12 @@ -- | Extract all of the 'BVar's out of a 'Traversable' container of -- 'BVar's.+--+-- Note that this associates gradients in order of occurrence in the+-- original data structure; the second item in the gradient is assumed to+-- correspond with the second item in the input, etc.; this can cause+-- unexpected behavior in 'Foldable' instances that don't have a fixed+-- number of items. sequenceVar :: forall t a s. (Reifies s W, Traversable t, Num a) => BVar s (t a)@@ -395,7 +402,7 @@ {-# INLINE sequenceVar #-} collectVar_- :: forall a t s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a)+ :: forall t a s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a) => t (BVar s a) -> IO (BVar s (t a)) collectVar_ !vs = withV (toList vs) $ \(vVec :: Vec n (BVar s a)) -> do@@ -412,12 +419,18 @@ -- | Collect all of the 'BVar's in a container into a 'BVar' of that -- container's contents. --+-- Note that this associates gradients in order of occurrence in the+-- original data structure; the second item in the total derivative and+-- gradient is assumed to correspond with the second item in the input,+-- etc.; this can cause unexpected behavior in 'Foldable' instances that+-- don't have a fixed number of items.+-- -- Note that this requires @t a@ to have a 'Num' instance. If you are -- using a list, I recommend using -- <https://hackage.haskell.org/package/vector-sized vector-sized> instead: -- it's a fixed-length vector type with a very appropriate 'Num' instance! collectVar- :: forall a t s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a)+ :: forall t a s. (Reifies s W, Foldable t, Functor t, Num (t a), Num a) => t (BVar s a) -> BVar s (t a) collectVar !vs = unsafePerformIO $ collectVar_ vs@@ -478,8 +491,8 @@ -> m (Runner s) initRunner (n, stns) (nx,xs) = do delts <- MV.new n- for_ (zip [n-1,n-2..] stns) $ \(i, STN (TN{..} :: TapeNode c)) ->- MV.write delts i $ unsafeCoerce @c 0+ for_ (zip [n-1,n-2..] stns) $ \(i, STN z (TN{..} :: TapeNode c)) ->+ MV.write delts i $ unsafeCoerce z inps <- MV.new nx for_ (zip [0..] xs) $ \(i, Some (Wit1 :: Wit1 Num c)) -> MV.write inps i $ unsafeCoerce @c 0@@ -498,7 +511,7 @@ zipWithM_ go [n-1,n-2..] stns where go :: Int -> SomeTapeNode -> m ()- go i (STN TN{..}) = do+ go i (STN _ TN{..}) = do delt <- MV.read _rDelta i let gs = _tnGrad (unsafeCoerce delt) zipWithPM_ propagate _tnInputs gs
src/Numeric/Backprop/Op.hs view
@@ -51,7 +51,9 @@ -- ** Giving gradients directly , op1, op2, op3 -- ** From Isomorphisms- , opCoerce, opTup, opIso, opLens, opIsoN+ , opCoerce, opTup, opIso, opIsoN, opLens+ -- ** No gradient+ , noGrad1, noGrad -- * Manipulation , composeOp, composeOp1, (~.) , composeOp', composeOp1'@@ -333,6 +335,40 @@ opCoerce = opIso coerce coerce {-# INLINE opCoerce #-} +-- | Create an 'Op' with no gradient. Can be evaluated with 'evalOp', but+-- will throw a runtime exception when asked for the gradient.+--+-- Can be used with 'BVar' with 'liftOp1', and 'evalBP' will work fine.+-- 'gradBP' and 'backprop' will also work fine if the result is never used+-- in the final answer, but will throw a runtime exception if the final+-- answer depends on the result of this operation.+--+-- Useful if your only API is exposed through /backprop/. Just be sure to+-- tell your users that this will explode when finding the gradient if the+-- result is used in the final result.+--+-- @since 0.1.3.0+noGrad1 :: (a -> b) -> Op '[a] b+noGrad1 f = op1 (\x -> (f x, error "noGrad: no gradient defined"))+{-# INLINE noGrad1 #-}++-- | Create an 'Op' with no gradient. Can be evaluated with 'evalOp', but+-- will throw a runtime exception when asked for the gradient.+--+-- Can be used with 'BVar' with 'liftOp', and 'evalBP' will work fine.+-- 'gradBP' and 'backprop' will also work fine if the result is never used+-- in the final answer, but will throw a runtime exception if the final+-- answer depends on the result of this operation.+--+-- Useful if your only API is exposed through /backprop/. Just be sure to+-- tell your users that this will explode when finding the gradient if the+-- result is used in the final result.+--+-- @since 0.1.3.0+noGrad :: (Tuple as -> b) -> Op as b+noGrad f = Op (\xs -> (f xs, error "noGrads: no gradient defined"))+{-# INLINE noGrad #-}+ -- | An 'Op' that just returns whatever it receives. The identity -- function. --@@ -368,6 +404,9 @@ -- have derivative 1, so will break for things like -- 'Numeric.Lens.exponentiating'. Basically, don't use this for any -- "numeric" isomorphisms.+--+-- In "Numeric.Backprop.Op" since version 0.1.2.0, but only exported from+-- "Numeric.Backprop" since version 0.1.3.0. -- -- @since 0.1.2.0 opIsoN :: (Tuple as -> b) -> (b -> Tuple as) -> Op as b
+ src/Prelude/Backprop.hs view
@@ -0,0 +1,214 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module : Prelude.Backprop+-- Copyright : (c) Justin Le 2018+-- License : BSD3+--+-- Maintainer : justin@jle.im+-- Stability : experimental+-- Portability : non-portable+--+-- Some lifted versions of common functions found in 'Prelude' (or /base/+-- in general).+--+-- Intended to work with 'Functor' / 'Foldable' / 'Traversable' instances+-- with "fixed" number of items, i.e.+-- <https://hackage.haskell.org/package/vector-sized vector-sized> vectors.+-- There might be unintended consequences when using it with instances+-- where the number of items is not fixed.+--+-- This module is intended to be a catch-all one, so feel free to suggest+-- other functions or submit a PR if you think one would make sense.+--+-- @since 0.1.3.0+--++module Prelude.Backprop (+ -- * Foldable and Traversable+ sum+ , product+ , length+ , minimum+ , maximum+ , traverse+ -- * Functor and Applicative+ , fmap+ , (<$>)+ , pure+ , liftA2+ , liftA3+ -- * Misc+ , coerce+ ) where++import Numeric.Backprop+import Prelude (Num(..), Fractional(..), Eq(..), Ord(..), Functor, Foldable, Traversable, Applicative, (.), ($))+import qualified Control.Applicative as P+import qualified Data.Coerce as C+import qualified Prelude as P++-- | Lifted 'P.sum'+sum :: forall t a s. (Foldable t, Functor t, Num (t a), Num a, Reifies s W)+ => BVar s (t a)+ -> BVar s a+sum = liftOp1 . op1 $ \xs ->+ ( P.sum xs+ , (P.<$ xs)+ )+{-# INLINE sum #-}++-- | Lifted 'P.pure'. Really intended only for 'Applicative' instances+-- with fixed number of items; untintended consequences might arise when+-- using it with containers with variable number of items.+pure+ :: forall t a s. (Foldable t, Applicative t, Num (t a), Num a, Reifies s W)+ => BVar s a+ -> BVar s (t a)+pure = liftOp1 . op1 $ \x ->+ ( P.pure x+ , P.sum+ )+{-# INLINE pure #-}++-- | Lifted 'P.product'+product+ :: forall t a s. (Foldable t, Functor t, Num (t a), Fractional a, Reifies s W)+ => BVar s (t a)+ -> BVar s a+product = liftOp1 . op1 $ \xs ->+ let p = P.product xs+ in ( p+ , \d -> (\x -> p * d / x) P.<$> xs+ )+{-# INLINE product #-}++-- | Lifted 'P.length'.+length+ :: forall t a b s. (Foldable t, Num (t a), Num b, Reifies s W)+ => BVar s (t a)+ -> BVar s b+length = liftOp1 . op1 $ \xs ->+ ( P.fromIntegral (P.length xs)+ , P.const 0+ )+{-# INLINE length #-}++-- | Lifted 'P.minimum'. Undefined for situations where 'P.minimum' would+-- be undefined.+minimum+ :: forall t a s. (Foldable t, Functor t, Num a, Ord a, Num (t a), Reifies s W)+ => BVar s (t a)+ -> BVar s a+minimum = liftOp1 . op1 $ \xs ->+ let m = P.minimum xs+ in ( m+ , \d -> (\x -> if x == m then d else 0) P.<$> xs+ )+{-# INLINE minimum #-}++-- | Lifted 'P.maximum'. Undefined for situations where 'P.maximum' would+-- be undefined.+maximum+ :: forall t a s. (Foldable t, Functor t, Num a, Ord a, Num (t a), Reifies s W)+ => BVar s (t a)+ -> BVar s a+maximum = liftOp1 . op1 $ \xs ->+ let m = P.maximum xs+ in ( m+ , \d -> (\x -> if x == m then d else 0) P.<$> xs+ )+{-# INLINE maximum #-}++-- | Lifted 'P.fmap'. Lifts backpropagatable functions to be+-- backpropagatable functions on 'Traversable' 'Functor's.+--+-- Really intended only for 'Functor' instances with fixed number of items;+-- untintended consequences might arise when using it with containers with+-- variable number of items.+fmap+ :: forall f a b s. (Traversable f, Num a, Num b, Num (f b), Reifies s W)+ => (BVar s a -> BVar s b)+ -> BVar s (f a)+ -> BVar s (f b)+fmap f = collectVar . P.fmap f . sequenceVar+{-# INLINE fmap #-}++-- | Alias for 'fmap'.+(<$>)+ :: forall f a b s. (Traversable f, Num a, Num b, Num (f b), Reifies s W)+ => (BVar s a -> BVar s b)+ -> BVar s (f a)+ -> BVar s (f b)+(<$>) = fmap+{-# INLINE (<$>) #-}++-- | Lifted 'P.traverse'. Lifts backpropagatable functions to be+-- backpropagatable functions on 'Traversable' 'Functor's.+--+-- Really intended only for 'Traversable' and 'Applicative' instances with+-- fixed number of items; untintended consequences might arise when using+-- it with containers with variable number of items.+traverse+ :: forall t f a b s. (Traversable t, Applicative f, Foldable f, Num a, Num b, Num (f (t b)), Num (t b), Reifies s W)+ => (BVar s a -> f (BVar s b))+ -> BVar s (t a)+ -> BVar s (f (t b))+traverse f = collectVar+ . P.fmap collectVar+ . P.traverse f+ . sequenceVar+{-# INLINE traverse #-}++-- | Lifted 'P.liftA2'. Lifts backpropagatable functions to be+-- backpropagatable functions on 'Traversable' 'Applicative's.+--+-- Really intended only for 'Traversable' and 'Applicative' instances with+-- fixed number of items; untintended consequences might arise when using+-- it with containers with variable number of items.+liftA2+ :: forall f a b c s.+ ( Traversable f+ , Applicative f+ , Num a, Num b, Num c, Num (f c)+ , Reifies s W+ )+ => (BVar s a -> BVar s b -> BVar s c)+ -> BVar s (f a)+ -> BVar s (f b)+ -> BVar s (f c)+liftA2 f x y = collectVar $ f P.<$> sequenceVar x+ P.<*> sequenceVar y+{-# INLINE liftA2 #-}++-- | Lifted 'P.liftA3'. Lifts backpropagatable functions to be+-- backpropagatable functions on 'Traversable' 'Applicative's.+--+-- Really intended only for 'Traversable' and 'Applicative' instances with+-- fixed number of items; untintended consequences might arise when using+-- it with containers with variable number of items.+liftA3+ :: forall f a b c d s.+ ( Traversable f+ , Applicative f+ , Num a, Num b, Num c, Num d, Num (f d)+ , Reifies s W+ )+ => (BVar s a -> BVar s b -> BVar s c -> BVar s d)+ -> BVar s (f a)+ -> BVar s (f b)+ -> BVar s (f c)+ -> BVar s (f d)+liftA3 f x y z = collectVar $ f P.<$> sequenceVar x+ P.<*> sequenceVar y+ P.<*> sequenceVar z+{-# INLINE liftA3 #-}++-- | Coerce items inside a 'BVar'.+coerce+ :: forall a b s. (C.Coercible a b, Num a, Num b, Reifies s W)+ => BVar s a+ -> BVar s b+coerce = liftOp1 $ opIso C.coerce C.coerce+{-# INLINE coerce #-}