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"""
531532function 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. nϵ
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
543657end
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
9501064end
9511065
0 commit comments