Skip to content

Commit c18cf3b

Browse files
committed
cont. multiple shooting method
1 parent 9e41dfe commit c18cf3b

File tree

6 files changed

+228
-77
lines changed

6 files changed

+228
-77
lines changed

src/controller/construct.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,23 @@ end
3636

3737
"Include all the data for the constraints of [`PredictiveController`](@ref)"
3838
struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
39+
# matrices for the terminal constraints:
3940
ẽx̂ ::Matrix{NT}
4041
fx̂ ::Vector{NT}
4142
gx̂ ::Matrix{NT}
4243
jx̂ ::Matrix{NT}
4344
kx̂ ::Matrix{NT}
4445
vx̂ ::Matrix{NT}
4546
bx̂ ::Vector{NT}
47+
# matrices for the zero defect constraints (N/A for single shooting transcriptions):
48+
Ẽŝ ::Matrix{NT}
49+
Fŝ ::Vector{NT}
50+
Gŝ ::Matrix{NT}
51+
Jŝ ::Matrix{NT}
52+
Kŝ ::Matrix{NT}
53+
Vŝ ::Matrix{NT}
54+
Bŝ ::Vector{NT}
55+
# bounds over the prediction horizon (deviation vectors from operating points):
4656
U0min ::Vector{NT}
4757
U0max ::Vector{NT}
4858
ΔŨmin ::Vector{NT}
@@ -51,6 +61,7 @@ struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
5161
Y0max ::Vector{NT}
5262
x̂0min ::Vector{NT}
5363
x̂0max ::Vector{NT}
64+
# A matrices for the linear inequality constraints:
5465
A_Umin ::Matrix{NT}
5566
A_Umax ::Matrix{NT}
5667
A_ΔŨmin ::Matrix{NT}
@@ -60,13 +71,18 @@ struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
6071
A_x̂min ::Matrix{NT}
6172
A_x̂max ::Matrix{NT}
6273
A ::Matrix{NT}
74+
# b vector for the linear inequality constraints:
6375
b ::Vector{NT}
76+
# indices of finite numbers in the b vector (linear inequality constraints):
6477
i_b ::BitVector
78+
# constraint softness parameter vectors for the nonlinear inequality constraints:
6579
C_ymin ::Vector{NT}
6680
C_ymax ::Vector{NT}
6781
c_x̂min ::Vector{NT}
6882
c_x̂max ::Vector{NT}
83+
# indices of finite numbers in the g vector (nonlinear inequality constraints):
6984
i_g ::BitVector
85+
# custom nonlinear inequality constraints:
7086
gc! ::GCfunc
7187
nc ::Int
7288
end

src/controller/execute.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ function addinfo!(info, mpc::PredictiveController)
167167
return info
168168
end
169169

170-
171170
@doc raw"""
172171
initpred!(mpc::PredictiveController, model::LinModel, d, D̂, R̂y, R̂u) -> nothing
173172
@@ -235,8 +234,8 @@ end
235234
236235
Common computations of `initpred!` for all types of [`SimModel`](@ref).
237236
238-
Will init `mpc.F` with 0 values, or with the stochastic predictions `Ŷs` if `mpc.estim` is
239-
an [`InternalModel`](@ref). The function returns `mpc.F`.
237+
Will also init `mpc.F` with 0 values, or with the stochastic predictions `Ŷs` if `mpc.estim`
238+
is an [`InternalModel`](@ref). The function returns `mpc.F`.
240239
"""
241240
function initpred_common!(mpc::PredictiveController, model::SimModel, d, D̂, R̂y, R̂u)
242241
lastu = mpc.buffer.u
@@ -713,7 +712,7 @@ function setmodel_controller!(mpc::PredictiveController, x̂op_old)
713712
transcription, optim, con = mpc.transcription, mpc.optim, mpc.con
714713
# --- predictions matrices ---
715714
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(
716-
estim, model, Hp, Hc, transcription
715+
model, estim, transcription, Hp, Hc
717716
)
718717
A_Ymin, A_Ymax, Ẽ = relaxŶ(model, mpc.nϵ, con.C_ymin, con.C_ymax, E)
719718
A_x̂min, A_x̂max, ẽx̂ = relaxterminal(model, mpc.nϵ, con.c_x̂min, con.c_x̂max, ex̂)

