Skip to content

Commit 522f443

Browse files
authored
Merge pull request #258 from JuliaControl/threads_ms
added: multithreading with `MultipleShooting` and `TrapezoidalCollocation`
2 parents 0c1cf4a + a910db6 commit 522f443

File tree

6 files changed

+118
-74
lines changed

6 files changed

+118
-74
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
33
authors = ["Francis Gagnon"]
4-
version = "1.10.0"
4+
version = "1.11.0"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"

benchmark/3_bench_predictive_control.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,24 @@ nmpc_ipopt_ms = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
264264
nmpc_ipopt_ms = setconstraint!(nmpc_ipopt_ms; umin, umax)
265265
JuMP.unset_time_limit_sec(nmpc_ipopt_ms.optim)
266266

267+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
268+
transcription = MultipleShooting(f_threads=true)
269+
nmpc_ipopt_mst = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
270+
nmpc_ipopt_mst = setconstraint!(nmpc_ipopt_mst; umin, umax)
271+
JuMP.unset_time_limit_sec(nmpc_ipopt_mst.optim)
272+
267273
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
268274
transcription = TrapezoidalCollocation()
269275
nmpc_ipopt_tc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
270276
nmpc_ipopt_tc = setconstraint!(nmpc_ipopt_tc; umin, umax)
271277
JuMP.unset_time_limit_sec(nmpc_ipopt_tc.optim)
272278

