Overview

Function Result Colouring Derivative mode
jacobian dense matrix optional forward or reverse
jacobian_sparse CSC sparse matrix required forward
jacobian_banded band matrix required forward

All three take an inner function f mapping a double vector to a double vector, the point x and an optional data argument passed to f unchanged.

Example function

A 1-D diffusion term: every output depends only on its neighbours, so the Jacobian is tridiagonal.

f <- function(x) {
  argtypes(x |> type(vec(double)))
  diffusion <- function(y) {
    argtypes(y |> type(vec(double)))
    returntype(vec(double))
    n <- length(y)
    out <- numeric(n)
    out[[1L]] <- y[[2L]] - y[[1L]]
    for (i in 2L:(n - 1L)) {
      out[[i]] <- y[[i - 1L]] - 2 * y[[i]] * y[[i]] + y[[i + 1L]]
    }
    out[[n]] <- y[[n - 1L]] - y[[n]]
    return(out)
  }
  return(jacobian(diffusion, x))
}
fcpp <- ast2ast::translate(f, derivative = "forward")
fcpp(c(0.3, 0.7, 1.1, 0.2, 0.9, 1.4))

Dense

jacobian(f, x) returns the m-by-n matrix. In forward mode it needs one sweep per column, in reverse mode one sweep per row.

Colouring

Columns that share no row can be computed in one forward sweep. For the tridiagonal example three sweeps are enough, independent of length(x).

return(jacobian(diffusion, x, colouring = "static", n = 6L))
return(jacobian(diffusion, x, colouring = "runtime"))

Colouring requires derivative = "forward".

Sparse and banded

jacobian_sparse and jacobian_banded compute and store only the entries of the sparsity pattern. Thus, colouring is required:

J <- jacobian_sparse(diffusion, x, colouring = "static", n = 6L)
J <- jacobian_banded(diffusion, x, colouring = "runtime")

Without it the translation fails:

jacobian_sparse needs colouring = "static", n = <integer literal> or colouring = "runtime"

Layouts

Both are stored as in SUNDIALS, so they can be handed to its solvers directly.

  • jacobian_sparse: CSC, 0-based (SUNMatrix_Sparse). Fields J$m, J$n, J$indexptrs, J$indexvals, J$data.
  • jacobian_banded: square, LAPACK band layout (SUNMatrix_Band). Fields J$n, J$mu, J$ml, J$ldim, J$data; A(i, j) is at data[j * ldim + mu + i - j] (0-based), ldim = mu + ml + 1.

For the tridiagonal 6 x 6 example: mu = ml = 1, ldim = 3, length(J$data) = 18.

Returned to R

Both are returned to R as a Matrix::dgCMatrix (package Matrix has to be installed):

# f from above with return(jacobian_sparse(diffusion, x, colouring = "runtime"))
J <- fcpp(c(0.3, 0.7, 1.1, 0.2, 0.9, 1.4))
class(J)
# [1] "dgCMatrix"
as.matrix(J)