Skip to content

Lie derivative and Lie bracket

ad(X, foo) is one function with two meanings, chosen by what foo returns.

julia
using OptimalControl

One function, two meanings

  • foo scalar-valued → the Lie derivative of foo along X.

  • foo vector-valued → the Lie bracket of X and foo.

Lie derivative

For a vector field and a scalar function ,

julia
X(x) = [x[2], -x[1]]
f(x) = x[1]^2 + x[2]^2   # energy of the harmonic oscillator

Xf = ad(X, f)
Xf([1.0, 2.0])
0.0

Energy is conserved along 's flow, so the Lie derivative vanishes everywhere.

Lie bracket

For two vector fields ,

where denotes the Jacobian.

julia
Y(x) = [x[1]^2, x[2]^2]
Z = ad(X, Y)
Z([1.0, 2.0])
2-element Vector{Float64}:
  0.0
 -3.0

Typed operands

Wrapping the inputs as VectorFields makes ad return a VectorField too — so it nests:

julia
XV = VectorField(x -> [x[2], -x[1]])
YV = VectorField(x -> [x[1]^2, x[2]^2])

ZV = ad(XV, YV)
typeof(ZV)
VectorField{CTLie.Ad{VectorField{Main.var"#2#3", CTBase.Traits.Autonomous, CTBase.Traits.Fixed, CTBase.Traits.OutOfPlace}, VectorField{Main.var"#5#6", CTBase.Traits.Autonomous, CTBase.Traits.Fixed, CTBase.Traits.OutOfPlace}, CTBase.Differentiation.DifferentiationInterface{CPU, CTBase.Strategies.StrategyOptions{@NamedTuple{ad_backend::CTBase.Options.OptionValue{AutoForwardDiff{nothing, Nothing}}}}}, CTBase.Traits.Autonomous, CTBase.Traits.Fixed}, CTBase.Traits.Autonomous, CTBase.Traits.Fixed, CTBase.Traits.OutOfPlace}
julia
ZZV = ad(ZV, YV)   # ad(ad(X, Y), Y) — a second-order bracket
ZZV([1.0, 2.0])
2-element Vector{Float64}:
  4.0
 -2.0

Non-autonomous and variable fields

is_autonomous= and is_variable= work exactly as on Lift; both operands must agree, or you get a PreconditionError naming both traits:

julia
julia> Xa = VectorField(x -> [x[2], -x[1]]);

julia> Xb = VectorField((t, x) -> [t + x[2], -x[1]]; is_autonomous=false);

julia> ad(Xa, Xb)
PreconditionError  top-level scope, REPL[3]:2

│  ad: TD/VD mismatch between X and Y

│  Reason   X: CTBase.Traits.Autonomous/CTBase.Traits.Fixed  Y: CTBase.Traits.NonAutonomous/CTBase.Traits.Fixed — both arguments must share the same TimeDependence and VariableDependence

│  Context  ad on AbstractVectorField
│  Hint     Ensure both vector fields have the same time and variable dependence traits
└─

Partial time derivative

∂ₜ computes for a non-autonomous f, VectorField, or Hamiltonian — its result is always NonAutonomous, regardless of whether the input already was:

julia
g(t, x) = t^2 + x[1] * x[2]
dg = ∂ₜ(g)
dg(3.0, [1.0, 2.0])   # ∂g/∂t = 2t = 6
6.0
julia
dXV = ∂ₜ(XV)   # XV::VectorField, defined above — still comes back NonAutonomous
typeof(dXV)
VectorField{CTLie.TimeDeriv_VF{VectorField{Main.var"#2#3", CTBase.Traits.Autonomous, CTBase.Traits.Fixed, CTBase.Traits.OutOfPlace}, CTBase.Differentiation.DifferentiationInterface{CPU, CTBase.Strategies.StrategyOptions{@NamedTuple{ad_backend::CTBase.Options.OptionValue{AutoForwardDiff{nothing, Nothing}}}}}, CTBase.Traits.Autonomous, CTBase.Traits.Fixed}, CTBase.Traits.NonAutonomous, CTBase.Traits.Fixed, CTBase.Traits.OutOfPlace}

Errors you will meet

SituationException
ad given an AbstractHamiltonianIncorrectArgument — points at Poisson
time/variable dependence mismatch between operandsPreconditionError
an InPlace operandNotImplemented
a HamiltonianVectorField passed to adNotImplemented
julia
julia> H = Hamiltonian((x, p) -> x[1] + p[1]);

julia> ad(H, x -> x)
IncorrectArgument  top-level scope, REPL[2]:2

│  ad is not defined for AbstractHamiltonian operands

│  Context  ad on AbstractHamiltonian
│  Hint     Use Poisson(H, G) for the Poisson bracket of Hamiltonians
└─

ad also refuses an InPlace operand:

julia
julia> Xip = VectorField((dx, x) -> (dx .= [x[2], -x[1]]); is_inplace=true);

julia> ad(Xip, x -> x[1]^2)
NotImplemented  top-level scope, REPL[2]:2

│  ad is not implemented for InPlace vector fields

│  Method   Use an OutOfPlace VectorField

│  Context  ad on AbstractVectorField
│  Hint     Reconstruct the VectorField without in-place flag
└─

Coming from v2.0

v2.0v2.1
Lie(X, f)ad(X, f)
X ⋅ fad(X, f)no operator replacement, is gone

See Migrating to v2.1 for the throwing shim on Lie.

See also

  • Poisson bracket — the Hamiltonian-side counterpart, linked by Poisson(Lift(X), Lift(Y)) ≈ Lift(ad(X, Y)).

  • The @Lie macroad/Poisson with bracket notation.