packages feed

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 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 @@ [![backprop on Hackage](https://img.shields.io/hackage/v/backprop.svg?maxAge=2592000)](https://hackage.haskell.org/package/backprop) [![Build Status](https://travis-ci.org/mstksg/backprop.svg?branch=master)](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 #-}