Skip to content

Commit bba9ce7

Browse files
authored
Merge pull request #290 from JuliaControl/getinfo_deriv
added: derivatives in `getinfo` dictionnary for `NonLinMPC` and `MovingHorizonEstimator`
2 parents 2730e8b + 78886db commit bba9ce7

File tree

9 files changed

+248
-17
lines changed

9 files changed

+248
-17
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
3-
version = "1.13.3"
3+
version = "1.14.0"
44
authors = ["Francis Gagnon"]
55

66
[deps]

src/ModelPredictiveControl.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ using RecipesBase
99

1010
using DifferentiationInterface: ADTypes.AbstractADType, AutoForwardDiff
1111
using DifferentiationInterface: AutoSparse, SecondOrder
12+
using DifferentiationInterface: gradient, jacobian, hessian
13+
using DifferentiationInterface: value_and_gradient, value_gradient_and_hessian
1214
using DifferentiationInterface: gradient!, value_and_gradient!, prepare_gradient
1315
using DifferentiationInterface: jacobian!, value_and_jacobian!, prepare_jacobian
1416
using DifferentiationInterface: hessian!, value_gradient_and_hessian!, prepare_hessian

src/controller/execute.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,20 @@ The function should be called after calling [`moveinput!`](@ref). It returns the
105105
- `:d` : current measured disturbance, ``\mathbf{d}(k)``
106106
107107
For [`LinMPC`](@ref) and [`NonLinMPC`](@ref), the field `:sol` also contains the optimizer
108-
solution summary that can be printed. Lastly, the economical cost `:JE` and the custom
109-
nonlinear constraints `:gc` values at the optimum are also available for [`NonLinMPC`](@ref).
108+
solution summary that can be printed. Lastly, for [`NonLinMPC`](@ref), the following fields
109+
are also available:
110+
111+
- `:JE`: economic cost value at the optimum, ``J_E``
112+
- `:gc`: custom nonlinear constraints values at the optimum, ``\mathbf{g_c}``
113+
- `:∇J` or *`:nablaJ`* : gradient of the objective function, ``\mathbf{\nabla} J``
114+
- `:∇²J` or *`:nabla2J`* : Hessian of the objective function, ``\mathbf{\nabla^2}J``
115+
- `:∇g` or *`:nablag`* : Jacobian of the inequality constraint, ``\mathbf{\nabla g}``
116+
- `:∇²ℓg` or *`:nabla2lg`* : Hessian of the inequality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g}}``
117+
- `:∇geq` or *`:nablageq`* : Jacobian of the equality constraint, ``\mathbf{\nabla g_{eq}}``
118+
- `:∇²ℓgeq` or *`:nabla2lgeq`* : Hessian of the equality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g_{eq}}}``
119+
120+
Note that Hessian of Lagrangians are not fully supported yet. Their nonzero coefficients are
121+
random values for now.
110122
111123
# Examples
112124
```jldoctest

src/controller/nonlinmpc.jl

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,9 +526,11 @@ end
526526
"""
527527
addinfo!(info, mpc::NonLinMPC) -> info
528528
529-
For [`NonLinMPC`](@ref), add `:sol` and the optimal economic cost `:JE`.
529+
For [`NonLinMPC`](@ref), add `:sol`, the custom nonlinear objective `:JE`, the custom
530+
constraint `:gc`, and the various derivatives.
530531
"""
531532
function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
533+
# --- variables specific to NonLinMPC ---
532534
U, Ŷ, D̂, ŷ, d, ϵ = info[:U], info[:Ŷ], info[:D̂], info[:ŷ], info[:d], info[]
533535
Ue = [U; U[(end - mpc.estim.model.nu + 1):end]]
534536
Ŷe = [ŷ; Ŷ]
@@ -539,6 +541,118 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
539541
info[:JE] = JE
540542
info[:gc] = LHS
541543
info[:sol] = JuMP.solution_summary(mpc.optim, verbose=true)
544+
# --- objective derivatives ---
545+
model, optim, con = mpc.estim.model, mpc.optim, mpc.con
546+
transcription = mpc.transcription
547+
nu, ny, nx̂, nϵ = model.nu, model.ny, mpc.estim.nx̂, mpc.
548+
nk = get_nk(model, transcription)
549+
Hp, Hc = mpc.Hp, mpc.Hc
550+
ng = length(con.i_g)
551+
nc, neq = con.nc, con.neq
552+
nU, nŶ, nX̂, nK = mpc.Hp*nu, Hp*ny, Hp*nx̂, Hp*nk
553+
nΔŨ, nUe, nŶe = nu*Hc + nϵ, nU + nu, nŶ + ny
554+
ΔŨ = zeros(NT, nΔŨ)
555+
x̂0end = zeros(NT, nx̂)
556+
K0 = zeros(NT, nK)
557+
Ue, Ŷe = zeros(NT, nUe), zeros(NT, nŶe)
558+
U0, Ŷ0 = zeros(NT, nU), zeros(NT, nŶ)
559+
Û0, X̂0 = zeros(NT, nU), zeros(NT, nX̂)
560+
gc, g = zeros(NT, nc), zeros(NT, ng)
561+
geq = zeros(NT, neq)
562+
J_cache = (
563+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
564+
Cache(Û0), Cache(K0), Cache(X̂0),
565+
Cache(gc), Cache(g), Cache(geq),
566+
)
567+
function J!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq)
568+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
569+
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
570+
end
571+
if !isnothing(mpc.hessian)
572+
_, ∇J, ∇²J = value_gradient_and_hessian(J!, mpc.hessian, mpc.Z̃, J_cache...)
573+
else
574+
∇J, ∇²J = gradient(J!, mpc.gradient, mpc.Z̃, J_cache...), nothing
575+
end
576+
# --- inequality constraint derivatives ---
577+
old_i_g = copy(con.i_g)
578+
con.i_g .= 1 # temporarily set all constraints as finite so g is entirely computed
579+
∇g_cache = (
580+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
581+
Cache(Û0), Cache(K0), Cache(X̂0),
582+
Cache(gc), Cache(geq),
583+
)
584+
function g!(g, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, geq)
585+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
586+
return nothing
587+
end
588+
∇g = jacobian(g!, g, mpc.jacobian, mpc.Z̃, ∇g_cache...)
589+
if !isnothing(mpc.hessian) && any(old_i_g)
590+
@warn(
591+
"Retrieving optimal Hessian of the Lagrangian is not fully supported yet.\n"*
592+
"Its nonzero coefficients are random values for now.", maxlog=1
593+
)
594+
function ℓ_g(Z̃, λ, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, geq, g)
595+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
596+
return dot(λ, g)
597+
end
598+
∇²g_cache = (
599+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
600+
Cache(Û0), Cache(K0), Cache(X̂0),
601+
Cache(gc), Cache(geq), Cache(g)
602+
)
603+
nonlincon = optim[:nonlinconstraint]
604+
λ = JuMP.dual.(nonlincon) # FIXME: does not work for now
605+
λ = rand(NT, ng)
606+
∇²ℓg = hessian(ℓ_g, mpc.hessian, mpc.Z̃, Constant(λ), ∇²g_cache...)
607+
else
608+
∇²ℓg = nothing
609+
end
610+
con.i_g .= old_i_g # restore original finite/infinite constraint indices
611+
# --- equality constraint derivatives ---
612+
geq_cache = (
613+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
614+
Cache(Û0), Cache(K0), Cache(X̂0),
615+
Cache(gc), Cache(g)
616+
)
617+
function geq!(geq, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g)
618+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
619+
return nothing
620+
end
621+
∇geq = jacobian(geq!, geq, mpc.jacobian, mpc.Z̃, geq_cache...)
622+
if !isnothing(mpc.hessian) && con.neq > 0
623+
@warn(
624+
"Retrieving optimal Hessian of the Lagrangian is not fully supported yet.\n"*
625+
"Its nonzero coefficients are random values for now.", maxlog=1
626+
)
627+
∇²geq_cache = (
628+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
629+
Cache(Û0), Cache(K0), Cache(X̂0),
630+
Cache(gc), Cache(geq), Cache(g)
631+
)
632+
function ℓ_geq(Z̃, λeq, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, geq, g)
633+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
634+
return dot(λeq, geq)
635+
end
636+
nonlinconeq = optim[:nonlinconstrainteq]
637+
λeq = JuMP.dual.(nonlinconeq) # FIXME: does not work for now
638+
λeq = ones(NT, neq)
639+
∇²ℓgeq = hessian(ℓ_geq, mpc.hessian, mpc.Z̃, Constant(λeq), ∇²geq_cache...)
640+
else
641+
∇²ℓgeq = nothing
642+
end
643+
info[:∇J] = ∇J
644+
info[:∇²J] = ∇²J
645+
info[:∇g] = ∇g
646+
info[:∇²ℓg] = ∇²ℓg
647+
info[:∇geq] = ∇geq
648+
info[:∇²ℓgeq] = ∇²ℓgeq
649+
# --- non-Unicode fields ---
650+
info[:nablaJ] = ∇J
651+
info[:nabla2J] = ∇²J
652+
info[:nablag] = ∇g
653+
info[:nabla2lg] = ∇²ℓg
654+
info[:nablageq] = ∇geq
655+
info[:nabla2lgeq] = ∇²ℓgeq
542656
return info
543657
end
544658

@@ -942,10 +1056,10 @@ function set_nonlincon!(
9421056
optim, JuMP.Vector{JuMP.VariableRef}, MOI.VectorNonlinearOracle{JNT}
9431057
)
9441058
map(con_ref -> JuMP.delete(optim, con_ref), nonlin_constraints)
945-
optim[:g_oracle] = g_oracle
946-
optim[:geq_oracle] = geq_oracle
947-
any(mpc.con.i_g) && @constraint(optim, Z̃var in g_oracle)
948-
mpc.con.neq > 0 && @constraint(optim, Z̃var in geq_oracle)
1059+
JuMP.unregister(optim, :nonlinconstraint)
1060+
JuMP.unregister(optim, :nonlinconstrainteq)
1061+
any(mpc.con.i_g) && @constraint(optim, nonlinconstraint, Z̃var in g_oracle)
1062+
mpc.con.neq > 0 && @constraint(optim, nonlinconstrainteq, Z̃var in geq_oracle)
9491063
return nothing
9501064
end
9511065

src/estimator/mhe/construct.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,9 +1504,7 @@ function get_nonlincon_oracle(
15041504
return dot(λi, gi)
15051505
end
15061506
Z̃_∇gi = fill(myNaN, nZ̃) # NaN to force update_predictions! at first call
1507-
∇gi_cache = (
1508-
Cache(V̂), Cache(X̂0), Cache(û0), Cache(k0), Cache(ŷ0), Cache(g)
1509-
)
1507+
∇gi_cache = (Cache(V̂), Cache(X̂0), Cache(û0), Cache(k0), Cache(ŷ0), Cache(g))
15101508
# temporarily "fill" the estimation window for the preparation of the gradient:
15111509
estim.Nk[] = He
15121510
∇gi_prep = prepare_jacobian(gi!, gi, jac, Z̃_∇gi, ∇gi_cache...; strict)
@@ -1577,7 +1575,7 @@ function set_nonlincon!(
15771575
optim, JuMP.Vector{JuMP.VariableRef}, MOI.VectorNonlinearOracle{JNT}
15781576
)
15791577
map(con_ref -> JuMP.delete(optim, con_ref), nonlin_constraints)
1580-
optim[:g_oracle] = g_oracle
1581-
any(estim.con.i_g) && @constraint(optim, Z̃var in g_oracle)
1578+
JuMP.unregister(optim, :nonlinconstraint)
1579+
any(estim.con.i_g) && @constraint(optim, nonlinconstraint, Z̃var in g_oracle)
15821580
return nothing
15831581
end

src/estimator/mhe/execute.jl

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ following fields:
9494
- `:D` : measured disturbances over ``N_k``, ``\mathbf{D}``
9595
- `:sol` : solution summary of the optimizer for printing
9696
97+
For [`NonLinModel`](@ref), it also includes the following derivative fields:
98+
99+
- `:JE`: economic cost value at the optimum, ``J_E``
100+
- `:gc`: custom nonlinear constraints values at the optimum, ``\mathbf{g_c}``
101+
- `:∇J` or *`:nablaJ`* : gradient of the objective function, ``\mathbf{\nabla} J``
102+
- `:∇²J` or *`:nabla2J`* : Hessian of the objective function, ``\mathbf{\nabla^2}J``
103+
- `:∇g` or *`:nablag`* : Jacobian of the inequality constraint, ``\mathbf{\nabla g}``
104+
- `:∇²ℓg` or *`:nabla2lg`* : Hessian of the inequality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g}}``
105+
106+
Note that Hessian of Lagrangians are not fully supported yet. Their nonzero coefficients are
107+
random values for now.
108+
97109
# Examples
98110
```jldoctest
99111
julia> model = LinModel(ss(1.0, 1.0, 1.0, 0, 5.0));
@@ -163,9 +175,85 @@ function getinfo(estim::MovingHorizonEstimator{NT}) where NT<:Real
163175
info[:Yhatm] = info[:Ŷm]
164176
# --- deprecated fields ---
165177
info[] = info[]
178+
info = addinfo!(info, estim, model)
166179
return info
167180
end
168181

