Discrete continuation

Using the warm start option, it is easy to implement a basic discrete continuation method, where a sequence of problems is solved using each solution as initial guess for the next problem. This usually gives better and faster convergence than solving each problem with the same initial guess, and is a way to handle problems that require a good initial guess.

Continuation on parametric OCP

The most compact syntax to perform a discrete continuation is to use a function that returns the OCP for a given value of the continuation parameter, and solve a sequence of these problems. We illustrate this on a very basic double integrator with increasing fixed final time.

First we load the required packages

using OptimalControl
using NLPModelsIpopt
using Printf
using Plots

and write a function that returns the OCP for a given final time

function ocp_T(T)
    ocp = @def begin
        t ∈ [0, T], time
        x ∈ R², state
        u ∈ R, control
        q = x₁
        v = x₂
        q(0) == 0
        v(0) == 0
        q(T) == 1
        v(T) == 0
        ẋ(t) == [ v(t), u(t) ]
        ∫(u(t)^2) → min
    end
    return ocp
end

Then we perform the continuation with a simple for loop, using each solution to initialize the next problem.

init1 = ()
for T=1:5
    ocp1 = ocp_T(T)
    sol1 = solve(ocp1; display=false, init=init1)
    global init1 = sol1
    @printf("T %.2f objective %9.6f iterations %d\n", T, sol1.objective, sol1.iterations)
end
T 1.00 objective 12.000766 iterations 2
T 2.00 objective  1.500096 iterations 3
T 3.00 objective  0.444473 iterations 3
T 4.00 objective  0.187512 iterations 3
T 5.00 objective  0.096006 iterations 3

Continuation on global variable

As a second example, we show how to avoid redefining a new OCP each time, and modify the original one instead. More precisely we now solve a Goddard problem for a decreasing maximal thrust. If we store the value for Tmax in a global variable, we can simply modify this variable and keep the same OCP problem during the continuation.

Let us first define the Goddard problem (note that the formulation below illustrates all the possible constraints types, and the problem could be defined in a more compact way).

Cd = 310
Tmax = 3.5
β = 500
b = 2
function F0(x)
    r, v, m = x
    D = Cd * v^2 * exp(-β*(r - 1))
    return [ v, -D/m - 1/r^2, 0 ]
end
function F1(x)
    r, v, m = x
    return [ 0, Tmax/m, -b*Tmax ]
end

ocp = Model(variable=true)

r0 = 1
v0 = 0
m0 = 1
mf = 0.6
x0=[r0,v0,m0]

vmax = 0.1

state!(ocp, 3)
control!(ocp, 1)
variable!(ocp, 1)
time!(ocp; t0=0, indf=1)

constraint!(ocp, :initial; lb=x0, ub=x0)
constraint!(ocp, :final; rg=3, lb=mf, ub=Inf)
constraint!(ocp, :state; lb=[r0,v0,mf], ub=[r0+0.2,vmax,m0])
constraint!(ocp, :control; lb=0, ub=1)
constraint!(ocp, :variable; lb=0.01, ub=Inf)

objective!(ocp, :mayer, (x0, xf, v) -> xf[1], :max)

dynamics!(ocp, (x, u, v) -> F0(x) + u*F1(x) )

sol0 = solve(ocp; display=false)
@printf("Objective for reference solution %.6f\n", objective(sol0))
Objective for reference solution 1.012576

Then we perform the continuation on the maximal thrust.

sol       = sol0
Tmax_list = []
obj_list  = []
for Tmax_local=3.5:-0.5:1
    global Tmax = Tmax_local
    global sol = solve(ocp; display=false, init=sol)
    @printf("Tmax %.2f objective %.6f iterations %d\n", Tmax, objective(sol), iterations(sol))
    push!(Tmax_list, Tmax)
    push!(obj_list, objective(sol))
end
Tmax 3.50 objective 1.012576 iterations 16
Tmax 3.00 objective 1.012372 iterations 16
Tmax 2.50 objective 1.012023 iterations 21
Tmax 2.00 objective 1.011263 iterations 21
Tmax 1.50 objective 1.009172 iterations 18
Tmax 1.00 objective 1.003593 iterations 19

We plot now the objective w.r.t the maximal thrust, as well as both solutions for Tmax=3.5 and Tmax=1.

using Plots.PlotMeasures # for leftmargin

plt_obj = plot(Tmax_list, obj_list;
    seriestype=:scatter,
    title="Goddard problem",
    label="r(tf)",
    xlabel="Maximal thrust (Tmax)",
    ylabel="Maximal altitude r(tf)")

plt_sol = plot(sol0; solution_label="(Tmax = "*string(Tmax_list[1])*")")
plot!(plt_sol, sol;  solution_label="(Tmax = "*string(Tmax_list[end])*")")

layout = grid(2, 1, heights=[0.2, 0.8])
plot(plt_obj, plt_sol; layout=layout, size=(800, 1000), leftmargin=5mm)
Example block output