Skip to content

Commit ea4c42a

Browse files
committed
wip: adding derivatives to getinfo(mpc::NonLinMPC)
1 parent 4d99637 commit ea4c42a

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed

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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,17 @@ 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}}}``
110119
111120
# Examples
112121
```jldoctest

src/controller/nonlinmpc.jl

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,8 @@ 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
532533
U, Ŷ, D̂, ŷ, d, ϵ = info[:U], info[:Ŷ], info[:D̂], info[:ŷ], info[:d], info[]
@@ -536,9 +537,99 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
536537
JE = mpc.JE(Ue, Ŷe, D̂e, mpc.p)
537538
LHS = Vector{NT}(undef, mpc.con.nc)
538539
mpc.con.gc!(LHS, Ue, Ŷe, D̂e, mpc.p, ϵ)
540+
model, optim = mpc.estim.model, mpc.optim
541+
transcription = mpc.transcription
542+
nu, ny, nx̂, nϵ = model.nu, model.ny, mpc.estim.nx̂, mpc.
543+
nk = get_nk(model, transcription)
544+
Hp, Hc = mpc.Hp, mpc.Hc
545+
ng = length(mpc.con.i_g)
546+
nc, neq = mpc.con.nc, mpc.con.neq
547+
nU, nŶ, nX̂, nK = mpc.Hp*nu, Hp*ny, Hp*nx̂, Hp*nk
548+
nΔŨ, nUe, nŶe = nu*Hc + nϵ, nU + nu, nŶ + ny
549+
ΔŨ = zeros(NT, nΔŨ)
550+
x̂0end = zeros(NT, nx̂)
551+
K0 = zeros(NT, nK)
552+
Ue, Ŷe = zeros(NT, nUe), zeros(NT, nŶe)
553+
U0, Ŷ0 = zeros(NT, nU), zeros(NT, nŶ)
554+
Û0, X̂0 = zeros(NT, nU), zeros(NT, nX̂)
555+
gc, g = zeros(NT, nc), zeros(NT, ng)
556+
geq = zeros(NT, neq)
557+
J_cache = (
558+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
559+
Cache(Û0), Cache(K0), Cache(X̂0),
560+
Cache(gc), Cache(g), Cache(geq),
561+
)
562+
function J!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq)
563+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
564+
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
565+
end
566+
if !isnothing(mpc.hessian)
567+
_, ∇J, ∇²J = value_gradient_and_hessian(J!, mpc.hessian, mpc.Z̃, J_cache...)
568+
else
569+
∇J, ∇²J = gradient(J!, mpc.gradient, mpc.Z̃, J_cache...), nothing
570+
end
571+
JNT = typeof(mpc.optim).parameters[1]
572+
nonlin_constraints = JuMP.all_constraints(
573+
optim, JuMP.Vector{JuMP.VariableRef}, MOI.VectorNonlinearOracle{JNT}
574+
)
575+
g_con, geq_func = nonlin_constraints
576+
λ, λeq = JuMP.dual.(g_con), JuMP.dual.(geq_func)
577+
display(λ)
578+
display(λeq)
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+
#=
590+
if !isnothing(mpc.hessian)
591+
function ℓ_g(Z̃, λ, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, geq, g)
592+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
593+
return dot(λ, g)
594+
end
595+
∇²g_cache = (
596+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
597+
Cache(Û0), Cache(K0), Cache(X̂0),
598+
Cache(gc), Cache(geq), Cache(g)
599+
)
600+
∇²ℓg = hessian(ℓ_g, mpc.hessian, mpc.Z̃, Constant(λ), ∇²g_cache...)
601+
else
602+
∇²ℓg = nothing
603+
end
604+
=# ∇²ℓg = nothing #TODO: delete this line when enabling the above block
605+
geq_cache = (
606+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
607+
Cache(Û0), Cache(K0), Cache(X̂0),
608+
Cache(gc), Cache(g)
609+
)
610+
function geq!(geq, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g)
611+
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
612+
return nothing
613+
end
614+
∇geq = jacobian(geq!, geq, mpc.jacobian, mpc.Z̃, geq_cache...)
615+
∇²ℓgeq = nothing # TODO: implement later
616+
539617
info[:JE] = JE
540618
info[:gc] = LHS
619+
info[:∇J] = ∇J
620+
info[:∇²J] = ∇²J
621+
info[:∇g] = ∇g
622+
info[:∇²ℓg] = ∇²ℓg
623+
info[:∇geq] = ∇geq
624+
info[:∇²ℓgeq] = ∇²ℓgeq
541625
info[:sol] = JuMP.solution_summary(mpc.optim, verbose=true)
626+
# --- non-Unicode fields ---
627+
info[:nablaJ] = ∇J
628+
info[:nabla2J] = ∇²J
629+
info[:nablag] = ∇g
630+
info[:nabla2lg] = ∇²ℓg
631+
info[:nablageq] = ∇geq
632+
info[:nabla2lgeq] = ∇²ℓgeq
542633
return info
543634
end
544635

0 commit comments

Comments
 (0)