Skip to content

Commit 2fc5d59

Browse files
authored
Merge pull request #346 from JuliaControl/color_getinfo
added: number of colors in Jacobians and Hessians sparsity patterns available in `getinfo` dictionnary.
2 parents 88ce88a + 39b1523 commit 2fc5d59

File tree

6 files changed

+70
-18
lines changed

6 files changed

+70
-18
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 = "2.2.3"
3+
version = "2.3.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,7 @@ using RecipesBase
99

1010
using DifferentiationInterface: ADTypes.AbstractADType, AutoForwardDiff
1111
using DifferentiationInterface: AutoSparse, SecondOrder
12+
using DifferentiationInterface: Prep, SparseJacobianPrep, SparseHessianPrep
1213
using DifferentiationInterface: gradient, jacobian, hessian
1314
using DifferentiationInterface: value_and_gradient, value_and_jacobian
1415
using DifferentiationInterface: value_gradient_and_hessian
@@ -20,6 +21,7 @@ using SparseConnectivityTracer: TracerSparsityDetector
2021
using SparseMatrixColorings: GreedyColoringAlgorithm, sparsity_pattern
2122
using SparseMatrixColorings: NaturalOrder, LargestFirst, SmallestLast
2223
using SparseMatrixColorings: IncidenceDegree, DynamicLargestFirst, RandomOrder
24+
using SparseMatrixColorings: ncolors
2325

2426
import ProgressLogging
2527

src/controller/execute.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,17 @@ Lastly, the following fields are also available for [`NonLinMPC`](@ref) only:
117117
- `:gc`: custom nonlinear constraints values at the optimum, ``\mathbf{g_c}``
118118
- `:∇J` or *`:nablaJ`* : optimal gradient of the objective function, ``\mathbf{\nabla} J``
119119
- `:∇²J` or *`:nabla2J`* : optimal Hessian of the objective function, ``\mathbf{\nabla^2}J``
120+
- `:∇²J_ncolors` or *`:nabla2J_ncolors`* : number of colors in `:∇²J` sparsity pattern
120121
- `:g` : optimal nonlinear inequality constraint values, ``\mathbf{g}``
121122
- `:∇g` or *`:nablag`* : optimal Jacobian of the inequality constraint, ``\mathbf{\nabla g}``
123+
- `:∇g_ncolors` or *`:nablag_ncolors`* : number of colors in `:∇g` sparsity pattern
122124
- `:∇²ℓg` or *`:nabla2lg`* : optimal Hessian of the inequality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g}}``
125+
- `:∇²ℓg_ncolors` or *`:nabla2lg_ncolors`* : number of colors in `:∇²ℓg` sparsity pattern
123126
- `:geq` : optimal nonlinear equality constraint values, ``\mathbf{g_{eq}}``
124127
- `:∇geq` or *`:nablageq`* : optimal Jacobian of the equality constraint, ``\mathbf{\nabla g_{eq}}``
128+
- `:∇geq_ncolors` or *`:nablageq_ncolors`* : number of colors in `:∇geq` sparsity pattern
125129
- `:∇²ℓgeq` or *`:nabla2lgeq`* : optimal Hessian of the equality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g_{eq}}}``
130+
- `:∇²ℓgeq_ncolors` or *`:nabla2lgeq_ncolors`* : number of colors in `:∇²ℓgeq` sparsity pattern
126131
127132
Note that the inequality constraint vectors and matrices only include the non-`Inf` values.
128133

