{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ViewPatterns #-} -- | -- Module : Numeric.Backprop -- Copyright : (c) Justin Le 2018 -- License : BSD3 -- -- Maintainer : justin@jle.im -- Stability : experimental -- Portability : non-portable -- -- Automatic differentation and backpropagation. -- -- Main idea: Write a function computing what you want, and the library -- automatically provies the gradient of that function as well, for usage -- with gradient descent and other training methods. -- -- See the for an introduction and -- walkthrough. -- -- In more detail: instead of working directly with values to produce your -- result, you work with 'BVar's containing those values. Working with -- these 'BVar's is made smooth with the usage of lenses and other -- combinators, and libraries can offer operatons on 'BVar's instead of -- those on normal types directly. -- -- Then, you can use: -- -- @ -- 'evalBP' :: (forall s. 'Reifies' s 'W'. 'BVar' s a -> BVar s b) -> (a -> b) -- @ -- -- to turn a 'BVar' function into the function on actual values @a -> b@. -- This has virtually zero overhead over writing the actual function -- directly. -- -- Then, there's: -- -- @ -- 'gradBP' :: (forall s. 'Reifies' s 'W'. 'BVar' s a -> BVar s b) -> (a -> a) -- @ -- -- to automatically get the /gradient/, as well, for a given input. -- -- Refer to the for more information and -- links to demonstrations and tutorials, or dive striaght in by reading -- the docs for 'BVar'. -- -- If you are writing a library, see -- for a guide for -- equipping your library with backpropatable operations. -- -- In the original version 0.1, this module required 'Num' instances for -- methods instead of 'Backprop' instances. This interface is still -- available in "Numeric.Backprop.Num", which has the same API as this -- module, except with 'Num' constraints on all values instead of -- 'Backprop' constraints. -- -- See "Prelude.Backprop.Explicit" for a version allowing you to provide -- 'zero', 'add', and 'one' explicitly, which can be useful when attempting -- to avoid orphan instances or when mixing both 'Backprop' and 'Num' -- styles. -- module Numeric.Backprop ( -- * Types BVar, W, Backprop(..), ABP(..), NumBP(..) -- * Running , backprop, E.evalBP, gradBP, backpropWith -- ** Multiple inputs , backprop2, E.evalBP2, gradBP2, backpropWith2 , backpropN, E.evalBPN, gradBPN, backpropWithN -- * Manipulating 'BVar' , E.evalBP0 , E.constVar, E.auto, E.coerceVar , (^^.), (.~~), (%~~), (^^?), (^^..), (^^?!) , viewVar, setVar, overVar , sequenceVar, collectVar , previewVar, toListOfVar , pattern T2, pattern T3 -- ** With Isomorphisms , isoVar, isoVar2, isoVar3, isoVarN -- ** With 'Op's#liftops# -- $liftops , liftOp , liftOp1, liftOp2, liftOp3 -- ** Generics#hkd# -- $hkd , splitBV , joinBV , pattern BV , E.BVGroup -- * 'Op' , Op(..) -- ** Creation , op0, opConst, idOp , bpOp -- *** Giving gradients directly , op1, op2, op3 -- *** From Isomorphisms , opCoerce, opTup, opIso, opIsoN, opLens -- *** No gradients , noGrad1, noGrad -- * Utility , Reifies ) where import Data.Functor.Identity import Data.Maybe import Data.Reflection import Data.Vinyl import GHC.Generics import Lens.Micro import Numeric.Backprop.Class import Numeric.Backprop.Explicit (BVar, W) import Numeric.Backprop.Op import qualified Numeric.Backprop.Explicit as E -- $liftops -- -- This library provides a few primitive actions for manipulating 'BVar's -- and the values inside them, including its 'Num', 'Fractional', and -- 'Floating' instances, and lens-based operations like '^^.', '.~~' '^^?', -- and '^^..'. -- -- However, the power of this library comes from manipulating many -- different types from libraries, like matrices and vectors. Libraries -- can provide their own @'BVar' s a -> 'BVar' s b@ functions, alongside -- (or in lieu of) @a -> b@ functions for their types. -- -- The easiest way to create a 'BVar' function is to use 'liftOp' with an -- 'Op' constructor. For example, imagine a vector library providing a dot -- product function. We can write this using 'liftOp2' and 'op2': -- -- @ -- dot :: 'BVar' s Vec -> BVar s Vec -> BVar s Double -- dot = 'liftOp2' . op2 $ \\xs ys -> -- ( sum (zipWith (*) xs ys) -- , \\g -> (map (*g) ys, map (*g) xs) -- ) -- @ -- -- We provide a function that, given the two inputs, returns: -- -- (1) The result of the function on those two inputs -- (2) A function taking the "total derivative", and returning the -- gradient with respect to each of the inputs. -- -- See documentation in "Numeric.Backprop.Op" for more information on the -- second part (the gradient). -- -- Nice 'Op's are how /backprop/ links together 'BVar's and tracks them to -- determine their gradient. Ideally, users would never have to deal with -- these when backpropagating their own functions, and library authors -- providing their matrix and vector operations, etc. would provide 'BVar' -- variants of their normal operations. -- -- In fact, 'BVar' operations could even be defined /instead/ of normal -- operations, since it is easy to go from @'BVar' s a -> 'BVar' s b@ to @a -- -> b@, using 'evalBP', and this carries virtually zero overhead, so some -- libraries might even provide 'BVar' versions by default. -- | 'backprop' generalized to multiple inputs of different types. See the -- "Numeric.Backprop.Op#prod" for a mini-tutorial on heterogeneous lists. -- -- Not strictly necessary, because you can always uncurry a function by -- passing in all of the inputs in a data type containing all of the -- arguments or a giant tuple. However, this could potentially also be -- more performant. -- -- A @'Rec' ('BVar' s) '[Double, Float, Double]@, for instance, is a tuple -- of @'BVar' s 'Double'@, @'BVar' s 'Float'@, and @'BVar' s 'Double'@, and -- can be pattern matched on using ':<' (cons) and 'Ø' (nil). -- -- The @'RPureConstrained' 'Backprop' as@ in the constraint says that every -- value in the type-level list @as@ must have a 'Backprop' instance. This -- means you can use, say, @'[Double, Float, Int]@, but not @'[Double, -- Bool, String]@. -- -- If you stick to /concerete/, monomorphic usage of this (with specific -- types, typed into source code, known at compile-time), then -- @'RPureConstrained' 'Backprop' as@ should be fulfilled automatically. backpropN :: (RPureConstrained Backprop as, Backprop b) => (forall s. Reifies s W => Rec (BVar s) as -> BVar s b) -> Rec Identity as -> (b, Rec Identity as) backpropN = E.backpropN E.zeroFuncs E.oneFunc {-# INLINE backpropN #-} -- | 'backpropN', but allows you to provide the gradient of the "final -- result" with respect to the output of your function. See 'backpropWith' -- for more details. -- -- Note that argument order changed in v0.2.4. -- -- @since 0.2.0.0 backpropWithN :: RPureConstrained Backprop as => (forall s. Reifies s W => Rec (BVar s) as -> BVar s b) -> Rec Identity as -> (b, b -> Rec Identity as) backpropWithN = E.backpropWithN E.zeroFuncs {-# INLINE backpropWithN #-} -- | Turn a function @'BVar' s a -> 'BVar' s b@ into the function @a -> b@ -- that it represents, also computing its gradient @a@ as well. -- -- The Rank-N type @forall s. 'Reifies' s 'W' => ...@ is used to ensure -- that 'BVar's do not leak out of the context (similar to how it is used -- in "Control.Monad.ST"), and also as a reference to an ephemeral Wengert -- tape used to track the graph of references. backprop :: (Backprop a, Backprop b) => (forall s. Reifies s W => BVar s a -> BVar s b) -> a -> (b, a) backprop = E.backprop E.zeroFunc E.oneFunc {-# INLINE backprop #-} -- | A version of 'backprop' that allows you to specify the gradent of your -- "final result" in with respect to the output of your function. -- -- Typically, this is just the scalar 1, or a value of components that are -- all 1. -- -- Instead of taking the @b@ gradient, the you may provide a @b -> b@, -- which 'backpropWith' calls with the result of your function as the -- argument. This allows you to return something with the correct "shape", -- if not a scalar. -- -- 'backprop' is essentially 'backpropWith' with @'const' 1@ for scalars -- and 'Num' instances. -- -- Note that argument order changed in v0.2.4 -- -- @since 0.2.0.0 backpropWith :: Backprop a => (forall s. Reifies s W => BVar s a -> BVar s b) -> a -> (b, b -> a) backpropWith = E.backpropWith E.zeroFunc {-# INLINE backpropWith #-} -- | Take a function @'BVar' s a -> 'BVar' s b@, interpreted as a function -- @a -> b@, and compute its gradient with respect to its input. -- -- The resulting @a -> a@ tells how the input (and its components) affects -- the output. Positive numbers indicate that the result will vary in the -- same direction as any adjustment in the input. Negative numbers -- indicate that the result will vary in the opposite direction as any -- adjustment in the input. Larger numbers indicate a greater sensitivity -- of change, and small numbers indicate lower sensitivity. -- -- See documentation of 'backprop' for more information. -- -- If you want to provide an explicit "final gradient" for the end, see -- 'backpropWith'. gradBP :: (Backprop a, Backprop b) => (forall s. Reifies s W => BVar s a -> BVar s b) -> a -> a gradBP = E.gradBP E.zeroFunc E.oneFunc {-# INLINE gradBP #-} -- | 'gradBP' generalized to multiple inputs of different types. See -- documentation for 'backpropN' for more details. gradBPN :: (RPureConstrained Backprop as, Backprop b) => (forall s. Reifies s W => Rec (BVar s) as -> BVar s b) -> Rec Identity as -> Rec Identity as gradBPN = E.gradBPN E.zeroFuncs E.oneFunc {-# INLINE gradBPN #-} -- | 'backprop' for a two-argument function. -- -- Not strictly necessary, because you can always uncurry a function by -- passing in all of the argument inside a data type, or just use a tuple. -- However, this could potentially be more performant. -- -- For 3 and more arguments, consider using 'backpropN'. backprop2 :: (Backprop a, Backprop b, Backprop c) => (forall s. Reifies s W => BVar s a -> BVar s b -> BVar s c) -> a -> b -> (c, (a, b)) backprop2 = E.backprop2 E.zeroFunc E.zeroFunc E.oneFunc {-# INLINE backprop2 #-} -- | 'backprop2', but allows you to provide the gradient of the "final -- result" with respect to the output of your function. See 'backpropWith' -- for more details. -- -- Note that argument order changed in v0.2.4 -- -- @since 0.2.0.0 backpropWith2 :: (Backprop a, Backprop b) => (forall s. Reifies s W => BVar s a -> BVar s b -> BVar s c) -> a -> b -> (c, c -> (a, b)) backpropWith2 = E.backpropWith2 E.zeroFunc E.zeroFunc {-# INLINE backpropWith2 #-} -- | 'gradBP' for a two-argument function. See 'backprop2' for notes. gradBP2 :: (Backprop a, Backprop b, Backprop c) => (forall s. Reifies s W => BVar s a -> BVar s b -> BVar s c) -> a -> b -> (a, b) gradBP2 = E.gradBP2 E.zeroFunc E.zeroFunc E.oneFunc {-# INLINE gradBP2 #-} -- | Create an 'Op' from a backpropagatable function. Can be useful for -- "storing" an otherwise Rank-N backpropagatable function in order to -- avoid impredicative types. But this is pretty uncommon, so this is -- mostly just used for low-level internal situations. -- -- @ -- 'liftOp' . 'bpOp' = 'id' -- 'bpOp' . 'liftOp' = 'id' -- @ bpOp :: RPureConstrained Backprop as => (forall s. Reifies s W => Rec (BVar s) as -> BVar s b) -> Op as b bpOp = E.bpOp E.zeroFuncs {-# INLINE bpOp #-} -- | An infix version of 'viewVar', meant to evoke parallels to '^.' from -- lens. -- -- With normal values, you can extract something from that value with -- a lens: -- -- @ -- x '^.' myLens -- @ -- -- would extract a piece of @x :: b@, specified by @myLens :: 'Lens'' b a@. -- The result has type @a@. -- -- @ -- xVar '^^.' myLens -- @ -- -- would extract a piece out of @xVar :: 'BVar' s b@ (a 'BVar' holding a -- @b@), specified by @myLens :: Lens' b a@. The result has type @'BVar' -- s a@ (a 'BVar' holding a @a@) -- -- This is the main way to pull out values from 'BVar' of container types. -- -- If you have control of your data type definitions, consider using -- 'splitBV', which lets you break out 'BVar's of values into 'BVar's of -- their individual fields automatically without requiring lenses. -- -- __NOTE__: Usage of '^^.' on many fields from the same item is usually -- the main source of overhead in /backprop/ code, if you are looking to -- optimize your code. See for more information, and details on mitigating -- this overhead. -- -- __WARNING__: Do not use with any lenses that operate "numerically" on -- the contents (like 'multiplying'). -- (^^.) :: forall b a s. (Backprop b, Backprop a, Reifies s W) => BVar s b -> Lens' b a -> BVar s a x ^^. l = viewVar l x infixl 8 ^^. {-# INLINE (^^.) #-} -- | Using a 'Lens'', extract a value /inside/ a 'BVar'. Meant to evoke -- parallels to 'view' from lens. -- -- See documentation for '^^.' for more information, caveats, and warnings. viewVar :: forall b a s. (Backprop a, Backprop b, Reifies s W) => Lens' b a -> BVar s b -> BVar s a viewVar = E.viewVar E.addFunc E.zeroFunc {-# INLINE viewVar #-} -- | An infix version of 'setVar', meant to evoke parallels to '.~' from -- lens. -- -- With normal values, you can set something in a value with a lens: -- -- @ -- x '&' myLens '.~' 'y' -- @ -- -- would "set" a part of @x :: b@, specified by @myLens :: 'Lens'' a b@, to -- a new value @y :: a@. -- -- @ -- xVar '&' myLens '.~~' yVar -- @ -- -- would "set" a part of @xVar :: 'BVar' s b@ (a 'BVar' holding a @b@), -- specified by @myLens :: 'Lens'' a b@, to a new value given by @yVar :: -- 'BVar' s a@. The result is a new (updated) value of type @'BVar' s b@. -- -- This is the main way to set values inside 'BVar's of container types. -- -- Note that this does not incurr the performance overhead issues of -- 'viewVar' and '^^.', and is fairly cheap. -- (.~~) :: (Backprop a, Backprop b, Reifies s W) => Lens' b a -> BVar s a -> BVar s b -> BVar s b l .~~ x = setVar l x infixl 8 .~~ {-# INLINE (.~~) #-} -- | Using a 'Lens'', set a value /inside/ a 'BVar'. Meant to evoke -- parallels to "set" from lens. -- -- See documentation for '.~~' for more information. setVar :: (Backprop a, Backprop b, Reifies s W) => Lens' b a -> BVar s a -> BVar s b -> BVar s b setVar = E.setVar E.addFunc E.addFunc E.zeroFunc {-# INLINE setVar #-} -- | An infix version of 'overVar', meant to evoke parallels to '%~' from -- lens. -- -- With normal values, you can set modify in a value with a lens: -- -- @ -- x '&' myLens '%~' 'negate' -- @ -- -- would "modify" a part of @x :: b@, specified by @myLens :: 'Lens'' a b@, -- using the function @negate :: a -> a@. -- -- @ -- xVar '&' myLens '%~~' 'negate' -- @ -- -- would "modify" a part of @xVar :: 'BVar' s b@ (a 'BVar' holding a @b@), -- specified by @myLens :: 'Lens'' a b@, using the function @negate :: BVar -- s a -> BVar s @. The result is a new (updated) value of type @'BVar' -- s b@. -- -- Is essentially a convenient wrapper over a 'viewVar' followed by -- a 'setVar'. -- -- @since 0.2.4.0 -- (%~~) :: (Backprop a, Backprop b, Reifies s W) => Lens' b a -> (BVar s a -> BVar s a) -> BVar s b -> BVar s b l %~~ f = overVar l f infixr 4 %~~ {-# INLINE (%~~) #-} -- | Using a 'Lens'', modify a value /inzide/ a 'BVar'. Meant to evoke -- parallels to "over" from lens. See documentation for '%~~' for more -- information. -- -- @since 0.2.4.0 overVar :: (Backprop a, Backprop b, Reifies s W) => Lens' b a -> (BVar s a -> BVar s a) -> BVar s b -> BVar s b overVar = E.overVar E.addFunc E.addFunc E.zeroFunc E.zeroFunc {-# INLINE overVar #-} -- | An infix version of 'previewVar', meant to evoke parallels to '^?' -- from lens. -- -- With normal values, you can (potentially) extract something from that -- value with a lens: -- -- @ -- x '^?' myPrism -- @ -- -- would (potentially) extract a piece of @x :: b@, specified by -- @myPrism :: 'Traversal'' b a@. The result has type @'Maybe' a@. -- -- @ -- xVar '^^?' myPrism -- @ -- -- would (potentially) extract a piece out of @xVar :: 'BVar' s b@ (a -- 'BVar' holding a @b@), specified by @myPrism :: Prism' b a@. -- The result has type @'Maybe' ('BVar' s a)@ ('Maybe' a 'BVar' holding -- a @a@). -- -- This is intended to be used with 'Prism''s (which hits at most one -- target), but will actually work with /any/ 'Traversal''. If the -- traversal hits more than one target, the first one found will be -- extracted. -- -- This can be used to "pattern match" on 'BVar's, by using prisms on -- constructors. -- -- __NOTE__: Has the same potential of performance overhead issues as -- '^^.'; see documentation of '^^.' for more details. (^^?) :: forall b a s. (Backprop b, Backprop a, Reifies s W) => BVar s b -> Traversal' b a -> Maybe (BVar s a) v ^^? t = previewVar t v infixl 8 ^^? {-# INLINE (^^?) #-} -- | An *UNSAFE* version of '^^?' and 'previewVar' assuming that the value -- is there. -- -- Is undefined if the 'Traversal' hits no targets. -- -- Is essentially '^^?' with 'fromJust', or '^^..' with 'head'. -- -- @since 0.2.1.0 (^^?!) :: forall b a s. (Backprop b, Backprop a, Reifies s W) => BVar s b -> Traversal' b a -> BVar s a v ^^?! t = fromMaybe (error e) (previewVar t v) where e = "Numeric.Backprop.^^?!: Empty traversal" infixl 8 ^^?! {-# INLINE (^^?!) #-} -- | Using a 'Traversal'', extract a single value /inside/ a 'BVar', if it -- exists. If more than one traversal target exists, returns te first. -- Meant to evoke parallels to 'preview' from lens. Really only intended -- to be used wth 'Prism''s, or up-to-one target traversals. -- -- See documentation for '^^?' for more information, warnings, and caveats. previewVar :: forall b a s. (Backprop b, Backprop a, Reifies s W) => Traversal' b a -> BVar s b -> Maybe (BVar s a) previewVar = E.previewVar E.addFunc E.zeroFunc {-# INLINE previewVar #-} -- | An infix version of 'toListOfVar', meant to evoke parallels to '^..' -- from lens. -- -- With normal values, you can extract all targets of a 'Traversal' from -- that value with a: -- -- @ -- x '^..' myTraversal -- @ -- -- would extract all targets inside of @x :: b@, specified by @myTraversal -- :: 'Traversal'' b a@. The result has type @[a]@. -- -- @ -- xVar '^^..' myTraversal -- @ -- -- would extract all targets inside of @xVar :: 'BVar' s b@ (a 'BVar' -- holding a @b@), specified by @myTraversal :: Traversal' b a@. The result -- has type @['BVar' s a]@ (A list of 'BVar's holding @a@s). -- -- __NOTE__: Has all of the performance overhead issues of 'sequenceVar'; -- see documentation for 'sequenceVar' for more information. -- (^^..) :: forall b a s. (Backprop b, Backprop a, Reifies s W) => BVar s b -> Traversal' b a -> [BVar s a] v ^^.. t = toListOfVar t v {-# INLINE (^^..) #-} -- | Using a 'Traversal'', extract all targeted values /inside/ a 'BVar'. -- Meant to evoke parallels to 'toListOf' from lens. -- -- See documentation for '^^..' for more information, warnings, and -- caveats. toListOfVar :: forall b a s. (Backprop b, Backprop a, Reifies s W) => Traversal' b a -> BVar s b -> [BVar s a] toListOfVar = E.toListOfVar E.addFunc E.zeroFunc {-# INLINE toListOfVar #-} -- | 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. -- -- __NOTE__: A potential source of performance overhead. If there are -- \(n\) total elements, and you use \(m\) of them, then there is an -- overhead cost on the order of \(\mathcal{O}(m n)\), with a constant -- factor dependent on the cost of 'add'. Should be negligible for types -- with cheap 'add' (like 'Double'), but may be costly for things like -- large matrices. See for for details. sequenceVar :: (Traversable t, Backprop a, Reifies s W) => BVar s (t a) -> t (BVar s a) sequenceVar = E.sequenceVar E.addFunc E.zeroFunc {-# INLINE sequenceVar #-} -- | 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 does __not__ suffer from the same performance overhead -- issues as 'sequenceVar'. 'collectVar' is \(\mathcal{O}(n)\), with -- a very small constant factor that consistent for all types. This -- reveals a general property of reverse-mode automatic differentiation; -- "many to one" is cheap, but "one to many" is expensive. collectVar :: (Foldable t, Functor t, Backprop a, Reifies s W) => t (BVar s a) -> BVar s (t a) collectVar = E.collectVar E.addFunc E.zeroFunc {-# INLINE collectVar #-} -- | Lift an 'Op' with an arbitrary number of inputs to a function on the -- appropriate number of 'BVar's. -- -- Should preferably be used only by libraries to provide primitive 'BVar' -- functions for their types for users. -- -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information, and "Numeric.Backprop.Op#prod" for a mini-tutorial on using -- 'Rec'. liftOp :: (RPureConstrained Backprop as, Reifies s W) => Op as b -> Rec (BVar s) as -> BVar s b liftOp = E.liftOp E.addFuncs {-# INLINE liftOp #-} -- | Lift an 'Op' with a single input to be a function on a single 'BVar'. -- -- Should preferably be used only by libraries to provide primitive 'BVar' -- functions for their types for users. -- -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp1 :: (Backprop a, Reifies s W) => Op '[a] b -> BVar s a -> BVar s b liftOp1 = E.liftOp1 E.addFunc {-# INLINE liftOp1 #-} -- | Lift an 'Op' with two inputs to be a function on a two 'BVar's. -- -- Should preferably be used only by libraries to provide primitive 'BVar' -- functions for their types for users. -- -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp2 :: (Backprop a, Backprop b, Reifies s W) => Op '[a,b] c -> BVar s a -> BVar s b -> BVar s c liftOp2 = E.liftOp2 E.addFunc E.addFunc {-# INLINE liftOp2 #-} -- | Lift an 'Op' with three inputs to be a function on a three 'BVar's. -- -- Should preferably be used only by libraries to provide primitive 'BVar' -- functions for their types for users. -- -- See "Numeric.Backprop#liftops" and documentation for 'liftOp' for more -- information. liftOp3 :: (Backprop a, Backprop b, Backprop c, Reifies s W) => Op '[a,b,c] d -> BVar s a -> BVar s b -> BVar s c -> BVar s d liftOp3 = E.liftOp3 E.addFunc E.addFunc E.addFunc {-# INLINE liftOp3 #-} -- | Convert the value inside a 'BVar' using a given isomorphism. Useful -- for things like constructors. -- -- If you have control of your data type definitions, consider using -- 'joinBV', which lets you use your data type constructors themselves to -- join together 'BVar's as their fields. -- -- Warning: This is unsafe! It assumes that the isomorphisms themselves -- have derivative 1, so will break for things like 'exp' & 'log'. -- Basically, don't use this for any "numeric" isomorphisms. -- -- @since 0.1.4.0 isoVar :: (Backprop a, Reifies s W) => (a -> b) -> (b -> a) -> BVar s a -> BVar s b isoVar = E.isoVar E.addFunc {-# INLINE isoVar #-} -- | Convert the values inside two 'BVar's using a given isomorphism. -- Useful for things like constructors. See 'isoVar' for caveats. -- -- If you have control of your data type definitions, consider using -- 'joinBV', which lets you use your data type constructors themselves to -- join together 'BVar's as their fields. -- -- @since 0.1.4.0 isoVar2 :: (Backprop a, Backprop b, Reifies s W) => (a -> b -> c) -> (c -> (a, b)) -> BVar s a -> BVar s b -> BVar s c isoVar2 = E.isoVar2 E.addFunc E.addFunc {-# INLINE isoVar2 #-} -- | Convert the values inside three 'BVar's using a given isomorphism. -- Useful for things like constructors. See 'isoVar' for caveats. -- -- @since 0.1.4.0 isoVar3 :: (Backprop a, Backprop b, Backprop c, Reifies s W) => (a -> b -> c -> d) -> (d -> (a, b, c)) -> BVar s a -> BVar s b -> BVar s c -> BVar s d isoVar3 = E.isoVar3 E.addFunc E.addFunc E.addFunc {-# INLINE isoVar3 #-} -- | Convert the values inside a tuple of 'BVar's using a given -- isomorphism. Useful for things like constructors. See 'isoVar' for -- caveats. -- -- If you have control of your data type definitions, consider using -- 'joinBV', which lets you use your data type constructors themselves to -- join together 'BVar's as their fields. -- -- @since 0.1.4.0 isoVarN :: (RPureConstrained Backprop as, Reifies s W) => (Rec Identity as -> b) -> (b -> Rec Identity as) -> Rec (BVar s) as -> BVar s b isoVarN = E.isoVarN E.addFuncs {-# INLINE isoVarN #-} -- | Useful pattern for constructing and deconstructing 'BVar's of -- two-tuples. -- -- @since 0.2.1.0 pattern T2 :: (Backprop a, Backprop b, Reifies s W) => BVar s a -> BVar s b -> BVar s (a, b) pattern T2 x y <- (\xy -> (xy ^^. _1, xy ^^. _2) -> (x, y)) where T2 = isoVar2 (,) id #if MIN_VERSION_base(4,10,0) {-# COMPLETE BV #-} #endif -- | Useful pattern for constructing and deconstructing 'BVar's -- three-tuples. -- -- @since 0.2.1.0 pattern T3 :: (Backprop a, Backprop b, Backprop c, Reifies s W) => BVar s a -> BVar s b -> BVar s c -> BVar s (a, b, c) pattern T3 x y z <- (\xyz -> (xyz ^^. _1, xyz ^^. _2, xyz ^^. _3) -> (x, y, z)) where T3 = isoVar3 (,,) id #if MIN_VERSION_base(4,10,0) {-# COMPLETE BV #-} #endif -- $hkd -- -- 'splitBV' and 'joinBV' let you split out a 'BVar' of a data type and -- join together a data type of 'BVar's using the "higher-kinded data type" -- technique, a la -- . -- -- It will let you take a data type like -- -- @ -- data MyType = MT { mtX :: 'Double', mtY :: [Double] } -- -- -- | Automatic instance -- instance Backprop MyType -- @ -- -- And automatically let you turn a @'BVar' s MyType@ into a @'BVar' -- s 'Double'@ and @BVar s [Double]@, without munging around with lenses -- and 'viewVar'. It'll also let you take a @BVar s Double@ and a @BVar -- s [Double]@ and turn it into a @BVar s MyType@ without messing around -- with manually lifting ops or 'isoVar'. -- -- To do this, rewrite 'MyType' to take a 'Functor' argument: -- -- @ -- -- | Can be re-used for every data type you use this trick with -- type family HKD f a where -- HKD 'Identity' a = a -- HKD f a = f a -- -- data MyType' f = MT { mtX :: HKD f Double, mtY :: HKD f [Double] } -- deriving Generic -- -- -- | This is the original data type, which can be used the same way as -- -- before -- type MyType = MyType' 'Identity' -- -- -- | Automatic instance -- instance 'Backprop' MyType -- @ -- -- Now, 'splitBV' can be used, with type: -- -- @ -- 'splitBV' :: BVar s MyType -> MyType' (BVar s) -- @ -- -- So you can use it lke: -- -- @ -- myFunction :: 'BVar' s MyType -> BVar s Double -- myFunction ('splitBV' -> MT x y) = x + 'Prelude.Backprop.sum' y -- @ -- -- Or also, using the 'BV' pattern synonym: -- -- @ -- myFunction :: 'BVar' s MyType -> BVar s Double -- myFunction ('BV' (MT x y)) = x + 'Prelude.Backprop.sum' y -- @ -- -- If you use 'splitBV', the contents will be a @BVar s Double@ and a @BVar -- s [Double]@. It lets you "extract" the fields, because your 'MyType'' -- constructor now holds a @'BVar' s Double@ and a @BVar s [Double]@, -- instead of just a normal 'Double' and @[Double]@. -- -- Note that access using 'splitBV' and pattern matching is slightly slower -- than access using lenses (by about 10-20%). -- -- With this trick, 'joinBV' can also be used, with the type: -- -- @ -- 'joinBV' :: MyType' (BVar s) -> BVar s MyType -- @ -- -- So you can take a bunch of 'BVar's and turn them into a 'BVar' of -- a 'MyType': -- -- @ -- myOtherFunction :: 'BVar' s Double -> BVar s [Double] -> BVar s MyType -- myOtherFunction x y = 'joinBV' $ MT x y -- @ -- -- The 'BV' pattern synonym abstracts over manual application of 'splitBV' -- and 'joinBV' as a pattern. -- -- This will work with all data types made with a single constructor, whose -- fields are all instances of 'Backprop', where the type itself has an -- instance of 'Backprop'. -- | Split out a 'BVar' of "higher-kinded data type", a la -- -- -- Lets you take 'BVar' of a value into a separate 'BVar' of every field of -- that value. -- -- See "Numeric.Backprop#hkd" for a tutorial on usage. -- -- This will work with all data types made with a single constructor, whose -- fields are all instances of 'Backprop', where the type itself has an -- instance of 'Backprop'. The type also must derive 'Generic'. -- -- Note that access using 'splitBV' and pattern matching is slightly slower -- than access using lenses (by about 10-20%). -- -- See also 'BV', pattern synonym version where the deconstructor is -- exactly a view into 'splitBV'. -- -- __NOTE__: Like '^^.' and 'viewVar', 'splitBV' usage could potentially be -- the main source of performance overhead in your program. If your data -- type has \(n\) fields, and you use 'splitBV' to later use \(m\) of those -- fields, there is an overhead cost on the order of \(\mathcal{O}(m n)\), -- with a constant factor dependent on the cost of 'add' for your original -- data type. Should be negligible for types with cheap 'add' (like -- 'Double'), but may be costly for things like large matrices. See -- for -- for details. -- -- However, there is some potential opportunities to re-write some core -- library functionality that would allow 'splitBV' to avoid all of the -- significant performance overhead issues of '^^.'. Contact me if you are -- interested in helping out! -- -- @since 0.2.2.0 splitBV :: ( Generic (z f) , Generic (z (BVar s)) , E.BVGroup s as (Rep (z f)) (Rep (z (BVar s))) , Backprop (z f) , Backprop (Rep (z f) ()) , RPureConstrained Backprop as , Reifies s W ) => BVar s (z f) -- ^ 'BVar' of value -> z (BVar s) -- ^ 'BVar's of fields splitBV = E.splitBV E.addFunc E.addFuncs E.zeroFunc E.zeroFuncs {-# INLINE splitBV #-} -- | Assemble a 'BVar' of "higher-kinded data type", a la -- -- -- It lets you take a 'BVar' of every field of a value, and join them into -- a 'BVar' of that value. -- -- See "Numeric.Backprop#hkd" for a tutorial on usage. -- -- This will work with all data types made with a single constructor, whose -- fields are all instances of 'Backprop', where the type itself has an -- instance of 'Backprop'. -- -- See also 'BV', a pattern synonym version where the constructor is -- exactly 'joinBV'. -- -- Note that 'joinBV' does not suffer the major performance overhead issues -- of 'splitBV'. This is a general property of reverse-mode automatic -- differentiation: "many to one" is cheap, but "one to many" is expensive. -- -- @since 0.2.2.0 joinBV :: ( Generic (z f) , Generic (z (BVar s)) , E.BVGroup s as (Rep (z f)) (Rep (z (BVar s))) , Backprop (z f) , Backprop (Rep (z f) ()) , RPureConstrained Backprop as , Reifies s W ) => z (BVar s) -- ^ 'BVar's of fields -> BVar s (z f) -- ^ 'BVar' of combined value joinBV = E.joinBV E.addFunc E.addFuncs E.zeroFunc E.zeroFuncs {-# INLINE joinBV #-} -- | Pattern synonym wrapping manual usage of 'splitBV' and 'joinBV'. It -- is a pattern for a @'BVar' s (z f)@ containing a @z ('BVar' s)@ -- -- @since 0.2.3.0 pattern BV :: ( Generic (z f) , Generic (z (BVar s)) , E.BVGroup s as (Rep (z f)) (Rep (z (BVar s))) , Backprop (Rep (z f) ()) , Backprop (z f) , RPureConstrained Backprop as , RecApplicative as , Reifies s W ) #if MIN_VERSION_base(4,10,0) => z (BVar s) -- ^ 'BVar's of fields -> BVar s (z f) -- ^ 'BVar' of combined value #else => z (BVar s) -> BVar s (z f) #endif pattern BV v <- (splitBV->v) where BV = joinBV #if MIN_VERSION_base(4,10,0) {-# COMPLETE BV #-} #endif