Save & load
Persist a solution to disk and read it back — for expensive solves you don't want to redo, warm starts across sessions, or sharing a result with someone who doesn't have the code that produced it.
using OptimalControl
using NLPModelsIpopt
t0 = 0
tf = 1
x0 = [-1, 0]
ocp = @def begin
t ∈ [t0, tf], time
x = (q, v) ∈ R², state
u ∈ R, control
x(t0) == x0
x(tf) == [0, 0]
ẋ(t) == [v(t), u(t)]
0.5∫(u(t)^2) → min
end
sol = solve(ocp; display=false)Two functions cover the whole API — export_ocp_solution and import_ocp_solution — each with a format= keyword picking the backend:
export_ocp_solution(sol; format::Symbol=:JLD, filename::String="solution")
import_ocp_solution(ocp; format::Symbol=:JLD, filename::String="solution")import_ocp_solution takes the model, not a filename, as its positional argument — the solution is reconstructed against it, so the model has to be given explicitly rather than inferred from the file.
JLD2
Exact round-trip of the underlying Julia values — the format to reach for by default.
using JLD2
export_ocp_solution(sol; format=:JLD, filename="solution")
sol_reloaded = import_ocp_solution(ocp; format=:JLD, filename="solution")
objective(sol_reloaded) == objective(sol)trueJSON3
Portable, human-readable, at the cost of not being Julia-specific:
using JSON3
export_ocp_solution(sol; format=:JSON, filename="solution")
sol_json = import_ocp_solution(ocp; format=:JSON, filename="solution")
objective(sol_json) ≈ objective(sol)trueWhich format
JLD2 round-trips Julia values exactly — the same types come back out. JSON3 is text, portable across languages and tools, but floating-point round-tripping through it isn't guaranteed bit-exact in general the way JLD2's is — use JLD2 unless portability specifically matters.
Reloading as an initial guess
A reloaded solution works as a warm start exactly like a freshly-computed one — see Initial guess for everything else init= accepts:
sol_warm = solve(ocp; init=sol_reloaded, display=false)Without the extension
Both functions are extensions — nothing works until the matching package is loaded:
julia> using OptimalControl
julia> export_ocp_solution(sol; format=:JLD)
ERROR: ExtensionError: missing dependencies to export solutions to JLD2 format
Missing JLD2
Hint Run: using JLD2Same pattern for format=:JSON, naming JSON3 instead. An invalid format is caught before either extension is even needed:
julia> export_ocp_solution(sol; format=:XML)
IncorrectArgument → top-level scope, REPL[1]:2
│
│ Invalid export format specified
│
│ Got format=XML
│ Expected :JLD or :JSON
│
│ Context export_ocp_solution - validating export format
│ Hint Use format=:JLD for binary files or format=:JSON for text files
└─Filenames have no extension of their own
filename is a base name — the extension is added automatically (.jld2 or .json), which is why the same filename= works unchanged for both formats above. Including the extension yourself doesn't get stripped — it gets doubled:
export_ocp_solution(sol; format=:JLD, filename="sol.jld2")
isfile("sol.jld2.jld2")trueSee also
Solution object — what gets saved and reloaded.
Initial guess — every other way to seed a solve, warm start included.