From 097f875fede8a0a51a242f9b6d391f4ebee99fff Mon Sep 17 00:00:00 2001 From: franckgaga Date: Thu, 12 Jun 2025 16:54:32 -0400 Subject: [PATCH 1/4] debug: support `Hermitian` weights in `PredictiveController` Added a generic dispatch to support `M_Hp`, `N_Hc` and `L_Hp` of any types, including `Hermitian` --- src/controller/construct.jl | 6 +++--- src/controller/explicitmpc.jl | 2 +- src/controller/linmpc.jl | 2 +- src/controller/nonlinmpc.jl | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controller/construct.jl b/src/controller/construct.jl index 4a3aef580..9a3baf367 100644 --- a/src/controller/construct.jl +++ b/src/controller/construct.jl @@ -88,9 +88,9 @@ struct ControllerWeights{ end "Outer constructor to convert weight matrix number type to `NT` if necessary." -function ControllerWeights{NT}( - model, Hp, Hc, M_Hp::MW, N_Hc::NW, L_Hp::LW, Cwt=Inf, Ewt=0 - ) where {NT<:Real, MW<:AbstractMatrix, NW<:AbstractMatrix, LW<:AbstractMatrix} +function ControllerWeights( + model::SimModel{NT}, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt=Inf, Ewt=0 + ) where {NT<:Real} return ControllerWeights{NT}(model, Hp, Hc, NT.(M_Hp), NT.(N_Hc), NT.(L_Hp), Cwt, Ewt) end diff --git a/src/controller/explicitmpc.jl b/src/controller/explicitmpc.jl index d3a249f70..684c33aa3 100644 --- a/src/controller/explicitmpc.jl +++ b/src/controller/explicitmpc.jl @@ -169,7 +169,7 @@ function ExplicitMPC( end nb = move_blocking(Hp, Hc) Hc = get_Hc(nb) - weights = ControllerWeights{NT}(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp) + weights = ControllerWeights(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp) return ExplicitMPC{NT}(estim, Hp, Hc, nb, weights) end diff --git a/src/controller/linmpc.jl b/src/controller/linmpc.jl index b7c324cde..edc906d0a 100644 --- a/src/controller/linmpc.jl +++ b/src/controller/linmpc.jl @@ -270,7 +270,7 @@ function LinMPC( end nb = move_blocking(Hp, Hc) Hc = get_Hc(nb) - weights = ControllerWeights{NT}(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt) + weights = ControllerWeights(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt) return LinMPC{NT}(estim, Hp, Hc, nb, weights, transcription, optim) end diff --git a/src/controller/nonlinmpc.jl b/src/controller/nonlinmpc.jl index ecfe1e96b..d8ebb5841 100644 --- a/src/controller/nonlinmpc.jl +++ b/src/controller/nonlinmpc.jl @@ -372,7 +372,7 @@ function NonLinMPC( Hc = get_Hc(nb) validate_JE(NT, JE) gc! = get_mutating_gc(NT, gc) - weights = ControllerWeights{NT}(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt) + weights = ControllerWeights(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt) return NonLinMPC{NT}( estim, Hp, Hc, nb, weights, JE, gc!, nc, p, transcription, optim, gradient, jacobian ) From 6da55a56b4db7da0bcb98df899daf4da7f4cb031 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Thu, 12 Jun 2025 17:14:01 -0400 Subject: [PATCH 2/4] test: verify if `Hermitian` weights do not crash --- test/3_test_predictive_control.jl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/3_test_predictive_control.jl b/test/3_test_predictive_control.jl index a24d7d088..95b171c2e 100644 --- a/test/3_test_predictive_control.jl +++ b/test/3_test_predictive_control.jl @@ -29,14 +29,14 @@ mpc9 = LinMPC(model, nint_u=[1, 1], nint_ym=[0, 0]) @test mpc9.estim.nint_u == [1, 1] @test mpc9.estim.nint_ym == [0, 0] - mpc10 = LinMPC(model, M_Hp=diagm(collect(1.01:0.01:1.2))) - @test mpc10.weights.M_Hp ≈ diagm(collect(1.01:0.01:1.2)) + mpc10 = LinMPC(model, M_Hp=Hermitian(diagm(1.01:0.01:1.2), :L)) + @test mpc10.weights.M_Hp ≈ diagm(1.01:0.01:1.2) @test mpc10.weights.M_Hp isa Hermitian{Float64, Matrix{Float64}} - mpc11 = LinMPC(model, N_Hc=diagm([0.1,0.11,0.12,0.13]), Cwt=Inf) + mpc11 = LinMPC(model, N_Hc=Hermitian(diagm([0.1,0.11,0.12,0.13]), :L), Cwt=Inf) @test mpc11.weights.Ñ_Hc ≈ diagm([0.1,0.11,0.12,0.13]) @test mpc11.weights.Ñ_Hc isa Hermitian{Float64, Matrix{Float64}} - mcp12 = LinMPC(model, L_Hp=diagm(collect(0.001:0.001:0.02))) - @test mcp12.weights.L_Hp ≈ diagm(collect(0.001:0.001:0.02)) + mcp12 = LinMPC(model, L_Hp=Hermitian(diagm(0.001:0.001:0.02), :L)) + @test mcp12.weights.L_Hp ≈ diagm(0.001:0.001:0.02) @test mcp12.weights.L_Hp isa Hermitian{Float64, Matrix{Float64}} model2 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0) mpc13 = LinMPC(model2) @@ -463,14 +463,14 @@ end mpc9 = ExplicitMPC(model, nint_u=[1, 1], nint_ym=[0, 0]) @test mpc9.estim.nint_u == [1, 1] @test mpc9.estim.nint_ym == [0, 0] - mpc10 = ExplicitMPC(model, M_Hp=diagm(collect(1.01:0.01:1.2))) - @test mpc10.weights.M_Hp ≈ diagm(collect(1.01:0.01:1.2)) + mpc10 = ExplicitMPC(model, M_Hp=Hermitian(diagm(1.01:0.01:1.2), :L)) + @test mpc10.weights.M_Hp ≈ diagm(1.01:0.01:1.2) @test mpc10.weights.M_Hp isa Hermitian{Float64, Matrix{Float64}} - mpc11 = ExplicitMPC(model, N_Hc=diagm([0.1,0.11,0.12,0.13])) + mpc11 = ExplicitMPC(model, N_Hc=Hermitian(diagm([0.1,0.11,0.12,0.13]), :L)) @test mpc11.weights.Ñ_Hc ≈ diagm([0.1,0.11,0.12,0.13]) @test mpc11.weights.Ñ_Hc isa Hermitian{Float64, Matrix{Float64}} - mcp12 = ExplicitMPC(model, L_Hp=diagm(collect(0.001:0.001:0.02))) - @test mcp12.weights.L_Hp ≈ diagm(collect(0.001:0.001:0.02)) + mcp12 = ExplicitMPC(model, L_Hp=Hermitian(diagm(0.001:0.001:0.02), :L)) + @test mcp12.weights.L_Hp ≈ diagm(0.001:0.001:0.02) @test mcp12.weights.L_Hp isa Hermitian{Float64, Matrix{Float64}} model2 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0) mpc13 = ExplicitMPC(model2) @@ -671,14 +671,14 @@ end nmpc11 = NonLinMPC(nonlinmodel, Hp=15, nint_u=[1, 1], nint_ym=[0, 0]) @test nmpc11.estim.nint_u == [1, 1] @test nmpc11.estim.nint_ym == [0, 0] - nmpc12 = NonLinMPC(nonlinmodel, Hp=10, M_Hp=diagm(collect(1.01:0.01:1.2))) - @test nmpc12.weights.M_Hp ≈ diagm(collect(1.01:0.01:1.2)) + nmpc12 = NonLinMPC(nonlinmodel, Hp=10, M_Hp=Hermitian(diagm(1.01:0.01:1.2), :L)) + @test nmpc12.weights.M_Hp ≈ diagm(1.01:0.01:1.2) @test nmpc12.weights.M_Hp isa Hermitian{Float64, Matrix{Float64}} - nmpc13 = NonLinMPC(nonlinmodel, Hp=10, N_Hc=diagm([0.1,0.11,0.12,0.13]), Cwt=Inf) + nmpc13 = NonLinMPC(nonlinmodel, Hp=10, N_Hc=Hermitian(diagm([0.1,0.11,0.12,0.13]), :L), Cwt=Inf) @test nmpc13.weights.Ñ_Hc ≈ diagm([0.1,0.11,0.12,0.13]) @test nmpc13.weights.Ñ_Hc isa Hermitian{Float64, Matrix{Float64}} - nmcp14 = NonLinMPC(nonlinmodel, Hp=10, L_Hp=diagm(collect(0.001:0.001:0.02))) - @test nmcp14.weights.L_Hp ≈ diagm(collect(0.001:0.001:0.02)) + nmcp14 = NonLinMPC(nonlinmodel, Hp=10, L_Hp=Hermitian(diagm(0.001:0.001:0.02), :L)) + @test nmcp14.weights.L_Hp ≈ diagm(0.001:0.001:0.02) @test nmcp14.weights.L_Hp isa Hermitian{Float64, Matrix{Float64}} nmpc15 = NonLinMPC(nonlinmodel, Hp=10, gc=(Ue,Ŷe,D̂e,p,ϵ)-> [p*dot(Ue,Ŷe)+sum(D̂e)+ϵ], nc=1, p=10) LHS = zeros(1) From e34f4ed9ef2421576c66b04e04990f523f4562f4 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Thu, 12 Jun 2025 18:04:49 -0400 Subject: [PATCH 3/4] changed: validate weights in outer constructor --- src/controller/construct.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controller/construct.jl b/src/controller/construct.jl index 9a3baf367..69e9917d9 100644 --- a/src/controller/construct.jl +++ b/src/controller/construct.jl @@ -53,14 +53,13 @@ struct ControllerWeights{ iszero_E::Bool isinf_C ::Bool function ControllerWeights{NT}( - model, Hp, Hc, M_Hp::MW, N_Hc::NW, L_Hp::LW, Cwt=Inf, Ewt=0 + M_Hp::MW, N_Hc::NW, L_Hp::LW, Cwt, Ewt ) where { NT<:Real, MW<:AbstractMatrix{NT}, NW<:AbstractMatrix{NT}, LW<:AbstractMatrix{NT} } - validate_weights(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt) nΔU = size(N_Hc, 1) C = Cwt isinf_C = isinf(C) @@ -87,11 +86,12 @@ struct ControllerWeights{ end end -"Outer constructor to convert weight matrix number type to `NT` if necessary." +"Outer constructor to validate and convert weight matrices if necessary." function ControllerWeights( model::SimModel{NT}, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt=Inf, Ewt=0 ) where {NT<:Real} - return ControllerWeights{NT}(model, Hp, Hc, NT.(M_Hp), NT.(N_Hc), NT.(L_Hp), Cwt, Ewt) + validate_weights(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt) + return ControllerWeights{NT}(NT.(M_Hp), NT.(N_Hc), NT.(L_Hp), Cwt, Ewt) end "Include all the data for the constraints of [`PredictiveController`](@ref)" From debae4dd8f1a6fb713f8af70b9c661ba897c11e8 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Thu, 12 Jun 2025 20:59:51 -0400 Subject: [PATCH 4/4] added: dispatch on `repeatdiag` to preserve `Diagonnal`s --- src/general.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/general.jl b/src/general.jl index 6567b0db5..1389a3f9b 100644 --- a/src/general.jl +++ b/src/general.jl @@ -65,6 +65,9 @@ isdifferent(x, y) = any(xi !== yi for (xi, yi) in zip(x, y)) "Generate a block diagonal matrix repeating `n` times the matrix `A`." repeatdiag(A, n::Int) = kron(I(n), A) +function repeatdiag(A::Hermitian{NT, Diagonal{NT, Vector{NT}}}, n::Int) where {NT<:Real} + return Hermitian(repeatdiag(A.data, n), :L) # to return hermitian of a `Diagonal` +end "In-place version of `repeat` but for vectors only." function repeat!(Y::Vector, a::Vector, n::Int)