The Backprop Typeclass

Most of the functions in this module require a Backprop constraint on values you wish to backpropagate. Even if you manage to get around it for the most part, gradBP (the actual function to compute gradients) requires it on both the inputs and outputs. Let's dig deeper into what it is, and how to define instances.

The Class

The typeclass contains three methods: zero, add, and one:

class Backprop a where
    zero :: a -> a
    add  :: a -> a -> a
    one  :: a -> a

zero is "zero" in the verb sense -- it takes a value and "zeroes out" all components. For a vector, this means returning a zero vector of the same shape. For a list, this means replacing all of the items with zero and returning a list of the same length.

one does the same thing but with one; the point of it is to be one = gradBP id --- the gradient of the identity function for your type.

add is used to add together contributions in gradients, and is usually a component-wise addition.

Instances are provided for most common data types where it makes sense.

Custom Instances

Generics

When defining your own custom types, if your custom type is has a single constructor where all fields are instances of Backprop, then GHC.Generics can be used to write your instances automatically:

data MyType = MkMyType Double [Float] (R 10) (L 20 10) (V.Vector Double)
  deriving Generic

Nice type. Since it has a single constructor and all of its fields are already Backprop instances, we can just write:

instance Backprop MyType

and now your type can be backpropagated!

Common Patterns

For writing "primitive" Backprop instances (types that aren't product types), you can use the provided "helpers" from the Numeric.Backprop.Class module.

If your type is a Num instance, you can use zeroNum, addNum, and oneNum:

instance Backprop Double where
    zero = zeroNum
    add  = addNum
    one  = oneNum

If your type is made using a Functor instance, you can use zeroFunctor and oneFunctor:

instance Backprop a => Backprop (V.Vector a) where
    zero = zeroFunctor
    add  = undefined        -- ??
    one  = oneFunctor

And if your type has an IsList instance, you can use addIsList:

instance Backprop a => Backprop (V.Vector a) where
    zero = zeroFunctor
    add  = addIsList
    one  = oneFunctor

Completely Custom

Completely custom instances are also possible; you just need to implement zero, add, and one as they make sense for your type. Just make sure that you obey the laws for sane behavior!

Moving On

At this point, feel free to jump into the haddocks, or read on further for a list of applications and resources.