Skip to content

Commit f690735

Browse files
authored
Merge pull request #14 from control-toolbox/uptuto
Update of the tutorials
2 parents 96d3a4c + 0edc668 commit f690735

17 files changed

+1332
-386
lines changed

docs/Project.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
[deps]
2+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
3+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
24
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
35
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
46
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
57
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
68
MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9"
79
MadNLP = "2621e9c9-9eb4-46b1-8089-e8c72242dfb6"
10+
NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6"
811
NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71"
912
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
1013
OptimalControl = "5f98b655-cc9a-415a-b60e-744165666948"
1114
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
1215
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
16+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1317
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
1418

1519
[compat]
20+
BenchmarkTools = "1.6"
21+
DataFrames = "1.7"
1622
DifferentiationInterface = "0.7"
1723
Documenter = "1.8"
1824
ForwardDiff = "0.10, 1"
1925
LinearAlgebra = "1"
2026
MINPACK = "1.3"
2127
MadNLP = "0.8"
28+
NLPModels = "0.21"
2229
NLPModelsIpopt = "0.10"
2330
NonlinearSolve = "4.6"
2431
OptimalControl = "1.0"
2532
OrdinaryDiffEq = "6.93"
2633
Plots = "1.40"
34+
Printf = "1"
2735
Suppressor = "0.2"
28-
julia = "1"
36+
julia = "1.10"

docs/extras/disc.jl

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using OptimalControl # to define the optimal control problem and more
2+
using NLPModelsIpopt # to solve the problem via a direct method
3+
using Plots # to plot the solution
4+
5+
t0 = 0 # initial time
6+
r0 = 1 # initial altitude
7+
v0 = 0 # initial speed
8+
m0 = 1 # initial mass
9+
vmax = 0.1 # maximal authorized speed
10+
mf = 0.6 # final mass to target
11+
12+
ocp = @def begin # definition of the optimal control problem
13+
14+
tf R, variable
15+
t [t0, tf], time
16+
x = (r, v, m) R³, state
17+
u R, control
18+
19+
x(t0) == [r0, v0, m0]
20+
m(tf) == mf
21+
0 u(t) 1
22+
r(t) r0
23+
0 v(t) vmax
24+
25+
(t) == F0(x(t)) + u(t) * F1(x(t))
26+
27+
r(tf) max
28+
29+
end;
30+
31+
# Dynamics
32+
Cd = 310
33+
Tmax = 3.5
34+
β = 500
35+
b = 2
36+
37+
F0(x) = begin
38+
r, v, m = x
39+
D = Cd * v^2 * exp(-β*(r - 1)) # Drag force
40+
return [ v, -D/m - 1/r^2, 0 ]
41+
end
42+
43+
F1(x) = begin
44+
r, v, m = x
45+
return [ 0, Tmax/m, -b*Tmax ]
46+
end
47+
48+
# Solve the problem with different discretization methods
49+
solutions = []
50+
schemes = [:trapeze, :midpoint, :euler, :euler_implicit, :gauss_legendre_2, :gauss_legendre_3]
51+
for scheme in schemes
52+
sol = solve(ocp; disc_method=scheme, tol=1e-8, display=false)
53+
push!(solutions, (scheme, sol))
54+
end
55+
56+
# Plot the results
57+
x_style = (legend=:none,)
58+
p_style = (legend=:none,)
59+
styles = (state_style=x_style, costate_style=p_style)
60+
61+
scheme, sol = solutions[1]
62+
plt = plot(sol; label=string(scheme), styles...)
63+
for (scheme, sol) in solutions[2:end]
64+
plt = plot!(sol; label=string(scheme), styles...)
65+
end
66+
plot(plt; size=(800, 800))
67+
68+
# Benchmark the problem with different AD backends
69+
using NLPModels
70+
using DataFrames
71+
using BenchmarkTools
72+
73+
# DataFrame to store the results
74+
data = DataFrame(
75+
Backend=Symbol[],
76+
NNZO=Int[],
77+
NNZJ=Int[],
78+
NNZH=Int[],
79+
PrepTime=Float64[],
80+
ExecTime=Float64[],
81+
Objective=Float64[],
82+
Iterations=Int[]
83+
)
84+
85+
# The different AD backends to test
86+
backends = [:optimized, :manual]
87+
88+
# Loop over the backends
89+
for adnlp_backend in backends
90+
91+
println("Testing backend: ", adnlp_backend)
92+
93+
# Discretize the problem with a large grid size and Gauss-Legendre method
94+
bt = @btimed direct_transcription($ocp;
95+
disc_method=:euler,
96+
grid_size=1000,
97+
adnlp_backend=$adnlp_backend,
98+
)
99+
docp, nlp = bt.value
100+
prepa_time = bt.time
101+
102+
# Get the number of non-zero elements
103+
nnzo = get_nnzo(nlp) # Gradient of the Objective
104+
nnzj = get_nnzj(nlp) # Jacobian of the constraints
105+
nnzh = get_nnzh(nlp) # Hessian of the Lagrangian
106+
107+
# Solve the problem
108+
bt = @btimed ipopt($nlp;
109+
print_level=0,
110+
mu_strategy="adaptive",
111+
tol=1e-8,
112+
sb="yes",
113+
)
114+
nlp_sol = bt.value
115+
exec_time = bt.time
116+
117+
# Store the results in the DataFrame
118+
push!(data,
119+
(
120+
Backend=adnlp_backend,
121+
NNZO=nnzo,
122+
NNZJ=nnzj,
123+
NNZH=nnzh,
124+
PrepTime=prepa_time,
125+
ExecTime=exec_time,
126+
Objective=-nlp_sol.objective,
127+
Iterations=nlp_sol.iter
128+
)
129+
)
130+
end
131+
println(data)

