Skip to content

Commit ffc3bda

Browse files
authored
Merge pull request #237 from JuliaControl/prerelease
added: tests and benchmarks for `TrapezoidalCollocation`
2 parents ad12293 + f62b79a commit ffc3bda

File tree

4 files changed

+69
-4
lines changed

4 files changed

+69
-4
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.8.2"
4+
version = "1.9.0"
55

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

benchmark/0_bench_setup.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ linmodel = setop!(LinModel(sys, Ts, i_d=[3]), uop=[10, 50], yop=[50, 30], dop=[5
1616
nonlinmodel = NonLinModel(f_lin!, h_lin!, Ts, 2, 4, 2, 1, p=linmodel, solver=nothing)
1717
nonlinmodel = setop!(nonlinmodel, uop=[10, 50], yop=[50, 30], dop=[5])
1818
u, d, y = [10, 50], [5], [50, 30]
19+
nonlinmodel_c = NonLinModel(
20+
(ẋ,x,u,d,_) -> ẋ .= -0.001x .+ u .+ d ,
21+
(y,x,d,_) -> y .= x .+ 0.001d, 500, 1, 1, 1, 1
22+
)
23+
u_c, d_c, y_c = [1], [0], [0]
1924

2025
G = [ tf(1.90, [18, 1]) tf(1.90, [18, 1]);
2126
tf(-0.74,[8, 1]) tf(0.74, [8, 1]) ]

benchmark/3_bench_predictive_control.jl

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ nmpc_nonlin_ms = NonLinMPC(
5050
nonlinmodel, transcription=MultipleShooting(),
5151
Mwt=[1, 1], Nwt=[0.1, 0.1], Lwt=[0.1, 0.1], Hp=10
5252
)
53+
nmpc_nonlin_tc = NonLinMPC(
54+
nonlinmodel_c, transcription=TrapezoidalCollocation(),
55+
Mwt=[1], Nwt=[0.1], Lwt=[0.1], Hp=10
56+
)
5357

5458
samples, evals, seconds = 10000, 1, 60
5559
UNIT_MPC["NonLinMPC"]["moveinput!"]["LinModel"]["SingleShooting"] =
@@ -76,6 +80,12 @@ UNIT_MPC["NonLinMPC"]["moveinput!"]["NonLinModel"]["MultipleShooting"] =
7680
setup=preparestate!($nmpc_nonlin_ms, $y, $d),
7781
samples=samples, evals=evals, seconds=seconds
7882
)
83+
UNIT_MPC["NonLinMPC"]["moveinput!"]["NonLinModel"]["TrapezoidalCollocation"] =
84+
@benchmarkable(
85+
moveinput!($nmpc_nonlin_tc, $y_c, $d_c),
86+
setup=preparestate!($nmpc_nonlin_tc, $y_c, $d_c),
87+
samples=samples, evals=evals, seconds=seconds
88+
)
7989

8090
## ----------------------------------------------------------------------------------------
8191
## ---------------------- CASE STUDIES ----------------------------------------------------
@@ -230,7 +240,6 @@ CASE_MPC["CSTR"]["LinMPC"]["With feedforward"]["Ipopt"]["MultipleShooting"] =
230240
samples=samples, evals=evals
231241
)
232242

233-
234243
# ----------------- Case study: Pendulum noneconomic -----------------------------
235244
model, p = pendulum_model, pendulum_p
236245
σQ = [0.1, 1.0]; σR=[5.0]; nint_u=[1]; σQint_u=[0.1]
@@ -255,13 +264,20 @@ nmpc_ipopt_ms = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
255264
nmpc_ipopt_ms = setconstraint!(nmpc_ipopt_ms; umin, umax)
256265
JuMP.unset_time_limit_sec(nmpc_ipopt_ms.optim)
257266

267+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
268+
transcription = TrapezoidalCollocation()
269+
nmpc_ipopt_tc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
270+
nmpc_ipopt_tc = setconstraint!(nmpc_ipopt_tc; umin, umax)
271+
JuMP.unset_time_limit_sec(nmpc_ipopt_tc.optim)
272+
258273
optim = JuMP.Model(MadNLP.Optimizer, add_bridges=false)
259274
transcription = SingleShooting()
260275
nmpc_madnlp_ss = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
261276
nmpc_madnlp_ss = setconstraint!(nmpc_madnlp_ss; umin, umax)
262277
JuMP.unset_time_limit_sec(nmpc_madnlp_ss.optim)
263278

