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).

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 = VectorField(x -> [x[2], 2x[1]]; is_autonomous=true, is_variable=false)
Y = 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 = 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 = VectorField(x -> [x[2]^2, -2x[1]*x[2]]; is_autonomous=true)
G = 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