Skip to content

Commit 6613a1e

Browse files
authored
Merge pull request #149 from JuliaControl/fasterNLPfunctions
Added: improve `NonLinMPC` and `MovingHorizonEstimator` performance
2 parents 0015711 + 23cfcf5 commit 6613a1e

File tree

6 files changed

+166
-124
lines changed

6 files changed

+166
-124
lines changed

Project.toml

+1-1
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.1.4"
4+
version = "1.2.0"
55

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

src/controller/execute.jl

+32-18
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ function getinfo(mpc::PredictiveController{NT}) where NT<:Real
121121
Ȳ, Ū = similar(mpc.Yop), similar(mpc.Uop)
122122
Ŷe, Ue = Vector{NT}(undef, nŶe), Vector{NT}(undef, nUe)
123123
Ŷ0, x̂0end = predict!(Ŷ0, x̂0, x̂0next, u0, û0, mpc, model, mpc.ΔŨ)
124-
Ue, Ŷe = extended_predictions!(Ue, Ŷe, Ū, mpc, model, Ŷ0, mpc.ΔŨ)
124+
Ŷe, Ue = extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, mpc.ΔŨ)
125125
J = obj_nonlinprog!(Ȳ, Ū, mpc, model, Ue, Ŷe, mpc.ΔŨ)
126-
U =
127-
U .= @views Ue[1:end-model.nu]
128-
=
129-
Ŷ .= @views Ŷe[model.ny+1:end]
126+
U, Ŷ = Ū, Ȳ
127+
U .= mul!(U, mpc.S̃, mpc.ΔŨ) .+ mpc.T_lastu
128+
Ŷ .= Ŷ0 .+ mpc.Yop
130129
oldF = copy(mpc.F)
131130
F = predictstoch!(mpc, mpc.estim)
132131
Ŷs .= F # predictstoch! init mpc.F with Ŷs value if estim is an InternalModel
@@ -376,26 +375,41 @@ function predict!(Ŷ0, x̂0, x̂0next, u0, û0, mpc::PredictiveController, mod
376375
end
377376

378377
"""
379-
extended_predictions!(Ue, Ŷe, Ū, mpc, model, Ŷ0, ΔŨ) -> Ŷe, Ue
378+
extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ) -> Ŷe, Ue
380379
381-
Compute the extended vectors `Ue` and `Ŷe` and for the nonlinear optimization.
380+
Compute the extended vectors `Ŷe` and `Ue` for the nonlinear optimization.
382381
383-
The function mutates `Ue`, `Ŷe` and `Ū` in arguments, without assuming any initial values.
382+
The function mutates `Ŷe`, `Ue` and `Ū` in arguments, without assuming any initial values
383+
for them. Using `nocustomfcts = mpc.weights.iszero_E && mpc.con.nc == 0`, there is two
384+
special cases in which `Ŷe`, `Ue` and `Ū` are not mutated:
385+
386+
- If `mpc.weights.iszero_M_Hp[] && nocustomfcts`, the `Ŷe` vector is not computed to reduce
387+
the burden in the optimization problem.
388+
- If `mpc.weights.iszero_L_Hc[] && nocustomfcts`, the `Ue` vector is not computed for the
389+
same reason as above.
384390
"""
385-
function extended_predictions!(Ue, Ŷe, Ū, mpc, model, Ŷ0, ΔŨ)
391+
function extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
386392
ny, nu = model.ny, model.nu
387-
# --- extended manipulated inputs Ue = [U; u(k+Hp-1)] ---
388-
U =
389-
U .= mul!(U, mpc.S̃, ΔŨ) .+ mpc.T_lastu
390-
Ue[1:end-nu] .= U
391-
# u(k + Hp) = u(k + Hp - 1) since Δu(k+Hp) = 0 (because Hc ≤ Hp):
392-
Ue[end-nu+1:end] .= @views U[end-nu+1:end]
393+
nocustomfcts = (mpc.weights.iszero_E && iszero_nc(mpc))
393394
# --- extended output predictions Ŷe = [ŷ(k); Ŷ] ---
394-
Ŷe[1:ny] .= mpc.
395-
Ŷe[ny+1:end] .= Ŷ0 .+ mpc.Yop
396-
return Ue, Ŷe
395+
if !(mpc.weights.iszero_M_Hp[] && nocustomfcts)
396+
Ŷe[1:ny] .= mpc.
397+
Ŷe[ny+1:end] .= Ŷ0 .+ mpc.Yop
398+
end
399+
# --- extended manipulated inputs Ue = [U; u(k+Hp-1)] ---
400+
if !(mpc.weights.iszero_L_Hp[] && nocustomfcts)
401+
U =
402+
U .= mul!(U, mpc.S̃, ΔŨ) .+ mpc.T_lastu
403+
Ue[1:end-nu] .= U
404+
# u(k + Hp) = u(k + Hp - 1) since Δu(k+Hp) = 0 (because Hc ≤ Hp):
405+
Ue[end-nu+1:end] .= @views U[end-nu+1:end]
406+
end
407+
return Ŷe, Ue
397408
end
398409

410+
"Verify if the custom nonlinear constraint has zero elements."
411+
iszero_nc(mpc::PredictiveController) = (mpc.con.nc == 0)
412+
399413
"""
400414
obj_nonlinprog!( _ , _ , mpc::PredictiveController, model::LinModel, Ue, Ŷe, ΔŨ)
401415

src/controller/explicitmpc.jl

+2
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ function predict!(Ŷ, x̂, _ , _ , _ , mpc::ExplicitMPC, ::LinModel, ΔŨ)
195195
return Ŷ, x̂
196196
end
197197

198+
"`ExplicitMPC` does not support custom nonlinear constraint, return `true`."
199+
iszero_nc(mpc::ExplicitMPC) = true
198200

199201
"""
200202
addinfo!(info, mpc::ExplicitMPC) -> info

src/controller/nonlinmpc.jl

+40-37
Original file line numberDiff line numberDiff line change
@@ -481,18 +481,19 @@ function init_optimization!(mpc::NonLinMPC, model::SimModel, optim)
481481
JuMP.set_attribute(optim, "nlp_scaling_max_gradient", 10.0/C)
482482
end
483483
end
484-
Jfunc, gfunc = get_optim_functions(mpc, mpc.optim)
484+
Jfunc, gfuncs = get_optim_functions(mpc, mpc.optim)
485485
@operator(optim, J, nΔŨ, Jfunc)
486486
@objective(optim, Min, J(ΔŨvar...))
487-
init_nonlincon!(mpc, model, gfunc)
487+
init_nonlincon!(mpc, model, gfuncs)
488488
set_nonlincon!(mpc, model, mpc.optim)
489489
return nothing
490490
end
491491

492492
"""
493-
get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel) -> Jfunc, gfunc
493+
get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel) -> Jfunc, gfuncs
494494
495-
Get the objective `Jfunc` and constraints `gfunc` functions for [`NonLinMPC`](@ref).
495+
Get the objective `Jfunc` function and constraint `gfuncs` function vector for
496+
[`NonLinMPC`](@ref).
496497
497498
Inspired from: [User-defined operators with vector outputs](https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-operators-with-vector-outputs)
498499
"""
@@ -513,89 +514,91 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
513514
û0_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nu), Ncache)
514515
g_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, ng), Ncache)
515516
gc_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nc), Ncache)
516-
function Jfunc(ΔŨtup::T...) where T<:Real
517-
ΔŨ1 = ΔŨtup[begin]
518-
ΔŨ, g = get_tmp(ΔŨ_cache, ΔŨ1), get_tmp(g_cache, ΔŨ1)
519-
for i in eachindex(ΔŨtup)
520-
ΔŨ[i] = ΔŨtup[i] # ΔŨ .= ΔŨtup seems to produce a type instability
521-
end
522-
Ŷe, Ue = get_tmp(Ŷe_cache, ΔŨ1), get_tmp(Ue_cache, ΔŨ1)
523-
Ȳ, Ū = get_tmp(Ȳ_cache, ΔŨ1), get_tmp(Ū_cache, ΔŨ1)
524-
x̂0, x̂0next = get_tmp(x̂0_cache, ΔŨ1), get_tmp(x̂0next_cache, ΔŨ1)
525-
u0, û0 = get_tmp(u0_cache, ΔŨ1), get_tmp(û0_cache, ΔŨ1)
526-
gc = get_tmp(gc_cache, ΔŨ1)
527-
Ŷ0, x̂0end = predict!(Ȳ, x̂0, x̂0next, u0, û0, mpc, model, ΔŨ)
528-
Ue, Ŷe = extended_predictions!(Ue, Ŷe, Ū, mpc, model, Ŷ0, ΔŨ)
529-
ϵ = (nϵ 0) ? ΔŨ[end] : zero(T) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
530-
gc = con_custom!(gc, mpc, Ue, Ŷe, ϵ)
531-
g = con_nonlinprog!(g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
532-
return obj_nonlinprog!(Ȳ, Ū, mpc, model, Ue, Ŷe, ΔŨ)::T
533-
end
534-
function gfunc_i(i, ΔŨtup::NTuple{N, T}) where {N, T<:Real}
535-
ΔŨ1 = ΔŨtup[begin]
536-
ΔŨ, g = get_tmp(ΔŨ_cache, ΔŨ1), get_tmp(g_cache, ΔŨ1)
517+
function update_simulations!(ΔŨ, ΔŨtup::NTuple{N, T}) where {N, T<:Real}
537518
if any(new !== old for (new, old) in zip(ΔŨtup, ΔŨ)) # new ΔŨtup, update predictions:
519+
ΔŨ1 = ΔŨtup[begin]
538520
for i in eachindex(ΔŨtup)
539521
ΔŨ[i] = ΔŨtup[i] # ΔŨ .= ΔŨtup seems to produce a type instability
540522
end
541523
Ŷe, Ue = get_tmp(Ŷe_cache, ΔŨ1), get_tmp(Ue_cache, ΔŨ1)
542524
Ȳ, Ū = get_tmp(Ȳ_cache, ΔŨ1), get_tmp(Ū_cache, ΔŨ1)
543525
x̂0, x̂0next = get_tmp(x̂0_cache, ΔŨ1), get_tmp(x̂0next_cache, ΔŨ1)
544526
u0, û0 = get_tmp(u0_cache, ΔŨ1), get_tmp(û0_cache, ΔŨ1)
545-
gc = get_tmp(gc_cache, ΔŨ1)
527+
gc, g = get_tmp(gc_cache, ΔŨ1), get_tmp(g_cache, ΔŨ1)
546528
Ŷ0, x̂0end = predict!(Ȳ, x̂0, x̂0next, u0, û0, mpc, model, ΔŨ)
547-
Ue, Ŷe = extended_predictions!(Ue, Ŷe, Ū, mpc, model, Ŷ0, ΔŨ)
529+
Ŷe, Ue = extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
548530
ϵ = (nϵ 0) ? ΔŨ[end] : zero(T) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
549531
gc = con_custom!(gc, mpc, Ue, Ŷe, ϵ)
550532
g = con_nonlinprog!(g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
551533
end
534+
return nothing
535+
end
536+
function Jfunc(ΔŨtup::Vararg{T, N}) where {N, T<:Real}
537+
ΔŨ1 = ΔŨtup[begin]
538+
ΔŨ = get_tmp(ΔŨ_cache, ΔŨ1)
539+
update_simulations!(ΔŨ, ΔŨtup)
540+
Ȳ, Ū = get_tmp(Ȳ_cache, ΔŨ1), get_tmp(Ū_cache, ΔŨ1)
541+
Ŷe, Ue = get_tmp(Ŷe_cache, ΔŨ1), get_tmp(Ue_cache, ΔŨ1)
542+
return obj_nonlinprog!(Ȳ, Ū, mpc, model, Ue, Ŷe, ΔŨ)::T
543+
end
544+
function gfunc_i(i, ΔŨtup::NTuple{N, T}) where {N, T<:Real}
545+
ΔŨ1 = ΔŨtup[begin]
546+
ΔŨ = get_tmp(ΔŨ_cache, ΔŨ1)
547+
update_simulations!(ΔŨ, ΔŨtup)
548+
g = get_tmp(g_cache, ΔŨ1)
552549
return g[i]::T
553550
end
554-
gfunc = [(ΔŨ...) -> gfunc_i(i, ΔŨ) for i in 1:ng]
555-
return Jfunc, gfunc
551+
gfuncs = Vector{Function}(undef, ng)
552+
for i in 1:ng
553+
# this is another syntax for anonymous function, allowing parameters T and N:
554+
gfuncs[i] = function (ΔŨtup::Vararg{T, N}) where {N, T<:Real}
555+
return gfunc_i(i, ΔŨtup)
556+
end
557+
end
558+
return Jfunc, gfuncs
556559
end
557560

558-
function init_nonlincon!(mpc::NonLinMPC, ::LinModel, gfunc::Vector{<:Function})
561+
function init_nonlincon!(mpc::NonLinMPC, ::LinModel, gfuncs::Vector{<:Function})
559562
optim, con = mpc.optim, mpc.con
560563
nΔŨ = length(mpc.ΔŨ)
561564
if length(con.i_g) 0
562565
i_base = 0
563566
for i in 1:con.nc
564567
name = Symbol("g_c_$i")
565-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
568+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
566569
end
567570
end
568571
return nothing
569572
end
570573

571-
function init_nonlincon!(mpc::NonLinMPC, ::NonLinModel, gfunc::Vector{<:Function})
574+
function init_nonlincon!(mpc::NonLinMPC, ::NonLinModel, gfuncs::Vector{<:Function})
572575
optim, con = mpc.optim, mpc.con
573576
ny, nx̂, Hp, nΔŨ = mpc.estim.model.ny, mpc.estim.nx̂, mpc.Hp, length(mpc.ΔŨ)
574577
if length(con.i_g) 0
575578
i_base = 0
576579
for i in eachindex(con.Y0min)
577580
name = Symbol("g_Y0min_$i")
578-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
581+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
579582
end
580583
i_base = 1Hp*ny
581584
for i in eachindex(con.Y0max)
582585
name = Symbol("g_Y0max_$i")
583-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
586+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
584587
end
585588
i_base = 2Hp*ny
586589
for i in eachindex(con.x̂0min)
587590
name = Symbol("g_x̂0min_$i")
588-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
591+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
589592
end
590593
i_base = 2Hp*ny + nx̂
591594
for i in eachindex(con.x̂0max)
592595
name = Symbol("g_x̂0max_$i")
593-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
596+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
594597
end
595598
i_base = 2Hp*ny + 2nx̂
596599
for i in 1:con.nc
597600
name = Symbol("g_c_$i")
598-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
601+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfuncs[i_base+i]; name)
599602
end
600603
end
601604
return nothing

src/estimator/mhe/construct.jl

+36-29
Original file line numberDiff line numberDiff line change
@@ -1251,36 +1251,36 @@ function init_optimization!(
12511251
JuMP.set_attribute(optim, "nlp_scaling_max_gradient", 10.0/C)
12521252
end
12531253
end
1254-
Jfunc, gfunc = get_optim_functions(estim, optim)
1254+
Jfunc, gfuncs = get_optim_functions(estim, optim)
12551255
@operator(optim, J, nZ̃, Jfunc)
12561256
@objective(optim, Min, J(Z̃var...))
12571257
nV̂, nX̂ = estim.He*estim.nym, estim.He*estim.nx̂
12581258
if length(con.i_g) 0
12591259
for i in eachindex(con.X̂0min)
12601260
name = Symbol("g_X̂0min_$i")
12611261
optim[name] = JuMP.add_nonlinear_operator(
1262-
optim, nZ̃, gfunc[i]; name
1262+
optim, nZ̃, gfuncs[i]; name
12631263
)
12641264
end
12651265
i_end_X̂min = nX̂
12661266
for i in eachindex(con.X̂0max)
12671267
name = Symbol("g_X̂0max_$i")
12681268
optim[name] = JuMP.add_nonlinear_operator(
1269-
optim, nZ̃, gfunc[i_end_X̂min + i]; name
1269+
optim, nZ̃, gfuncs[i_end_X̂min + i]; name
12701270
)
12711271
end
12721272
i_end_X̂max = 2*nX̂
12731273
for i in eachindex(con.V̂min)
12741274
name = Symbol("g_V̂min_$i")
12751275
optim[name] = JuMP.add_nonlinear_operator(
1276-
optim, nZ̃, gfunc[i_end_X̂max + i]; name
1276+
optim, nZ̃, gfuncs[i_end_X̂max + i]; name
12771277
)
12781278
end
12791279
i_end_V̂min = 2*nX̂ + nV̂
12801280
for i in eachindex(con.V̂max)
12811281
name = Symbol("g_V̂max_$i")
12821282
optim[name] = JuMP.add_nonlinear_operator(
1283-
optim, nZ̃, gfunc[i_end_V̂min + i]; name
1283+
optim, nZ̃, gfuncs[i_end_V̂min + i]; name
12841284
)
12851285
end
12861286
end
@@ -1289,9 +1289,10 @@ end
12891289

12901290

12911291
"""
1292-
get_optim_functions(estim::MovingHorizonEstimator, ::JuMP.GenericModel) -> Jfunc, gfunc
1292+
get_optim_functions(estim::MovingHorizonEstimator, ::JuMP.GenericModel) -> Jfunc, gfuncs
12931293
1294-
Get the objective `Jfunc` and constraints `gfunc` functions for [`MovingHorizonEstimator`](@ref).
1294+
Get the objective `Jfunc` function and constraint `gfuncs` function vector for
1295+
[`MovingHorizonEstimator`](@ref).
12951296
12961297
Inspired from: [User-defined operators with vector outputs](https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-operators-with-vector-outputs)
12971298
"""
@@ -1309,35 +1310,41 @@ function get_optim_functions(
13091310
x̄_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nx̂), Nc)
13101311
û0_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nu), Nc)
13111312
ŷ0_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nŷ), Nc)
1312-
function Jfunc(Z̃tup::T...)::T where {T <: Real}
1313-
Z̃1 = Z̃tup[begin]
1314-
Z̃, g = get_tmp(Z̃_cache, Z̃1), get_tmp(g_cache, Z̃1)
1315-
for i in eachindex(Z̃tup)
1316-
Z̃[i] = Z̃tup[i] # Z̃ .= Z̃tup seems to produce a type instability
1317-
end
1318-
V̂, X̂0 = get_tmp(V̂_cache, Z̃1), get_tmp(X̂0_cache, Z̃1)
1319-
û0, ŷ0 = get_tmp(û0_cache, Z̃1), get_tmp(ŷ0_cache, Z̃1)
1320-
V̂, X̂0 = predict!(V̂, X̂0, û0, ŷ0, estim, model, Z̃)
1321-
ϵ = (nϵ 0) ? Z̃[begin] : zero(T) # ϵ = 0 if Cwt=Inf (meaning: no relaxation)
1322-
g = con_nonlinprog!(g, estim, model, X̂0, V̂, ϵ)
1323-
= get_tmp(x̄_cache, Z̃1)
1324-
return obj_nonlinprog!(x̄, estim, model, V̂, Z̃)::T
1325-
end
1326-
function gfunc_i(i, Z̃tup::NTuple{N, T})::T where {N, T <:Real}
1327-
Z̃1 = Z̃tup[begin]
1328-
Z̃, g = get_tmp(Z̃_cache, Z̃1), get_tmp(g_cache, Z̃1)
1313+
function update_simulations!(Z̃, Z̃tup::NTuple{N, T}) where {N, T <:Real}
13291314
if any(new !== old for (new, old) in zip(Z̃tup, Z̃)) # new Z̃tup, update predictions:
1330-
V̂, X̂0 = get_tmp(V̂_cache, Z̃1), get_tmp(X̂0_cache, Z̃1)
1331-
û0, ŷ0 = get_tmp(û0_cache, Z̃1), get_tmp(ŷ0_cache, Z̃1)
1315+
Z̃1 = Z̃tup[begin]
13321316
for i in eachindex(Z̃tup)
13331317
Z̃[i] = Z̃tup[i] # Z̃ .= Z̃tup seems to produce a type instability
13341318
end
1335-
V̂, X̂0 = predict!(V̂, X̂0, û0, ŷ0, estim, model, Z̃)
1319+
V̂, X̂0 = get_tmp(V̂_cache, Z̃1), get_tmp(X̂0_cache, Z̃1)
1320+
û0, ŷ0 = get_tmp(û0_cache, Z̃1), get_tmp(ŷ0_cache, Z̃1)
1321+
g = get_tmp(g_cache, Z̃1)
1322+
V̂, X̂0 = predict!(V̂, X̂0, û0, ŷ0, estim, model, Z̃)
13361323
ϵ = (nϵ 0) ? Z̃[begin] : zero(T) # ϵ = 0 if Cwt=Inf (meaning: no relaxation)
13371324
g = con_nonlinprog!(g, estim, model, X̂0, V̂, ϵ)
13381325
end
1326+
return nothing
1327+
end
1328+
function Jfunc(Z̃tup::Vararg{T, N}) where {N, T<:Real}
1329+
Z̃1 = Z̃tup[begin]
1330+
= get_tmp(Z̃_cache, Z̃1)
1331+
update_simulations!(Z̃, Z̃tup)
1332+
x̄, V̂ = get_tmp(x̄_cache, Z̃1), get_tmp(V̂_cache, Z̃1)
1333+
return obj_nonlinprog!(x̄, estim, model, V̂, Z̃)::T
1334+
end
1335+
function gfunc_i(i, Z̃tup::NTuple{N, T})::T where {N, T <:Real}
1336+
Z̃1 = Z̃tup[begin]
1337+
= get_tmp(Z̃_cache, Z̃1)
1338+
update_simulations!(Z̃, Z̃tup)
1339+
g = get_tmp(g_cache, Z̃1)
13391340
return g[i]
13401341
end
1341-
gfunc = [(Z̃...) -> gfunc_i(i, Z̃) for i in 1:ng]
1342-
return Jfunc, gfunc
1342+
gfuncs = Vector{Function}(undef, ng)
1343+
for i in 1:ng
1344+
# this is another syntax for anonymous function, allowing parameters T and N:
1345+
gfuncs[i] = function (ΔŨtup::Vararg{T, N}) where {N, T<:Real}
1346+
return gfunc_i(i, ΔŨtup)
1347+
end
1348+
end
1349+
return Jfunc, gfuncs
13431350
end

0 commit comments

Comments
 (0)