Solve a problem
This manual explains how to use the solve function to solve optimal control problems with OptimalControl.jl. The solve function provides a descriptive mode where you specify strategies using symbolic tokens, with automatic option routing and validation.
For advanced usage, see:
Quick start
Let us define a basic optimal control problem:
using OptimalControl
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
nothing # hide<< @example-block not executed in draft mode >>The simplest way to solve it is:
using NLPModelsIpopt
sol = solve(ocp)
nothing # hide<< @example-block not executed in draft mode >>This uses default strategies: collocation discretization, ADNLP modeler, and Ipopt solver, all running on CPU.
You must load a solver package (e.g., using NLPModelsIpopt) before calling solve. Otherwise, you'll get:
julia> solve(ocp)
ERROR: ExtensionError. Please make: julia> using NLPModelsIpoptDisplay
Control the configuration display with the display option:
# Suppress all output
sol = solve(ocp; display=false)
nothing # hide<< @example-block not executed in draft mode >>Initial guess
Provide an initial guess using initial_guess (or the alias init):
# Using the @init macro
init = @init ocp begin
u = 0.5
end
sol = solve(ocp; initial_guess=init, grid_size=50, display=false)
nothing # hide<< @example-block not executed in draft mode >># Or using the alias
sol = solve(ocp; init=init, grid_size=50, display=false)
nothing # hide<< @example-block not executed in draft mode >>For more details on initial guess specification, see Set an initial guess.
Available methods
OptimalControl.jl provides multiple solving strategies. To see all available combinations, call:
methods()<< @example-block not executed in draft mode >>Each method is a quadruplet (discretizer, modeler, solver, parameter):
Discretizer — how to discretize the continuous OCP:
:collocation: collocation method (currently the only option)
Modeler — how to build the NLP model:
:adnlp: usesADNLPModels.ADNLPModelwith automatic differentiation:exa: usesExaModels.ExaModelwith SIMD optimization (GPU-capable). Only compatible with problems built via@def, not with the functional API
Solver — which NLP solver to use:
Parameter — execution backend:
:cpu: CPU execution (default):gpu: GPU execution (only for:examodeler with:madnlpor:madnclsolvers)
You can inspect which strategies use a given parameter:
describe(:cpu)<< @example-block not executed in draft mode >>describe(:gpu)<< @example-block not executed in draft mode >>The order of methods in the list above determines the priority for auto-completion. When you provide a partial description, the first matching method from top to bottom is selected. This is why the first method (:collocation, :adnlp, :ipopt, :cpu) is the default.
The first method in the list is the default, so:
solve(ocp)is equivalent to:
solve(ocp, :collocation, :adnlp, :ipopt, :cpu)Choosing a method
You can specify a complete method description:
using MadNLP
sol = solve(ocp, :collocation, :adnlp, :madnlp, :cpu)
nothing # hide<< @example-block not executed in draft mode >>Or provide a partial description. Missing tokens are auto-completed using the first matching method from methods() (top-to-bottom priority):
# Only specify the solver → defaults to :collocation, :adnlp, :cpu
sol = solve(ocp, :madnlp; print_level=MadNLP.ERROR)
nothing # hide<< @example-block not executed in draft mode >>The completion algorithm searches methods() from top to bottom and selects the first quadruplet that matches all provided tokens. For example:
solve(ocp, :madnlp)matches(:collocation, :adnlp, :madnlp, :cpu)(first match with:madnlp)solve(ocp, :exa)matches(:collocation, :exa, :ipopt, :cpu)(first match with:exa)solve(ocp, :gpu)matches(:collocation, :exa, :madnlp, :gpu)(first GPU method)
All of these are equivalent (they all complete to :collocation, :adnlp, :ipopt, :cpu):
solve(ocp) # empty → use first method
solve(ocp, :collocation) # specify discretizer
solve(ocp, :adnlp) # specify modeler
solve(ocp, :ipopt) # specify solver
solve(ocp, :cpu) # specify parameter
solve(ocp, :collocation, :adnlp) # specify discretizer + modeler
solve(ocp, :collocation, :ipopt) # specify discretizer + solver
solve(ocp, :collocation, :adnlp, :ipopt, :cpu) # complete descriptionSolver requirements
Each solver requires its package to be loaded to provide the solver implementation:
- Ipopt:
using NLPModelsIpopt - MadNLP:
using MadNLP(CPU) orusing MadNLPGPU(GPU) - Uno:
using UnoSolver - MadNCL:
using MadNCLandusing MadNLP(requires both) - Knitro:
using NLPModelsKnitro(commercial license required)
For GPU solving with MadNLP or MadNCL, you also need: using CUDA
Passing options to strategies
You can pass options as keyword arguments. They are automatically routed to the appropriate strategy:
sol = solve(ocp, :madnlp;
grid_size=100, # → discretizer (Collocation)
max_iter=500, # → solver (MadNLP)
print_level=MadNLP.ERROR # → solver (MadNLP)
)
nothing # hide<< @example-block not executed in draft mode >>The solve function displays the configuration and shows which options were applied:
sol = solve(ocp, :ipopt;
grid_size=50,
scheme=:trapeze,
max_iter=100,
print_level=0
)
nothing # hide<< @example-block not executed in draft mode >>Notice the 📦 Configuration box showing:
- Discretizer:
collocationwithgrid_size = 50, scheme = trapeze - Modeler:
adnlp(no custom options) - Solver:
ipoptwithmax_iter = 100, print_level = 0
Strategy options
Each strategy declares its available options. You can inspect them using describe.
When describe shows (default: NotProvided) for an option, it means OptimalControl does not override the strategy's native default value. For example:
- For Ipopt options with
(default: NotProvided), Ipopt's own default values are used - For MadNLP options with
(default: NotProvided), MadNLP's own default values are used - For other strategies, the same principle applies
Only options with explicit default values (e.g., (default: 100)) are overridden by OptimalControl.
Discretizer options
The collocation discretizer supports multiple integration schemes:
:trapeze- Trapezoidal rule (second-order accurate):midpoint- Midpoint rule (second-order accurate):euleror:euler_explicitor:euler_forward- Explicit Euler method (first-order accurate):euler_implicitor:euler_backward- Implicit Euler method (first-order accurate, more stable for stiff problems)
When using the :adnlp modeler, two additional high-order collocation schemes are available:
:gauss_legendre_2- 2-point Gauss-Legendre collocation (fourth-order accurate):gauss_legendre_3- 3-point Gauss-Legendre collocation (sixth-order accurate)
These schemes provide higher accuracy but require more computational effort.
describe(:collocation)<< @example-block not executed in draft mode >>Modeler options
describe(:adnlp)<< @example-block not executed in draft mode >>using CUDA
describe(:exa)<< @example-block not executed in draft mode >>Solver options
using NLPModelsIpopt
describe(:ipopt)<< @example-block not executed in draft mode >>using MadNLPGPU
describe(:madnlp)<< @example-block not executed in draft mode >>using MadNCL
describe(:madncl)<< @example-block not executed in draft mode >>using UnoSolver
describe(:uno)<< @example-block not executed in draft mode >>Official documentation
For complete option lists, see the official documentation:
- ADNLP: ADNLPModels documentation
- Exa: ExaModels documentation
- Ipopt: Ipopt options
- MadNLP: MadNLP options
- Uno: Uno documentation
- MadNCL: MadNCL documentation
- Knitro: Knitro options
See also
- Advanced options: option routing,
route_tofor disambiguation,bypassfor unknown options, introspection tools - Explicit mode: using typed components (
Collocation(),Ipopt()) instead of symbols - GPU solving: using the
:gpuparameter orExa{GPU}()/MadNLP{GPU}()types - Initial guess: detailed guide on the
@initmacro - Solution: working with the returned solution object