docs/extras/iss.jl

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using OptimalControl # to define the optimal control problem and its flow
2+
using OrdinaryDiffEq # to get the Flow function from OptimalControl
3+
using MINPACK # NLE solver: use to solve the shooting equation
4+
using Plots # to plot the solution
5+
6+
const t0 = 0
7+
const tf = 1
8+
const x0 = -1
9+
const xf = 0
10+
const α = 1.5
11+
ocp = @def begin
12+
13+
t [t0, tf], time
14+
x R, state
15+
u R, control
16+
17+
x(t0) == x0
18+
x(tf) == xf
19+
20+
(t) == -x(t) + α * x(t)^2 + u(t)
21+
22+
( 0.5u(t)^2 ) min
23+
24+
end
25+
nothing # hide
26+
27+
u(x, p) = p
28+
φ = Flow(ocp, u)
29+
30+
π((x, p)) = x
31+
32+
S(p0) = π( φ(t0, x0, p0, tf) ) - xf # shooting function
33+
34+
ξ = [0.1] # initial guess
35+
36+
using MINPACK
37+
function fsolve(f, j, x; kwargs...)
38+
try
39+
MINPACK.fsolve(f, j, x; kwargs...)
40+
catch e
41+
println("Erreur using MINPACK")
42+
println(e)
43+
println("hybrj not supported. Replaced by hybrd even if it is not visible on the doc.")
44+
MINPACK.fsolve(f, x; kwargs...)
45+
end
46+
end
47+
48+
using DifferentiationInterface
49+
import ForwardDiff
50+
backend = AutoForwardDiff()
51+
52+
nle! = ( s, ξ) -> s[1] = S(ξ[1]) # auxiliary function
53+
jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) #
54+
55+
56+
indirect_sol = fsolve(nle!, jnle!, ξ; show_trace=true) # resolution of S(p0) = 0
57+
p0_sol = indirect_sol.x[1] # costate solution
58+
println("\ncostate: p0 = ", p0_sol)
59+
println("shoot: |S(p0)| = ", abs(S(p0_sol)), "\n")
60+
61+
sol = φ((t0, tf), x0, p0_sol)
62+
plot(sol)
63+
64+
using Plots.PlotMeasures
65+
function Plots.plot(S::Function, p0::Float64; Np0=20, kwargs...)
66+
67+
# times for wavefronts
68+
times = range(t0, tf, length=3)
69+
70+
# times for trajectories
71+
tspan = range(t0, tf, length=100)
72+
73+
# interval of initial covector
74+
p0_min = -0.5
75+
p0_max = 2
76+
77+
# covector solution
78+
p0_sol = p0
79+
80+
# plot of the flow in phase space
81+
plt_flow = plot()
82+
p0s = range(p0_min, p0_max, length=Np0)
83+
for i eachindex(p0s)
84+
sol = φ((t0, tf), x0, p0s[i])
85+
x = state(sol).(tspan)
86+
p = costate(sol).(tspan)
87+
label = i==1 ? "extremals" : false
88+
plot!(plt_flow, x, p, color=:blue, label=label)
89+
end
90+
91+
# plot of wavefronts in phase space
92+
p0s = range(p0_min, p0_max, length=200)
93+
xs = zeros(length(p0s), length(times))
94+
ps = zeros(length(p0s), length(times))
95+
for i eachindex(p0s)
96+
sol = φ((t0, tf), x0, p0s[i], saveat=times)
97+
xs[i, :] .= state(sol).(times)
98+
ps[i, :] .= costate(sol).(times)
99+
end
100+
for j eachindex(times)
101+
label = j==1 ? "flow at times" : false
102+
plot!(plt_flow, xs[:, j], ps[:, j], color=:green, linewidth=2, label=label)
103+
end
104+
105+
#
106+
plot!(plt_flow, xlims=(-1.1, 1), ylims=(p0_min, p0_max))
107+
plot!(plt_flow, [0, 0], [p0_min, p0_max], color=:black, xlabel="x", ylabel="p", label="x=xf")
108+
109+
# solution
110+
sol = φ((t0, tf), x0, p0_sol)
111+
x = state(sol).(tspan)
112+
p = costate(sol).(tspan)
113+
plot!(plt_flow, x, p, color=:red, linewidth=2, label="extremal solution")
114+
plot!(plt_flow, [x[end]], [p[end]], seriestype=:scatter, color=:green, label=false)
115+
116+
# plot of the shooting function
117+
p0s = range(p0_min, p0_max, length=200)
118+
plt_shoot = plot(xlims=(p0_min, p0_max), ylims=(-2, 4), xlabel="p₀", ylabel="y")
119+
plot!(plt_shoot, p0s, S, linewidth=2, label="S(p₀)", color=:green)
120+
plot!(plt_shoot, [p0_min, p0_max], [0, 0], color=:black, label="y=0")
121+
plot!(plt_shoot, [p0_sol, p0_sol], [-2, 0], color=:black, label="p₀ solution", linestyle=:dash)
122+
plot!(plt_shoot, [p0_sol], [0], seriestype=:scatter, color=:green, label=false)
123+
124+
# final plot
125+
plot(plt_flow, plt_shoot; layout=(1,2), leftmargin=15px, bottommargin=15px, kwargs...)
126+
127+
end
128+
129+
p0_sol
130+
131+
plot(S, p0_sol; size=(800, 450))

