Skip to content

Limitations & configuration

This page collects the constraints of the CTLie operators and the knobs available to configure them.

Limitations

No in-place support

The operators are defined for out-of-place objects only. A field built with is_inplace=true (mutability InPlace) is rejected by ad and ∂ₜ with a CTBase.Exceptions.NotImplemented error. Reconstruct the field out-of-place before taking brackets or time derivatives:

julia
Xip = VectorField(x -> [x[2], -x[1]]; is_inplace=true)
ad(Xip, Xip)        # ❌ NotImplemented — ad is not defined for in-place fields

No Lie operations on a Hamiltonian vector field

A HamiltonianVectorField lives on phase space with signature (x, p), not (x), so it is not a valid operand for the Lie bracket / Lie derivative, nor for the Lift. Both raise CTBase.Exceptions.NotImplemented:

julia
Z = HamiltonianVectorField((x, p) -> [x[1], -p[1]]; is_autonomous=true)
ad(Z, Z)            # ❌ NotImplemented — signature is (x, p), not (x)
Lift(Z)             # ❌ NotImplemented — Z already lives on phase space

Use the underlying plain VectorField instead.

Operands must share traits

ad (on two vector fields) and Poisson (on two Hamiltonians) require their operands to have the same time- and variable-dependence. A mismatch raises CTBase.Exceptions.IncorrectArgument:

julia
Xa = VectorField(x -> [x[2], -x[1]];      is_autonomous=true)
Xt = VectorField((t, x) -> [x[2], -x[1]]; is_autonomous=false)
ad(Xa, Xt)          # ❌ IncorrectArgument — TD/VD mismatch between X and Y

The same rule is enforced by @Lie; see The @Lie macro.

Plain functions default to autonomous & fixed

A bare Julia Function carries no traits, so the operators assume it is autonomous and fixed unless told otherwise via is_autonomous / is_variable (for ad, Poisson, Lift) or the matching keywords of @Lie. When in doubt, wrap the function in a typed VectorField / Hamiltonian so the traits are explicit and checked.

Configuration

AD backend

ad, Poisson and ∂ₜ differentiate through a pluggable backend. The default is built on DifferentiationInterface.jl (with ForwardDiff under the hood) and must be loaded for gradients/derivatives to be available:

julia
import DifferentiationInterface   # activates the CTBaseDifferentiationInterface extension

The global default backend is read and set with dg_ad_backend / dg_ad_backend!:

julia
using ADTypes
dg_ad_backend!(AutoForwardDiff())   # set global default
dg_ad_backend()                     # query it

Every operator also accepts a per-call ad_backend keyword that overrides the global default for that call only — including @Lie via ad_backend=…:

julia
ad(X, Y; ad_backend=AutoForwardDiff())
@Lie [X, Y] ad_backend=AutoForwardDiff()

Code generation by @Lie

The @Lie macro expands to fully qualified calls (CTLie._lie_mac / CTLie._poisson_mac, with the trait types CTBase.Traits.*) so that the generated code resolves at the call site regardless of the caller's module. As a consequence, the macro must be used from a module where both CTLie and CTBase are resolvable — using CTLie plus CTBase importable is enough.

See also