Skip to content

Commit f7d7293

Browse files
gdallefranckgaga
authored andcommitted
Make Hessian sparsity detection work with SCT (prototype)
1 parent d7f0fbc commit f7d7293

File tree

6 files changed

+53
-36
lines changed

6 files changed

+53
-36
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
1616
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
1717
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1818
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
19+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1920
SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5"
2021
SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35"
2122

@@ -36,6 +37,7 @@ PrecompileTools = "1"
3637
ProgressLogging = "0.1"
3738
Random = "1.10"
3839
RecipesBase = "1"
40+
SparseArrays = "1.11.0"
3941
SparseConnectivityTracer = "0.6.13"
4042
SparseMatrixColorings = "0.4.14"
4143
TestItemRunner = "1"

src/ModelPredictiveControl.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module ModelPredictiveControl
33
using PrecompileTools
44
using LinearAlgebra
55
using Random: randn
6+
using SparseArrays
67

78
using RecipesBase
89
using ProgressLogging
@@ -47,6 +48,7 @@ include("state_estim.jl")
4748
include("predictive_control.jl")
4849
include("plot_sim.jl")
4950

51+
#=
5052
@setup_workload begin
5153
# Putting some things in `@setup_workload` instead of `@compile_workload` can reduce the
5254
# size of the precompile file and potentially make loading faster.
@@ -56,5 +58,6 @@ include("plot_sim.jl")
5658
include("precompile.jl")
5759
end
5860
end
61+
=#
5962

6063
end

src/controller/construct.jl

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
struct PredictiveControllerBuffer{NT<:Real}
1+
struct PredictiveControllerBuffer{NT<:Real,M<:AbstractMatrix{NT}}
22
u ::Vector{NT}
33
::Vector{NT}
44
::Vector{NT}
55
::Vector{NT}
66
U ::Vector{NT}
77
::Matrix{NT}
8-
P̃u::Matrix{NT}
8+
P̃u::M
99
empty::Vector{NT}
1010
end
1111

@@ -29,14 +29,19 @@ function PredictiveControllerBuffer(
2929
= Matrix{NT}(undef, ny*Hp, nZ̃)
3030
P̃u = Matrix{NT}(undef, nu*Hp, nZ̃)
3131
empty = Vector{NT}(undef, 0)
32-
return PredictiveControllerBuffer{NT}(u, Z̃, D̂, Ŷ, U, Ẽ, P̃u, empty)
32+
return PredictiveControllerBuffer{NT,typeof(P̃u)}(u, Z̃, D̂, Ŷ, U, Ẽ, P̃u, empty)
3333
end
3434

3535
"Include all the objective function weights of [`PredictiveController`](@ref)"
36-
struct ControllerWeights{NT<:Real}
37-
M_Hp::Hermitian{NT, Matrix{NT}}
38-
Ñ_Hc::Hermitian{NT, Matrix{NT}}
39-
L_Hp::Hermitian{NT, Matrix{NT}}
36+
struct ControllerWeights{
37+
NT<:Real,
38+
H1<:Hermitian{NT, <:AbstractMatrix{NT}},
39+
H2<:Hermitian{NT, <:AbstractMatrix{NT}},
40+
H3<:Hermitian{NT, <:AbstractMatrix{NT}},
41+
}
42+
M_Hp::H1
43+
Ñ_Hc::H2
44+
L_Hp::H3
4045
E ::NT
4146
iszero_M_Hp::Vector{Bool}
4247
iszero_Ñ_Hc::Vector{Bool}
@@ -46,15 +51,15 @@ struct ControllerWeights{NT<:Real}
4651
model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt=Inf, Ewt=0
4752
) where NT<:Real
4853
validate_weights(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt)
49-
# convert `Diagonal` to normal `Matrix` if required:
50-
M_Hp = Hermitian(convert(Matrix{NT}, M_Hp), :L)
51-
N_Hc = Hermitian(convert(Matrix{NT}, N_Hc), :L)
52-
L_Hp = Hermitian(convert(Matrix{NT}, L_Hp), :L)
54+
M_Hp = Hermitian(convert.(NT, M_Hp), :L)
55+
N_Hc = Hermitian(convert.(NT, N_Hc), :L)
56+
L_Hp = Hermitian(convert.(NT, L_Hp), :L)
5357
nΔU = size(N_Hc, 1)
5458
C = Cwt
5559
if !isinf(Cwt)
5660
# ΔŨ = [ΔU; ϵ] (ϵ is the slack variable)
57-
Ñ_Hc = Hermitian([N_Hc zeros(NT, nΔU, 1); zeros(NT, 1, nΔU) C], :L)
61+
# Ñ_Hc = Hermitian([N_Hc zeros(NT, nΔU, 1); zeros(NT, 1, nΔU) C], :L)
62+
Ñ_Hc = Hermitian(blockdiag(sparse(N_Hc), sparse(Diagonal([C]))), :L)
5863
else
5964
# ΔŨ = ΔU (only hard constraints)
6065
Ñ_Hc = N_Hc
@@ -64,7 +69,7 @@ struct ControllerWeights{NT<:Real}
6469
iszero_Ñ_Hc = [iszero(Ñ_Hc)]
6570
iszero_L_Hp = [iszero(L_Hp)]
6671
iszero_E = iszero(E)
67-
return new{NT}(M_Hp, Ñ_Hc, L_Hp, E, iszero_M_Hp, iszero_Ñ_Hc, iszero_L_Hp, iszero_E)
72+
return new{NT,typeof(M_Hp),typeof(Ñ_Hc),typeof(L_Hp)}(M_Hp, Ñ_Hc, L_Hp, E, iszero_M_Hp, iszero_Ñ_Hc, iszero_L_Hp, iszero_E)
6873
end
6974
end
7075