264-
# TODO: does not work well with MadNLP and MultipleShooting, figure out why. Current theory:
279+
# TODO: does not work well with MadNLP and MultipleShooting or TrapezoidalCollocation,
280+
# figure out why. Current theory:
265281
# MadNLP LBFGS approximation is less robust than Ipopt version. Re-test when exact Hessians
266282
# will be supported in ModelPredictiveControl.jl. The following attributes kinda work with
267283
# the MadNLP LBFGS approximation but super slow (~1000 times slower than Ipopt):
@@ -285,6 +301,11 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["MultipleShooting"] =
285301
sim!($nmpc_ipopt_ms, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
286302
samples=samples, evals=evals, seconds=seconds
287303
)
304+
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["TrapezoidalCollocation"] =
305+
@benchmarkable(
306+
sim!($nmpc_ipopt_tc, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
307+
samples=samples, evals=evals, seconds=seconds
308+
)
288309
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["MadNLP"]["SingleShooting"] =
289310
@benchmarkable(
290311
sim!($nmpc_madnlp_ss, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
@@ -316,13 +337,19 @@ empc_ipopt_ms = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, tr
316337
empc_ipopt_ms = setconstraint!(empc_ipopt_ms; umin, umax)
317338
JuMP.unset_time_limit_sec(empc_ipopt_ms.optim)
318339

340+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
341+
transcription = TrapezoidalCollocation()
342+
empc_ipopt_tc = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, transcription, p)
343+
empc_ipopt_tc = setconstraint!(empc_ipopt_tc; umin, umax)
344+
JuMP.unset_time_limit_sec(empc_ipopt_tc.optim)
345+
319346
optim = JuMP.Model(MadNLP.Optimizer, add_bridges=false)
320347
transcription = SingleShooting()
321348
empc_madnlp_ss = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, transcription, p)
322349
empc_madnlp_ss = setconstraint!(empc_madnlp_ss; umin, umax)
323350
JuMP.unset_time_limit_sec(empc_madnlp_ss.optim)
324351

325-
# TODO: test EMPC with MadNLP and MultipleShooting, see comment above.
352+
# TODO: test EMPC with MadNLP and MultipleShooting and TrapezoidalCollocation, see comment above.
326353

327354
samples, evals, seconds = 100, 1, 15*60
328355
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["SingleShooting"] =
@@ -335,6 +362,11 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["MultipleShooting"] =
335362
sim!($empc_ipopt_ms, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
336363
samples=samples, evals=evals, seconds=seconds
337364
)
365+
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["TrapezoidalCollocation"] =
366+
@benchmarkable(
367+
sim!($empc_ipopt_tc, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
368+
samples=samples, evals=evals, seconds=seconds
369+
)
338370
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["MadNLP"]["SingleShooting"] =
339371
@benchmarkable(
340372
sim!($empc_madnlp_ss, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
@@ -373,8 +405,17 @@ nmpc2_ipopt_ms = NonLinMPC(estim2;
373405
nmpc2_ipopt_ms = setconstraint!(nmpc2_ipopt_ms; umin, umax)
374406
JuMP.unset_time_limit_sec(nmpc2_ipopt_ms.optim)
375407

408+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
409+
transcription = TrapezoidalCollocation()
410+
nmpc2_ipopt_tc = NonLinMPC(estim2;
411+
Hp, Hc, Nwt=Nwt, Mwt=[0.5, 0], Cwt, gc!, nc, p=Pmax, optim, transcription
412+
)
413+
nmpc2_ipopt_tc = setconstraint!(nmpc2_ipopt_tc; umin, umax)
414+
JuMP.unset_time_limit_sec(nmpc2_ipopt_tc.optim)
415+
376416
# TODO: test custom constraints with MadNLP and SingleShooting, see comment above.
377417
# TODO: test custom constraints with MadNLP and MultipleShooting, see comment above.
418+
# TODO: test custom constraints with MadNLP and TrapezoidalCollocation, see comment above.
378419

379420
samples, evals, seconds = 100, 1, 15*60
380421
CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["SingleShooting"] =
@@ -387,6 +428,11 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["MultipleShooti
387428
sim!($nmpc2_ipopt_ms, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
388429
samples=samples, evals=evals, seconds=seconds
389430
)
431+
CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["TrapezoidalCollocation"] =
432+
@benchmarkable(
433+
sim!($nmpc2_ipopt_tc, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
434+
samples=samples, evals=evals, seconds=seconds
435+
)
390436

391437
# ----------------- Case study: Pendulum successive linearization -------------------------
392438
linmodel = linearize(model, x=[0, 0], u=[0])

test/3_test_predictive_control.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,12 @@ end
699699
@test length(nmpc16.Z̃) == nonlinmodel.nu*nmpc16.Hc + nmpc16.estim.nx̂*nmpc16.Hp + nmpc16.
700700
@test nmpc16.con.neq == nmpc16.estim.nx̂*nmpc16.Hp
701701
@test nmpc16.con.nc == 10
702+
nonlinmodel_c = NonLinModel((ẋ,x,u,_,_)->ẋ .= -0.1x .+ u, (y,x,_,_)->y.=x, 1, 1, 1, 1)
703+
nmpc16 = NonLinMPC(nonlinmodel_c, Hp=10, transcription=TrapezoidalCollocation(), nc=10, gc=gc!)
704+
@test nmpc16.transcription == TrapezoidalCollocation()
705+
@test length(nmpc16.Z̃) == nonlinmodel_c.nu*nmpc16.Hc + nmpc16.estim.nx̂*nmpc16.Hp + nmpc16.
706+
@test nmpc16.con.neq == nmpc16.estim.nx̂*nmpc16.Hp
707+
@test nmpc16.con.nc == 10
702708
nmpc17 = NonLinMPC(linmodel1, Hp=10, transcription=MultipleShooting())
703709
@test nmpc17.transcription == MultipleShooting()
704710
@test length(nmpc17.Z̃) == linmodel1.nu*nmpc17.Hc + nmpc17.estim.nx̂*nmpc17.Hp + nmpc17.
@@ -721,6 +727,7 @@ end
721727
@test_throws ErrorException NonLinMPC(nonlinmodel, Hp=15, JE = (_,_,_)->0.0)
722728
@test_throws ErrorException NonLinMPC(nonlinmodel, Hp=15, gc = (_,_,_,_)->[0.0], nc=1)
723729
@test_throws ErrorException NonLinMPC(nonlinmodel, Hp=15, gc! = (_,_,_,_)->[0.0], nc=1)
730+
@test_throws ArgumentError NonLinMPC(nonlinmodel, transcription=TrapezoidalCollocation())
724731

725732
@test_logs (:warn, Regex(".*")) NonLinMPC(nonlinmodel, Hp=15, JE=(Ue,_,_,_)->Ue)
726733
@test_logs (:warn, Regex(".*")) NonLinMPC(nonlinmodel, Hp=15, gc=(Ue,_,_,_,_)->Ue, nc=0)
@@ -792,6 +799,13 @@ end
792799
# execute update_predictions! branch in `geqfunc_i` for coverage:
793800
geq_end = nmpc5.optim[:geq_2].func
794801
@test_nowarn geq_end(5.0, 4.0, 3.0, 2.0)
802+
f! = (ẋ,x,u,_,_) -> ẋ .= -0.001x .+ u
803+
h! = (y,x,_,_) -> y .= x
804+
nonlinmodel_c = NonLinModel(f!, h!, 500, 1, 1, 1)
805+
nmpc5 = NonLinMPC(nonlinmodel_c, Nwt=[0], Hp=100, Hc=1, transcription=TrapezoidalCollocation())
806+
preparestate!(nmpc5, [0.0])
807+
u = moveinput!(nmpc5, [1/0.001])
808+
@test u [1.0] atol=5e-2
795809
nmpc6 = NonLinMPC(linmodel3, Hp=10)
796810
preparestate!(nmpc6, [0])
797811
@test moveinput!(nmpc6, [0]) [0.0] atol=5e-2

0 commit comments

Comments
 (0)