From 376b4409359d7cec4bd2d7771dd401810122c983 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Thu, 5 Nov 2026 18:04:42 -0500 Subject: [PATCH 1/4] added: `hessian=true` in precompilation There is no path that compute Hessians with AD right now in the precompilation, probably worth the addition --- src/precompile.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/precompile.jl b/src/precompile.jl index 0479fca89..04d0b25ac 100644 --- a/src/precompile.jl +++ b/src/precompile.jl @@ -98,9 +98,9 @@ R̂y = repeat([55; 30], 3) daemodel = NonLinModelDAE(fq_dae!, h_dae!, 100.0, 1, 1, 1, 1; transcription) sim!(daemodel, 2, [10]) - mhe_dae = MovingHorizonEstimator(daemodel, He=2, direct=false) + mhe_dae = MovingHorizonEstimator(daemodel, He=2, direct=false, hessian=true) setconstraint!(mhe_dae, x̂min=[-50, 50], x̂max=[50, 50]) - nmpc_dae = NonLinMPC(mhe_dae, Hp=3) + nmpc_dae = NonLinMPC(mhe_dae, Hp=3, hessian=true) setconstraint!(nmpc_dae, ymin=[-50], ymax=[50]) preparestate!(nmpc_dae, [5]) moveinput!(nmpc_dae, [0.1]) From bd40005522ce5bbdc89d6721ec929247878cc4b5 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Fri, 6 Nov 2026 10:25:13 -0500 Subject: [PATCH 2/4] test: 80% of RAM for threshold --- .github/workflows/juliaci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/juliaci.yml b/.github/workflows/juliaci.yml index 3c882dd5f..b3317d4ac 100644 --- a/.github/workflows/juliaci.yml +++ b/.github/workflows/juliaci.yml @@ -15,7 +15,7 @@ jobs: julia-ci: uses: franckgaga/testitem-workflow/.github/workflows/juliaci.yml@patch-1 with: - memory-threshold: 0.9 # try to avoid OOM error on ubuntu and Julia 1.10 + memory-threshold: 0.8 # try to avoid OOM error on ubuntu and Julia 1.10 include-windows-x86: false # not worth it, windows 32bit is too uncommon include-macos-x64: false # most mac are aarch64 nowadays include-macos-aarch64: false # would like to include it, but too unreliable for now From dba8d43d16f6e602330a8e973818d8a9f01ca90c Mon Sep 17 00:00:00 2001 From: franckgaga Date: Fri, 6 Nov 2026 11:54:17 -0500 Subject: [PATCH 3/4] test: remove `linux-x86` from CI This is too unreliable, frequent OOM error from LLVM, disregard of the `memory-threshold` setting. The provided machine seems quite low on RAM. --- .github/workflows/juliaci.yml | 2 +- test/2_test_state_estim.jl | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/juliaci.yml b/.github/workflows/juliaci.yml index b3317d4ac..0cf3f944e 100644 --- a/.github/workflows/juliaci.yml +++ b/.github/workflows/juliaci.yml @@ -15,10 +15,10 @@ jobs: julia-ci: uses: franckgaga/testitem-workflow/.github/workflows/juliaci.yml@patch-1 with: - memory-threshold: 0.8 # try to avoid OOM error on ubuntu and Julia 1.10 include-windows-x86: false # not worth it, windows 32bit is too uncommon include-macos-x64: false # most mac are aarch64 nowadays include-macos-aarch64: false # would like to include it, but too unreliable for now + include-linux-x86: false # would like to include it, but too unreliable for now permissions: write-all secrets: codecov_token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/test/2_test_state_estim.jl b/test/2_test_state_estim.jl index c24efde7c..959a784df 100644 --- a/test/2_test_state_estim.jl +++ b/test/2_test_state_estim.jl @@ -1241,9 +1241,9 @@ end end @testitem "MHE estim. & getinfo (NonLinModel, SS)" setup=[SetupMPCtests] begin - using .SetupMPCtests, ControlSystemsBase, LinearAlgebra, ForwardDiff + using .SetupMPCtests, ControlSystemsBase, LinearAlgebra using JuMP, Ipopt, DifferentiationInterface, SparseMatrixColorings, SparseConnectivityTracer - import ForwardDiff + import FiniteDiff linmodel = LinModel(sys,Ts,i_u=[1,2], i_d=[3]) linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5]) @@ -1251,7 +1251,7 @@ end h(x,d,model) = model.C*x + model.Dd*d nonlinmodel = NonLinModel(f, h, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel) nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30], dop=[5]) - +#= mhe1 = MovingHorizonEstimator(nonlinmodel, He=3) JuMP.set_attribute(mhe1.optim, "tol", 1e-7) preparestate!(mhe1, [50, 30], [5]) @@ -1327,7 +1327,7 @@ end end preparestate!(mhe2, [50, 30], [5]) @test mhe2([5]) ≈ [50, 30] atol=5e-3 - +=# Q̂ = diagm([1/4, 1/4, 1/4, 1/4].^2) R̂ = diagm([1, 1].^2) optim = Model(Ipopt.Optimizer) @@ -1344,8 +1344,7 @@ end @test info[:Ŷ][end-1:end] ≈ [50, 30] atol=1e-9 # coverage of the branch with error termination status (with an infeasible problem): - mhe_infeas = MovingHorizonEstimator(nonlinmodel, He=1, Cwt=Inf) - mhe_infeas = setconstraint!(mhe_infeas, v̂min=[1, 1], v̂max=[-1, -1]) + mhe_infeas = setconstraint!(mhe5, v̂min=[1, 1], v̂max=[-1, -1]) @test_logs( (:error, "MHE terminated without solution: estimation in open-loop "* "(more info in debug log)"), @@ -1355,19 +1354,16 @@ end @test_nowarn ModelPredictiveControl.info2debugstr(info) @test_throws ErrorException setstate!(mhe1, [1,2,3,4,5,6], diagm(.1:.1:.6)) - hessian = AutoSparse( - AutoForwardDiff(); - sparsity_detector=TracerSparsityDetector(), - coloring_algorithm=GreedyColoringAlgorithm(), - ) + hessian = AutoFiniteDiff() mhe8 = MovingHorizonEstimator(nonlinmodel; He=2, hessian) + @test mhe8.hessian isa AutoFiniteDiff @test_logs( (:warn, "NaN values in the MHE measurements ym: ignoring them in the objective"), preparestate!(mhe8, [50, NaN], [5]) ) @test mhe8.x̂0 ≈ zeros(6) atol=1e-9 - +#= function f!(ẋ, x, u, d, p) ẋ[] = -p[] * (x[] - 0.2 * u[] - 0.2 * d[]) return nothing @@ -1388,6 +1384,7 @@ end @test mhe_foh([0.0]) ≈ [1.5] info = getinfo(mhe_foh) @test info[:x̂] ≈ x̂ +=# end @testitem "MHE estim. & getinfo (NonLinModel, MS)" setup=[SetupMPCtests] begin From f7b774e55f72249cf3a40e9b60b82d7e87e2ebc0 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Fri, 6 Nov 2026 12:52:19 -0500 Subject: [PATCH 4/4] test: added a `FiniteDiff` test with MHE --- test/2_test_state_estim.jl | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/test/2_test_state_estim.jl b/test/2_test_state_estim.jl index 959a784df..4831e19b7 100644 --- a/test/2_test_state_estim.jl +++ b/test/2_test_state_estim.jl @@ -1251,7 +1251,7 @@ end h(x,d,model) = model.C*x + model.Dd*d nonlinmodel = NonLinModel(f, h, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel) nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30], dop=[5]) -#= + mhe1 = MovingHorizonEstimator(nonlinmodel, He=3) JuMP.set_attribute(mhe1.optim, "tol", 1e-7) preparestate!(mhe1, [50, 30], [5]) @@ -1327,7 +1327,7 @@ end end preparestate!(mhe2, [50, 30], [5]) @test mhe2([5]) ≈ [50, 30] atol=5e-3 -=# + Q̂ = diagm([1/4, 1/4, 1/4, 1/4].^2) R̂ = diagm([1, 1].^2) optim = Model(Ipopt.Optimizer) @@ -1341,29 +1341,35 @@ end @test evaloutput(mhe5, [5]) ≈ mhe5([5]) ≈ [50, 30] info = getinfo(mhe5) @test info[:x̂] ≈ x̂ atol=1e-9 - @test info[:Ŷ][end-1:end] ≈ [50, 30] atol=1e-9 + @test info[:Ŷ][end-1:end] ≈ [50, 30] atol=1e-9 + + @test_nowarn ModelPredictiveControl.info2debugstr(info) + @test_throws ErrorException setstate!(mhe1, [1,2,3,4,5,6], diagm(.1:.1:.6)) # coverage of the branch with error termination status (with an infeasible problem): - mhe_infeas = setconstraint!(mhe5, v̂min=[1, 1], v̂max=[-1, -1]) + mhe_infeas = MovingHorizonEstimator(nonlinmodel, He=1, Cwt=Inf) + mhe_infeas = setconstraint!(mhe_infeas, v̂min=[1, 1], v̂max=[-1, -1]) @test_logs( (:error, "MHE terminated without solution: estimation in open-loop "* "(more info in debug log)"), preparestate!(mhe_infeas, [0, 0], [0]) ) - @test_nowarn ModelPredictiveControl.info2debugstr(info) - @test_throws ErrorException setstate!(mhe1, [1,2,3,4,5,6], diagm(.1:.1:.6)) - - hessian = AutoFiniteDiff() + gradient, jacobian, hessian = AutoFiniteDiff(), AutoFiniteDiff(), AutoFiniteDiff() - mhe8 = MovingHorizonEstimator(nonlinmodel; He=2, hessian) - @test mhe8.hessian isa AutoFiniteDiff + mhe8 = MovingHorizonEstimator(nonlinmodel; He=2, gradient, jacobian, hessian) + @test mhe8.gradient isa AutoFiniteDiff + @test mhe8.jacobian isa AutoFiniteDiff + @test mhe8.hessian isa AutoFiniteDiff + x̂ = preparestate!(mhe8, [50, 30], [5]) + @test x̂ ≈ zeros(6) atol=1e-6 + updatestate!(mhe8, [10, 50], [50, 30], [5]) @test_logs( (:warn, "NaN values in the MHE measurements ym: ignoring them in the objective"), preparestate!(mhe8, [50, NaN], [5]) ) - @test mhe8.x̂0 ≈ zeros(6) atol=1e-9 -#= + @test mhe8.x̂0 ≈ zeros(6) atol=1e-6 + function f!(ẋ, x, u, d, p) ẋ[] = -p[] * (x[] - 0.2 * u[] - 0.2 * d[]) return nothing @@ -1384,7 +1390,7 @@ end @test mhe_foh([0.0]) ≈ [1.5] info = getinfo(mhe_foh) @test info[:x̂] ≈ x̂ -=# + end @testitem "MHE estim. & getinfo (NonLinModel, MS)" setup=[SetupMPCtests] begin