Trajectories
The primal part of a solution is read through accessors that return callables of time (interpolated from the stored samples) for the trajectories, and plain values for the variable and the objective.
| Accessor | Returns | Shape |
|---|---|---|
state | x(t) | callable → |
control | u(t) | callable → |
costate | p(t) | callable → |
variable | v | value |
objective | optimal cost | scalar |
using CTModels
pre = CTModels.PreModel()
CTModels.variable!(pre, 1, "v")
CTModels.time!(pre; t0=0.0, tf=1.0)
CTModels.state!(pre, 2)
CTModels.control!(pre, 1)
CTModels.dynamics!(pre, (r, t, x, u, v) -> (r[1] = x[2]; r[2] = u[1]; nothing))
CTModels.objective!(pre, :min; lagrange=(t, x, u, v) -> 0.5u[1]^2)
CTModels.time_dependence!(pre; autonomous=true)
ocp = CTModels.build(pre)
N = 101
T = collect(range(0.0, 1.0; length=N))
X = hcat(cos.(T), -sin.(T))
U = reshape(-cos.(T), N, 1)
P = zeros(N, 2)
v = [2.0] # the optimisation variable value
sol = CTModels.build_solution(ocp, T, X, U, v, P;
objective=0.5, iterations=10, constraints_violation=1e-9,
message="ok", status=:optimal, successful=true)Reading trajectories
The trajectories are callables, so they can be evaluated at any time in the interval, not only at grid points — the samples are interpolated:
x = CTModels.state(sol)
u = CTModels.control(sol)
p = CTModels.costate(sol)julia> x(0.0)
2-element Vector{Float64}:
1.0
-0.0
julia> x(0.123)
2-element Vector{Float64}:
0.9924346132121428
-0.12268878788815202
julia> u(0.5)
-0.8775825618903728
julia> p(1.0)
2-element Vector{Float64}:
0.0
0.0julia> CTModels.variable(sol)
2.0
julia> CTModels.objective(sol)
0.5Interpolation of the control
State and costate are interpolated linearly. The control may instead be piecewise constant — typical of direct collocation — selected by the control_interpolation keyword of build_solution (:linear or :constant). The choice is recorded and read back with control_interpolation:
sol_const = CTModels.build_solution(ocp, T, X, U, v, P;
objective=0.5, iterations=10, constraints_violation=1e-9,
message="ok", status=:optimal, successful=true,
control_interpolation=:constant)julia> CTModels.control_interpolation(sol)
:constant
julia> CTModels.control_interpolation(sol_const)
:constantBecause the interpolation kind is a stored field — not baked into an opaque closure — downstream code (plotting, serialization) can branch on it explicitly. The Duals & diagnostics page covers the remaining accessors.