Skip to content

Commit ff96c6d

Browse files
committed
Merge branch 'main' into Ipopt_VectorNonlinearOracle
2 parents 754b5f5 + 438c8e2 commit ff96c6d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1518
-861
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
version: 2
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/" # Location of package manifests
6+
schedule:
7+
interval: "weekly"

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Set JULIA_DEBUG environment variable if applicable
3535
if: ${{ runner.debug == '1' }}
3636
run: echo "JULIA_DEBUG=ModelPredictiveControl" >> $GITHUB_ENV
37-
- uses: actions/checkout@v4
37+
- uses: actions/checkout@v5
3838
- uses: julia-actions/setup-julia@v2
3939
with:
4040
version: ${{ matrix.version }}

.github/workflows/DocCleanup.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111
- name: Checkout gh-pages branch
12-
uses: actions/checkout@v2
12+
uses: actions/checkout@v5
1313
with:
1414
ref: gh-pages
1515

.github/workflows/benchmark.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ name: Benchmark
22
on:
33
pull_request_target:
44
branches: [ main ]
5+
types: [labeled, opened, synchronize, reopened]
6+
concurrency:
7+
# Skip intermediate builds: always.
8+
# Cancel intermediate builds: only if it is a pull request build.
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
511
permissions:
612
pull-requests: write # needed to post comments
713
jobs:
814
bench:
915
runs-on: ubuntu-latest
16+
if: ${{ contains(github.event.pull_request.labels.*.name, 'benchmark') }}
1017
steps:
1118
- uses: MilesCranmer/AirspeedVelocity.jl@action-v1
1219
with:

.github/workflows/documentation.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
contents: write
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v2
17-
- uses: julia-actions/setup-julia@v1
16+
- uses: actions/checkout@v5
17+
- uses: julia-actions/setup-julia@v2
1818
with:
1919
version: '1'
2020
- name: Install dependencies

Project.toml

Lines changed: 2 additions & 2 deletions
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.11.0"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
@@ -21,7 +21,7 @@ SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5"
2121
SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35"
2222