docs/extras/lqr.jl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Pkg
2+
Pkg.activate("docs")
3+
4+
using OptimalControl
5+
using NLPModelsIpopt
6+
using Plots
7+
8+
function lqr(tf)
9+
10+
x0 = [ 0
11+
1 ]
12+
13+
A = [ 0 1
14+
-1 0 ]
15+
16+
B = [ 0
17+
1 ]
18+
19+
Q = [ 1 0
20+
0 1 ]
21+
22+
R = 1
23+
24+
ocp = @def begin
25+
t [0, tf], time
26+
x R², state
27+
u R, control
28+
x(0) == x0
29+
(t) == [x₂(t), - x₁(t) + u(t)]
30+
0.5( x₁(t)^2 + x₂(t)^2 + u(t)^2 ) min
31+
end
32+
33+
return ocp
34+
end
35+
36+
solutions = [] # empty list of solutions
37+
tfs = [3, 5, 30]
38+
39+
for tf tfs
40+
solution = solve(lqr(tf); display=false)
41+
push!(solutions, solution)
42+
end
43+
44+
plt = plot(solutions[1], time=:normalize)
45+
for sol solutions[2:end]
46+
plot!(plt, sol, time=:normalize)
47+
end
48+
49+
# we plot only the state and control variables and we add the legend
50+
N = length(tfs)
51+
px1 = plot(plt[1], legend=false, xlabel="s", ylabel="x₁")
52+
px2 = plot(plt[2], label=reshape(["tf = $tf" for tf tfs], (1, N)), xlabel="s", ylabel="x₂")
53+
pu = plot(plt[5], legend=false, xlabel="s", ylabel="u")
54+
55+
using Plots.PlotMeasures # for leftmargin, bottommargin
56+
plot(px1, px2, pu, layout=(1, 3), size=(800, 300), leftmargin=5mm, bottommargin=5mm)
57+
58+
59+
####
60+
61+
plt = plot(solutions[1], :state, :control; time=:normalize, label="tf = $(tfs[1])")
62+
for (tf, sol) zip(tfs[2:end], solutions[2:end])
63+
plot!(plt, sol, :state, :control; time=:normalize, label="tf = $tf")
64+
end
65+
66+
px1 = plot(plt[1], legend=false, xlabel="s", ylabel="x₁")
67+
px2 = plot(plt[2], legend=true, xlabel="s", ylabel="x₂")
68+
pu = plot(plt[3], legend=false, xlabel="s", ylabel="u")
69+
plot(px1, px2, pu, layout=(1, 3), size=(800, 300), leftmargin=5mm, bottommargin=5mm)

0 commit comments

Comments
 (0)