Note
Go to the end to download the full example code.
Forward-mode automatic differentiation via dual numbers#
Dual numbers compute a function’s value and its exact derivative
together, with no finite-difference step-size tradeoff between
truncation error (large h) and floating-point cancellation (small
h).
import math
import numpy as np
from mathematicskit.calculus.systems.dual_numbers import derivative
from mathematicskit.calculus.systems.finite_differences import central_difference
Exact derivative, compared against central differences at various h#
f_dual = lambda x: (x * x).sin() # d/dx sin(x^2) = 2x cos(x^2)
f_plain = lambda x: math.sin(x * x)
x0 = 1.3
exact = 2.0 * x0 * math.cos(x0**2)
dual_result = derivative(f_dual, x0)
print(f"dual-number derivative: {dual_result:.15f} (exact: {exact:.15f}, error {abs(dual_result - exact):.2e})")
for h in (1e-1, 1e-3, 1e-5, 1e-7, 1e-9, 1e-11):
cd = central_difference(f_plain, x0, h=h)
print(f"central diff h={h:.0e}: {cd:.15f}, error {abs(cd - exact):.2e}")
dual-number derivative: -0.309196080171192 (exact: -0.309196080171192, error 0.00e+00)
central diff h=1e-01: -0.331234137017592, error 2.20e-02
central diff h=1e-03: -0.309198313356795, error 2.23e-06
central diff h=1e-05: -0.309196080394702, error 2.24e-10
central diff h=1e-07: -0.309196080405805, error 2.35e-10
central diff h=1e-09: -0.309196113157384, error 3.30e-08
central diff h=1e-11: -0.309197112358106, error 1.03e-06
Composite functions and the chain rule fall out automatically#
d/dx log(exp(x)/(x+1)) at x=2.0: 0.6666666667
A NumPy sanity check against a nearby closed-form derivative#
numpy_gradient_estimate = np.gradient([f_plain(x0 - 1e-6), f_plain(x0), f_plain(x0 + 1e-6)], 1e-6)[1]
print("close to numpy finite-difference estimate:", bool(np.isclose(dual_result, numpy_gradient_estimate, atol=1e-3)))
close to numpy finite-difference estimate: True
Total running time of the script: (0 minutes 0.001 seconds)