Skip to content

Commit 03b5a44

Browse files
authored
Merge pull request #348 from JuliaControl/mhe_allocs
added : reduce allocations in `MovingHorizonEstimator` and `initstate!`
2 parents e9597f6 + 3f9ddf6 commit 03b5a44

File tree

6 files changed

+46
-23
lines changed

6 files changed

+46
-23
lines changed

src/estimator/construct.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
struct StateEstimatorBuffer{NT<:Real}
22
u ::Vector{NT}
33
::Vector{NT}
4-
k::Vector{NT}
4+
k ::Vector{NT}
55
::Vector{NT}
6+
::Vector{NT}
7+
::Vector{NT}
8+
::Vector{NT}
69
::Matrix{NT}
710
::Matrix{NT}
811
::Matrix{NT}
@@ -21,12 +24,19 @@ Create a buffer for `StateEstimator` objects for estimated states and measured o
2124
The buffer is used to store intermediate results during estimation without allocating.
2225
"""
2326
function StateEstimatorBuffer{NT}(
24-
nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int, nk::Int=0
27+
nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int, nk::Int=0, He::Int=0, nε::Int=0
2528
) where NT <: Real
29+
nŵ = nx̂
30+
nZ̃ = nx̂ + nŵ*He +
31+
nV̂ = nym*He
32+
nX̂ = nx̂*He
2633
u = Vector{NT}(undef, nu)
2734
= Vector{NT}(undef, nu)
2835
k = Vector{NT}(undef, nk)
2936
= Vector{NT}(undef, nx̂)
37+
= Vector{NT}(undef, nZ̃)
38+
= Vector{NT}(undef, nV̂)
39+
= Vector{NT}(undef, nX̂)
3040
= Matrix{NT}(undef, nx̂, nx̂)
3141
= Matrix{NT}(undef, nx̂, nx̂)
3242
= Matrix{NT}(undef, nym, nym)
@@ -35,7 +45,7 @@ function StateEstimatorBuffer{NT}(
3545
= Vector{NT}(undef, ny)
3646
d = Vector{NT}(undef, nd)
3747
empty = Vector{NT}(undef, 0)
38-
return StateEstimatorBuffer{NT}(u, û, k, x̂, P̂, Q̂, R̂, K̂, ym, ŷ, d, empty)
48+
return StateEstimatorBuffer{NT}(u, û, k, x̂, Z̃, V̂, X̂, P̂, Q̂, R̂, K̂, ym, ŷ, d, empty)
3949
end
4050

4151
"Include all the covariance matrices for the Kalman filters and moving horizon estimator."

src/estimator/execute.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,15 @@ measured outputs ``\mathbf{y^m}``.
213213
function init_estimate!(estim::StateEstimator, ::LinModel, y0m, d0, u0)
214214
Â, B̂u, B̂d = estim.Â, estim.B̂u, estim.B̂d
215215
Ĉm, D̂dm = estim.Ĉm, estim.D̂dm
216-
# TODO: use estim.buffer.x̂ to reduce allocations
217-
estim.x̂0 .= [I - Â; Ĉm]\[B̂u*u0 + B̂d*d0 + estim.f̂op - estim.x̂op; y0m - D̂dm*d0]
216+
x̂_tmp, ŷ_tmp = estim.buffer.x̂, estim.buffer.
217+
x̂_tmp .= estim.f̂op .- estim.x̂op
218+
mul!(x̂_tmp, B̂u, u0, 1, 1)
219+
mul!(x̂_tmp, B̂d, d0, 1, 1)
220+
ŷm_tmp = @views ŷ_tmp[estim.i_ym]
221+
mul!(ŷm_tmp, D̂dm, d0)
222+
ŷm_tmp .= y0m .- ŷm_tmp
223+
M = [I - Â; Ĉm]
224+
estim.x̂0 .= M\[x̂_tmp; ŷm_tmp]
218225
return nothing
219226
end
220227
"""

