Skip to content

Commit 4785eb8

Browse files
committed
debug memoize function
1 parent d896e02 commit 4785eb8

File tree

5 files changed

+77
-13
lines changed

5 files changed

+77
-13
lines changed

LocalPreferences.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[ModelPredictiveControl]
2+
precompile_workload = false

example/juMPC.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ using Pkg
44
using Revise
55
Pkg.activate(".")
66

7+
78
using ModelPredictiveControl
9+
using Preferences
10+
set_preferences!(ModelPredictiveControl, "precompile_workload" => false; force=true)
11+
12+
813
#using JuMP, DAQP
914
#using JuMP, HiGHS
1015
using JuMP, Ipopt
@@ -71,13 +76,16 @@ nmpc1 = NonLinMPC(uscKalmanFilter1)
7176

7277
nmpc2 = NonLinMPC(nonLinModel2, Hp=15, Hc=1, Mwt=[1, 1] , Nwt=[0.1, 0.1], Cwt=1e5)
7378

79+
7480
setconstraint!(nmpc2, c_umin=[0,0], c_umax=[0,0])
7581
setconstraint!(nmpc2, c_ŷmin=[1,1], c_ŷmax=[1,1])
7682
setconstraint!(nmpc2, umin=[5, 9.9], umax=[Inf,Inf])
7783
setconstraint!(nmpc2, ŷmin=[-Inf,-Inf], ŷmax=[55, 35])
7884
setconstraint!(nmpc2, Δumin=[-Inf,-Inf],Δumax=[+Inf,+Inf])
7985

8086

87+
88+
8189
nx = linModel4.nx
8290
kf = KalmanFilter(linModel4, σP0=10*ones(nx), σQ=0.01*ones(nx), σR=[0.1, 0.1], σQ_int=0.05*ones(2), σP0_int=10*ones(2))
8391

@@ -97,6 +105,7 @@ setconstraint!(nmpc, umin=[5, 9.9], umax=[Inf,Inf])
97105
setconstraint!(nmpc, ŷmin=[-Inf,-Inf], ŷmax=[55, 35])
98106
setconstraint!(nmpc, Δumin=[-Inf,-Inf],Δumax=[+Inf,+Inf])
99107

108+
100109
function test_mpc(model, mpc)
101110
N = 200
102111
u_data = zeros(2,N)
@@ -133,9 +142,9 @@ function test_mpc(model, mpc)
133142
return u_data, y_data, r_data, d_data
134143
end
135144

136-
#@time u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
145+
@time u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
137146

138-
#@time u_data, y_data, r_data, d_data = test_mpc(linModel4, nmpc)
147+
@time u_data, y_data, r_data, d_data = test_mpc(linModel4, nmpc)
139148

140149
@time u_data, y_data, r_data, d_data = test_mpc(nonLinModel2, nmpc2)
141150

@@ -160,5 +169,3 @@ pd = plot(0:N-1,d_data[1,:],label=raw"$d_1$")
160169
display(pd)
161170
display(pu)
162171
display(py)
163-
164-

