From Hamiltonians
The constructors on this page build a flow without an optimal control problem: from a Hamiltonian, a Hamiltonian vector field, a pseudo-Hamiltonian and a control law, or a plain vector field. They are the building blocks From an OCP is made of, and they are useful when you have worked out the Hamiltonian yourself.
using OptimalControl
using OrdinaryDiffEqTsit5Extremals
We take the energy-minimal double integrator again:
maximised by
are the extremals. From the initial costate
t0, tf = 0, 1
x0 = [-1, 0]
p0 = [12, 6]From a Hamiltonian
Give
H(x, p) = p[1] * x[2] + p[2]^2 / 2
f_h = Flow(Hamiltonian(H))
xf, pf = f_h(t0, x0, p0, tf)([-4.158277980903794e-16, 5.038973481527467e-15], [12.0, -5.999999999999998])A Hamiltonian is H(x, p) by default. If it depends on time, write H(t, x, p) and declare it with Hamiltonian(H; is_autonomous=false); if it depends on a variable, H(x, p, v) with is_variable=true. Without the declaration, the call fails with a MethodError.
From a Hamiltonian vector field
Give
Hv(x, p) = ([x[2], p[2]], [0, -p[1]])
f_hv = Flow(HamiltonianVectorField(Hv))
f_hv(t0, x0, p0, tf)([-4.158277980903794e-16, 5.038973481527467e-15], [12.0, -5.999999999999998])From a pseudo-Hamiltonian and a control law
Give
Hp(x, p, u) = p[1] * x[2] + p[2] * u - u^2 / 2
law(x, p) = p[2]
f_hp = Flow(PseudoHamiltonian(Hp), DynClosedLoop(law))
f_hp(t0, x0, p0, tf)([-4.158277980903794e-16, 5.038973481527467e-15], [12.0, -5.999999999999998])hamiltonian_type=:partial freezes the control instead of differentiating through the law (see Total or partial Hamiltonian).
From a pseudo-Hamiltonian vector field and a control law
Give
Hpv(x, p, u) = ([x[2], u], [0, -p[1]])
f_hpv = Flow(PseudoHamiltonianVectorField(Hpv), DynClosedLoop(law))
f_hpv(t0, x0, p0, tf)([-4.158277980903794e-16, 5.038973481527467e-15], [12.0, -5.999999999999998])Here the partial derivatives are given, so there is nothing to differentiate through the law, and hamiltonian_type is not an option of this flow. Passing it raises an error, which reads as an unknown integrator option:
julia> Flow(PseudoHamiltonianVectorField(Hpv), DynClosedLoop(law); hamiltonian_type=:partial)
IncorrectArgument → top-level scope, REPL[1]:2
│
│ Unknown option provided
│
│ Got option :hamiltonian_type in method (:sciml, :cpu)
│ Expected valid option name for one of the strategies
│
│ Context route_options - unknown option validation
│ Hint Did you mean?
│ - :alg (aliases: algorithm, solver) [distance: 11]
│ - :adaptive (aliases: adaptive_step, adaptive_stepping) [distance: 11]
│ - :d_discontinuities [distance: 12]
│ If you're confident this option exists for a specific strategy, use bypass() to skip validation:
│ custom_opt = route_to(<strategy_id>=bypass(<value>))
└─From a vector field
A flow of the state alone, is_autonomous=false. With the optimal control
f_x = Flow(VectorField((t, x) -> [x[2], 6 - 12t]; is_autonomous=false))
f_x(t0, x0, tf)2-element Vector{Float64}:
-2.7259067227910837e-15
-9.06882116070656e-16To simulate a control system under a given control, open-loop or feedback, see Simulation.
From a SciML problem
An ODEFunction or an ODEProblem of the SciML ecosystem can be used directly, with OrdinaryDiffEqTsit5 alone. The parameter p of the SciML function f!(dx, x, p, t) is the variable of the flow, passed with the keyword variable. Here
rhs!(dx, x, p, t) = (dx[1] = -p * x[1]; nothing)
f_ode = Flow(ODEFunction(rhs!))
f_ode(0, [1.0], 1; variable=2) # exp(-2)0.13533528340900092So the keyword is required even when the function does not use p: pass variable=nothing. Without it, the call raises an error:
julia> f_ode(0, [1.0], 1)
PreconditionError → top-level scope, REPL[1]:2
│
│ variable not provided for a NonFixed flow
│
│ Reason flow depends on an extra variable parameter but none was given
│
│ Context call — NonFixed flow with missing variable
│ Hint Pass `variable=v` when calling the flow
└─An ODEProblem works the same way:
f_prob = Flow(ODEProblem(rhs!, [1.0], (0.0, 1.0), 2.0))
f_prob(0, [1.0], 1; variable=2)1-element Vector{Float64}:
0.13533528340900092Summary
| Constructor | You give | Automatic differentiation | Point call returns |
|---|---|---|---|
Flow(Hamiltonian(H)) | yes | (x(tf), p(tf)) | |
Flow(HamiltonianVectorField(Hv)) | no | (x(tf), p(tf)) | |
Flow(PseudoHamiltonian(Hp), law) | yes | (x(tf), p(tf)) | |
Flow(PseudoHamiltonianVectorField(Hpv), law) | no | (x(tf), p(tf)) | |
Flow(VectorField(f)) | no | x(tf) | |
Flow(ODEFunction(f!)), Flow(ODEProblem(…)) | a SciML function or problem | no | x(tf) |
Hamiltonian, HamiltonianVectorField, PseudoHamiltonian, PseudoHamiltonianVectorField and VectorField take the keywords is_autonomous=false and is_variable=true for a function of time or of a variable. Every Flow takes the integrator options of Flows overview. Called on a time span, f((t0, tf), x0, p0) returns a trajectory: see Simulation.
See also
From an OCP: the same flows, with the problem supplying the pseudo-Hamiltonian; you only write the law.
Accessors: read the Hamiltonian, its vector field and the law back from a flow.
Lift: the Hamiltonian lift of a vector field, and the other tools of the geometry section.