Trajectories
A trajectory integration returns a solution object that wraps the raw ODE result and exposes semantic accessors. The CTFlows.Trajectories submodule provides these wrappers.
Solution types
| Type | Produced by | Content |
|---|---|---|
VectorFieldTrajectory | StateFlow trajectory call | state trajectory |
HamiltonianVectorFieldTrajectory | HamiltonianFlow trajectory call | state + costate trajectories |
VectorFieldTrajectory
sol # produced by flow((t0, tf), x0)VectorFieldTrajectory
result: SciMLIntegrationResultAccessors
ts = Trajectories.time_grid(sol) # vector of time points
ts[1], ts[end](0.0, 1.0)x = Trajectories.state(sol) # callable: x(t) → state at time t
x(0.0) # initial state (exact)2-element Vector{Float64}:
1.0
0.0x(0.5) # interpolated at t = 0.52-element Vector{Float64}:
0.6065306592843308
0.0x.(ts) # broadcast over the time grid16-element Vector{Vector{Float64}}:
[1.0, 0.0]
[0.9877640316387758, 0.0]
[0.9586392120306756, 0.0]
[0.9219104181991034, 0.0]
[0.8795807933369617, 0.0]
[0.8322170230625516, 0.0]
[0.7816374670004691, 0.0]
[0.7290594161179201, 0.0]
[0.6758982310332805, 0.0]
[0.6231970367115284, 0.0]
[0.571858602091371, 0.0]
[0.5225200133759598, 0.0]
[0.47564070023807775, 0.0]
[0.431502170190499, 0.0]
[0.39025901687118947, 0.0]
[0.36787944127643135, 0.0]state(sol) returns sol itself, which is callable. The two forms state(sol)(t) and sol(t) are equivalent. time_grid is an alias for times.
Point integration vs trajectory
Point integration (flow(t0, x0, tf)) returns the final state directly as a Vector, not a solution object:
xf = flow(0.0, x0, 1.0) # Vector, not VectorFieldTrajectory
typeof(xf)Vector{Float64} (alias for Array{Float64, 1})Use trajectory integration (flow((t0, tf), x0)) when you need the full history.
HamiltonianVectorFieldTrajectory
hsol # produced by hflow((t0, tf), x0, p0)HamiltonianVectorFieldTrajectory
result: SciMLIntegrationResultAccessors
ts_h = Trajectories.time_grid(hsol)18-element Vector{Float64}:
0.0
0.010717734625362933
0.03678924587914857
0.07082975288098367
0.11171621300174836
0.15977761725931772
0.214066312625275
0.27414099662100727
0.339163563060794
0.40850604198933105
0.48147597812035586
0.557505923126691
0.6360680321223098
0.716722082543547
0.7990884851440951
0.8828514711671517
0.9677468775678137
1.0x_h = Trajectories.state(hsol) # state trajectory: x(t)
p_h = Trajectories.costate(hsol) # costate trajectory: p(t)
x_h(0.0), p_h(0.0)([1.0, 0.0], [0.0, 1.0])x_h(0.5), p_h(0.5)([0.8775825617654902, 0.4794255388798979], [-0.4794255388798979, 0.8775825617654902])Plotting
Load Plots (or any Plots-compatible backend) to unlock plot on solution objects:
using Plots
plot(sol) # plots each component of the state trajectory
plot(hsol) # plots state and costate componentsThe plot recipe is provided by the CTFlowsPlots extension (activated automatically when Plots is loaded).
Low-level: integration result
Under the hood, solution objects wrap an AbstractIntegrationResult which exposes:
Integrators.times(result)— the time gridIntegrators.evaluate_at(result, t)— interpolated value attIntegrators.final_state(result)— the final value
These are used internally by the solution wrappers and normally not called directly.
See also
CTFlows.Trajectories.VectorFieldTrajectory,CTFlows.Trajectories.HamiltonianVectorFieldTrajectory— solution container types.CTFlows.Trajectories.state,CTFlows.Trajectories.costate— trajectory accessors.CTFlows.Trajectories.time_grid,CTFlows.Integrators.times— time grid accessors.CTFlows.Integrators.final_state,CTFlows.Integrators.evaluate_at— low-level result accessors.