src/estimator/internal_model.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,12 @@ This estimator does not augment the state vector, thus ``\mathbf{x̂ = x̂_d}``.
326326
"""
327327
function init_estimate!(estim::InternalModel, model::LinModel{NT}, y0m, d0, u0) where NT<:Real
328328
x̂d, x̂s = estim.x̂d, estim.x̂s
329-
# also updates estim.x̂0 (they are the same object):
330-
# TODO: use estim.buffer.x̂ to reduce the allocation:
331-
x̂d .= (I - model.A)\(model.Bu*u0 + model.Bd*d0 + model.fop - model.xop)
329+
x̂_tmp = estim.buffer.
330+
x̂_tmp .= estim.f̂op .- estim.x̂op
331+
mul!(x̂_tmp, model.Bu, u0, 1, 1)
332+
mul!(x̂_tmp, model.Bd, d0, 1, 1)
333+
M = I - model.A
334+
x̂d .= M\x̂_tmp # also updates estim.x̂0 (they are the same object)
332335
ŷ0d = estim.buffer.
333336
h!(ŷ0d, model, x̂d, d0, model.p)
334337
ŷs = ŷ0d

src/estimator/mhe/construct.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ struct MovingHorizonEstimator{
160160
nD0 = direct ? nd*(He+1) : nd*He
161161
U0, D0 = zeros(NT, nu*He), zeros(NT, nD0)
162162
= zeros(NT, nx̂*He)
163-
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd, nk)
163+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd, nk, He, nε)
164164
x̂0arr_old = zeros(NT, nx̂)
165165
P̂arr_old = copy(cov.P̂_0)
166166
Nk = [0]

src/estimator/mhe/execute.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,10 @@ If first warm-starts the solver with [`set_warmstart_mhe!`](@ref). It then calls
490490
"""
491491
function optim_objective!(estim::MovingHorizonEstimator{NT}) where NT<:Real
492492
model, optim, buffer = estim.model, estim.optim, estim.buffer
493-
nym, nx̂, nŵ, nε, Nk = estim.nym, estim.nx̂, estim.nx̂, estim., estim.Nk[]
494-
nx̃ =+ nx̂
493+
nŵ, nx̂, Nk = estim.nx̂, estim.nx̂, estim.Nk[]
494+
nx̃ = estim.+ nx̂
495495
Z̃var::Vector{JuMP.VariableRef} = optim[:Z̃var]
496-
= Vector{NT}(undef, nym*Nk) # TODO: remove this allocation
497-
X̂0 = Vector{NT}(undef, nx̂*Nk) # TODO: remove this allocation
498-
Z̃s = set_warmstart_mhe!(V̂, X̂0, estim, Z̃var)
496+
Z̃s = set_warmstart_mhe!(estim, Z̃var)
499497
# ------- solve optimization problem --------------
500498
try
501499
JuMP.optimize!(optim)
@@ -534,14 +532,15 @@ function optim_objective!(estim::MovingHorizonEstimator{NT}) where NT<:Real
534532
# --------- update estimate -----------------------
535533
û0, ŷ0, k = buffer.û, buffer.ŷ, buffer.k
536534
estim.Ŵ[1:nŵ*Nk] .= @views estim.Z̃[nx̃+1:nx̃+nŵ*Nk] # update Ŵ with optimum for warm-start
537-
V̂, X̂0 = predict_mhe!(V̂, X̂0, û0, k, ŷ0, estim, model, estim.Z̃)
538-
x̂0next = @views X̂0[end-nx̂+1:end]
535+
V̂, X̂0 = estim.buffer.V̂, estim.buffer.
536+
predict_mhe!(V̂, X̂0, û0, k, ŷ0, estim, model, estim.Z̃)
537+
x̂0next = @views X̂0[Nk*nx̂-nx̂+1:Nk*nx̂]
539538
estim.x̂0 .= x̂0next
540539
return estim.
541540
end
542541

543542
@doc raw"""
544-
set_warmstart_mhe!(V̂, X̂0, estim::MovingHorizonEstimator, Z̃var) -> Z̃s
543+
set_warmstart_mhe!(estim::MovingHorizonEstimator, Z̃var) -> Z̃s
545544
546545
Set and return the warm-start value of `Z̃var` for [`MovingHorizonEstimator`](@ref).
547546
@@ -564,11 +563,11 @@ computed at the last time step ``k-1``. If the objective function is not finite
564563
point, all the process noises ``\mathbf{ŵ}_{k-1}(k-j)`` are warm-started at zeros. The
565564
method mutates all the arguments.
566565
"""
567-
function set_warmstart_mhe!(V̂, X̂0, estim::MovingHorizonEstimator{NT}, Z̃var) where NT<:Real
566+
function set_warmstart_mhe!(estim::MovingHorizonEstimator{NT}, Z̃var) where NT<:Real
568567
model, buffer = estim.model, estim.buffer
569-
nε, nx̂, nŵ, nZ̃, Nk = estim.nε, estim.nx̂, estim.nx̂, length(estim.Z̃), estim.Nk[]
568+
nε, nx̂, nŵ, Nk = estim.nε, estim.nx̂, estim.nx̂, estim.Nk[]
570569
nx̃ =+ nx̂
571-
Z̃s = Vector{NT}(undef, nZ̃) # TODO: remove this allocation
570+
Z̃s = estim.buffer.
572571
û0, ŷ0, x̄, k = buffer.û, buffer.ŷ, buffer.x̂, buffer.k
573572
# --- slack variable ε ---
574573
estim.== 1 && (Z̃s[begin] = estim.Z̃[begin])
@@ -577,7 +576,8 @@ function set_warmstart_mhe!(V̂, X̂0, estim::MovingHorizonEstimator{NT}, Z̃var
577576
# --- process noise estimates Ŵ ---
578577
Z̃s[nx̃+1:end] = estim.
579578
# verify definiteness of objective function:
580-
V̂, X̂0 = predict_mhe!(V̂, X̂0, û0, k, ŷ0, estim, model, Z̃s)
579+
V̂, X̂0 = estim.buffer.V̂, estim.buffer.
580+
predict_mhe!(V̂, X̂0, û0, k, ŷ0, estim, model, Z̃s)
581581
Js = obj_nonlinprog!(x̄, estim, model, V̂, Z̃s)
582582
if !isfinite(Js)
583583
Z̃s[nx̃+1:end] = 0

src/model/linmodel.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,13 @@ disturbances ``\mathbf{d_0 = d - d_{op}}``. The Moore-Penrose pseudo-inverse com
266266
``\mathbf{(I - A)^{-1}}`` to support integrating `model` (integrator states will be 0).
267267
"""
268268
function steadystate!(model::LinModel, u0, d0)
269+
x_tmp = model.buffer.x
270+
x_tmp .= model.fop .- model.xop
271+
mul!(x_tmp, model.Bu, u0, 1, 1)
272+
mul!(x_tmp, model.Bd, d0, 1, 1)
269273
M = I - model.A
270274
rtol = sqrt(eps(real(float(oneunit(eltype(M)))))) # pinv docstring recommendation
271-
#TODO: use model.buffer.x to reduce allocations
272-
model.x0 .= pinv(M; rtol)*(model.Bu*u0 + model.Bd*d0 + model.fop - model.xop)
275+
model.x0 .= pinv(M; rtol)*x_tmp
273276
return nothing
274277
end
275278

0 commit comments

Comments
 (0)