2323
[compat]
24-
ControlSystemsBase = "1.9"
24+
ControlSystemsBase = "1.18.2"
2525
DAQP = "0.6, 0.7.1"
2626
DifferentiationInterface = "0.6.45, 0.7"
2727
Documenter = "1"

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![codecov](https://codecov.io/gh/JuliaControl/ModelPredictiveControl.jl/branch/main/graph/badge.svg?token=K4V0L113M4)](https://codecov.io/gh/JuliaControl/ModelPredictiveControl.jl)
55
[![doc-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaControl.github.io/ModelPredictiveControl.jl/stable)
66
[![doc-dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaControl.github.io/ModelPredictiveControl.jl/dev)
7+
[![arXiv](https://img.shields.io/badge/arXiv-2411.09764-b31b1b.svg)](https://arxiv.org/abs/2411.09764)
78

89
An open source [model predictive control](https://en.wikipedia.org/wiki/Model_predictive_control)
910
package for Julia.
@@ -115,6 +116,7 @@ for more detailed examples.
115116
- supported transcription methods of the optimization problem:
116117
- direct single shooting
117118
- direct multiple shooting
119+
- trapezoidal collocation
118120
- additional information about the optimum to ease troubleshooting
119121
- real-time control loop features:
120122
- implementations that carefully limits the allocations

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: 71 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,32 @@ 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 = 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+
273+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
274+
transcription = TrapezoidalCollocation()
275+
nmpc_ipopt_tc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
276+
nmpc_ipopt_tc = setconstraint!(nmpc_ipopt_tc; umin, umax)
277+
JuMP.unset_time_limit_sec(nmpc_ipopt_tc.optim)
278+
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+
258285
optim = JuMP.Model(MadNLP.Optimizer, add_bridges=false)
259286
transcription = SingleShooting()
260287
nmpc_madnlp_ss = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt, optim, transcription)
261288
nmpc_madnlp_ss = setconstraint!(nmpc_madnlp_ss; umin, umax)
262289
JuMP.unset_time_limit_sec(nmpc_madnlp_ss.optim)
263290

264-
# TODO: does not work well with MadNLP and MultipleShooting, figure out why. Current theory:
291+
# TODO: does not work well with MadNLP and MultipleShooting or TrapezoidalCollocation,
292+
# figure out why. Current theory:
265293
# MadNLP LBFGS approximation is less robust than Ipopt version. Re-test when exact Hessians
266294
# will be supported in ModelPredictiveControl.jl. The following attributes kinda work with
267295
# the MadNLP LBFGS approximation but super slow (~1000 times slower than Ipopt):
@@ -285,6 +313,21 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["MultipleShooting"] =
285313
sim!($nmpc_ipopt_ms, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
286314
samples=samples, evals=evals, seconds=seconds
287315
)
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+
)
321+
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["Ipopt"]["TrapezoidalCollocation"] =
322+
@benchmarkable(
323+
sim!($nmpc_ipopt_tc, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
324+
samples=samples, evals=evals, seconds=seconds
325+
)
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+
)
288331
CASE_MPC["Pendulum"]["NonLinMPC"]["Noneconomic"]["MadNLP"]["SingleShooting"] =
289332
@benchmarkable(
290333
sim!($nmpc_madnlp_ss, $N, $ry; plant=$plant, x_0=$x_0, x̂_0=$x̂_0),
@@ -316,13 +359,19 @@ empc_ipopt_ms = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, tr
316359
empc_ipopt_ms = setconstraint!(empc_ipopt_ms; umin, umax)
317360
JuMP.unset_time_limit_sec(empc_ipopt_ms.optim)
318361

362+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
363+
transcription = TrapezoidalCollocation()
364+
empc_ipopt_tc = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, transcription, p)
365+
empc_ipopt_tc = setconstraint!(empc_ipopt_tc; umin, umax)
366+
JuMP.unset_time_limit_sec(empc_ipopt_tc.optim)
367+
319368
optim = JuMP.Model(MadNLP.Optimizer, add_bridges=false)
320369
transcription = SingleShooting()
321370
empc_madnlp_ss = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=Mwt2, Cwt, JE, Ewt, optim, transcription, p)
322371
empc_madnlp_ss = setconstraint!(empc_madnlp_ss; umin, umax)
323372
JuMP.unset_time_limit_sec(empc_madnlp_ss.optim)
324373

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

327376
samples, evals, seconds = 100, 1, 15*60
328377
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["SingleShooting"] =
@@ -335,6 +384,11 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["MultipleShooting"] =
335384
sim!($empc_ipopt_ms, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
336385
samples=samples, evals=evals, seconds=seconds
337386
)
387+
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["Ipopt"]["TrapezoidalCollocation"] =
388+
@benchmarkable(
389+
sim!($empc_ipopt_tc, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
390+
samples=samples, evals=evals, seconds=seconds
391+
)
338392
CASE_MPC["Pendulum"]["NonLinMPC"]["Economic"]["MadNLP"]["SingleShooting"] =
339393
@benchmarkable(
340394
sim!($empc_madnlp_ss, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
@@ -373,8 +427,17 @@ nmpc2_ipopt_ms = NonLinMPC(estim2;
373427
nmpc2_ipopt_ms = setconstraint!(nmpc2_ipopt_ms; umin, umax)
374428
JuMP.unset_time_limit_sec(nmpc2_ipopt_ms.optim)
375429

430+
optim = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"), add_bridges=false)
431+
transcription = TrapezoidalCollocation()
432+
nmpc2_ipopt_tc = NonLinMPC(estim2;
433+
Hp, Hc, Nwt=Nwt, Mwt=[0.5, 0], Cwt, gc!, nc, p=Pmax, optim, transcription
434+
)
435+
nmpc2_ipopt_tc = setconstraint!(nmpc2_ipopt_tc; umin, umax)
436+
JuMP.unset_time_limit_sec(nmpc2_ipopt_tc.optim)
437+
376438
# TODO: test custom constraints with MadNLP and SingleShooting, see comment above.
377439
# TODO: test custom constraints with MadNLP and MultipleShooting, see comment above.
440+
# TODO: test custom constraints with MadNLP and TrapezoidalCollocation, see comment above.
378441

379442
samples, evals, seconds = 100, 1, 15*60
380443
CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["SingleShooting"] =
@@ -387,6 +450,11 @@ CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["MultipleShooti
387450
sim!($nmpc2_ipopt_ms, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
388451
samples=samples, evals=evals, seconds=seconds
389452
)
453+
CASE_MPC["Pendulum"]["NonLinMPC"]["Custom constraints"]["Ipopt"]["TrapezoidalCollocation"] =
454+
@benchmarkable(
455+
sim!($nmpc2_ipopt_tc, $N, $ry; plant=$plant2, x_0=$x_0, x̂_0=$x̂_0),
456+
samples=samples, evals=evals, seconds=seconds
457+
)
390458

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

docs/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ Documenter = "1"
1616
JuMP = "1"
1717
LinearAlgebra = "1.10"
1818
Logging = "1.10"
19-
ModelingToolkit = "9.50 - 9.76"
19+
ModelingToolkit = "10"
2020
Plots = "1"

0 commit comments

Comments
 (0)