Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Automatic differentiation

With the autodiff feature, Diffsol can generate the Jacobian-vector and negative-transpose products needed by adjoint sensitivity analysis from ordinary Rust closures. This avoids manually writing rhs_jac, rhs_adjoint, rhs_sens_adjoint, and the initial-condition parameter-adjoint closure.

As in the previous closure examples, consider the logistic equation

$$\frac{dy}{dt} = r y (1 - y/K),$$

with the parameterized initial state

$$y(0) = y_0.$$

The parameter vector is \(p = [r, K, y_0]\), so the right-hand side is

$$f(y, p, t) = p_0 y (1 - y/p_1),$$

and the initial-condition function is

$$y_0(p, t) = p_2.$$

Using the autodiff feature, we can simply supply these equations, and std::autodiff will automatically generate the required gradients for any type of solver in diffsol.

use super::{C, M, T, V};
use diffsol::{OdeBuilder, OdeEquationsImplicitAdjoint, OdeSolverProblem};

pub fn problem_autodiff(
    r: f64,
    k: f64,
    y0: f64,
) -> OdeSolverProblem<impl OdeEquationsImplicitAdjoint<M = M, V = V, T = T, C = C>> {
    OdeBuilder::<M>::new()
        .p([r, k, y0])
        .rhs_autodiff(|x: &V, p: &V, _t, y: &mut V| {
            y[0] = p[0] * x[0] * (1.0 - x[0] / p[1]);
        })
        .init_autodiff(
            |p: &V, _t, y: &mut V| {
                y[0] = p[2];
            },
            1,
        )
        .build()
        .unwrap()
}

Compiling standalone programs

Autodiff requires nightly Rust, the Enzyme backend, and fat LTO. Install the backend once for the nightly toolchain:

rustup +nightly component add enzyme

You can then enable autodiff for a single crate with RUSTFLAGS:

CARGO_PROFILE_RELEASE_LTO=fat \
RUSTFLAGS="-Zautodiff=Enable" \
cargo +nightly run --release

For more details on installing and using std::autodiff please consult the internal and user docs

Compiling workspaces

RUSTFLAGS applies to every crate that Cargo compiles, including dependencies. When compiling the diffsol tests, we found that some of our dependencies can fail to compile with -Zautodiff=Enable. If you run into a similar problem, a rustc wrapper can append the flag only for crates that use autodiff. This repository's wrapper is an example:

#!/bin/sh

rustc="$1"
shift

case "$2" in
    diffsol|logistic_autodiff)
        exec "$rustc" "$@" -Zautodiff=Enable
        ;;
esac

exec "$rustc" "$@"

Use it when building the logistic-autodiff workspace example:

CARGO_PROFILE_RELEASE_LTO=fat \
RUSTC_WRAPPER="$PWD/.github/scripts/autodiff-rustc-wrapper.sh" \
cargo +nightly run -p logistic-autodiff --features autodiff --release