Skip to content

Choosing an AD backend

julia
using OptimalControl

Everything here is AD-backed, except Lift

ad, Poisson, ∂ₜ, and @Lie all differentiate under the hood; Lift is purely algebraic and never touches a backend at all.

The default

julia
dg_ad_backend()
DifferentiationInterface{CPU} (instance, id=:di)
└─ ad_backend = AutoForwardDiff  [computed]
Tip: use describe(DifferentiationInterface{CPU}) to see all available options.

DifferentiationInterface{CPU} over ForwardDiff, the same default used throughout CTBase/CTLie.

Reading the current backend

dg_ad_backend() (above) always returns the backend that will be used when no ad_backend= keyword is given.

Changing it globally

julia
import CTBase: Differentiation

dg_ad_backend!(Differentiation.DifferentiationInterface())
dg_ad_backend()
DifferentiationInterface{CPU} (instance, id=:di)
└─ ad_backend = AutoForwardDiff  [computed]
Tip: use describe(DifferentiationInterface{CPU}) to see all available options.

Changing it for one call

Every operation in this section accepts its own ad_backend=, overriding the global setting just for that call:

julia
X(x) = [x[2], -x[1]]
f(x) = x[1]^2 + x[2]^2

ad(X, f; ad_backend=Differentiation.DifferentiationInterface())([1.0, 2.0])
0.0

GPU

A GPU-parameterized backend is constructed the same way, with the GPU strategy instead of CPU:

julia
import CTBase: Differentiation, Strategies

dg_ad_backend!(Differentiation.DifferentiationInterface{Strategies.GPU}())

This block is not executed on this page — no CUDA-capable GPU is available in this development environment or in CI, the same caveat as GPU on the solve side.

Introspection

julia
describe(:di)
DifferentiationInterface (strategy)
├─ id: :di
├─ hierarchy: DifferentiationInterface → AbstractADBackend → AbstractStrategy
├─ description: AD backend wrapping DifferentiationInterface.jl backends (e.g., AutoForwardDiff).
├─ family: AbstractADBackend
├─ default parameter: CPU
├─ parameters: CPU, GPU

├─ computed options for CPU:
│  └─ ad_backend (backend, ad)::ADTypes.AbstractADType (default: AutoForwardDiff() [computed])
│     description: DifferentiationInterface.jl backend (e.g. AutoForwardDiff() on CPU).

└─ computed options for GPU:
   └─ ad_backend (backend, ad)::ADTypes.AbstractADType (default: ADTypes.AutoMooncake() [computed])
      description: DifferentiationInterface.jl backend for GPU execution. Default: AutoMooncake(), validated end-to-end on CuArray including through a mutating in-place RHS. AutoForwardDiff() does not work on GPU (scalar-indexes a CuArray). AutoZygote() can be selected explicitly; it was found unreliable in some device call contexts (an unexpected KernelException was observed on a non-mutating call).

If nothing works

If DifferentiationInterface (and a concrete AD package, such as ForwardDiff) isn't loaded, the extension that actually performs the differentiation never arms, and calling ad, Poisson, ∂ₜ, or @Lie fails. OptimalControl loads DifferentiationInterface and ForwardDiff itself, so this only bites if you're using CTLie standalone.

See also

  • Overview — where each operation sits relative to AD.

  • GPU — the same CPU/GPU strategy split on the solve side.

  • Flows overview — the method=:cpu/:gpu construction-time keyword on Flow.