From Hamiltonians and vector fields
Every constructor below builds a flow without an OCP — the building blocks From an OCP is itself assembled from. One section per constructor.
using OptimalControl
using OrdinaryDiffEqTsit5From a vector field
The plain ODE case, f(t, x) if non-autonomous — see below):
f = Flow(VectorField(x -> -x))
f(0.0, 1.0, 1.0) # (t0, x0, tf) → x(tf), exp(-1)0.36787944125650673From a Hamiltonian
is obtained by automatic differentiation:
h(x, p) = 0.5 * (x^2 + p^2)
fh = Flow(Hamiltonian(h))
fh(0.0, 1.0, 0.0, 1.0) # (t0, x0, p0, tf) → (x(tf), p(tf))(0.5403023057535162, -0.8414709847374632)Hamiltonian defaults to the natural 2-arg autonomous form h(x, p). A 3-arg h(t, x, p) silently mis-dispatches unless declared explicitly non-autonomous — see below.
From a Hamiltonian vector field
Same system, but
hvf(x, p) = (p, -x)
fhvf = Flow(HamiltonianVectorField(hvf))
fhvf(0.0, 1.0, 0.0, 1.0)(0.5403023057535162, -0.8414709847374632)From a pseudo-Hamiltonian and a control law
plus a law Flow substitutes and differentiates through it (AD), same as From an OCP's default hamiltonian_type=:total:
htilde(x, p, u) = p * u - 0.5 * u^2
law(x, p) = p # the maximiser of h̃ above
fph = Flow(PseudoHamiltonian(htilde), DynClosedLoop(law))
fph(0.0, 1.0, 0.0, 1.0)(1.0, 0.0)From a pseudo-Hamiltonian vector field and a control law
Same idea, vector-field form: hamiltonian_type= here — there's no pseudo-Hamiltonian scalar to differentiate two different ways, so passing it is rejected:
htildevf(x, p, u) = (u, 0.0)
fphvf = Flow(PseudoHamiltonianVectorField(htildevf), DynClosedLoop(law))
fphvf(0.0, 1.0, 0.0, 1.0)(1.0, 0.0)julia> Flow(
PseudoHamiltonianVectorField(htildevf),
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 SciML problem
An ODEFunction/ODEProblem from the SciML ecosystem works directly — reachable through OrdinaryDiffEqTsit5 alone, nothing extra to load:
rhs!(dx, x, p, t) = (dx[1] = -x[1]; nothing)
f_fun = Flow(ODEFunction(rhs!))SciML-backed flows are always NonFixed — even with no real free variable, a call needs variable=nothing:
julia> f_fun(0.0, [1.0], 1.0)
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
└─f_fun(0.0, [1.0], 1.0; variable=nothing)0.36787944125650673prob = ODEProblem(rhs!, [1.0], (0.0, 1.0))
f_prob = Flow(prob)
typeof(f_prob)CTFlowsSciMLFlows.SciMLProblemFlow{ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, SciMLBase.AutoSpecialize, typeof(Main.rhs!), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, Nothing, Nothing}, Base.Pairs{Symbol, Union{}, Nothing, @NamedTuple{}}, SciMLBase.StandardODEProblem}, SciML{CPU, CTBase.Strategies.StrategyOptions{@NamedTuple{internalnorm::CTBase.Options.OptionValue{typeof(CTSolvers.Integrators.real_norm)}, alg::CTBase.Options.OptionValue{Tsit5{typeof(OrdinaryDiffEqCore.trivial_limiter!), typeof(OrdinaryDiffEqCore.trivial_limiter!), FastBroadcast.Serial}}, reltol::CTBase.Options.OptionValue{Float64}, save_everystep::CTBase.Options.OptionValue{Symbol}, abstol::CTBase.Options.OptionValue{Float64}, save_start::CTBase.Options.OptionValue{Symbol}, dense::CTBase.Options.OptionValue{Symbol}}}, Dict{Symbol, Any}, Dict{Symbol, Any}}}Non-autonomous and variable-dependent forms
is_autonomous= and is_variable= (not autonomous=, an old spelling that no longer exists) declare the extra arguments a function needs:
g(t, x) = -t * x
fg = Flow(VectorField(g; is_autonomous=false))
fg(0.0, 1.0, 1.0)0.6065306595385606The stale form fails two ways at once — there is no Flow(::Function) at all, and even fixed to the right type, the keyword itself changed name:
julia> VectorField(g; autonomous=false)
MethodError: no method matching VectorField(::typeof(Main.g); autonomous::Bool)
This method does not support all of the given keyword arguments (and may not support any).
Closest candidates are:
VectorField(::Any; is_autonomous, is_variable, is_inplace) got unsupported keyword argument "autonomous"
@ CTBase ~/.julia/packages/CTBase/clz4y/src/Data/vector_field.jl:183
VectorField(::Any, !Matched::Type{TD}, !Matched::Type{VD}, !Matched::Type{MD}) where {TD<:CTBase.Traits.TimeDependence, VD<:CTBase.Traits.VariableDependence, MD<:CTBase.Traits.AbstractMutabilityTrait} got unsupported keyword argument "autonomous"
@ CTBase ~/.julia/packages/CTBase/clz4y/src/Data/vector_field.jl:203is_inplace= similarly declares an in-place (mutating, f!(dx, x)) vs out-of-place (f(x) -> dx) function — inferred automatically in most cases, settable explicitly when it isn't.
Summary table
| Constructor | Uses AD? | Returns |
|---|---|---|
Flow(VectorField(f)) | no | StateFlow |
Flow(Hamiltonian(h)) | yes | HamiltonianFlow |
Flow(HamiltonianVectorField(hvf)) | no | HamiltonianFlow |
Flow(PseudoHamiltonian(h̃), law) | yes | HamiltonianFlow |
Flow(PseudoHamiltonianVectorField(h̃vf), law) | no | HamiltonianFlow |
Flow(ODEFunction(...)) / Flow(ODEProblem(...)) | no | StateFlow / SciMLProblemFlow |
See also
From an OCP — the same constructors, specialised: an OCP supplies
automatically, you only supply the law. What you can get back from a flow — which accessor works on which of the flows built here.
Lift and Poisson brackets — the differential-geometric layer these constructors sit on top of.