4 Registering primitives
This module allows new primitives to be registered, by specifying for each a modified function definition, augmenting the primal computation with a backpropagator.
A transformation is associated with a particular binding (and not, for instance, a procedure value or a symbolic name).
An existing binding can be registered as a primitive with register-primitive!, and a binding can be imported into a module as a newly-registered primitive with require/backprop or require/primal+backprop.
Once a primitive is registered, the augmented definition is available from any other module in the namespace where that binding is available (that is, registering a primitive is an effect visible across module boundaries).
syntax
(register-primitive! prim-id prim-augmented-def)
Once registered, the augmented definition is available from any other module in the same namespace that uses the binding.
prim-id is an identifier that binds a procedure.
prim-augmented-def is the syntax to use for the augmented definition of prim-id in differentiated code. It should evaluate to a function that takes the same number of arguments as prim-id and returns a proc-result struct, whose primal field contains the result of evaluting prim-id at the arguments given, and whose backprop field contains a backpropagator function.
The function can share work between the primal and backpropagator.
Associating derivative information with a function confers no overhead to calls to the undifferentiated function.
syntax
(require/primal+backprop require-spec [id prim-augmented-def] ...)
> (require/primal+backprop racket/base [exp (lambda (x) (let ([exp-x (exp x)]) (proc-result exp-x (lambda (Aw Abox) (list '() (scale Aw exp-x))))))])
syntax
(require/backprop require-spec [(prim-id args ... . rest-args) backprop-def] ...)