src/controller/nonlinmpc.jl

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,13 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
590590
return obj_nonlinprog!(Ŷ0, U0, mpc, Ue, Ŷe, ΔŨ)
591591
end
592592
if !isnothing(hess)
593-
_, ∇J_opt, ∇²J_opt = value_gradient_and_hessian(J!, hess, mpc.Z̃, J_cache...)
593+
prep_∇²J = prepare_hessian(J!, hess, mpc.Z̃, J_cache...)
594+
_, ∇J_opt, ∇²J_opt = value_gradient_and_hessian(J!, prep_∇²J, hess, mpc.Z̃, J_cache...)
595+
∇²J_ncolors = get_ncolors(prep_∇²J)
594596
else
595-
∇J_opt, ∇²J_opt = gradient(J!, mpc.gradient, mpc.Z̃, J_cache...), nothing
597+
prep_∇J = prepare_gradient(J!, mpc.gradient, mpc.Z̃, J_cache...)
598+
∇J_opt = gradient(J!, prep_∇J, mpc.gradient, mpc.Z̃, J_cache...)
599+
∇²J_opt, ∇²J_ncolors = nothing, nothing
596600
end
597601
# --- inequality constraint derivatives ---
598602
∇g_cache = (
@@ -605,7 +609,9 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
605609
gi .= @views g[i_g]
606610
return nothing
607611
end
608-
g_opt, ∇g_opt = value_and_jacobian(gi!, gi, mpc.jacobian, mpc.Z̃, ∇g_cache...)
612+
prep_∇g = prepare_jacobian(gi!, gi, mpc.jacobian, mpc.Z̃, ∇g_cache...)
613+
g_opt, ∇g_opt = value_and_jacobian(gi!, gi, prep_∇g, mpc.jacobian, mpc.Z̃, ∇g_cache...)
614+
∇g_ncolors = get_ncolors(prep_∇g)
609615
if !isnothing(hess) && ngi > 0
610616
nonlincon = optim[:nonlinconstraint]
611617
λi = try
@@ -631,9 +637,11 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
631637
gi .= @views g[i_g]
632638
return dot(λi, gi)
633639
end
634-
∇²ℓg_opt = hessian(ℓ_gi, hess, mpc.Z̃, Constant(λi), ∇²g_cache...)
640+
prep_∇²ℓg = prepare_hessian(ℓ_gi, hess, mpc.Z̃, Constant(λi), ∇²g_cache...)
641+
∇²ℓg_opt = hessian(ℓ_gi, prep_∇²ℓg, hess, mpc.Z̃, Constant(λi), ∇²g_cache...)
642+
∇²ℓg_ncolors = get_ncolors(prep_∇²ℓg)
635643
else
636-
∇²ℓg_opt = nothing
644+
∇²ℓg_opt, ∇²ℓg_ncolors = nothing, nothing
637645
end
638646
# --- equality constraint derivatives ---
639647
geq_cache = (
@@ -645,7 +653,9 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
645653
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K, X̂0, gc, g, geq, mpc, Z̃)
646654
return nothing
647655
end
648-
geq_opt, ∇geq_opt = value_and_jacobian(geq!, geq, mpc.jacobian, mpc.Z̃, geq_cache...)
656+
prep_∇geq = prepare_jacobian(geq!, geq, mpc.jacobian, mpc.Z̃, geq_cache...)
657+
geq_opt, ∇geq_opt = value_and_jacobian(geq!, geq, prep_∇geq, mpc.jacobian, mpc.Z̃, geq_cache...)
658+
∇geq_ncolors = get_ncolors(prep_∇geq)
649659
if !isnothing(hess) && con.neq > 0
650660
nonlinconeq = optim[:nonlinconstrainteq]
651661
λeq = try
@@ -670,25 +680,37 @@ function addinfo!(info, mpc::NonLinMPC{NT}) where NT<:Real
670680
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K, X̂0, gc, g, geq, mpc, Z̃)
671681
return dot(λeq, geq)
672682
end
673-
∇²ℓgeq_opt = hessian(ℓ_geq, hess, mpc.Z̃, Constant(λeq), ∇²geq_cache...)
683+
prep_∇²ℓgeq = prepare_hessian(ℓ_geq, hess, mpc.Z̃, Constant(λeq), ∇²geq_cache...)
684+
∇²ℓgeq_opt = hessian(ℓ_geq, prep_∇²ℓgeq, hess, mpc.Z̃, Constant(λeq), ∇²geq_cache...)
685+
∇²ℓgeq_ncolors = get_ncolors(prep_∇²ℓgeq)
674686
else
675-
∇²ℓgeq_opt = nothing
687+
∇²ℓgeq_opt, ∇²ℓgeq_ncolors = nothing, nothing
676688
end
677689
info[:∇J] = ∇J_opt
678690
info[:∇²J] = ∇²J_opt
691+
info[:∇²J_ncolors] = ∇²J_ncolors
679692
info[:g] = g_opt
680693
info[:∇g] = ∇g_opt
694+
info[:∇g_ncolors] = ∇g_ncolors
681695
info[:∇²ℓg] = ∇²ℓg_opt
696+
info[:∇²ℓg_ncolors] = ∇²ℓg_ncolors
682697
info[:geq] = geq_opt
683698
info[:∇geq] = ∇geq_opt
699+
info[:∇geq_ncolors] = ∇geq_ncolors
684700
info[:∇²ℓgeq] = ∇²ℓgeq_opt
701+
info[:∇²ℓgeq_ncolors] = ∇²ℓgeq_ncolors
685702
# --- non-Unicode fields ---
686703
info[:nablaJ] = ∇J_opt
687704
info[:nabla2J] = ∇²J_opt
705+
info[:nabla2J_ncolors] = ∇²J_ncolors
688706
info[:nablag] = ∇g_opt
707+
info[:nablag_ncolors] = ∇g_ncolors
689708
info[:nabla2lg] = ∇²ℓg_opt
709+
info[:nabla2lg_ncolors] = ∇²ℓg_ncolors
690710
info[:nablageq] = ∇geq_opt
711+
info[:nablageq_ncolors] = ∇geq_ncolors
691712
info[:nabla2lgeq] = ∇²ℓgeq_opt
713+
info[:nabla2lgeq_ncolors] = ∇²ℓgeq_ncolors
692714
return info
693715
end
694716