279+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
280+
transcription = TrapezoidalCollocation(f_threads=true)
281+
nmpc_ipopt_tct = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
282+
nmpc_ipopt_tct = setconstraint!(nmpc_ipopt_tct; umin, umax)
283+
JuMP.unset_time_limit_sec(nmpc_ipopt_tct.optim)
284+
273285
optim = JuMP.Model(MadNLP.Optimizer, add_bridges=false)
274286
transcription = SingleShooting()
275287
nmpc_madnlp_ss = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
@@ -301,11 +313,21 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["MultipleShooting"] =
301313
sim!($nmpc_ipopt_ms, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
302314
samples=samples, evals=evals, seconds=seconds
303315
)
316+
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["MultipleShooting (threaded)"] =
317+
@benchmarkable(
318+
sim!($nmpc_ipopt_mst, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
319+
samples=samples, evals=evals, seconds=seconds
320+
)
304321
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["TrapezoidalCollocation"] =
305322
@benchmarkable(
306323
sim!($nmpc_ipopt_tc, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
307324
samples=samples, evals=evals, seconds=seconds
308325
)
326+
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["TrapezoidalCollocation (threaded)"] =
327+
@benchmarkable(
328+
sim!($nmpc_ipopt_tct, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
329+
samples=samples, evals=evals, seconds=seconds
330+
)
309331
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["MadNLP"]["SingleShooting"] =
310332
@benchmarkable(
311333
sim!($nmpc_madnlp_ss, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),

src/controller/transcription.jl

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ plant model/constraints.
3131
struct SingleShooting <: ShootingMethod end
3232

3333
@doc raw"""
34-
MultipleShooting()
34+
MultipleShooting(; f_threads=false, h_threads=false)
3535
3636
Construct a direct multiple shooting [`TranscriptionMethod`](@ref).
3737
@@ -56,24 +56,33 @@ for [`NonLinModel`](@ref).
5656
This transcription computes the predictions by calling the augmented discrete-time model
5757
in the equality constraint function recursively over ``H_p``, or by updating the linear
5858
equality constraint vector for [`LinModel`](@ref). It is generally more efficient for large
59-
control horizon ``H_c``, unstable or highly nonlinear plant models/constraints.
59+
control horizon ``H_c``, unstable or highly nonlinear models/constraints. Multithreading
60+
with `threads_f` or `threads_h` keyword arguments can be advantageous if ``\mathbf{f}`` or
61+
``\mathbf{h}`` in the [`NonLinModel`](@ref) is expensive to evaluate, respectively.
6062
6163
Sparse optimizers like `OSQP` or `Ipopt` and sparse Jacobian computations are recommended
6264
for this transcription method.
6365
"""
64-
struct MultipleShooting <: ShootingMethod end
66+
struct MultipleShooting <: ShootingMethod
67+
f_threads::Bool
68+
h_threads::Bool
69+
function MultipleShooting(; f_threads=false, h_threads=false)
70+
return new(f_threads, h_threads)
71+
end
72+
end
6573

6674
@doc raw"""
67-
TrapezoidalCollocation(h::Int=0)
75+
TrapezoidalCollocation(h::Int=0; f_threads=false, h_threads=false)
6876
6977
Construct an implicit trapezoidal [`TranscriptionMethod`](@ref) with `h`th order hold.
7078
7179
This is the simplest collocation method. It supports continuous-time [`NonLinModel`](@ref)s
7280
only. The decision variables are the same as for [`MultipleShooting`](@ref), hence similar
73-
computational costs. The `h` argument is `0` or `1`, for piecewise constant or linear
74-
manipulated inputs ``\mathbf{u}`` (`h=1` is slightly less expensive). Note that the various
75-
[`DiffSolver`](@ref) here assume zero-order hold, so `h=1` will induce a plant-model
76-
mismatch if the plant is simulated with these solvers.
81+
computational costs. See the same docstring for descriptions of `f_threads` and `h_threads`
82+
keywords. The `h` argument is `0` or `1`, for piecewise constant or linear manipulated
83+
inputs ``\mathbf{u}`` (`h=1` is slightly less expensive). Note that the various [`DiffSolver`](@ref)
84+
here assume zero-order hold, so `h=1` will induce a plant-model mismatch if the plant is
85+
simulated with these solvers.
7786
7887
This transcription computes the predictions by calling the continuous-time model in the
7988
equality constraint function and by using the implicit trapezoidal rule. It can handle
@@ -96,12 +105,14 @@ transcription method.
96105
struct TrapezoidalCollocation <: CollocationMethod
97106
h::Int
98107
nc::Int
99-
function TrapezoidalCollocation(h::Int=0)
108+
f_threads::Bool
109+
h_threads::Bool
110+
function TrapezoidalCollocation(h::Int=0; f_threads=false, h_threads=false)
100111
if !(h == 0 || h == 1)
101112
throw(ArgumentError("h argument must be 0 or 1 for TrapezoidalCollocation."))
102113
end
103114
nc = 2 # 2 collocation points per interval for trapezoidal rule
104-
return new(h, nc)
115+
return new(h, nc, f_threads, h_threads)
105116
end
106117
end
107118

@@ -1171,17 +1182,17 @@ function predict!(
11711182
nu, nx̂, ny, nd, nk, Hp = model.nu, mpc.estim.nx̂, model.ny, model.nd, model.nk, mpc.Hp
11721183
D̂0 = mpc.D̂0
11731184
x̂0 = @views mpc.estim.x̂0[1:nx̂]
1174-
d0 = @views mpc.d0[1:nd]
1185+
d̂0 = @views mpc.d0[1:nd]
11751186
for j=1:Hp
11761187
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
11771188
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
11781189
k0 = @views K0[(1 + nk*(j-1)):(nk*j)]
11791190
x̂0next = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
1180-
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d0)
1191+
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d̂0)
11811192
x̂0 = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
1182-
d0 = @views D̂0[(1 + nd*(j-1)):(nd*j)]
1193+
d̂0 = @views D̂0[(1 + nd*(j-1)):(nd*j)]
11831194
ŷ0 = @views Ŷ0[(1 + ny*(j-1)):(ny*j)]
1184-
ĥ!(ŷ0, mpc.estim, model, x̂0, d0)
1195+
ĥ!(ŷ0, mpc.estim, model, x̂0, d̂0)
11851196
end
11861197
Ŷ0 .+= mpc.F # F = Ŷs if mpc.estim is an InternalModel, else F = 0.
11871198
x̂0end .= x̂0
@@ -1206,21 +1217,21 @@ in which ``\mathbf{x̂_0}`` is the augmented state extracted from the decision v
12061217
"""
12071218
function predict!(
12081219
Ŷ0, x̂0end, _, _, _,
1209-
mpc::PredictiveController, model::NonLinModel, ::TranscriptionMethod,
1220+
mpc::PredictiveController, model::NonLinModel, transcription::TranscriptionMethod,
12101221
_ , Z̃
12111222
)
12121223
nu, ny, nd, nx̂, Hp, Hc = model.nu, model.ny, model.nd, mpc.estim.nx̂, mpc.Hp, mpc.Hc
1224+
h_threads = transcription.h_threads
12131225
X̂0 = @views Z̃[(nu*Hc+1):(nu*Hc+nx̂*Hp)] # Z̃ = [ΔU; X̂0; ϵ]
12141226
D̂0 = mpc.D̂0
1215-
local x̂0
1216-
for j=1:Hp
1227+
@threadsif h_threads for j=1:Hp
12171228
x̂0 = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
1218-
d0 = @views D̂0[(1 + nd*(j-1)):(nd*j)]
1229+
d̂0 = @views D̂0[(1 + nd*(j-1)):(nd*j)]
12191230
ŷ0 = @views Ŷ0[(1 + ny*(j-1)):(ny*j)]
1220-
ĥ!(ŷ0, mpc.estim, model, x̂0, d0)
1231+
ĥ!(ŷ0, mpc.estim, model, x̂0, d̂0)
12211232
end
12221233
Ŷ0 .+= mpc.F # F = Ŷs if mpc.estim is an InternalModel, else F = 0.
1223-
x̂0end .= x̂0
1234+
x̂0end .= @views X̂0[(1+nx̂*(Hp-1)):(nx̂*Hp)]
12241235
return Ŷ0, x̂0end
12251236
end
12261237

@@ -1329,28 +1340,30 @@ in which the augmented state ``\mathbf{x̂_0}`` are extracted from the decision
13291340
"""
13301341
function con_nonlinprogeq!(
13311342
geq, X̂0, Û0, K0,
1332-
mpc::PredictiveController, model::NonLinModel, ::MultipleShooting, U0, Z̃
1343+
mpc::PredictiveController, model::NonLinModel, transcription::MultipleShooting, U0, Z̃
13331344
)
13341345
nu, nx̂, nd, nk = model.nu, mpc.estim.nx̂, model.nd, model.nk
13351346
Hp, Hc = mpc.Hp, mpc.Hc
13361347
nΔU, nX̂ = nu*Hc, nx̂*Hp
1348+
f_threads = transcription.f_threads
13371349
D̂0 = mpc.D̂0
1338-
X̂0_Z̃ = @views Z̃[(nΔU+1):(nΔU+nX̂)]
1339-
x̂0 = @views mpc.estim.x̂0[1:nx̂]
1340-
d0 = @views mpc.d0[1:nd]
1341-
#TODO: allow parallel for loop or threads?
1342-
for j=1:Hp
1350+
X̂0_Z̃ = @views Z̃[(nΔU+1):(nΔU+nX̂)]
1351+
@threadsif f_threads for j=1:Hp
1352+
if j < 2
1353+
x̂0 = @views mpc.estim.x̂0[1:nx̂]
1354+
d̂0 = @views mpc.d0[1:nd]
1355+
else
1356+
x̂0 = @views X̂0_Z̃[(1 + nx̂*(j-2)):(nx̂*(j-1))]
1357+
d̂0 = @views D̂0[(1 + nd*(j-2)):(nd*(j-1))]
1358+
end
13431359
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
13441360
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
13451361
k0 = @views K0[(1 + nk*(j-1)):(nk*j)]
1346-
d0next = @views D̂0[(1 + nd*(j-1)):(nd*j)]
13471362
x̂0next = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
13481363
x̂0next_Z̃ = @views X̂0_Z̃[(1 + nx̂*(j-1)):(nx̂*j)]
13491364
ŝnext = @views geq[(1 + nx̂*(j-1)):(nx̂*j)]
1350-
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d0)
1365+
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d̂0)
13511366
ŝnext .= x̂0next .- x̂0next_Z̃
1352-
x̂0 = x̂0next_Z̃ # using states in Z̃ for next iteration (allow parallel for)
1353-
d0 = d0next
13541367
end
13551368
return geq
13561369
end
@@ -1400,25 +1413,23 @@ function con_nonlinprogeq!(
14001413
nu, nx̂, nd, nx, h = model.nu, mpc.estim.nx̂, model.nd, model.nx, transcription.h
14011414
Hp, Hc = mpc.Hp, mpc.Hc
14021415
nΔU, nX̂ = nu*Hc, nx̂*Hp
1416+
f_threads = transcription.f_threads
14031417
Ts, p = model.Ts, model.p
14041418
As, Cs_u = mpc.estim.As, mpc.estim.Cs_u
14051419
nk = get_nk(model, transcription)
14061420
D̂0 = mpc.D̂0
14071421
X̂0_Z̃ = @views Z̃[(nΔU+1):(nΔU+nX̂)]
1408-
x̂0 = @views mpc.estim.x̂0[1:nx̂]
1409-
d0 = @views mpc.d0[1:nd]
1410-
if !iszero(h)
1411-
k1, u0, û0 = @views K0[1:nx], U0[1:nu], Û0[1:nu]
1412-
x0, xs = @views x̂0[1:nx], x̂0[nx+1:end]
1413-
mul!(û0, Cs_u, xs)
1414-
û0 .+= u0
1415-
model.f!(k1, x0, û0, d0, p)
1416-
lastk2 = k1
1417-
end
14181422
#TODO: allow parallel for loop or threads?
1419-
for j=1:Hp
1423+
@threadsif f_threads for j=1:Hp
1424+
if j < 2
1425+
x̂0 = @views mpc.estim.x̂0[1:nx̂]
1426+
d̂0 = @views mpc.d0[1:nd]
1427+
else
1428+
x̂0 = @views X̂0_Z̃[(1 + nx̂*(j-2)):(nx̂*(j-1))]
1429+
d̂0 = @views D̂0[(1 + nd*(j-2)):(nd*(j-1))]
1430+
end
14201431
k0 = @views K0[(1 + nk*(j-1)):(nk*j)]
1421-
d0next = @views D̂0[(1 + nd*(j-1)):(nd*j)]
1432+
d̂0next = @views D̂0[(1 + nd*(j-1)):(nd*j)]
14221433
x̂0next = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
14231434
x̂0next_Z̃ = @views X̂0_Z̃[(1 + nx̂*(j-1)):(nx̂*j)]
14241435
ŝnext = @views geq[(1 + nx̂*(j-1)):(nx̂*j)]
@@ -1431,39 +1442,28 @@ function con_nonlinprogeq!(
14311442
mul!(xsnext, As, xs)
14321443
ssnext .= @. xsnext - xsnext_Z̃
14331444
# ----------------- deterministic defects --------------------------------------
1434-
if iszero(h) # piecewise constant manipulated inputs u:
1435-
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
1436-
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
1437-
mul!(û0, Cs_u, xs) # ys_u(k) = Cs_u*xs(k)
1438-
û0 .+= u0 # û0(k) = u0(k) + ys_u(k)
1439-
model.f!(k1, x0, û0, d0, p)
1440-
model.f!(k2, x0next_Z̃, û0, d0next, p)
1441-
else # piecewise linear manipulated inputs u:
1442-
k1 .= lastk2
1443-
j == Hp && break # special case, treated after the loop
1445+
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
1446+
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
1447+
mul!(û0, Cs_u, xs) # ys_u(k) = Cs_u*xs(k)
1448+
û0 .+= u0 # û0(k) = u0(k) + ys_u(k)
1449+
if f_threads || h < 1 || j < 2
1450+
# we need to recompute k1 with multi-threading, even with h==1, since the
1451+
# last iteration (j-1) may not be executed (iterations are re-orderable)
1452+
model.f!(k1, x0, û0, d̂0, p)
1453+
else
1454+
k1 .= @views K0[(1 + nk*(j-1)-nx):(nk*(j-1))] # k2 of of the last iter. j-1
1455+
end
1456+
if h < 1 || j Hp
1457+
# j = Hp special case: u(k+Hp-1) = u(k+Hp) since Hc ≤ Hp implies Δu(k+Hp) = 0
1458+
û0next = û0
1459+
else
14441460
u0next = @views U0[(1 + nu*j):(nu*(j+1))]
14451461
û0next = @views Û0[(1 + nu*j):(nu*(j+1))]
14461462
mul!(û0next, Cs_u, xsnext_Z̃) # ys_u(k+1) = Cs_u*xs(k+1)
14471463
û0next .+= u0next # û0(k+1) = u0(k+1) + ys_u(k+1)
1448-
model.f!(k2, x0next_Z̃, û0next, d0next, p)
1449-
lastk2 = k2
14501464
end
1465+
model.f!(k2, x0next_Z̃, û0next, d̂0next, p)
14511466
sdnext .= @. x0 - x0next_Z̃ + 0.5*Ts*(k1 + k2)
1452-
x̂0 = x̂0next_Z̃ # using states in Z̃ for next iteration (allow parallel for)
1453-
d0 = d0next
1454-
end
1455-
if !iszero(h)
1456-
# j = Hp special case: u(k+Hp-1) = u(k+Hp) since Hc ≤ Hp implies Δu(k+Hp)=0
1457-
x̂0, x̂0next_Z̃ = @views X̂0_Z̃[end-2nx̂+1:end-nx̂], X̂0_Z̃[end-nx̂+1:end]
1458-
k1, k2 = @views K0[end-2nx+1:end-nx], K0[end-nx+1:end] # k1 already filled
1459-
d0next = @views D̂0[end-nd+1:end]
1460-
û0next, u0next = @views Û0[end-nu+1:end], U0[end-nu+1:end] # correspond to u(k+Hp-1)
1461-
x0, x0next_Z̃, xsnext_Z̃ = @views x̂0[1:nx], x̂0next_Z̃[1:nx], x̂0next_Z̃[nx+1:end]
1462-
sdnext = @views geq[end-nx̂+1:end-nx̂+nx] # ssnext already filled
1463-
mul!(û0next, Cs_u, xsnext_Z̃)
1464-
û0next .+= u0next
1465-
model.f!(k2, x0next_Z̃, û0next, d0next, p)
1466-
sdnext .= @. x0 - x0next_Z̃ + (Ts/2)*(k1 + k2)
14671467
end
14681468
return geq
14691469
end

src/estimator/internal_model.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ It calls [`f!`](@ref) directly since this estimator does not augment the states.
177177
function f̂!(x̂0next, _ , k0, estim::InternalModel, model::NonLinModel, x̂0, u0, d0)
178178
f!(x̂0next, k0, model, x̂0, u0, d0, model.p)
179179
x̂0next .+= estim.f̂op .- estim.x̂op
180-
return x̂0next
180+
return nothing
181181
end
182182

183183
@doc raw"""

src/general.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,15 @@ function inv!(A::Hermitian{<:Real, <:AbstractMatrix})
122122
invA = inv(Achol)
123123
A .= Hermitian(invA, :L)
124124
return A
125+
end
126+
127+
"Add `Threads.@threads` to a `for` loop if `flag==true`, else leave the loop as is."
128+
macro threadsif(flag, expr)
129+
quote
130+
if $(flag)
131+
Threads.@threads $expr
132+
else
133+
$expr
134+
end
135+
end |> esc
125136
end

test/3_test_predictive_control.jl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,11 +814,13 @@ end
814814
f! = (ẋ,x,u,_,_) -> ẋ .= -0.001x .+ u
815815
h! = (y,x,_,_) -> y .= x
816816
nonlinmodel_c = NonLinModel(f!, h!, 500, 1, 1, 1)
817-
nmpc5 = NonLinMPC(nonlinmodel_c, Nwt=[0], Hp=100, Hc=1, transcription=TrapezoidalCollocation())
817+
transcription = TrapezoidalCollocation(0, f_threads=true, h_threads=true)
818+
nmpc5 = NonLinMPC(nonlinmodel_c; Nwt=[0], Hp=100, Hc=1, transcription)
818819
preparestate!(nmpc5, [0.0])
819820
u = moveinput!(nmpc5, [1/0.001])
820821
@test u [1.0] atol=5e-2
821-
nmpc5_1 = NonLinMPC(nonlinmodel_c, Nwt=[0], Hp=100, Hc=1, transcription=TrapezoidalCollocation(1))
822+
transcription = TrapezoidalCollocation(1)
823+
nmpc5_1 = NonLinMPC(nonlinmodel_c; Nwt=[0], Hp=100, Hc=1, transcription)
822824
preparestate!(nmpc5_1, [0.0])
823825
u = moveinput!(nmpc5_1, [1/0.001])
824826
@test u [1.0] atol=5e-2
@@ -831,13 +833,22 @@ end
831833
ModelPredictiveControl.h!(y, nonlinmodel2, Float32[0,0], Float32[0], nonlinmodel2.p)
832834
preparestate!(nmpc7, [0], [0])
833835
@test moveinput!(nmpc7, [0], [0]) [0.0] atol=5e-2
834-
nmpc8 = NonLinMPC(nonlinmodel, Nwt=[0], Hp=100, Hc=1, transcription=MultipleShooting())
836+
transcription = MultipleShooting()
837+
nmpc8 = NonLinMPC(nonlinmodel; Nwt=[0], Hp=100, Hc=1, transcription)
835838
preparestate!(nmpc8, [0], [0])
836839
u = moveinput!(nmpc8, [10], [0])
837840
@test u [2] atol=5e-2
838841
info = getinfo(nmpc8)
839842
@test info[:u] u
840843
@test info[:Ŷ][end] 10 atol=5e-2
844+
transcription = MultipleShooting(f_threads=true, h_threads=true)
845+
nmpc8t = NonLinMPC(nonlinmodel; Nwt=[0], Hp=100, Hc=1, transcription)
846+
preparestate!(nmpc8t, [0], [0])
847+
u = moveinput!(nmpc8t, [10], [0])
848+
@test u [2] atol=5e-2
849+
info = getinfo(nmpc8t)
850+
@test info[:u] u
851+
@test info[:Ŷ][end] 10 atol=5e-2
841852
nmpc9 = NonLinMPC(linmodel, Nwt=[0], Hp=100, Hc=1, transcription=MultipleShooting())
842853
preparestate!(nmpc9, [10])
843854
u = moveinput!(nmpc9, [20])

0 commit comments

Comments
 (0)