Skip to content

Lie derivative and Lie bracket

The single function ad ("adjoint action") computes two related objects, dispatching on the output of its second argument:

  • if the second argument is scalar-valued, ad returns the Lie derivative;

  • if it is vector-valued, ad returns the Lie bracket.

Both are computed by automatic differentiation, so the AD backend extension must be loaded.

Lie derivative

The Lie derivative of a scalar function along a vector field is the directional derivative of in the direction :

It measures the infinitesimal rate of change of along the flow of .

Lie bracket

The Lie bracket of two vector fields and is itself a vector field. With the convention used here,

where and are the Jacobian matrices. The bracket is antisymmetric,   , and it vanishes when the flows of and commute. Iterated brackets appear throughout geometric control (e.g. in the analysis of singular extremals).

julia
using CTLie                        # Lift, ad, Poisson, ∂ₜ, @Lie
using CTBase: Data                 # VectorField, Hamiltonian, … (accessed as Data.VectorField, etc.)
using CTBase: Traits               # trait types (Autonomous, Variable, OutOfPlace, …)
using DifferentiationInterface: DifferentiationInterface  # activates the AD backend extension

On plain functions

For Julia Functions the traits come from the is_autonomous / is_variable keywords.

Lie derivative — energy along a rotation

The rotation field    preserves the squared norm   , so   :

julia
X = x -> [x[2], -x[1]]
f = x -> x[1]^2 + x[2]^2
L = ad(X, f)
L([1.0, 2.0])
0.0

Lie bracket — two autonomous fields

julia
F = x -> [x[2], 2x[1]]
G = x -> [3x[2], -x[1]]
B = ad(F, G)
B([1.0, 2.0])
2-element Vector{Float64}:
   7.0
 -14.0

Non-autonomous — (t, x)

julia
Ft = (t, x) -> [t + x[2], -2x[1]]
Gt = (t, x) -> [t + 3x[2], -x[1]]
Bt = ad(Ft, Gt; is_autonomous=false)
Bt(1.0, [1.0, 2.0])
2-element Vector{Float64}:
 -5.0
 11.0

Variable-dependent — (x, v)

julia
Fv = (x, v) -> [x[2] + v, 2x[1]]
Gv = (x, v) -> [3x[2], v - x[1]]
Bv = ad(Fv, Gv; is_variable=true)
Bv([1.0, 2.0], 1.0)
2-element Vector{Float64}:
   6.0
 -15.0

Non-autonomous and variable-dependent — (t, x, v)

julia
Ftv = (t, x, v) -> [t + x[2] + v, -2x[1] - v]
Gtv = (t, x, v) -> [t + 3x[2] + v, -x[1] - v]
Btv = ad(Ftv, Gtv; is_autonomous=false, is_variable=true)
Btv(1.0, [1.0, 2.0], 1.0)
2-element Vector{Float64}:
 -7.0
 12.0

On typed vector fields

When both arguments are typed VectorFields, ad returns a typed VectorField whose traits are inherited from the operands. The two fields must share the same time- and variable-dependence (see Limitations & configuration).

julia
X = Data.VectorField(x -> [x[2], 2x[1]]; is_autonomous=true, is_variable=false)
Y = Data.VectorField(x -> [3x[2], -x[1]]; is_autonomous=true, is_variable=false)
Z = ad(X, Y)
VectorField: autonomous, fixed (no variable), out-of-place
  natural call: f(x)
  uniform call: f(t, x, v)
julia
Z([1.0, 2.0])
2-element Vector{Float64}:
   7.0
 -14.0

The result being itself a VectorField, brackets nest naturally:

julia
Z2 = ad(ad(X, Y), Y)
Z2([1.0, 2.0])
2-element Vector{Float64}:
 -84.0
 -14.0

Lie derivative with a typed field

A typed VectorField against a scalar Function gives the Lie derivative, returned as a plain Function:

julia
X = Data.VectorField(x -> [x[2], -x[1]]; is_autonomous=true)
g = x -> x[1]^2 + x[2]^2
ad(X, g)([1.0, 2.0])
0.0

Antisymmetry, numerically

julia
F = Data.VectorField(x -> [x[2]^2, -2x[1]*x[2]]; is_autonomous=true)
G = Data.VectorField(x -> [x[1]*(1 + x[2]), 3x[2]^3]; is_autonomous=true)
x0 = [1.0, 2.0]
ad(F, G)(x0)  -ad(G, F)(x0)
true

See also