src/estimator/mhe/execute.jl

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ For [`NonLinModel`](@ref), it also includes the following fields:
9898
9999
- `:∇J` or *`:nablaJ`* : optimal gradient of the objective function, ``\mathbf{\nabla} J``
100100
- `:∇²J` or *`:nabla2J`* : optimal Hessian of the objective function, ``\mathbf{\nabla^2}J``
101+
- `:∇²J_ncolors` or *`:nabla2J_ncolors`* : number of colors in `:∇²J` sparsity pattern
101102
- `:g` : optimal nonlinear inequality constraint values, ``\mathbf{g}``
102103
- `:∇g` or *`:nablag`* : optimal Jacobian of the inequality constraint, ``\mathbf{\nabla g}``
104+
- `:∇g_ncolors` or *`:nablag_ncolors`* : number of colors in `:∇g` sparsity pattern
103105
- `:∇²ℓg` or *`:nabla2lg`* : optimal Hessian of the inequality Lagrangian, ``\mathbf{\nabla^2}\ell_{\mathbf{g}}``
106+
- `:∇²ℓg_ncolors` or *`:nabla2lg_ncolors`* : number of colors in `:∇²ℓg` sparsity pattern
104107
105108
Note that the inequality constraint vectors and matrices only include the non-`Inf` values.
106109
@@ -207,9 +210,13 @@ function addinfo!(
207210
return obj_nonlinprog!(x̄, estim, model, V̂, Z̃)
208211
end
209212
if !isnothing(hess)
210-
_, ∇J_opt, ∇²J_opt = value_gradient_and_hessian(J!, hess, estim.Z̃, J_cache...)
213+
prep_∇²J = prepare_hessian(J!, hess, estim.Z̃, J_cache...)
214+
_, ∇J_opt, ∇²J_opt = value_gradient_and_hessian(J!, prep_∇²J, hess, estim.Z̃, J_cache...)
215+
∇²J_ncolors = get_ncolors(prep_∇²J)
211216
else
212-
∇J_opt, ∇²J_opt = gradient(J!, estim.gradient, estim.Z̃, J_cache...), nothing
217+
prep_∇J = prepare_gradient(J!, estim.gradient, estim.Z̃, J_cache...)
218+
∇J_opt = gradient(J!, prep_∇J, estim.gradient, estim.Z̃, J_cache...)
219+
∇²J_opt, ∇²J_ncolors = nothing, nothing
213220
end
214221
# --- inequality constraint derivatives ---
215222
∇g_cache = (Cache(V̂), Cache(X̂0), Cache(û0), Cache(k), Cache(ŷ0), Cache(g))
@@ -218,7 +225,9 @@ function addinfo!(
218225
gi .= @views g[i_g]
219226
return nothing
220227
end
221-
g_opt, ∇g_opt = value_and_jacobian(gi!, gi, estim.jacobian, estim.Z̃, ∇g_cache...)
228+
prep_∇g = prepare_jacobian(gi!, gi, estim.jacobian, estim.Z̃, ∇g_cache...)
229+
g_opt, ∇g_opt = value_and_jacobian(gi!, gi, prep_∇g, estim.jacobian, estim.Z̃, ∇g_cache...)
230+
∇g_ncolors = get_ncolors(prep_∇g)
222231
if !isnothing(hess) && ngi > 0
223232
nonlincon = optim[:nonlinconstraint]
224233
λi = try
@@ -239,25 +248,31 @@ function addinfo!(
239248
)
240249
function ℓ_gi(Z̃, λi, V̂, X̂0, û0, k, ŷ0, g, gi)
241250
update_prediction!(V̂, X̂0, û0, k, ŷ0, g, estim, Z̃)
242-
@show size(g)
243-
@show size(gi)
244251
gi .= @views g[i_g]
245252
return dot(λi, gi)
246253
end
247-
∇²ℓg_opt = hessian(ℓ_gi, hess, estim.Z̃, Constant(λi), ∇²g_cache...)
254+
prep_∇²ℓg = prepare_hessian(ℓ_gi, hess, estim.Z̃, Constant(λi), ∇²g_cache...)
255+
∇²ℓg_opt = hessian(ℓ_gi, prep_∇²ℓg, hess, estim.Z̃, Constant(λi), ∇²g_cache...)
256+
∇²ℓg_ncolors = get_ncolors(prep_∇²ℓg)
248257
else
249-
∇²ℓg_opt = nothing
258+
∇²ℓg_opt, ∇²ℓg_ncolors = nothing, nothing
250259
end
251260
info[:∇J] = ∇J_opt
252261
info[:∇²J] = ∇²J_opt
262+
info[:∇²J_ncolors] = ∇²J_ncolors
253263
info[:g] = g_opt
254264
info[:∇g] = ∇g_opt
265+
info[:∇g_ncolors] = ∇g_ncolors
255266
info[:∇²ℓg] = ∇²ℓg_opt
267+
info[:∇²ℓg_ncolors] = ∇²ℓg_ncolors
256268
# --- non-Unicode fields ---
257269
info[:nablaJ] = ∇J_opt
258270
info[:nabla2J] = ∇²J_opt
271+
info[:nabla2J_ncolors] = ∇²J_ncolors
259272
info[:nablag] = ∇g_opt
273+
info[:nablag_ncolors] = ∇g_ncolors
260274
info[:nabla2lg] = ∇²ℓg_opt
275+
info[:nabla2lg_ncolors] = ∇²ℓg_ncolors
261276
return info
262277
end
263278

src/general.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ const ALL_COLORING_ORDERS = (
1818

1919
const HIDDEN_GETINFO_KEYS_MHE = (
2020
:What, :xhatarr, :epsilon, :Xhat, :xhat, :Vhat, :Pbar, :xbar, :Yhat, :Yhatm, ,
21-
:nablaJ, :nabla2J, :nablag, :nabla2lg, :nablageq, :nabla2lgeq
21+
:nablaJ, :nabla2J, :nabla2J_ncolors,
22+
:nablag, :nablag_ncolors, :nabla2lg, :nabla2lg_ncolors,
23+
:nablageq, :nablag_ncolors, :nabla2lgeq, :nabla2lgeq_ncolors
2224
)
2325

2426
const HIDDEN_GETINFO_KEYS_MPC = (
2527
:DeltaU, :epsilon, :Dhat, :xhat, :yhat, :Yhat, :xhatend, :Yhats, :Rhaty, :Rhatu,
26-
:nablaJ, :nabla2J, :nablag, :nabla2lg, :nablageq, :nabla2lgeq
28+
:nablaJ, :nabla2J, :nabla2J_ncolors,
29+
:nablag, :nablag_ncolors, :nabla2lg, :nabla2lg_ncolors,
30+
:nablageq, :nablag_ncolors, :nabla2lgeq, :nabla2lgeq_ncolors
2731
)
2832

2933
"Termination status that means 'no solution available'."
@@ -140,6 +144,10 @@ dense_backend(backend::AbstractADType) = backend
140144
dense_backend(backend::AutoSparse) = backend.dense_ad
141145
dense_backend(backend::SecondOrder) = backend.inner
142146

147+
"Get the number of colors in preparation object `prep`, or `nothing` if not applicable."
148+
get_ncolors(::Prep) = nothing
149+
get_ncolors(prep::Union{SparseJacobianPrep, SparseHessianPrep}) = ncolors(prep)
150+
143151
"Validate `hessian` keyword argument and return the differentiation `backend`."
144152
function validate_hessian(hessian, gradient, default)
145153
if hessian == true

0 commit comments

Comments
 (0)