182+
183+
"""
184+
addinfo!(info, estim::MovingHorizonEstimator, model::NonLinModel)
185+
186+
For [`NonLinModel`](@ref), add the various derivatives.
187+
"""
188+
function addinfo!(
189+
info, estim::MovingHorizonEstimator{NT}, model::NonLinModel
190+
) where NT <:Real
191+
# --- objective derivatives ---
192+
optim, con = estim.optim, estim.con
193+
nx̂, nym, nŷ, nu, nk = estim.nx̂, estim.nym, model.ny, model.nu, model.nk
194+
He = estim.He
195+
nV̂, nX̂, ng = He*nym, He*nx̂, length(con.i_g)
196+
V̂, X̂0 = zeros(NT, nV̂), zeros(NT, nX̂)
197+
k0 = zeros(NT, nk)
198+
û0, ŷ0 = zeros(NT, nu), zeros(NT, nŷ)
199+
g = zeros(NT, ng)
200+
= zeros(NT, nx̂)
201+
J_cache = (
202+
Cache(V̂), Cache(X̂0), Cache(û0), Cache(k0), Cache(ŷ0),
203+
Cache(g),
204+
Cache(x̄),
205+
)
206+
function J!(Z̃, V̂, X̂0, û0, k0, ŷ0, g, x̄)
207+
update_prediction!(V̂, X̂0, û0, k0, ŷ0, g, estim, Z̃)
208+
return obj_nonlinprog!(x̄, estim, model, V̂, Z̃)
209+
end
210+
if !isnothing(estim.hessian)
211+
_, ∇J, ∇²J = value_gradient_and_hessian(J!, estim.hessian, estim.Z̃, J_cache...)
212+
else
213+
∇J, ∇²J = gradient(J!, estim.gradient, estim.Z̃, J_cache...), nothing
214+
end
215+
# --- inequality constraint derivatives ---
216+
old_i_g = copy(estim.con.i_g)
217+
estim.con.i_g .= 1 # temporarily set all constraints as finite so g is entirely computed
218+
∇g_cache = (Cache(V̂), Cache(X̂0), Cache(û0), Cache(k0), Cache(ŷ0))
219+
function g!(g, Z̃, V̂, X̂0, û0, k0, ŷ0)
220+
update_prediction!(V̂, X̂0, û0, k0, ŷ0, g, estim, Z̃)
221+
return nothing
222+
end
223+
∇g = jacobian(g!, g, estim.jacobian, estim.Z̃, ∇g_cache...)
224+
if !isnothing(estim.hessian) && any(old_i_g)
225+
@warn(
226+
"Retrieving optimal Hessian of the Lagrangian is not fully supported yet.\n"*
227+
"Its nonzero coefficients are random values for now.", maxlog=1
228+
)
229+
∇²g_cache = (Cache(V̂), Cache(X̂0), Cache(û0), Cache(k0), Cache(ŷ0), Cache(g))
230+
function ℓ_g(Z̃, λ, V̂, X̂0, û0, k0, ŷ0, g)
231+
update_prediction!(V̂, X̂0, û0, k0, ŷ0, g, estim, Z̃)
232+
return dot(λ, g)
233+
end
234+
nonlincon = optim[:nonlinconstraint]
235+
λ = JuMP.dual.(nonlincon) # FIXME: does not work for now
236+
λ = ones(NT, ng)
237+
∇²ℓg = hessian(ℓ_g, estim.hessian, estim.Z̃, Constant(λ), ∇²g_cache...)
238+
else
239+
∇²ℓg = nothing
240+
end
241+
estim.con.i_g .= old_i_g # restore original finite/infinite constraint indices
242+
info[:∇J] = ∇J
243+
info[:∇²J] = ∇²J
244+
info[:∇g] = ∇g
245+
info[:∇²ℓg] = ∇²ℓg
246+
# --- non-Unicode fields ---
247+
info[:nablaJ] = ∇J
248+
info[:nabla2J] = ∇²J
249+
info[:nablag] = ∇g
250+
info[:nabla2lg] = ∇²ℓg
251+
return info
252+
end
253+
254+
"Nothing to add in the `info` dict for [`LinModel`](@ref)."
255+
addinfo!(info, ::MovingHorizonEstimator, ::LinModel) = info
256+
169257
"""
170258
getε(estim::MovingHorizonEstimator, Z̃) -> ε
171259

src/general.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ const ALL_COLORING_ORDERS = (
1616
RandomOrder(StableRNG(0), 0)
1717
)
1818

19+
const HIDDEN_GETINFO_KEYS_MHE = (
20+
:What, :xhatarr, :epsilon, :Xhat, :xhat, :Vhat, :Pbar, :xbar, :Yhat, :Yhatm, ,
21+
:nablaJ, :nabla2J, :nablag, :nabla2lg
22+
)
23+
24+
const HIDDEN_GETINFO_KEYS_MPC = (
25+
:DeltaU, :epsilon, :Dhat, :yhat, :Yhat, :xhatend, :Yhats, :Rhaty, :Rhatu,
26+
:nablaJ, :nabla2J, :nablag, :nabla2lg, :nablageq, :nabla2lgeq
27+
)
28+
1929
"Termination status that means 'no solution available'."
2030
const ERROR_STATUSES = (
2131
JuMP.INFEASIBLE, JuMP.DUAL_INFEASIBLE, JuMP.LOCALLY_INFEASIBLE,
@@ -40,12 +50,17 @@ function info2debugstr(info)
4050
mystr = "Content of getinfo dictionary:\n"
4151
for (key, value) in info
4252
(key == :sol) && continue
53+
if key in HIDDEN_GETINFO_KEYS_MHE || key in HIDDEN_GETINFO_KEYS_MPC
54+
# skip the redundant non-Unicode keys
55+
continue
56+
end
4357
mystr *= " :$key => $value\n"
4458
end
4559
if haskey(info, :sol)
4660
split_sol = split(string(info[:sol]), "\n")
47-
solstr = join((lpad(line, length(line) + 2) for line in split_sol), "\n", "")
48-
mystr *= " :sol => \n"*solstr
61+
# Add the treeview prefix to each line
62+
solstr = join((" " * line for line in split_sol), "\n")
63+
mystr *= " :sol => \n" * solstr * "\n" # Ensure a trailing newline
4964
end
5065
return mystr
5166
end

test/2_test_state_estim.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ end
967967
@test info[:Ŷ][end-1:end] [50, 30] atol=1e-9
968968

969969
mhe1c = MovingHorizonEstimator(nonlinmodel, He=2, direct=false, hessian=true)
970+
mhe1c = setconstraint!(mhe1c, v̂min = [-1000, -1000], v̂max = [1000, 1000]) # coverage of getinfo Hessian of Lagrangian
970971
preparestate!(mhe1c, [50, 30], [5])
971972
= updatestate!(mhe1c, [10, 50], [50, 30], [5])
972973
@test zeros(6) atol=1e-9

test/3_test_predictive_control.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,6 @@ end
814814
preparestate!(nmpc4, [0], [0])
815815
u = moveinput!(nmpc4, [0], d, R̂u=fill(12, nmpc4.Hp))
816816
@test u [12] atol=5e-2
817-
linmodel3 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), 0, 0, 3000.0)
818817
nmpc5 = NonLinMPC(nonlinmodel, Hp=1, Hc=1, Cwt=Inf, transcription=MultipleShooting())
819818
nmpc5 = setconstraint!(nmpc5, ymin=[1])
820819
f! = (ẋ,x,u,_,_) -> ẋ .= -0.001x .+ u
@@ -830,6 +829,7 @@ end
830829
preparestate!(nmpc5_1, [0.0])
831830
u = moveinput!(nmpc5_1, [1/0.001])
832831
@test u [1.0] atol=5e-2
832+
linmodel3 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), 0, 0, 3000.0)
833833
nmpc6 = NonLinMPC(linmodel3, Hp=10)
834834
preparestate!(nmpc6, [0])
835835
@test moveinput!(nmpc6, [0]) [0.0] atol=5e-2
@@ -848,7 +848,8 @@ end
848848
@test info[:u] u
849849
@test info[:Ŷ][end] 10 atol=5e-2
850850
transcription = MultipleShooting(f_threads=true, h_threads=true)
851-
nmpc8t = NonLinMPC(nonlinmodel; Nwt=[0], Hp=100, Hc=1, transcription)
851+
nmpc8t = NonLinMPC(nonlinmodel; Nwt=[0], Hp=100, Hc=1, transcription, hessian=true)
852+
nmpc8t = setconstraint!(nmpc8t, ymax=[100], ymin=[-100]) # coverage of getinfo! Hessians of Lagrangian
852853
preparestate!(nmpc8t, [0], [0])
853854
u = moveinput!(nmpc8t, [10], [0])
854855
@test u [2] atol=5e-2

0 commit comments

Comments
 (0)