@@ -584,10 +589,10 @@ function relaxU(Pu::Matrix{NT}, C_umin, C_umax, nϵ) where NT<:Real
584589
# ϵ impacts Z → U conversion for constraint calculations:
585590
A_Umin, A_Umax = -[Pu C_umin], [Pu -C_umax]
586591
# ϵ has no impact on Z → U conversion for prediction calculations:
587-
P̃u = [Pu zeros(NT, size(Pu, 1))]
592+
P̃u = sparse_hcat(sparse(Pu), spzeros(NT, size(Pu, 1)))
588593
else # Z̃ = Z (only hard constraints)
589594
A_Umin, A_Umax = -Pu, Pu
590-
P̃u = Pu
595+
P̃u = sparse(Pu)
591596
end
592597
return A_Umin, A_Umax, P̃u
593598
end
@@ -621,17 +626,17 @@ bound, which is more precise than a linear inequality constraint. However, it is
621626
convenient to treat it as a linear inequality constraint since the optimizer `OSQP.jl` does
622627
not support pure bounds on the decision variables.
623628
"""
624-
function relaxΔU(PΔu::Matrix{NT}, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) where NT<:Real
629+
function relaxΔU(PΔu::AbstractMatrix{NT}, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) where NT<:Real
625630
nZ = size(PΔu, 2)
626631
if== 1 # Z̃ = [Z; ϵ]
627632
ΔŨmin, ΔŨmax = [ΔUmin; NT[0.0]], [ΔUmax; NT[Inf]] # 0 ≤ ϵ ≤ ∞
628633
A_ϵ = [zeros(NT, 1, nZ) NT[1.0]]
629634
A_ΔŨmin, A_ΔŨmax = -[PΔu C_Δumin; A_ϵ], [PΔu -C_Δumax; A_ϵ]
630-
P̃Δu = [PΔu zeros(NT, size(PΔu, 1), 1); zeros(NT, 1, size(PΔu, 2)) NT[1.0]]
635+
P̃Δu = blockdiag(sparse(PΔu), spdiagm([one(NT)]))
631636
else # Z̃ = Z (only hard constraints)
632637
ΔŨmin, ΔŨmax = ΔUmin, ΔUmax
633638
A_ΔŨmin, A_ΔŨmax = -PΔu, PΔu
634-
P̃Δu = PΔu
639+
P̃Δu = sparse(PΔu)
635640
end
636641
return A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃Δu
637642
end

src/controller/execute.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,12 @@ function obj_nonlinprog!(
370370
end
371371
# --- economic term ---
372372
E_JE = obj_econ(mpc, model, Ue, Ŷe)
373-
return JR̂y + JΔŨ + JR̂u + E_JE
373+
return (
374+
JR̂y +
375+
JΔŨ +
376+
JR̂u +
377+
E_JE
378+
)
374379
end
375380

376381
"No custom nonlinear constraints `gc` by default, return `gc` unchanged."

src/controller/nonlinmpc.jl

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ struct NonLinMPC{
1717
JB<:AbstractADType,
1818
PT<:Any,
1919
JEfunc<:Function,
20-
GCfunc<:Function
20+
GCfunc<:Function,
21+
M<:AbstractMatrix{NT}
2122
} <: PredictiveController{NT}
2223
estim::SE
2324
transcription::TM
@@ -37,8 +38,8 @@ struct NonLinMPC{
3738
p::PT
3839
R̂u::Vector{NT}
3940
R̂y::Vector{NT}
40-
P̃Δu::Matrix{NT}
41-
P̃u ::Matrix{NT}
41+
P̃Δu::M
42+
P̃u ::M
4243
Tu ::Matrix{NT}
4344
Tu_lastu0::Vector{NT}
4445
::Matrix{NT}
@@ -107,7 +108,7 @@ struct NonLinMPC{
107108
nZ̃ = get_nZ(estim, transcription, Hp, Hc) +
108109
= zeros(NT, nZ̃)
109110
buffer = PredictiveControllerBuffer(estim, transcription, Hp, Hc, nϵ)
110-
mpc = new{NT, SE, TM, JM, GB, JB, PT, JEfunc, GCfunc}(
111+
mpc = new{NT, SE, TM, JM, GB, HB, JB, PT, JEfunc, GCfunc, typeof(P̃u)}(
111112
estim, transcription, optim, con,
112113
gradient, jacobian,
113114
Z̃, ŷ,
@@ -279,9 +280,9 @@ function NonLinMPC(
279280
Mwt = fill(DEFAULT_MWT, model.ny),
280281
Nwt = fill(DEFAULT_NWT, model.nu),
281282
Lwt = fill(DEFAULT_LWT, model.nu),
282-
M_Hp = diagm(repeat(Mwt, Hp)),
283-
N_Hc = diagm(repeat(Nwt, Hc)),
284-
L_Hp = diagm(repeat(Lwt, Hp)),
283+
M_Hp = Diagonal(repeat(Mwt, Hp)),
284+
N_Hc = Diagonal(repeat(Nwt, Hc)),
285+
L_Hp = Diagonal(repeat(Lwt, Hp)),
285286
Cwt = DEFAULT_CWT,
286287
Ewt = DEFAULT_EWT,
287288
JE ::Function = (_,_,_,_) -> 0.0,
@@ -310,9 +311,9 @@ function NonLinMPC(
310311
Mwt = fill(DEFAULT_MWT, model.ny),
311312
Nwt = fill(DEFAULT_NWT, model.nu),
312313
Lwt = fill(DEFAULT_LWT, model.nu),
313-
M_Hp = diagm(repeat(Mwt, Hp)),
314-
N_Hc = diagm(repeat(Nwt, Hc)),
315-
L_Hp = diagm(repeat(Lwt, Hp)),
314+
M_Hp = Diagonal(repeat(Mwt, Hp)),
315+
N_Hc = Diagonal(repeat(Nwt, Hc)),
316+
L_Hp = Diagonal(repeat(Lwt, Hp)),
316317
Cwt = DEFAULT_CWT,
317318
Ewt = DEFAULT_EWT,
318319
JE ::Function = (_,_,_,_) -> 0.0,
@@ -365,9 +366,9 @@ function NonLinMPC(
365366
Mwt = fill(DEFAULT_MWT, estim.model.ny),
366367
Nwt = fill(DEFAULT_NWT, estim.model.nu),
367368
Lwt = fill(DEFAULT_LWT, estim.model.nu),
368-
M_Hp = diagm(repeat(Mwt, Hp)),
369-
N_Hc = diagm(repeat(Nwt, Hc)),
370-
L_Hp = diagm(repeat(Lwt, Hp)),
369+
M_Hp = Diagonal(repeat(Mwt, Hp)),
370+
N_Hc = Diagonal(repeat(Nwt, Hc)),
371+
L_Hp = Diagonal(repeat(Lwt, Hp)),
371372
Cwt = DEFAULT_CWT,
372373
Ewt = DEFAULT_EWT,
373374
JE ::Function = (_,_,_,_) -> 0.0,

src/controller/transcription.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ function init_ZtoΔU end
8585
function init_ZtoΔU(
8686
estim::StateEstimator{NT}, transcription::SingleShooting, _ , Hc
8787
) where {NT<:Real}
88-
PΔu = Matrix{NT}(I, estim.model.nu*Hc, estim.model.nu*Hc)
88+
PΔu = Diagonal(fill(one(NT), estim.model.nu*Hc))
8989
return PΔu
9090
end
9191

9292
function init_ZtoΔU(
9393
estim::StateEstimator{NT}, transcription::MultipleShooting, Hp, Hc
9494
) where {NT<:Real}
95-
I_nu_Hc = Matrix{NT}(I, estim.model.nu*Hc, estim.model.nu*Hc)
96-
PΔu = [I_nu_Hc zeros(NT, estim.model.nu*Hc, estim.nx̂*Hp)]
95+
I_nu_Hc = Diagonal(fill(one(NT), estim.model.nu*Hc))
96+
PΔu = sparse_hcat(I_nu_Hc , spzeros(NT, estim.model.nu*Hc, estim.nx̂*Hp))
9797
return PΔu
9898
end
9999

@@ -145,7 +145,8 @@ function init_ZtoU(
145145
) where {NT<:Real}
146146
model = estim.model
147147
# Pu and Tu are `Matrix{NT}`, conversion is faster than `Matrix{Bool}` or `BitMatrix`
148-
I_nu = Matrix{NT}(I, model.nu, model.nu)
148+
I_nu = Diagonal(fill(one(NT), model.nu))
149+
# TODO: make PU and friends sparse
149150
PU_Hc = LowerTriangular(repeat(I_nu, Hc, Hc))
150151
PUdagger = [PU_Hc; repeat(I_nu, Hp - Hc, Hc)]
151152
Pu = init_PUmat(estim, transcription, Hp, Hc, PUdagger)

0 commit comments

Comments
 (0)