Overview
solve is the entry point for the direct methods: transcribe the problem, hand it to an NLP solver, get a Solution back. This page shows the quickest way to call it, how to read what it printed, and the two ways to steer it away from its defaults.
Quick start
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)▫ This is OptimalControl 2.1.0-beta, solving with: collocation → adnlp → ipopt (cpu)
📦 Configuration:
├─ Discretizer: collocation
├─ Modeler: adnlp
└─ Solver: ipopt
▫ This is Ipopt version 3.14.19, running with linear solver MUMPS 5.9.0.
Number of nonzeros in equality constraint Jacobian...: 1754
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 250
Total number of variables............................: 752
variables with only lower bounds: 0
variables with lower and upper bounds: 0
variables with only upper bounds: 0
Total number of equality constraints.................: 504
Total number of inequality constraints...............: 0
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 0
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 5.0000000e-03 1.10e+00 2.24e-14 0.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 6.0000960e+00 2.22e-16 1.78e-15 -11.0 6.08e+00 - 1.00e+00 1.00e+00h 1
Number of Iterations....: 1
(scaled) (unscaled)
Objective...............: 6.0000960015360381e+00 6.0000960015360381e+00
Dual infeasibility......: 1.7763568394002505e-15 1.7763568394002505e-15
Constraint violation....: 2.2204460492503131e-16 2.2204460492503131e-16
Variable bound violation: 0.0000000000000000e+00 0.0000000000000000e+00
Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00
Overall NLP error.......: 1.7763568394002505e-15 1.7763568394002505e-15
Number of objective function evaluations = 2
Number of objective gradient evaluations = 2
Number of equality constraint evaluations = 2
Number of inequality constraint evaluations = 0
Number of equality constraint Jacobian evaluations = 2
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 1
Total seconds in IPOPT = 4.174
EXIT: Optimal Solution Found.A solver package must be loaded before calling solve — here using NLPModelsIpopt provides the default :ipopt. Without it, solve raises an ExtensionError naming the missing package and the exact using statement that fixes it.
Reading the display
By default solve prints a configuration table before running: which discretizer, modeler, and solver were selected, and every option that ends up applied to each — tagged by where the value came from:
:user— you passed it explicitly,:default— the strategy's own default,:computed— derived from the problem (e.g. a grid size picked from the time span).
This is the fastest way to answer "what did solve actually do with the call I just wrote?" without reading source.
Turning the display off
sol = solve(ocp; display=false)Useful once you trust a configuration and are solving in a loop, a test, or a script.
The defaults
Calling solve(ocp) with no strategy tokens is equivalent to:
solve(ocp, :collocation, :adnlp, :ipopt, :cpu)This particular quadruplet isn't special-cased — it's simply the first entry of methods(), and completion always takes the first match, top to bottom (see Choosing a method for the full list and how partial descriptions are completed).
Two ways to steer it
solve can be pointed at a different strategy in two styles:
descriptive — symbolic tokens, e.g.
solve(ocp, :madnlp)(see Choosing a method),explicit — typed component instances, e.g.
solve(ocp; solver=OptimalControl.MadNLP())(see Explicit mode).
The one thing worth knowing before either of those pages: which mode you're in is decided by the type of a keyword's value, never by the keyword's name. Any keyword argument whose value isa AbstractDiscretizer, AbstractNLPModeler, or AbstractNLPSolver switches solve into explicit mode, no matter what that keyword is called. Mixing a typed component with a non-empty symbolic description is rejected outright:
julia> using MadNLP
julia> solve(ocp, :collocation; solver=OptimalControl.MadNLP())
IncorrectArgument → _explicit_or_descriptive, mode_detection.jl:60
│
│ Cannot mix explicit components with symbolic description
│
│ Got explicit components + symbolic description (:collocation,)
│ Expected either explicit components OR symbolic description
│
│ Context solve function call
│ Hint Use either solve(ocp; discretizer=..., modeler=..., solver=...) OR solve(ocp, :collocation, :adnlp, :ipopt)
└─When it fails
A solve that doesn't converge still returns a Solution — inspect it rather than assuming success:
println(successful(sol)) # true/false — did the solver report success?
println(status(sol)) # a Symbol, e.g. :first_order, :max_iter
println(message(sol)) # the solver's own message
println(constraints_violation(sol))true
first_order
Ipopt/generic
2.220446049250313e-16See also
Choosing a method — the full list of strategies and how partial descriptions are completed.
Explicit mode — build and pass typed components directly.
Set an initial guess — every way to hand
solvea starting point.