(Forward) automatic differentiation from scratch
The purpose of this project is learning about automatic differentiation. There are many Python libraries about this topic which certainly are more efficient and professional than this one. Nevertheless, I wanted to implement it from scratch in order to get a deep understanding of it. And boy! The mathematics behind it are beautiful!
Forward automatic differentiation is made possible by dual numbers. Dual numbers are similar to two-dimensional vectors:
and the idea is that
Dual numbers can be summed, subtracted and multipliced by a scalar following the usual linearity rules:
But they have special rules for multiplication and division. Multiplication, for instance, looks like:
Looks familiar? The first "coordinate" just implements a regular multiplication. While the second "coordinate" describes the rule of product for derivatives.
Regarding divison, the idea is the same:
It is also quite useful to define the power of a dual number and a real number as:
Dual numbers defined like this have an extraordinary property:
If
$q(x)$ is any algebraic function1then
$q((x_0, 1)) = (q(x_0), q'(x_0))$
This means that just evaluating the function with
I implemented these basic properties (and others) in the class Dual
.
But hey, what about non-algebraic functions2? Just as we'll do with an undergraduate math student, we can teach our dual numbers the derivative of some basic functions. Remember those tables you had to learn in high school?
For instance, if we define:
now our dual numbers can also deal with functions involving sines.
I've implemented some of these derivatives in src/primitives.py
.
The list can be easily extended in case of need.
Imagine you want to differentiate the function:
using DualDiff
function f(x)
return sin(x^2)
end
All you have to do is to use the autodifferentiable
decorator:
f_auto = autodifferentiable(f)
Decorated this way, by evaluating
x0 = 3
f_auto(x0)
> Dual(0.4121184852417566, # f(x_0)
-5.466781571308061) # f'(x_0)