# NLP and DOCP manipulations

We describe here some more advanced operations related to the discretized optimal control problem. When calling `solve(ocp)`

three steps are performed internally:

- first, the OCP is discretized into a DOCP (a nonlinear optimization problem) with
`direct_transcription`

, - then, this DOCP is solved (with the internal function
`solve_docp`

), - finally, a functional solution of the OCP is rebuilt from the solution of the discretized problem, with
`OptimalControlSolution`

.

These steps can also be done separately, for instance if you want to use your own NLP solver.

Let us load the packages.

```
using OptimalControl
using Plots
```

## Definition of the optimal control problem

We define a test problem

```
ocp = @def begin
t ∈ [0, 1], time
x ∈ R², state
u ∈ R, control
x(0) == [ -1, 0 ]
x(1) == [ 0, 0 ]
ẋ(t) == [ x₂(t), u(t) ]
∫( 0.5u(t)^2 ) → min
end
```

## Discretization and NLP problem

We discretize the problem.

`docp, nlp = direct_transcription(ocp)`

The DOCP contains information related to the transcription, including a copy of the original OCP, and the NLP is the resulting discretized problem, in our case an `ADNLPModel`

.

We can now use the solver of our choice to solve it.

## Resolution of the NLP problem

For a first example we use the `ipopt`

solver from NLPModelsIpopt.jl package to solve the NLP problem.

```
using NLPModelsIpopt
nlp_sol = ipopt(nlp; print_level=5, mu_strategy="adaptive", tol=1e-8, sb="yes")
```

```
This is Ipopt version 3.14.16, running with linear solver MUMPS 5.7.3.
Number of nonzeros in equality constraint Jacobian...: 3005
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 251
Total number of variables............................: 1004
variables with only lower bounds: 0
variables with lower and upper bounds: 0
variables with only upper bounds: 0
Total number of equality constraints.................: 755
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 1.0000000e-01 1.10e+00 1.99e-14 0.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 -5.0000000e-03 7.36e-02 2.66e-15 -11.0 6.08e+00 - 1.00e+00 1.00e+00h 1
2 6.0003829e+00 8.88e-16 1.78e-15 -11.0 6.01e+00 - 1.00e+00 1.00e+00h 1
Number of Iterations....: 2
(scaled) (unscaled)
Objective...............: 6.0003828724303254e+00 6.0003828724303254e+00
Dual infeasibility......: 1.7763568394002505e-15 1.7763568394002505e-15
Constraint violation....: 8.8817841970012523e-16 8.8817841970012523e-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 = 3
Number of objective gradient evaluations = 3
Number of equality constraint evaluations = 3
Number of inequality constraint evaluations = 0
Number of equality constraint Jacobian evaluations = 3
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 2
Total seconds in IPOPT = 0.030
EXIT: Optimal Solution Found.
```

Then we can rebuild and plot an optimal control problem solution (note that the multipliers are optional, but the OCP costate will not be retrieved if the multipliers are not provided).

```
sol = OptimalControlSolution(docp; primal=nlp_sol.solution, dual=nlp_sol.multipliers)
plot(sol)
```

## Change the NLP solver

Alternatively, we can use MadNLP.jl to solve anew the NLP problem:

```
using MadNLP
nlp_sol = madnlp(nlp)
```

`"Execution stats: Optimal Solution Found (tol = 1.0e-08)."`

Another possible NLP solver is Percival.jl.

```
using Percival
nlp_sol = percival(nlp; verbose=1)
```

`"Execution stats: maximum elapsed time"`

## Initial guess

An initial guess, including warm start, can be passed to `direct_transcription`

the same way as for `solve`

.

`docp, nlp = direct_transcription(ocp; init=sol)`

It can also be changed after the transcription is done, with `set_initial_guess`

.

`set_initial_guess(docp, nlp, sol)`