D(f) returns the derivative of f as a new function.
D(f, x) evaluates the derivative at x.
D(D(f)) composes for second-order derivatives, and so on.
D(f, x = NULL, order = 1L)If x is NULL, a function. Otherwise, a numeric
vector, matrix, or array of the appropriate tensor shape.
Each application of D appends one n-dimension to the output
shape, where n = length(x):
For f: R^n -> R: D gives (n) gradient, D^2 gives
(n,n) Hessian, D^3 gives (n,n,n), etc.
For f: R^n -> R^m: D gives (m,n) Jacobian, D^2 gives
(m,n,n), etc.
The composability works because the S4 dispatch for dualr arithmetic
handles nested duals recursively. When D(f) is called with dual
inputs (from an outer D), derivative propagation is automatic.
gradient(), hessian(), and jacobian() are convenience
wrappers: gradient(f, x) is D(f, x), hessian(f, x) is
D(f, x, order = 2), and jacobian(f, x) is D(f, x).
f <- function(x) x[1]^2 * x[2]
D(f, c(3, 4)) # gradient: c(24, 9)
#> [1] 24 9
D(f, c(3, 4), order = 2) # Hessian matrix
#> [,1] [,2]
#> [1,] 8 6
#> [2,] 6 0
g <- function(x) list(x[1] * x[2], x[1]^2)
D(g, c(2, 3)) # 2x2 Jacobian
#> [,1] [,2]
#> [1,] 3 2
#> [2,] 4 0
# Composition: D(D(f)) == D(f, order = 2)
Df <- D(f)
DDf <- D(Df)
DDf(c(3, 4)) # same as D(f, c(3,4), order=2)
#> [,1] [,2]
#> [1,] 8 6
#> [2,] 6 0