Copyright | (c) Justin Le 2017 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
Provides the BP
monad and the BVar
type; after manipulating BVar
s
(inputs to your function) to produce a result, the library tracks internal data
dependencies, which are used to perform back-propagation (reverse-mode
automatic differentiation) to calculate the gradient of the output with
respect to the inputs.
Similar to automatic differentiation from the ad library and Numeric.AD.Mode.Reverse, except for a few key differences:
- Most importantly, this library implements heterogeneous back-propagation, so you can manipulate values of different types (like different matrix and vector types, and product and sum types). This is essential for things like back-propagation for neural networks.
- This module allows you to explicitly build your data dependency
graph if you wish, which allows the library to perform optimizations and
reduce extra allocation, which may or may not provide advantages over
Numeric.AD.Mode.Reverse's
unsafePerformIO
-based implicit graph building.
See the README for more information
and links to demonstrations and tutorials. If you want to plunge right
in, you can also look directly at the main types, BP
, BPOp
, BVar
,
Op
, and the main functions, backprop
and opVar
.
Note that every type involved has to be an instance of Num
. This is
because gradients all need to be "summable" (which is implemented using
sum
and +
), and we also need to able to generate gradients of '1'
and '0'.
- data BP s rs a
- type BPOp s rs a = BP s rs (BVar s rs a)
- type BPOpI s rs a = Prod (BVar s rs) rs -> BVar s rs a
- data BVar :: Type -> [Type] -> Type -> Type
- type Op as a = forall m. Monad m => OpM m as a
- type OpB s as a = OpM (ST s) as a
- data Prod k f a :: forall k. (k -> *) -> [k] -> * where
- type Tuple = Prod * I
- newtype I a :: * -> * = I {
- getI :: a
- backprop :: Every Num rs => (forall s. BPOp s rs a) -> Tuple rs -> (a, Tuple rs)
- evalBPOp :: (forall s. BPOp s rs a) -> Tuple rs -> a
- gradBPOp :: Every Num rs => (forall s. BPOp s rs a) -> Tuple rs -> Tuple rs
- withInps :: Known Length rs => (Prod (BVar s rs) rs -> BP s rs a) -> BP s rs a
- implicitly :: (Known Length rs, Num a) => BPOpI s rs a -> BPOp s rs a
- withInps' :: Length rs -> (Prod (BVar s rs) rs -> BP s rs a) -> BP s rs a
- implicitly' :: Num a => Length rs -> BPOpI s rs a -> BPOp s rs a
- constVar :: a -> BVar s rs a
- inpVar :: Index rs a -> BVar s rs a
- inpVars :: Known Length rs => Prod (BVar s rs) rs
- bpOp :: Every Num rs => BPOp s rs a -> OpB s rs a
- bindVar :: Num a => BVar s rs a -> BP s rs (BVar s rs a)
- inpVars' :: Length rs -> Prod (BVar s rs) rs
- opVar :: forall s rs as a. Num a => OpB s as a -> Prod (BVar s rs) as -> BP s rs (BVar s rs a)
- (~$) :: Num a => OpB s as a -> Prod (BVar s rs) as -> BP s rs (BVar s rs a)
- opVar1 :: Num b => OpB s '[a] b -> BVar s rs a -> BP s rs (BVar s rs b)
- opVar2 :: Num c => OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BP s rs (BVar s rs c)
- opVar3 :: Num d => OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BP s rs (BVar s rs d)
- (-$) :: (Every Num as, Known Length as, Num a) => BPOp s as a -> Prod (BVar s rs) as -> BPOp s rs a
- partsVar :: forall s rs bs b. Every Num bs => Iso' b (Tuple bs) -> BVar s rs b -> BP s rs (Prod (BVar s rs) bs)
- (#<~) :: (Every Num bs, Known Length bs) => Iso' b (Tuple bs) -> BVar s rs b -> BP s rs (Prod (BVar s rs) bs)
- withParts :: Every Num bs => Iso' b (Tuple bs) -> BVar s rs b -> (Prod (BVar s rs) bs -> BP s rs a) -> BP s rs a
- splitVars :: forall s rs as. Every Num as => BVar s rs (Tuple as) -> BP s rs (Prod (BVar s rs) as)
- gSplit :: (Every Num bs, Generic b, Code b ~ '[bs]) => BVar s rs b -> BP s rs (Prod (BVar s rs) bs)
- gTuple :: (Generic a, Code a ~ '[as]) => Iso' a (Tuple as)
- choicesVar :: forall s rs bs b. Every Num bs => Iso' b (Sum I bs) -> BVar s rs b -> BP s rs (Sum (BVar s rs) bs)
- (?<~) :: (Every Num bs, Known Length bs) => Iso' b (Sum I bs) -> BVar s rs b -> BP s rs (Sum (BVar s rs) bs)
- withChoices :: forall s rs bs b a. Every Num bs => Iso' b (Sum I bs) -> BVar s rs b -> (Sum (BVar s rs) bs -> BP s rs a) -> BP s rs a
- data Sum k f a :: forall k. (k -> *) -> [k] -> * where
- sopVar :: forall s rs bss b. Every (Every Num) bss => Iso' b (Sum Tuple bss) -> BVar s rs b -> BP s rs (Sum (Prod (BVar s rs)) bss)
- gSplits :: forall s rs b. (Generic b, Every (Every Num) (Code b)) => BVar s rs b -> BP s rs (Sum (Prod (BVar s rs)) (Code b))
- gSOP :: Generic a => Iso' a (Sum Tuple (Code a))
- liftB :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a
- (.$) :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a
- liftB1 :: OpB s '[a] b -> BVar s rs a -> BVar s rs b
- liftB2 :: OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BVar s rs c
- liftB3 :: OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BVar s rs d
- op1 :: Num a => (forall s. AD s (Forward a) -> AD s (Forward a)) -> Op '[a] a
- op2 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a] a
- op3 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a, a] a
- opN :: (Num a, Known Nat n) => (forall s. Reifies s Tape => Vec n (Reverse s a) -> Reverse s a) -> Op (Replicate n a) a
- composeOp :: (Monad m, Every Num as, Known Length as) => Prod (OpM m as) bs -> OpM m bs c -> OpM m as c
- composeOp1 :: (Monad m, Every Num as, Known Length as) => OpM m as b -> OpM m '[b] c -> OpM m as c
- (~.) :: (Monad m, Known Length as, Every Num as) => OpM m '[b] c -> OpM m as b -> OpM m as c
- op1' :: (a -> (b, Maybe b -> a)) -> Op '[a] b
- op2' :: (a -> b -> (c, Maybe c -> (a, b))) -> Op '[a, b] c
- op3' :: (a -> b -> c -> (d, Maybe d -> (a, b, c))) -> Op '[a, b, c] d
- pattern (:>) :: forall k f a b. f a -> f b -> Prod k f ((:) k a ((:) k b ([] k)))
- only :: f a -> Prod k f ((:) k a ([] k))
- head' :: Prod k f ((:<) k a as) -> f a
- pattern (::<) :: forall a as. a -> Tuple as -> Tuple ((:<) * a as)
- only_ :: a -> Tuple ((:) * a ([] *))
- (+.) :: Num a => Op '[a, a] a
- (-.) :: Num a => Op '[a, a] a
- (*.) :: Num a => Op '[a, a] a
- negateOp :: Num a => Op '[a] a
- absOp :: Num a => Op '[a] a
- signumOp :: Num a => Op '[a] a
- (/.) :: Fractional a => Op '[a, a] a
- recipOp :: Fractional a => Op '[a] a
- expOp :: Floating a => Op '[a] a
- logOp :: Floating a => Op '[a] a
- sqrtOp :: Floating a => Op '[a] a
- (**.) :: Floating a => Op '[a, a] a
- logBaseOp :: Floating a => Op '[a, a] a
- sinOp :: Floating a => Op '[a] a
- cosOp :: Floating a => Op '[a] a
- tanOp :: Floating a => Op '[a] a
- asinOp :: Floating a => Op '[a] a
- acosOp :: Floating a => Op '[a] a
- atanOp :: Floating a => Op '[a] a
- sinhOp :: Floating a => Op '[a] a
- coshOp :: Floating a => Op '[a] a
- tanhOp :: Floating a => Op '[a] a
- asinhOp :: Floating a => Op '[a] a
- acoshOp :: Floating a => Op '[a] a
- atanhOp :: Floating a => Op '[a] a
Types
Backprop types
A Monad allowing you to explicitly build hetereogeneous data dependency graphs and that the library can perform back-propagation on.
A
is a BP
s rs aBP
action that uses an environment of rs
returning a a
. When "run", it will compute a gradient that is a tuple
of rs
. (The phantom parameter s
is used to ensure that any BVar
s
aren't leaked out of the monad)
Note that you can only "run" a
that produces a BP
s rsBVar
--
that is, things of the form
BP
s rs (BVar
s rs a)
The above is a BP
action that returns a BVar
containing an a
.
When this is run, it'll produce a result of type a
and a gradient of
that is a tuple of rs
. (This form has a type synonym,
BPOp
, for convenience)
For example, a
is a monad that
represents a computation with an BP
s '[ Int, Double, Double ]Int
, Double
, and Double
as
inputs. And, if you ran a
BP
s '[ Int, Double, Double ] (BVar
s '[ Int, Double, Double ] Double)
Or, using the BPOp
type synonym:
BPOp
s '[ Int, Double, Double ] Double
with backprop
or gradBPOp
, it'll
return a gradient on the inputs (Int
, Double
, and Double
) and
produce a value of type Double
.
Now, one powerful thing about this type is that a BP
is itself an
Op
(or more precisely, an OpB
, which is a subtype of
OpM
). So, once you create your fancy BP
computation, you can
transform it into an OpM
using bpOp
.
type BPOp s rs a = BP s rs (BVar s rs a) Source #
A handy type synonym representing a BP
action that returns a BVar
.
This is handy because this is the form of BP
actions that
backprop
and gradBPOp
(etc.) expects.
A value of type:
BPOp
s rs a
is an action that takes an input environment of rs
and produces
a BVar
containing a value of type a
. Because it returns a BVar
,
the library can track the data dependencies between the BVar
and the
input environment and perform back-propagation.
See documentation for BP
for an explanation of the phantom type
parameter s
.
type BPOpI s rs a = Prod (BVar s rs) rs -> BVar s rs a Source #
An "implicit" operation on BVar
s that can be backpropagated.
A value of type:
BPOpI
s rs a
takes a bunch of BVar
s containg rs
and uses them to (purely) produce
a BVar
containing an a
.
foo :: BPOpI s '[ Double, Double ] Double foo (x :< y :< Ø) = x + sqrt y
If you are exclusively doing implicit back-propagation by combining
BVar
s and using BPOpI
s, you are probably better off just importing
Numeric.Backprop.Implicit, which provides better tools. This type
synonym exists in Numeric.Backprop just for the implicitly
function,
which can convert "implicit" backprop functions like a
into an "explicit" graph backprop function, a BPOpI
s rs a
.BPOp
s rs a
data BVar :: Type -> [Type] -> Type -> Type Source #
The basic unit of manipulation inside BP
(or inside an
implicit-graph backprop function). Instead of directly working with
values, you work with BVar
s contating those values. When you work
with a BVar
, the backprop library can keep track of what values
refer to which other values, and so can perform back-propagation to
compute gradients.
A
refers to a value of type BVar
s rs aa
, with an environment
of values of the types rs
. The phantom parameter s
is used to
ensure that stray BVar
s don't leak outside of the backprop process.
(That is, if you're using implicit backprop, it ensures that you interact
with BVar
s in a polymorphic way. And, if you're using explicit
backprop, it ensures that a
never leaves the BVar
s rs a
that it was created in.)BP
s rs
BVar
s have Num
, Fractional
, Floating
, etc. instances, so they
can be manipulated using polymorphic functions and numeric functions in
Haskell. You can add them, subtract them, etc., in "implicit" backprop
style.
(However, note that if you directly manipulate BVar
s using those
instances or using liftB
, it delays evaluation, so every usage site
has to re-compute the result/create a new node. If you want to re-use
a BVar
you created using +
or -
or liftB
, use
bindVar
to force it first. See documentation for
bindVar
for more details.)
Floating a => Floating (BVar s rs a) Source # | See note for |
Fractional a => Fractional (BVar s rs a) Source # | See note for |
Num a => Num (BVar s rs a) Source # | Note that if you use the |
type Op as a = forall m. Monad m => OpM m as a Source #
An
describes a differentiable function from Op
as aas
to a
.
For example, a value of type
Op
'[Int, Bool] Double
is a function from an Int
and a Bool
, returning a Double
. It can
be differentiated to give a gradient of an Int
and a Bool
if given
a total derivative for the Double
. If we call Bool
\(2\), then,
mathematically, it is akin to a:
\[ f : \mathbb{Z} \times 2 \rightarrow \mathbb{R} \]
See runOp
, gradOp
, and gradOpWith
for examples on how to run it,
and Op
for instructions on creating it.
This type is abstracted over using the pattern synonym with constructor
Op
, so you can create one from scratch with it. However, it's
simplest to create it using op2'
, op1'
, op2'
, and op3'
helper
smart constructors And, if your function is a numeric function, they
can even be created automatically using op1
, op2
, op3
, and opN
with a little help from Numeric.AD from the ad library.
Note that this type is a subset or subtype of OpM
(and also of
OpB
). So, if a function ever expects an
(or a OpM
m as
aOpB
), you can always provide an
instead.Op
as a
Many functions in this library will expect an
(or
an OpM
m as a
), and in all of these cases, you can
provide an OpB
s as a
.Op
as a
type OpB s as a = OpM (ST s) as a Source #
A subclass of OpM
(and superclass of Op
), representing Op
s that
the backprop library uses to perform backpropation.
An
OpB
s rs a
represents a differentiable function that takes a tuple of rs
and
produces an a a
, which can be run on
s and also inside BVar
s
s. For example, an BP
s
takes an OpB
s '[ Int, Double ] BoolInt
and
a Double
and produces a Bool
, and does it in a differentiable way.
OpB
is a superset of Op
, so, if you see any function
that expects an OpB
(like opVar'
and
~$
, for example), you can give them an Op
, as well.
You can think of OpB
as a superclass/parent class of Op
in this
sense, and of Op
as a subclass of OpB
.
Tuple types
Prod
, from the <http://hackage.haskell.org/package/type-combinators
type-combinators> library (in Data.Type.Prod) is a heterogeneous
list/tuple type, which allows you to tuple together multiple values of
different types and operate on them generically.
A
contains an Prod
f '[a, b, c]f a
, an f b
, and an f c
, and
is constructed by consing them together with :<
(using 'Ø' as nil):
I
"hello":<
I True :< I 7.8 :< Ø ::Prod
I
'[String, Bool, Double]C
"hello" :< C "world" :< C "ok" :< Ø ::Prod
(C
String) '[a, b, c]Proxy
:< Proxy :< Proxy :< Ø ::Prod
Proxy
'[a, b, c]
(I
is the identity functor, and C
is the constant functor)
So, in general:
x :: f a y :: f b z :: f c x :< y :< z :< Ø :: Prod f '[a, b, c]
If you're having problems typing 'Ø', you can use only
:
only z :: Prod f '[c] x :< y :< only z :: Prod f '[a, b, c]
Tuple
is provided as a convenient type synonym for Prod
I
, and has
a convenient pattern synonym ::<
(and only_
), which can also be used
for pattern matching:
x :: a y :: b z :: conly_
z ::Tuple
'[c] x::<
y ::< z ::< Ø ::Tuple
'[a, b, c] x ::< y ::< only_ z ::Tuple
'[a, b, c]
data Prod k f a :: forall k. (k -> *) -> [k] -> * where #
Witness ØC ØC (Prod k f (Ø k)) | |
IxFunctor1 k [k] (Index k) (Prod k) | |
IxFoldable1 k [k] (Index k) (Prod k) | |
IxTraversable1 k [k] (Index k) (Prod k) | |
Functor1 [k] k (Prod k) | |
Foldable1 [k] k (Prod k) | |
Traversable1 [k] k (Prod k) | |
TestEquality k f => TestEquality [k] (Prod k f) | |
BoolEquality k f => BoolEquality [k] (Prod k f) | |
Eq1 k f => Eq1 [k] (Prod k f) | |
Ord1 k f => Ord1 [k] (Prod k f) | |
Show1 k f => Show1 [k] (Prod k f) | |
Read1 k f => Read1 [k] (Prod k f) | |
(Known [k] (Length k) as, Every k (Known k f) as) => Known [k] (Prod k f) as | |
(Witness p q (f a1), Witness s t (Prod a f as)) => Witness (p, s) (q, t) (Prod a f ((:<) a a1 as)) | |
ListC ((<$>) Constraint * Eq ((<$>) * k f as)) => Eq (Prod k f as) | |
(ListC ((<$>) Constraint * Eq ((<$>) * k f as)), ListC ((<$>) Constraint * Ord ((<$>) * k f as))) => Ord (Prod k f as) | |
ListC ((<$>) Constraint * Show ((<$>) * k f as)) => Show (Prod k f as) | |
type WitnessC ØC ØC (Prod k f (Ø k)) | |
type KnownC [k] (Prod k f) as | |
type WitnessC (p, s) (q, t) (Prod a f ((:<) a a1 as)) | |
BP
Backprop
backprop :: Every Num rs => (forall s. BPOp s rs a) -> Tuple rs -> (a, Tuple rs) Source #
Perform back-propagation on the given BPOp
. Returns the result of
the operation it represents, as well as the gradient of the result with
respect to its inputs. See module header for Numeric.Backprop and
package documentation for examples and usages.
Simply run the BPOp
on an input tuple, getting the result without
bothering with the gradient or with back-propagation.
Run the BPOp
on an input tuple and return the gradient of the result
with respect to the input tuple.
Utility combinators
withInps :: Known Length rs => (Prod (BVar s rs) rs -> BP s rs a) -> BP s rs a Source #
Runs a continuation on a Prod
of all of the input BVar
s.
Handy for bringing the environment into scope and doing stuff with it:
foo ::BPOp
'[Double, Int] a foo =withInps
$ \(x :< y :< Ø) -> do -- do stuff with inputs
Looks kinda like foo (x :< y :< Ø) = -- ...
, don't it?
Note that the above is the same as
foo ::BPOp
'[Double, Int] a foo = do caseinpVars
of x ::< Ø - do -- do stuff with inputs
But just a little nicer!
implicitly :: (Known Length rs, Num a) => BPOpI s rs a -> BPOp s rs a Source #
Convert a BPOpI
into a BPOp
. That is, convert a function on
a bundle of BVar
s (generating an implicit graph) into a fully fledged
BPOp
that you can run backprop
on. See BPOpI
for more
information.
If you are going to write exclusively using implicit BVar
operations,
it might be more convenient to use Numeric.Backprop.Implicit instead,
which is geared around that use case.
withInps' :: Length rs -> (Prod (BVar s rs) rs -> BP s rs a) -> BP s rs a Source #
A version of withInps
taking explicit Length
, indicating the
number of inputs required and their types.
Mostly useful for rare "extremely polymorphic" situations, where GHC
can't infer the type and length of the list of inputs. If you ever
actually explicitly write down rs
as a list of types, you should be
able to just use withInps
.
implicitly' :: Num a => Length rs -> BPOpI s rs a -> BPOp s rs a Source #
A version of implicitly
taking explicit Length
, indicating the
number of inputs required and their types.
Requiring an explicit Length
is mostly useful for rare "extremely
polymorphic" situations, where GHC can't infer the type and length of
the list of inputs. If you ever actually explicitly write down rs
as
a list of types, you should be able to just use implicitly
.
Vars
inpVar :: Index rs a -> BVar s rs a Source #
Create a BVar
given an index into the input environment. For an
example,
inpVar
IZ
would refer to the first input variable (the Int
in a
), andBP
s '[Int, Bool]
inpVar
(IS
IZ
)
Would refer to the second input variable (the Bool
in a
)BP
s '[Int, Bool]
Typically, there shouldn't be any reason to use inpVar
directly. It's
cleaner to get all of your input BVar
s together using withInps
or
inpVars
.
inpVars :: Known Length rs => Prod (BVar s rs) rs Source #
Get a Prod
(tupling) of BVar
s for all of the input environment
(rs
) of the BP
s rs
For example, if your BP
has an Int
and Double
in its input
environment (a
), this would return a BP
s '[Int, Double]BVar
pointing to the Int
and a BVar
pointing to the Double
.
case (inpVars
::Prod
(BVar
s '[Int, Double]) '[Int, Double]) of x :< y :< Ø -> do -- the first item, x, is a var to the inputInt
-- x ::BVar
s '[Int, Double] Int -- the second item, y, is a var to the inputDouble
-- y ::BVar
s '[Int, Double] Double
bpOp :: Every Num rs => BPOp s rs a -> OpB s rs a Source #
Turn a BPOp
into an OpB
. Basically converts a BP
taking an rs
and producing an a
into an Op
taking an rs
and returning an a
,
with all of the powers and utility of an Op
, including all of its
gradient-finding glory.
Really just reveals the fact that any
is itself an BPOp
s rs aOp
,
an
, which makes it a differentiable function.OpB
s rs a
Handy because an OpB
can be used with almost all of
the Op
-related functions in this moduel, including opVar
, ~$
, etc.
bindVar :: Num a => BVar s rs a -> BP s rs (BVar s rs a) Source #
Concretizes a delayed BVar
. If you build up a BVar
using numeric
functions like +
or *
or using liftB
, it'll defer the evaluation,
and all of its usage sites will create a separate graph node.
Use bindVar
if you ever intend to use a BVar
in more than one
location.
-- bad errSquared :: Num a =>BP
s '[a, a] a errSquared =withInp
$ \(r :< t :< Ø) -> do let err = r - treturn
(err * err) -- err is used twice! -- good errSquared :: Num a =>BP
s '[a, a] a errSquared =withInps
$ \(r :< t :< Ø) -> do let err = r - t e <-bindVar
err -- force e, so that it's safe to use twice!return
(e * e) -- better errSquared :: Num a =>BP
s '[a, a] a errSquared =withInps
$ \(r :< t :< Ø) -> do let err = r - t e <-bindVar
errbindVar
(e * e) -- result is forced so user doesn't have to worry
Note the relation to opVar
~$
liftB
/ .$
:
opVar
o xs =bindVar
(liftB
o xs) o~$
xs =bindVar
(o.$
xs)op2
(*)~$
(x :< y :< Ø) =bindVar
(x * y)
So you can avoid bindVar
altogether if you use the explicitly binding
~$
and opVar
etc.
Note that bindVar
on BVar
s that are already forced is a no-op.
inpVars' :: Length rs -> Prod (BVar s rs) rs Source #
A version of inpVars
taking explicit Length
, indicating the
number of inputs required and their types.
Mostly useful for rare "extremely polymorphic" situations, where GHC
can't infer the type and length of the list of inputs. If you ever
actually explicitly write down rs
as a list of types, you should be
able to just use inpVars
.
From Ops
opVar :: forall s rs as a. Num a => OpB s as a -> Prod (BVar s rs) as -> BP s rs (BVar s rs a) Source #
Apply an OpB
to a Prod
(tupling) of BVar
s.
If you had an
, this function will expect a 3-Prod
of a OpB
s '[a, b, c] d
, a BVar
s rs a
, and a BVar
s rs b
, and the
result will be a BVar
s rs c
:BVar
s rs d
myOp ::OpB
s '[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs c x :< y :< z :< Ø ::Prod
(BVar
s rs) '[a, b, c]opVar
myOp (x :< y :< z :< Ø) ::BP
s rs (BVar
s rs d)
Note that OpB
is a superclass of Op
, so you can provide any Op
here, as well (like those created by op1
, op2
, constOp
, op0
etc.)
opVar
has an infix alias, ~$
, so the above example can also be
written as:
myOp~$
(x :< y :< z :< Ø) ::BP
s rs (BVar
s rs d)
to let you pretend that you're applying the myOp
function to three
inputs.
Also note the relation between opVar
and liftB
and bindVar
:
opVar
o xs =bindVar
(liftB
o xs)
(~$) :: Num a => OpB s as a -> Prod (BVar s rs) as -> BP s rs (BVar s rs a) infixr 5 Source #
Infix synonym for opVar
, which lets you pretend that you're applying
OpB
s as if they were functions:
myOp ::OpB
s '[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs c x :< y :< z :< Ø ::Prod
(BVar
s rs) '[a, b, c] myOp~$
(x :< y :< z :< Ø) ::BP
s rs (BVar
s rs d)
Note that OpB
is a superclass of Op
, so you can pass in any Op
here, as well (like those created by op1
, op2
, constOp
, op0
etc.)
~$
can also be thought of as a "binding" version of .$
:
o~$
xs =bindVar
(o.$
xs)
opVar1 :: Num b => OpB s '[a] b -> BVar s rs a -> BP s rs (BVar s rs b) Source #
Convenient wrapper over opVar
that takes an OpB
with one argument
and a single BVar
argument. Lets you not have to type out the entire
Prod
.
opVar1
o x =opVar
o (x:<
'Ø') myOp ::Op
'[a] b x ::BVar
s rs aopVar1
myOp x ::BP
s rs (BVar
s rs b)
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op1
) as well.
opVar2 :: Num c => OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BP s rs (BVar s rs c) Source #
Convenient wrapper over opVar
that takes an OpB
with two arguments
and two BVar
arguments. Lets you not have to type out the entire
Prod
.
opVar2
o x y =opVar
o (x:<
y:<
'Ø') myOp ::Op
'[a, b] c x ::BVar
s rs a y ::BVar
s rs bopVar2
myOp x y ::BP
s rs (BVar
s rs c)
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op2
) as well.
opVar3 :: Num d => OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BP s rs (BVar s rs d) Source #
Convenient wrapper over opVar
that takes an OpB
with three arguments
and three BVar
arguments. Lets you not have to type out the entire
Prod
.
opVar3
o x y z =opVar
o (x:<
y:<
z:<
'Ø') myOp ::Op
'[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs copVar3
myOp x y z ::BP
s rs (BVar
s rs d)
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op3
) as well.
(-$) :: (Every Num as, Known Length as, Num a) => BPOp s as a -> Prod (BVar s rs) as -> BPOp s rs a infixr 5 Source #
Lets you treat a
as an BPOp
s as b
, and "apply"
arguments to it just like you would with an Op
as bOp
and ~$
/ opVar
.
Basically a convenient wrapper over bpOp
and ~$
:
o-$
xs = bpOp o~$
xs
So for a
, you can "plug in" BPOp
s as bBVar
s to as
, and get
a b
as a result.
Useful for running a
that you got from a different function, and
"plugging in" its BPOp
s as bas
inputs with BVar
s from your current
environment.
Var manipulation
As parts
partsVar :: forall s rs bs b. Every Num bs => Iso' b (Tuple bs) -> BVar s rs b -> BP s rs (Prod (BVar s rs) bs) Source #
Use an Iso
(or compatible Iso
from the lens
library) to "pull out" the parts of a data type and work with each part
as a BVar
.
If there is an isomorphism between a b
and a
(that is, if
an Tuple
asa
is just a container for a bunch of as
), then it lets you break
out the as
inside and work with those.
data Foo = F Int Bool fooIso ::Iso'
Foo (Tuple '[Int, Bool]) fooIso =iso
(\(F i b) -> i ::< b ::< Ø) (\(i ::< b ::< Ø) -> F i b )partsVar
fooIso ::BVar
rs Foo ->BP
s rs (Prod
(BVar
s rs) '[Int, Bool]) stuff ::BP
s '[Foo] a stuff =withInps
$ \(foo :< Ø) -> do i :< b :< Ø <- partsVar fooIso foo -- now, i is aBVar
pointing to theInt
inside foo -- and b is aBVar
pointing to theBool
inside foo -- you can do stuff with the i and b here
You can use this to pass in product types as the environment to a BP
,
and then break out the type into its constituent products.
Note that for a type like Foo
, fooIso
can be generated automatically
with Generic
from GHC.Generics and
Generic
from Generics.SOP and generics-sop, using the
gTuple
iso. See gSplit
for more information.
Also, if you are literally passing a tuple (like
) then you can give in the identity
isomorphism (BP
s '[Tuple '[Int, Bool]id
) or use splitVars
.
(#<~) :: (Every Num bs, Known Length bs) => Iso' b (Tuple bs) -> BVar s rs b -> BP s rs (Prod (BVar s rs) bs) infixr 1 Source #
A useful infix alias for partsVar
.
Building on the example from partsVar
:
data Foo = F Int Bool fooIso ::Iso'
Foo (Tuple '[Int, Bool]) fooIso =iso
(\(F i b) -> i ::< b ::< Ø) (\(i ::< b ::< Ø) -> F i b ) stuff ::BP
s '[Foo] a stuff =withInps
$ \(foo :< Ø) -> do i :< b :< Ø <- fooIso#<~
foo -- now, i is aBVar
pointing to theInt
inside foo -- and b is aBVar
pointing to theBool
inside foo -- you can do stuff with the i and b here
See gSplit
for an example usage of splitting up an arbitrary product
type (like Foo
) using GHC.Geneics and Generics.SOP.
withParts :: Every Num bs => Iso' b (Tuple bs) -> BVar s rs b -> (Prod (BVar s rs) bs -> BP s rs a) -> BP s rs a Source #
A continuation-based version of partsVar
. Instead of binding the
parts and using it in the rest of the block, provide a continuation to
handle do stuff with the parts inside.
Building on the example from partsVar
:
data Foo = F Int Bool fooIso ::Iso'
Foo (Tuple '[Int, Bool]) fooIso =iso
(\(F i b) -> i ::< b ::< Ø) (\(i ::< b ::< Ø) -> F i b ) stuff ::BP
s '[Foo] a stuff =withInps
$ \(foo :< Ø) -> dowithParts
fooIso foo $ \(i :< b :< Ø) -> do -- now, i is aBVar
pointing to theInt
inside foo -- and b is aBVar
pointing to theBool
inside foo -- you can do stuff with the i and b here
Useful so that you can work with the internal parts of the data type
in a closure, so the parts don't leak out to the rest of your BP
.
But, mostly just a stylistic choice.
splitVars :: forall s rs as. Every Num as => BVar s rs (Tuple as) -> BP s rs (Prod (BVar s rs) as) Source #
Split out a BVar
of a tuple into a tuple (Prod
) of BVar
s.
-- the environment is a single Int-Bool tuple, tup stuff ::BP
s '[ Tuple '[Int, Bool] ] a stuff =withInps
$ \(tup :< Ø) -> do i :< b :< Ø <-splitVars
tup -- now, i is aBVar
pointing to theInt
inside tup -- and b is aBVar
pointing to theBool
inside tup -- you can do stuff with the i and b here
Note that
splitVars
=partsVar
id
gSplit :: (Every Num bs, Generic b, Code b ~ '[bs]) => BVar s rs b -> BP s rs (Prod (BVar s rs) bs) Source #
Using Generic
from GHC.Generics and
Generic
from Generics.SOP, split a BVar
containing
a product type into a tuple (Prod
) of BVar
s pointing to each value
inside.
Building on the example from partsVar
:
import qualified Generics.SOP as SOP data Foo = F Int Bool deriving Generic instance SOP.Generic FoogSplit
::BVar
rs Foo ->BP
s rs (Prod
(BVar
s rs) '[Int, Bool]) stuff ::BP
s '[Foo] a stuff =withInps
$ \(foo :< Ø) -> do i :< b :< Ø <-gSplit
foo -- now, i is aBVar
pointing to theInt
inside foo -- and b is aBVar
pointing to theBool
inside foo -- you can do stuff with the i and b here
Because Foo
is a straight up product type, gSplit
can use
GHC.Generics and take out the items inside.
Note that because
gSplit
=splitVars
gTuple
Then, you can also use gTuple
with #<~
:
stuff ::BP
s '[Foo] a stuff =withInps
$ \(foo :< Ø) -> do i :< b :< Ø <-gTuple
#<~
foo -- now, i is aBVar
pointing to theInt
inside foo -- and b is aBVar
pointing to theBool
inside foo -- you can do stuff with the i and b here
gTuple :: (Generic a, Code a ~ '[as]) => Iso' a (Tuple as) Source #
An Iso
between a type that is a product type, and a tuple that
contains all of its components. Uses Generics.SOP and the
Generic
typeclass.
>>>
import qualified Generics.SOP as SOP
>>>
data Foo = A Int Bool deriving Generic
>>>
instance SOP.Generic Foo
>>>
view gTuple (A 10 True)
10 ::< True ::< Ø>>>
review gTuple (15 ::< False ::< Ø)
A 15 False
As sums
choicesVar :: forall s rs bs b. Every Num bs => Iso' b (Sum I bs) -> BVar s rs b -> BP s rs (Sum (BVar s rs) bs) Source #
Use an Iso
(or compatible Iso
from the lens
library) to "pull out" the different constructors of a sum type and
return a (choice) sum of BVar
s that you can pattern match on.
If there is an isomorphism between a b
and a
(that is,
if an Sum
I
asa
is just a sum type for every type in as
), then it lets you
branch on which constructor is used inside the b
.
Essentially implements pattern matching on BVar
values.
data Bar = A Int | B Bool | C String barIso ::Iso'
Bar (Sum
I '[Int, Bool, String]) barIso =iso
(\case A i ->InL
(I i) B b ->InR
(InL
(I b)) C s ->InR
(InR
(InL
(I s)) ) (\caseInL
(I i) -> A iInR
(InL
(I b)) -> B bInR
(InR
(InL
(I s))) -> C s ) choicesVar barIso :: BVar rs Bar -> BP s rs (Sum I (BVar s rs) '[Int, Bool, String]) stuff ::BP
s '[Bar] a stuff =withInps
$ \(bar :< Ø) -> do c <-choicesVar
barIso bar case c ofInL
i -> do -- in this branch, bar was made with the A constructor -- i is the Int inside itInR
(InL
b) -> do -- in this branch, bar was made with the B constructor -- b is the Bool inside itInR
(InR
(InL
s)) -> do -- in this branch, bar was made with the B constructor -- s is the String inside it
You can use this to pass in sum types as the environment to a BP
, and
then branch on which constructor the value was made with.
See Numeric.Backprop for a mini-tutorial on Sum
.
(?<~) :: (Every Num bs, Known Length bs) => Iso' b (Sum I bs) -> BVar s rs b -> BP s rs (Sum (BVar s rs) bs) infixr 1 Source #
A useful infix alias for choicesVar
.
Building on the example from choicesVar
:
data Bar = A Int | B Bool | C String barIso ::Iso'
Bar (Sum
I '[Int, Bool, String]) barIso =iso
(\case A i ->InL
(I i) B b ->InR
(InL
(I b)) C s ->InR
(InR
(InL
(I s)) ) (\caseInL
(I i) -> A iInR
(InL
(I b)) -> B bInR
(InR
(InL
(I s))) -> C s ) stuff ::BP
s '[Bar] a stuff =withInps
$ \(bar :< Ø) -> do c <- barIso?<~
bar case c ofInL
i -> do -- in this branch, bar was made with the A constructor -- i is the Int inside itInR
(InL
b) -> do -- in this branch, bar was made with the B constructor -- b is the Bool inside itInR
(InR
(InL
s)) -> do -- in this branch, bar was made with the B constructor -- s is the String inside it
withChoices :: forall s rs bs b a. Every Num bs => Iso' b (Sum I bs) -> BVar s rs b -> (Sum (BVar s rs) bs -> BP s rs a) -> BP s rs a Source #
A continuation-based version of choicesVar
. Instead of binding the
parts and using it in the rest of the block, provide a continuation that
will handle every possible constructor/case of the type of the value the
BVar
points to.
Building on the example from choicesVar
:
data Bar = A Int | B Bool | C String barIso ::Iso'
Bar (Sum
I '[Int, Bool, String]) barIso =iso
(\case A i ->InL
(I i) B b ->InR
(InL
(I b)) C s ->InR
(InR
(InL
(I s)) ) (\caseInL
(I i) -> A iInR
(InL
(I b)) -> B bInR
(InR
(InL
(I s))) -> C s )choicesVar
barIso :: BVar rs Bar -> BP s rs (Sum I (BVar s rs) '[Int, Bool, String]) stuff ::BP
s '[Bar] a stuff =withInps
$ \(bar :< Ø) -> dowithChoices
barIso bar $ caseInL
i -> do -- in this branch, bar was made with the A constructor -- i is the Int inside itInR
(InL
b) -> do -- in this branch, bar was made with the B constructor -- b is the Bool inside itInR
(InR
(InL
s)) -> do -- in this branch, bar was made with the B constructor -- s is the String inside it
Nicer than choicesVar
directly, because you don't have to give the
result a superfluous name before pattern matching on it. You can just
directly pattern match in the lambda, so there's a lot less syntactical
noise.
Like the Prod
type (see mini-tutorial at Numeric.Backprop), the
Sum
type (from the
type-combinators
library, in Data.Type.Sum) lets you make arbitrary sum types over
different types and work with them generically.
A
contains either an Sum
f '[a, b, c]f a
, an f b
, or an f
c
, and is constructed with the constructors InL
and InR
, which are
analogous to Left
and Right
.
For a value of type
, there are three
constructors:Sum
f '[Int, Bool, String]
InL
:: f Int ->Sum
f '[Int, Bool, String] InL . InR :: f Bool -> Sum f '[Int, Bool, String] InL . InR . InR :: f String -> Sum f '[Int, Bool, String]
Each InR
"pushes deeper" into the Sum
.
Likewise, if you have a value of type
,
you can see which constructor it was made (and what type it contains)
with by pattern matching:Sum
f '[Int, Bool, String]
foo ::Sum
f '[Int, Bool, String] case foo ofInL
i -> -- foo contains an "f Int"InR
(InL b) -> -- foo contains an "f Bool" InR (InR (InL s)) -> -- foo contains an "f String"
data Sum k f a :: forall k. (k -> *) -> [k] -> * where #
Witness p q (f a) => Witness p q (Sum k f ((:) k a ([] k))) | |
(Witness p q (f a1), Witness p q (Sum a f ((:<) a b as))) => Witness p q (Sum a f ((:<) a a1 ((:<) a b as))) | |
IxFunctor1 k [k] (Index k) (Sum k) | |
IxFoldable1 k [k] (Index k) (Sum k) | |
IxTraversable1 k [k] (Index k) (Sum k) | |
Functor1 [k] k (Sum k) | |
Foldable1 [k] k (Sum k) | |
Traversable1 [k] k (Sum k) | |
Eq1 k f => Eq1 [k] (Sum k f) | |
Ord1 k f => Ord1 [k] (Sum k f) | |
Show1 k f => Show1 [k] (Sum k f) | |
Read1 k f => Read1 [k] (Sum k f) | |
ListC ((<$>) Constraint * Eq ((<$>) * k f as)) => Eq (Sum k f as) | |
(ListC ((<$>) Constraint * Eq ((<$>) * k f as)), ListC ((<$>) Constraint * Ord ((<$>) * k f as))) => Ord (Sum k f as) | |
ListC ((<$>) Constraint * Show ((<$>) * k f as)) => Show (Sum k f as) | |
type WitnessC p q (Sum a f ((:<) a a1 ((:<) a b as))) | |
type WitnessC p q (Sum k f ((:) k a ([] k))) | |
As sums of products
sopVar :: forall s rs bss b. Every (Every Num) bss => Iso' b (Sum Tuple bss) -> BVar s rs b -> BP s rs (Sum (Prod (BVar s rs)) bss) Source #
A combination of partsVar
and choicesVar
, that lets you split
a type into a sum of products. Using an Iso
(or compatible
Iso
from the lens library), you can pull out a type
that is a sum of products into a sum of products of BVar
s.
Implements branching on the constructors of a value that a BVar
contains, and also splitting out the different items inside each
constructor.
data Baz = A Int Bool | B String Double bazIso ::Iso'
Baz (Sum
Tuple
'[ '[Int, Bool], '[String, Double] ]) bazIso =iso
(\case A i b ->InL
(I (i ::< b ::< Ø)) B s d ->InR
(InL
(I (s ::< d ::< Ø))) ) (\caseInL
(I (i ::::< Ø)) - A i bInR
(InL
(I (s ::::< Ø))) - B s d )sopVar
bazIso ::BVar
rs Baz ->BP
s rs (Sum
(Prod
(BVar
s rs)) '[ '[Int, Bool], '[String, Double] ]) stuff ::BP
s '[Baz] a stuff =withInps
$ \(baz :< Ø) -> do c <-sopVar
barIso baz case c ofInL
(i ::< Ø) - do -- in this branch, baz was made with the A constructor -- i and b are the Int and Bool inside itInR
(InL
(s ::< Ø)) - do -- in this branch, baz was made with the B constructor -- s and d are the String and Double inside it
Essentially exists to implement "pattern matching" on multiple
constructors and fields for the value inside a BVar
.
Note that for a type like Baz
, bazIso
can be generated automatically
with Generic
from GHC.Generics and
Generic
from Generics.SOP and generics-sop, with
gSOP
. See gSplits
for more information.
See Numeric.Backprop for a mini-tutorial on Sum
.
gSplits :: forall s rs b. (Generic b, Every (Every Num) (Code b)) => BVar s rs b -> BP s rs (Sum (Prod (BVar s rs)) (Code b)) Source #
Using Generic
from GHC.Generics and
Generic
from Generics.SOP, split a BVar
containing
a sum of products (any simple ADT, essentialy) into a Sum
of each
constructor, each containing a tuple (Prod
) of BVar
s pointing to
each value inside.
Building on the example from sopVar
:
import qualified Generics.SOP as SOP data Baz = A Int Bool | B String Double deriving Generic instance SOP.Generic BazgSplits
::BVar
rs Baz ->BP
s rs (Sum
(Prod
(BVar
s rs)) '[ '[Int, Bool], '[String, Double] ]) stuff ::BP
s '[Baz] a stuff =withInps
$ \(baz :< Ø) -> do c <- gSplits baz case c ofInL
(i ::< Ø) - do -- in this branch, baz was made with the A constructor -- i and b are the Int and Bool inside itInR
(InL
(s ::< Ø)) - do -- in this branch, baz was made with the B constructor -- s and d are the String and Double inside it
Because Foo
is a straight up sum-of-products type, gSplits
can use
GHC.Generics and take out the items inside.
Note:
gSplit
=splitVars
gSOP
See Numeric.Backprop for a mini-tutorial on Sum
.
gSOP :: Generic a => Iso' a (Sum Tuple (Code a)) Source #
An Iso
between a sum type whose constructors are products, and a sum
(Sum
) of products (Tuple
). Uses Generics.SOP and the
Generic
typeclass.
>>>
import qualified Generics.SOP as SOP
>>>
data Bar = A Int Bool | B String Double
>>>
instance SOP.Generic Bar
>>>
'view' 'gSOP' (A 10 True)
'InL' (10 ::< True ::< Ø)>>>
'view' 'gSOP' (B "hello" 3.4)
'InR' ('InL' ("hello" ::< 3.4 ::< Ø))>>>
'review' 'gTuple' ('InL' (15 ::< False ::< Ø))
A 15 False>>>
'review' 'gTuple' ('InR' ('InL' ("bye" ::< 9.8 ::< Ø)))
B "bye" 9.8
Combining
liftB :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a Source #
Apply OpB
over a Prod
of BVar
s, as inputs. Provides
"implicit-graph" back-propagation, with deferred evaluation.
If you had an
, this function will expect a 3-Prod
of a OpB
s '[a, b, c] d
, a BVar
s rs a
, and a BVar
s rs b
, and the
result will be a BVar
s rs c
:BVar
s rs d
myOp ::OpB
s '[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs c x :< y :< z :< Ø ::Prod
(BVar
s rs) '[a, b, c]liftB
myOp (x :< y :< z :< Ø) ::BVar
s rs d
Note that OpB
is a superclass of Op
, so you can provide any Op
here, as well (like those created by op1
, op2
, constOp
, op0
etc.)
liftB
has an infix alias, .$
, so the above example can also be
written as:
myOp.$
(x :< y :< z :< Ø) ::BVar
s rs d
to let you pretend that you're applying the myOp
function to three
inputs.
The result is a new deferred BVar
. This should be fine in most
cases, unless you use the result in more than one location. This will
cause evaluation to be duplicated and multiple redundant graph nodes to
be created. If you need to use it in two locations, you should use
opVar
instead of liftB
, or use bindVar
:
opVar
o xs =bindVar
(liftB
o xs)
liftB
can be thought of as a "deferred evaluation" version of opVar
.
(.$) :: OpB s as a -> Prod (BVar s rs) as -> BVar s rs a infixr 5 Source #
Infix synonym for liftB
, which lets you pretend that you're applying
OpB
s as if they were functions:
myOp ::OpB
s '[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs c x :< y :< z :< Ø ::Prod
(BVar
s rs) '[a, b, c] myOp.$
(x :< y :< z :< Ø) ::BVar
s rs d
Note that OpB
is a superclass of Op
, so you can pass in any Op
here, as well (like those created by op1
, op2
, constOp
, op0
etc.)
See the documentation for liftB
for all the caveats of this usage.
.$
can also be thought of as a "deferred evaluation" version of ~$
:
o~$
xs =bindVar
(o.$
xs)
liftB1 :: OpB s '[a] b -> BVar s rs a -> BVar s rs b Source #
Convenient wrapper over liftB
that takes an OpB
with one argument
and a single BVar
argument. Lets you not have to type out the entire
Prod
.
liftB1
o x =liftB
o (x:<
'Ø') myOp ::Op
'[a] b x ::BVar
s rs aliftB1
myOp x ::BVar
s rs b
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op1
) as well.
See the documentation for liftB
for caveats and potential problematic
situations with this.
liftB2 :: OpB s '[a, b] c -> BVar s rs a -> BVar s rs b -> BVar s rs c Source #
Convenient wrapper over liftB
that takes an OpB
with two arguments
and two BVar
arguments. Lets you not have to type out the entire
Prod
.
liftB2
o x y =liftB
o (x:<
y:<
'Ø') myOp ::Op
'[a, b] c x ::BVar
s rs a y ::BVar
s rs bliftB2
myOp x y ::BVar
s rs c
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op2
) as well.
See the documentation for liftB
for caveats and potential problematic
situations with this.
liftB3 :: OpB s '[a, b, c] d -> BVar s rs a -> BVar s rs b -> BVar s rs c -> BVar s rs d Source #
Convenient wrapper over liftB
that takes an OpB
with three arguments
and three BVar
arguments. Lets you not have to type out the entire
Prod
.
liftB3
o x y z =liftB
o (x:<
y:<
z:<
'Ø') myOp ::Op
'[a, b, c] d x ::BVar
s rs a y ::BVar
s rs b z ::BVar
s rs cliftB3
myOp x y z ::BVar
s rs d
Note that OpB
is a superclass of Op
, so you can pass in an Op
here
(like one made with op3
) as well.
See the documentation for liftB
for caveats and potential problematic
situations with this.
Op
op2 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a] a Source #
op3 :: Num a => (forall s. Reifies s Tape => Reverse s a -> Reverse s a -> Reverse s a -> Reverse s a) -> Op '[a, a, a] a Source #
opN :: (Num a, Known Nat n) => (forall s. Reifies s Tape => Vec n (Reverse s a) -> Reverse s a) -> Op (Replicate n a) a Source #
composeOp1 :: (Monad m, Every Num as, Known Length as) => OpM m as b -> OpM m '[b] c -> OpM m as c Source #
(~.) :: (Monad m, Known Length as, Every Num as) => OpM m '[b] c -> OpM m as b -> OpM m as c infixr 9 Source #
op1' :: (a -> (b, Maybe b -> a)) -> Op '[a] b Source #
Create an Op
of a function taking one input, by giving its explicit
derivative. The function should return a tuple containing the result of
the function, and also a function taking the derivative of the result
and return the derivative of the input.
If we have
\[ \eqalign{ f &: \mathbb{R} \rightarrow \mathbb{R}\cr y &= f(x)\cr z &= g(y) } \]
Then the derivative \( \frac{dz}{dx} \), it would be:
\[ \frac{dz}{dx} = \frac{dz}{dy} \frac{dy}{dx} \]
If our Op
represents \(f\), then the second item in the resulting
tuple should be a function that takes \(\frac{dz}{dy}\) and returns
\(\frac{dz}{dx}\).
If the input is Nothing
, then \(\frac{dz}{dy}\) should be taken to be
\(1\).
As an example, here is an Op
that squares its input:
square :: Num a =>Op
'[a] a square =op1'
$ \x -> (x*x, \case Nothing -> 2 * x Just d -> 2 * d * x )
Remember that, generally, end users shouldn't directly construct Op
s;
they should be provided by libraries or generated automatically.
For numeric functions, single-input Op
s can be generated automatically
using op1
.
op2' :: (a -> b -> (c, Maybe c -> (a, b))) -> Op '[a, b] c Source #
Create an Op
of a function taking two inputs, by giving its explicit
gradient. The function should return a tuple containing the result of
the function, and also a function taking the derivative of the result
and return the derivative of the input.
If we have
\[ \eqalign{ f &: \mathbb{R}^2 \rightarrow \mathbb{R}\cr z &= f(x, y)\cr k &= g(z) } \]
Then the gradient \( \left< \frac{\partial k}{\partial x}, \frac{\partial k}{\partial y} \right> \) would be:
\[ \left< \frac{\partial k}{\partial x}, \frac{\partial k}{\partial y} \right> = \left< \frac{dk}{dz} \frac{\partial z}{dx}, \frac{dk}{dz} \frac{\partial z}{dy} \right> \]
If our Op
represents \(f\), then the second item in the resulting
tuple should be a function that takes \(\frac{dk}{dz}\) and returns
\( \left< \frac{\partial k}{dx}, \frac{\partial k}{dx} \right> \).
If the input is Nothing
, then \(\frac{dk}{dz}\) should be taken to be
\(1\).
As an example, here is an Op
that multiplies its inputs:
mul :: Num a =>Op
'[a, a] a mul =op2'
$ \x y -> (x*y, \case Nothing -> (y , x ) Just d -> (d*y, x*d) )
Remember that, generally, end users shouldn't directly construct Op
s;
they should be provided by libraries or generated automatically.
For numeric functions, two-input Op
s can be generated automatically
using op2
.
Utility
pattern (:>) :: forall k f a b. f a -> f b -> Prod k f ((:) k a ((:) k b ([] k))) infix 6 #
Construct a two element Prod. Since the precedence of (:>) is higher than (:<), we can conveniently write lists like:
>>>
a :< b :> c
Which is identical to:
>>>
a :< b :< c :< Ø
Numeric Ops
Optimized ops for numeric functions. See Numeric.Backprop.Op for more information.