Numerical Differentiation
The Classic API¶
Consider the following use case: the computation of the (approximate) derivative of a function with Newton's difference quotient:
def d(f, x, h=1e-13):
dq = (f(x + h) - f(x)) / h
return dq
This function d
would typically be used like this:
>>> from math import sin
>>> d(sin, x=0.0)
0.9999999999999983
Now, an improved version of d
would compute an error estimate error
def d(f, x, h=1e-7):
dq = (f(x + h) - f(x)) / h
dq2 = (f(x + 0.5*h) - f(x)) / (0.5*h)
error = 2 * (dq - dq2)
return dq, error
whose usage would be
>>> d(sin, x=0.0)
(0.9999999999999983, -2.4424906541753444e-15)
While this is a sound idea to provide an error estimate, we have broken the original API. Now, in the simplest and more common use case, when we do not care about the error, the code is more complex than it should be:
>>> dq, _ = d(sin, x=0.0)
>>> dq
0.9999999999999983
We have used the underscore instead of error
for the second
returned value: in the classic approach, this is the standard
way to signal this value will be ignored.
With Wish¶
To get a decent API in both cases, we may use wish and specify in the function signature an extra argument that lists the value(s) returned by default
def d(f, x, h=1e-7, returns="dq"):
dq = (f(x + h) - f(x)) / h
dq2 = (f(x + 0.5*h) - f(x)) / (0.5*h)
error = 2 * (dq - dq2)
return wish.grant(returns)
With this pattern, the common case is still simple
>>> d(sin, x=0.0)
0.9999999999999983
but the more complex case is possible and the intent is explicit:
>>> d(sin, x=0.0, returns="dq, error")
(0.9999999999999983, -2.4424906541753444e-15)