Skip to content

Lift

Given a vector field   , its lift is the Hamiltonian

It is a purely algebraic construction — no differentiation, no AD.

julia
using OptimalControl

From a plain function

julia
X(x) = [x[2], -x[1]]
H = Lift(X)
H([1.0, 2.0], [3.0, 4.0])
2.0

H is an OptimalControl.LiftedHamiltonianFunction — a callable, not a Hamiltonian:

julia
typeof(H)
LiftedHamiltonianFunction{typeof(Main.X), CTBase.Traits.Autonomous, CTBase.Traits.Fixed}

From a typed vector field

Lifting a typed VectorField instead gives back a real Hamiltonian:

julia
XV = VectorField(x -> [x[2], -x[1]])
HV = Lift(XV)
HV isa AbstractHamiltonian
true
julia
HV([1.0, 2.0], [3.0, 4.0])
2.0

Non-autonomous and variable forms

julia
Xt(t, x) = [t * x[2], -x[1]]
Ht = Lift(Xt; is_autonomous=false)
Ht(2.0, [1.0, 2.0], [3.0, 4.0])   # H(t, x, p)
8.0
julia
Xv(x, v) = [x[2] + v, -x[1]]
Hv = Lift(Xv; is_variable=true)
Hv([1.0, 2.0], [3.0, 4.0], 1.0)   # H(x, p, v)
5.0

Which one do I get

You liftYou getSignature
f::FunctionOptimalControl.LiftedHamiltonianFunction (<: Function)h(x,p), h(t,x,p), h(x,p,v), h(t,x,p,v) depending on the keywords
X::AbstractVectorFieldHamiltoniansame call signatures, inherited from X's own traits

Lift(f::Function) is not an AbstractHamiltonian

OptimalControl.LiftedHamiltonianFunction <: Function only, not <: AbstractHamiltonian — a change from v2.0, where the plain-function form and the typed-VectorField form both produced the same kind of object. Any isa/<: test against the old hierarchy is now quietly wrong:

julia
H = Lift(X)          # X::Function
H isa AbstractHamiltonian
false

Only the plain-Function overload changed — Lift(X::AbstractVectorField) still returns a Hamiltonian, confirmed above. See Migration for the full list of silent v2.0 → v2.1 semantics changes.

What you can do with it

Feed a lift straight into Poisson (see The bridge identity), or into Flow to integrate the associated Hamiltonian system — see From Hamiltonians and vector fields.

A trap to know about

Lifting a HamiltonianVectorField doesn't work — it already lives on the cotangent space, so there's nothing left to lift:

julia
julia> hvf = HamiltonianVectorField((x, p) -> (p, -x));

julia> Lift(hvf)
NotImplemented  top-level scope, REPL[2]:2

│  ad on AbstractHamiltonianVectorField is not implemented (signature is (x,p), not (x))

│  Context  ad on AbstractVectorField
│  Hint     Use ad on a plain VectorField
└─

The message talks about ad, not Lift — both operations share the same internal guard against HamiltonianVectorField operands, so the wording doesn't adapt to which one triggered it. Harmless, but don't be thrown by it: the operation that actually failed is Lift.

See also