src/controller/explicitmpc.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
22
estim::SE
3+
transcription::SingleShooting
34
ΔŨ::Vector{NT}
45
::Vector{NT}
56
Hp::Int
@@ -45,9 +46,9 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
4546
L_Hp = Hermitian(convert(Matrix{NT}, L_Hp), :L)
4647
# dummy vals (updated just before optimization):
4748
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
48-
transcription = :singleshooting
49-
S, T = init_ZtoU(estim, Hp, Hc, transcription)
50-
E, G, J, K, V, B = init_predmat(estim, model, Hp, Hc, transcription)
49+
transcription = SingleShooting() # explicit MPC only supports SingleShooting
50+
S, T = init_ZtoU(estim, transcription, Hp, Hc)
51+
E, G, J, K, V, B = init_predmat(model, estim, transcription, Hp, Hc)
5152
# dummy val (updated just before optimization):
5253
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
5354
S̃, Ñ_Hc, Ẽ = S, N_Hc, E # no slack variable ϵ for ExplicitMPC
@@ -64,6 +65,7 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
6465
buffer = PredictiveControllerBuffer{NT}(nu, ny, nd, Hp, Hc, nϵ)
6566
mpc = new{NT, SE}(
6667
estim,
68+
transcription,
6769
ΔŨ, ŷ,
6870
Hp, Hc, nϵ,
6971
weights,
@@ -212,7 +214,7 @@ function setmodel_controller!(mpc::ExplicitMPC, _ )
212214
estim, model = mpc.estim, mpc.estim.model
213215
nu, ny, nd, Hp, Hc = model.nu, model.ny, model.nd, mpc.Hp, mpc.Hc
214216
# --- predictions matrices ---
215-
E, G, J, K, V, B = init_predmat(estim, model, Hp, Hc)
217+
E, G, J, K, V, B = init_predmat(model, estim, transcription, Hp, Hc)
216218
= E # no slack variable ϵ for ExplicitMPC
217219
mpc.Ẽ .=
218220
mpc.G .= G

src/controller/linmpc.jl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
const DEFAULT_LINMPC_OPTIMIZER = OSQP.MathOptInterfaceOSQP.Optimizer
2-
const DEFAULT_LINMPC_TRANSCRIPTION = :singleshooting
2+
const DEFAULT_LINMPC_TRANSCRIPTION = SingleShooting()
33

44
struct LinMPC{
55
NT<:Real,
66
SE<:StateEstimator,
7+
TM<:TranscriptionMethod,
78
JM<:JuMP.GenericModel
89
} <: PredictiveController{NT}
910
estim::SE
1011
# note: `NT` and the number type `JNT` in `JuMP.GenericModel{JNT}` can be
1112
# different since solvers that support non-Float64 are scarce.
12-
transcription::Symbol
13+
transcription::TM
1314
optim::JM
1415
con::ControllerConstraint{NT, Nothing}
1516
ΔŨ::Vector{NT}
@@ -44,17 +45,17 @@ struct LinMPC{
4445
buffer::PredictiveControllerBuffer{NT}
4546
function LinMPC{NT}(
4647
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt,
47-
transcription, optim::JM
48-
) where {NT<:Real, SE<:StateEstimator, JM<:JuMP.GenericModel}
48+
transcription::TM, optim::JM
49+
) where {NT<:Real, SE<:StateEstimator, TM<:TranscriptionMethod, JM<:JuMP.GenericModel}
4950
model = estim.model
5051
nu, ny, nd, nx̂ = model.nu, model.ny, model.nd, estim.nx̂
5152
= copy(model.yop) # dummy vals (updated just before optimization)
5253
weights = ControllerWeights{NT}(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt)
5354
# dummy vals (updated just before optimization):
5455
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
55-
S, T = init_ZtoU(estim, Hp, Hc, transcription)
56+
S, T = init_ZtoU(estim, transcription, Hp, Hc)
5657
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(
57-
estim, model, Hp, Hc, transcription
58+
model, estim, transcription, Hp, Hc
5859
)
5960
# dummy vals (updated just before optimization):
6061
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
@@ -71,7 +72,7 @@ struct LinMPC{
7172
nΔŨ = size(Ẽ, 2)
7273
ΔŨ = zeros(NT, nΔŨ)
7374
buffer = PredictiveControllerBuffer{NT}(nu, ny, nd, Hp, Hc, nϵ)
74-
mpc = new{NT, SE, JM}(
75+
mpc = new{NT, SE, TM, JM}(
7576
estim, transcription, optim, con,
7677
ΔŨ, ŷ,
7778
Hp, Hc, nϵ,
@@ -98,7 +99,7 @@ Construct a linear predictive controller based on [`LinModel`](@ref) `model`.
9899
The controller minimizes the following objective function at each discrete time ``k``:
99100
```math
100101
\begin{aligned}
101-
\min_{\mathbf{ΔU}, ϵ} \mathbf{(R̂_y - Ŷ)}' \mathbf{M}_{H_p} \mathbf{(R̂_y - Ŷ)}
102+
\min_{\mathbf{Z}, ϵ} \mathbf{(R̂_y - Ŷ)}' \mathbf{M}_{H_p} \mathbf{(R̂_y - Ŷ)}
102103
+ \mathbf{(ΔU)}' \mathbf{N}_{H_c} \mathbf{(ΔU)} \\
103104
+ \mathbf{(R̂_u - U)}' \mathbf{L}_{H_p} \mathbf{(R̂_u - U)}
104105
+ C ϵ^2
@@ -114,8 +115,10 @@ subject to [`setconstraint!`](@ref) bounds, and in which the weight matrices are
114115
\end{aligned}
115116
```
116117
Time-varying and non-diagonal weights are also supported. Modify the last block in
117-
``\mathbf{M}_{H_p}`` to specify a terminal weight. The ``\mathbf{ΔU}`` includes the input
118-
increments ``\mathbf{Δu}(k+j) = \mathbf{u}(k+j) - \mathbf{u}(k+j-1)`` from ``j=0`` to
118+
``\mathbf{M}_{H_p}`` to specify a terminal weight. The content of the decision variable
119+
vector ``\mathbf{Z}`` depends on the chosen [`TranscriptionMethod`](@ref) (default to
120+
[`SingleShooting`](@ref)). The ``\mathbf{ΔU}`` includes the input increments
121+
``\mathbf{Δu}(k+j) = \mathbf{u}(k+j) - \mathbf{u}(k+j-1)`` from ``j=0`` to
119122
``H_c-1``, the ``\mathbf{Ŷ}`` vector, the output predictions ``\mathbf{ŷ}(k+j)`` from
120123
``j=1`` to ``H_p``, and the ``\mathbf{U}`` vector, the manipulated inputs ``\mathbf{u}(k+j)``
121124
from ``j=0`` to ``H_p-1``. The slack variable ``ϵ`` relaxes the constraints, as described
@@ -135,12 +138,10 @@ arguments. This controller allocates memory at each time step for the optimizati
135138
- `N_Hc=diagm(repeat(Nwt,Hc))` : positive semidefinite symmetric matrix ``\mathbf{N}_{H_c}``.
136139
- `L_Hp=diagm(repeat(Lwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{L}_{H_p}``.
137140
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
138-
- `transcription=:singleshooting` : transcription method for the nonlinear optimization
139-
problem, can be `:singleshooting` or `:multipleshoting`.
141+
- `transcription=SingleShooting()` : a [`TranscriptionMethod`](@ref) for the optimization.
140142
- `optim=JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)` : quadratic optimizer used in
141143
the predictive controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
142144
(default to [`OSQP`](https://osqp.org/docs/parsers/jump.html) optimizer).
143-
144145
- additional keyword arguments are passed to [`SteadyKalmanFilter`](@ref) constructor.
145146
146147
# Examples
@@ -171,9 +172,11 @@ LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SteadyKalmanFil
171172
| :------------------- | :------------------------------------------------------- | :--------------- |
172173
| ``H_p`` | prediction horizon (integer) | `()` |
173174
| ``H_c`` | control horizon (integer) | `()` |
175+
| ``\mathbf{Z}`` | decision variable vector (excluding ``ϵ``) | var. |
174176
| ``\mathbf{ΔU}`` | manipulated input increments over ``H_c`` | `(nu*Hc,)` |
175177
| ``\mathbf{D̂}`` | predicted measured disturbances over ``H_p`` | `(nd*Hp,)` |
176178
| ``\mathbf{Ŷ}`` | predicted outputs over ``H_p`` | `(ny*Hp,)` |
179+
| ``\mathbf{X̂}`` | predicted states over ``H_p`` | `(nx̂*Hp,)` |
177180
| ``\mathbf{U}`` | manipulated inputs over ``H_p`` | `(nu*Hp,)` |
178181
| ``\mathbf{R̂_y}`` | predicted output setpoints over ``H_p`` | `(ny*Hp,)` |
179182
| ``\mathbf{R̂_u}`` | predicted manipulated input setpoints over ``H_p`` | `(nu*Hp,)` |
@@ -194,7 +197,7 @@ function LinMPC(
194197
N_Hc = diagm(repeat(Nwt, Hc)),
195198
L_Hp = diagm(repeat(Lwt, Hp)),
196199
Cwt = DEFAULT_CWT,
197-
transcription = DEFAULT_LINMPC_TRANSCRIPTION,
200+
transcription::TranscriptionMethod = DEFAULT_LINMPC_TRANSCRIPTION,
198201
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
199202
kwargs...
200203
)
@@ -237,7 +240,7 @@ function LinMPC(
237240
N_Hc = diagm(repeat(Nwt, Hc)),
238241
L_Hp = diagm(repeat(Lwt, Hp)),
239242
Cwt = DEFAULT_CWT,
240-
transcription = DEFAULT_LINMPC_TRANSCRIPTION,
243+
transcription::TranscriptionMethod = DEFAULT_LINMPC_TRANSCRIPTION,
241244
optim::JM = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
242245
) where {NT<:Real, SE<:StateEstimator{NT}, JM<:JuMP.GenericModel}
243246
isa(estim.model, LinModel) || error("estim.model type must be a LinModel")

src/controller/nonlinmpc.jl

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
const DEFAULT_NONLINMPC_OPTIMIZER = optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes")
2-
const DEFAULT_NONLINMPC_TRANSCRIPTION = :singleshooting
2+
const DEFAULT_NONLINMPC_TRANSCRIPTION = SingleShooting()
33

44
struct NonLinMPC{
55
NT<:Real,
66
SE<:StateEstimator,
7+
TM<:TranscriptionMethod,
78
JM<:JuMP.GenericModel,
89
P<:Any,
910
JEfunc<:Function,
1011
GCfunc<:Function
1112
} <: PredictiveController{NT}
1213
estim::SE
13-
transcription::Symbol
14+
transcription::TM
1415
# note: `NT` and the number type `JNT` in `JuMP.GenericModel{JNT}` can be
1516
# different since solvers that support non-Float64 are scarce.
1617
optim::JM
@@ -50,10 +51,11 @@ struct NonLinMPC{
5051
function NonLinMPC{NT}(
5152
estim::SE,
5253
Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt, JE::JEfunc, gc!::GCfunc, nc, p::P,
53-
transcription, optim::JM
54+
transcription::TM, optim::JM
5455
) where {
5556
NT<:Real,
5657
SE<:StateEstimator,
58+
TM<:TranscriptionMethod,
5759
JM<:JuMP.GenericModel,
5860
P<:Any,
5961
JEfunc<:Function,
@@ -65,9 +67,9 @@ struct NonLinMPC{
6567
weights = ControllerWeights{NT}(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt)
6668
# dummy vals (updated just before optimization):
6769
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
68-
S, T = init_ZtoU(estim, Hp, Hc, transcription)
70+
S, T = init_ZtoU(estim, transcription, Hp, Hc)
6971
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(
70-
estim, model, Hp, Hc, transcription
72+
model, estim, transcription, Hp, Hc
7173
)
7274
# dummy vals (updated just before optimization):
7375
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
@@ -85,7 +87,7 @@ struct NonLinMPC{
8587
nΔŨ = size(Ẽ, 2)
8688
ΔŨ = zeros(NT, nΔŨ)
8789
buffer = PredictiveControllerBuffer{NT}(nu, ny, nd, Hp, Hc, nϵ)
88-
mpc = new{NT, SE, JM, P, JEfunc, GCfunc}(
90+
mpc = new{NT, SE, TM, JM, P, JEfunc, GCfunc}(
8991
estim, transcription, optim, con,
9092
ΔŨ, ŷ,
9193
Hp, Hc, nϵ,
@@ -125,17 +127,14 @@ subject to [`setconstraint!`](@ref) bounds, and the custom inequality constraint
125127
```math
126128
\mathbf{g_c}(\mathbf{U_e}, \mathbf{Ŷ_e}, \mathbf{D̂_e}, \mathbf{p}, ϵ) ≤ \mathbf{0}
127129
```
128-
and the slack ``ϵ`` and the ``\mathbf{Z}`` vector as the decision variables:
129-
130-
- ``\mathbf{Z} = \mathbf{ΔU}`` if the transcription method is `:singleshooting`
131-
- ``\mathbf{Z} = [\begin{smallmatrix} \mathbf{ΔU}' & \mathbf{X̂}' \end{smallmatrix}]'`` if the transcription method is `:multipleshoting`
132-
133-
The economic function ``J_E`` can penalizes solutions with high economic costs. Setting all
134-
the weights to 0 except ``E`` creates a pure economic model predictive controller (EMPC).
135-
As a matter of fact, ``J_E`` can be any nonlinear function to customize the objective, even
136-
if there is no economic interpretation to it. The arguments of ``J_E`` and ``\mathbf{g_c}``
137-
include the manipulated inputs, predicted outputs and measured disturbances, extended from
138-
``k`` to ``k + H_p`` (inclusively, see Extended Help for more details):
130+
with the decision variables ``\mathbf{Z}`` and slack ``ϵ``. By default, a [`SingleShooting`](@ref)
131+
transcription method is used, hence ``\mathbf{Z=ΔU}``. The economic function ``J_E`` can
132+
penalizes solutions with high economic costs. Setting all the weights to 0 except ``E``
133+
creates a pure economic model predictive controller (EMPC). As a matter of fact, ``J_E`` can
134+
be any nonlinear function as a custom objective, even if there is no economic interpretation
135+
to it. The arguments of ``J_E`` and ``\mathbf{g_c}`` include the manipulated inputs,
136+
predicted outputs and measured disturbances, extended from ``k`` to ``k+H_p`` (inclusively,
137+
see Extended Help for more details):
139138
```math
140139
\mathbf{U_e} = \begin{bmatrix} \mathbf{U} \\ \mathbf{u}(k+H_p-1) \end{bmatrix} , \quad
141140
\mathbf{Ŷ_e} = \begin{bmatrix} \mathbf{ŷ}(k) \\ \mathbf{Ŷ} \end{bmatrix} , \quad
@@ -179,8 +178,7 @@ This controller allocates memory at each time step for the optimization.
179178
not (details in Extended Help).
180179
- `nc=0` : number of custom inequality constraints.
181180
- `p=model.p` : ``J_E`` and ``\mathbf{g_c}`` functions parameter ``\mathbf{p}`` (any type).
182-
- `transcription=:singleshooting` : transcription method for the nonlinear optimization
183-
problem, can be `:singleshooting` or `:multipleshoting`.
181+
- `transcription=SingleShooting()` : a [`TranscriptionMethod`](@ref) for the optimization.
184182
- `optim=JuMP.Model(Ipopt.Optimizer)` : nonlinear optimizer used in the predictive
185183
controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
186184
(default to [`Ipopt`](https://github.com/jump-dev/Ipopt.jl) optimizer).
@@ -258,7 +256,7 @@ function NonLinMPC(
258256
gc ::Function = gc!,
259257
nc::Int = 0,
260258
p = model.p,
261-
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
259+
transcription::TranscriptionMethod = DEFAULT_NONLINMPC_TRANSCRIPTION,
262260
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
263261
kwargs...
264262
)
@@ -287,7 +285,7 @@ function NonLinMPC(
287285
gc ::Function = gc!,
288286
nc::Int = 0,
289287
p = model.p,
290-
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
288+
transcription::TranscriptionMethod = DEFAULT_NONLINMPC_TRANSCRIPTION,
291289
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
292290
kwargs...
293291
)
@@ -339,14 +337,12 @@ function NonLinMPC(
339337
gc!::Function = (_,_,_,_,_,_) -> nothing,
340338
gc ::Function = gc!,
341339
nc = 0,
342-
p::P = estim.model.p,
343-
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
344-
optim::JM = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
340+
p = estim.model.p,
341+
transcription::TranscriptionMethod = DEFAULT_NONLINMPC_TRANSCRIPTION,
342+
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
345343
) where {
346344
NT<:Real,
347-
SE<:StateEstimator{NT},
348-
JM<:JuMP.GenericModel,
349-
P<:Any
345+
SE<:StateEstimator{NT}
350346
}
351347
nk = estimate_delays(estim.model)
352348
if Hp nk

0 commit comments

Comments
 (0)