Numerical differentiation: numdiff

### 6.19.6 Numerical differentiation: numdiff

The numdiff command finds numerical approximations to derivatives.

- numdiff takes three mandatory arguments and one optional argument.
  - X=[α0,α1,…,αn], Y=[β0,β1,…,βn], two lists of real numbers, where n≥ 1.
  - x0, a real number.
  - Optionally, m, an integer or a sequence of integers (by default 1).
- numdiff(X,Y,x0 ⟨,m⟩) returns an approximation of the m-th derivative of a function f at x0, or a sequence of derivatives of order given by the sequence m, where f has values given by f(αk)=βk, k=0,1,…,n.

numdiff uses Fornberg’s algorithm described in “Generation of Finite Difference Formulas on Arbitrarily Spaced Grids”, Mathematics of Computation, 51(184):699–706, 1988. The complexity of this algorithm is O(n2m) in both time and space. To avoid numerical instabilities, numdiff operates in exact arithmetic.

Note that α0,α1,…,αn do not have to be equally spaced, but they must be mutually different and input in ascending order. There are no restrictions on the choice of x0.

Examples.

- Let f(x)=sin(x)e−x, x∈[0,1]. Sample this function at the points in

  X=[0,0.1,0.2,0.4,0.5,0.7,0.8,1]

  to approximate f″(1/π).
  Input:

  f:=unapply(sin(x)*exp(-x),x):;
  X:=[0,0.1,0.2,0.4,0.5,0.7,0.8,1]:;
  Y:=apply(f,X):;

  Now you can approximate the second derivative of f at the point x0=1/π.
  Input:

  x0:=1/pi:;
  d:=numdiff(X,Y,x0,2)

  Output:

  −1.38167652799

  Finally, compute the relative error of the obtained approximation.
  Input:
  abs(d-f’’(x0))/abs(f’’(x0))*100
  Output:

  2.82975186496×10−5

  The result is expressed in percentages.
- Use a sequence of values for the parameter m to find a list of approximations of the respective derivatives at x0. This is faster than calling numdiff to approximate one derivative at a time.
  Specifically, approximate the zeroth, first and second derivative of the function

  f(x)=1− |
  1
  1+x2
  | , x∈[0,1],

  at the point x0=γ, where γ≈ 0.57722 is the Euler-Mascheroni constant, by sampling f at 21 equidistant points in the segment [0,1].
  Input:

  f:=unapply(1-1/(1+x^2),x) X:=[(0.05*k)$(k=0..20)]:; Y:=apply(f,X):; numdiff(X,Y,euler_gamma,0,1,2)

  Output:

  ⎡ ⎣ | 0.249912571952,0.649519026356,0.000393517941567 | ⎤ ⎦

  The correct values are f(γ)=0.249912571952, f′(γ)=0.649519026356 and f″(γ)=0.000393517946748.

numdiff can be used for generating custom finite-difference stencils for approximation of derivatives.

Example.
Let X=[−1,0,2,4], Y=[a,b,c,d] and x0=1. To obtain an approximation formula for the second derivative:
Input:

numdiff([-1,0,2,4],[a,b,c,d],1,2)

Output:

2
5
| a− |
b
2
| + |
d
10

The approximation is always a linear combination of elements in Y, regardless of X, x0 and m.

Given the lists X=[α0,α1,…,αn] and Y=[β0,β1,…,βn], the Lagrange polynomial passing through points (αk,βk) where k=0,1,…,n can be obtained by setting m=0 and entering a symbol for x0.

Example.
Let X=[−2,0,1] and Y=[2,4,1]:
Input:

expand(numdiff([-2,0,1],[2,4,1],x,0))

Output:

− |
4
3
| x2− |
5
3
| x+4

The same result is obtained by entering lagrange([-2,0,1],[2,4,1],x).