src/controller/linmpc.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ function LinMPC(
198198
return LinMPC{S}(estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru, optim)
199199
end
200200

201+
setnontlincon!(mpc::LinMPC, model::LinModel) = nothing
202+
201203
function init_objective!(mpc::LinMPC, ΔŨ)
202204
set_objective_function(mpc.optim, obj_quadprog(ΔŨ, mpc.P̃, mpc.q̃))
203205
return nothing

src/controller/nonlinmpc.jl

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,13 @@ struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
8080
nonlinconstraint = let mpc=mpc, model=model # capture mpc and model variables
8181
(ΔŨ...) -> con_nonlinprog(mpc, model, ΔŨ)
8282
end
83-
register(mpc.optim, :nonlinconstraint, nvar, nonlinconstraint, autodiff=true)
84-
ncon = sum(con.i_Ŷmin) + sum(con.i_Ŷmax)
85-
@NLconstraint(mpc.optim, [i=1:ncon], nonlinconstraint(ΔŨ...) zeros(ncon))
86-
83+
nonlincon_memoized = memoize(nonlinconstraint, 2*ny*Hp)
84+
for i=1:ny*Hp
85+
register(mpc.optim, Symbol("C_Ŷmin_$(i)"), nvar, nonlincon_memoized[i], autodiff=true)
86+
end
87+
for i=1:ny*Hp
88+
register(mpc.optim, Symbol("C_Ŷmax_$(i)"), nvar, nonlincon_memoized[ny*Hp+i], autodiff=true)
89+
end
8790
set_silent(optim)
8891
return mpc
8992
end
@@ -193,6 +196,23 @@ function NonLinMPC(
193196
return NonLinMPC{S, JEFunc}(estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, Ewt, JE, ru, optim)
194197
end
195198

199+
setnontlincon!(mpc::NonLinMPC, model::LinModel) = nothing
200+
201+
function setnonlincon!(mpc::NonLinMPC, model::NonLinModel)
202+
optim = mpc.optim
203+
ΔŨ = mpc.optim[:ΔŨ]
204+
con = mpc.con
205+
map(con -> delete(optim, con), all_nonlinear_constraints(optim))
206+
for i in findall(con.i_Ŷmin)
207+
f_sym = Symbol("C_Ŷmin_$(i)")
208+
add_nonlinear_constraint(optim, :($(f_sym)($(ΔŨ...)) <= 0))
209+
end
210+
for i in findall(con.i_Ŷmax)
211+
f_sym = Symbol("C_Ŷmax_$(i)")
212+
add_nonlinear_constraint(optim, :($(f_sym)($(ΔŨ...)) <= 0))
213+
end
214+
return nothing
215+
end
196216

197217
init_objective!(mpc::NonLinMPC, _ ) = nothing
198218

@@ -236,11 +256,12 @@ function con_nonlinprog(mpc::NonLinMPC, model::SimModel, ΔŨ::NTuple{N, T}) wh
236256
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
237257
U0 = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0)
238258
= evalŶ(mpc, model, mpc.x̂d, mpc.d0, mpc.D̂0, U0)
239-
C_Ŷmin = (mpc.con.Ŷmin - Ŷ)[mpc.con.i_Ŷmin]
240-
C_Ŷmax = (Ŷ - mpc.con.Ŷmax)[mpc.con.i_Ŷmin]
259+
C_Ŷmin = (mpc.con.Ŷmin - Ŷ)
260+
C_Ŷmax = (Ŷ - mpc.con.Ŷmax)
241261
if !isinf(mpc.C) # constraint softening activated :
242-
C_Ŷmin = C_Ŷmin - ΔŨ[end]*mpc.con.c_Ŷmin[mpc.con.i_Ŷmin]
243-
C_Ŷmax = C_Ŷmax - ΔŨ[end]*mpc.con.c_Ŷmin[mpc.con.i_Ŷmin]
262+
ϵ = ΔŨ[end]
263+
C_Ŷmin = C_Ŷmin - ϵ*mpc.con.c_Ŷmin
264+
C_Ŷmax = C_Ŷmax - ϵ*mpc.con.c_Ŷmin
244265
end
245266
C = [C_Ŷmin; C_Ŷmax]
246267
return C
@@ -258,3 +279,32 @@ function evalŶ(mpc, model, x̂d, d0, D̂0, U0::Vector{T}) where {T}
258279
return Ŷd0 + mpc.F
259280
end
260281

282+
"""
283+
memoize(myfunc::Function, n_outputs::Int)
284+
285+
Take a function `myfunc` and return a vector of length `n_outputs`, where element
286+
`i` is a function that returns the equivalent of `myfunc(x...)[i]`.
287+
288+
To avoid duplication of work, cache the most-recent evaluations of `myfunc`.
289+
Because `myfunc_i` is auto-differentiated with ForwardDiff, our cache needs to
290+
work when `x` is a `Float64` and a `ForwardDiff.Dual`.
291+
"""
292+
function memoize(f::Function, n_outputs::Int)
293+
last_ΔŨ , last_f = nothing, nothing
294+
function f_i(i, ΔŨ::Float64...)
295+
if ΔŨ !== last_ΔŨ
296+
last_f = f(ΔŨ...)
297+
last_ΔŨ = ΔŨ
298+
end
299+
return last_f[i]
300+
end
301+
last_dΔŨ, last_dfdΔŨ = nothing, nothing
302+
function f_i(i, dΔŨ::T...) where {T<:Real}
303+
if dΔŨ !== last_dΔŨ
304+
last_dfdΔŨ = f(dΔŨ...)
305+
last_dΔŨ = dΔŨ
306+
end
307+
return last_dfdΔŨ[i]
308+
end
309+
return [(x...) -> f_i(i, x...) for i in 1:n_outputs]
310+
end

src/predictive_control.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,12 @@ function setconstraint!(
184184
delete(mpc.optim, mpc.optim[:linconstraint])
185185
unregister(mpc.optim, :linconstraint)
186186
@constraint(mpc.optim, linconstraint, A*ΔŨ .≤ b)
187+
setnonlincon!(mpc, model)
187188
return mpc
188189
end
189190

191+
setnonlincon!(::PredictiveController, ::SimModel) = nothing
192+
190193
@doc raw"""
191194
moveinput!(
192195
mpc::PredictiveController,
@@ -639,7 +642,7 @@ function init_defaultcon(model, Hp, Hc, C, S_Hp, S_Hc, N_Hc, E)
639642
nu, ny = model.nu, model.ny
640643
umin, umax = fill(-Inf, nu), fill(+Inf, nu)
641644
Δumin, Δumax = fill(-Inf, nu), fill(+Inf, nu)
642-
ŷmin, ŷmax = fill(-Inf, ny), fill(+Inf, ny)
645+
ŷmin, ŷmax = fill(-999, ny), fill(+999, ny)
643646
c_umin, c_umax = fill(0.0, nu), fill(0.0, nu)
644647
c_Δumin, c_Δumax = fill(0.0, nu), fill(0.0, nu)
645648
c_ŷmin, c_ŷmax = fill(1.0, ny), fill(1.0, ny)

0 commit comments

Comments
 (0)