Skip to content

Commit 8e8cdb6

Browse files
SebastianM-Cclaude
andcommitted
Add test for Lagrangian Hessian with σ=0 edge case
Adds regression test to ensure lag_h! correctly handles the σ=0 case where the Lagrangian Hessian reduces to just the weighted sum of constraint Hessians. Tests both single and multiple constraint scenarios with different AD backends (ForwardDiff, ReverseDiff, Zygote). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 01095ae commit 8e8cdb6

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
using OptimizationBase, Test, DifferentiationInterface
2+
using ADTypes, ForwardDiff, ReverseDiff, Zygote
3+
4+
@testset "Lagrangian Hessian with σ = 0" begin
5+
# Test that lag_h works correctly when σ = 0
6+
# This is a regression test for the bug where lag_h! would fail when
7+
# cons_h was not generated but lag_h needed to compute constraint Hessians
8+
9+
x0 = [0.5, 0.5]
10+
rosenbrock(x, p = nothing) = (1 - x[1])^2 + 100 * (x[2] - x[1]^2)^2
11+
12+
# Single constraint
13+
cons1 = (res, x, p) -> (res[1] = x[1]^2 + x[2]^2; return nothing)
14+
15+
# Two constraints
16+
cons2 = (res, x, p) -> begin
17+
res[1] = x[1]^2 + x[2]^2
18+
res[2] = x[2] * sin(x[1]) - x[1]
19+
return nothing
20+
end
21+
22+
@testset "Single constraint with σ = 0" begin
23+
# Create optimization function WITHOUT cons_h but WITH lag_h
24+
optf = OptimizationFunction(
25+
rosenbrock,
26+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
27+
cons = cons1
28+
)
29+
30+
optprob = OptimizationBase.instantiate_function(
31+
optf, x0,
32+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
33+
nothing, 1,
34+
g = true, h = true,
35+
cons_j = true,
36+
cons_h = false, # Don't generate cons_h!
37+
lag_h = true # Only generate lag_h!
38+
)
39+
40+
# Test with σ = 0 - this should compute only constraint Hessians
41+
H_lag = Array{Float64}(undef, 2, 2)
42+
λ = [2.0] # arbitrary multiplier
43+
σ = 0.0
44+
45+
# This should work and compute H = λ[1] * ∇²c₁
46+
optprob.lag_h(H_lag, x0, σ, λ)
47+
48+
# Expected: constraint Hessian is [2 0; 0 2] for c(x) = x₁² + x₂²
49+
# Scaled by λ[1] = 2.0 gives [4 0; 0 4]
50+
@test H_lag [4.0 0.0; 0.0 4.0]
51+
52+
# Test with σ ≠ 0 for comparison
53+
σ = 1.0
54+
optprob.lag_h(H_lag, x0, σ, λ)
55+
56+
# Expected objective Hessian at x0 = [0.5, 0.5]
57+
H_obj = zeros(2, 2)
58+
H_obj[1, 1] = 2.0 - 400.0 * x0[2] + 1200.0 * x0[1]^2
59+
H_obj[1, 2] = -400.0 * x0[1]
60+
H_obj[2, 1] = -400.0 * x0[1]
61+
H_obj[2, 2] = 200.0
62+
63+
# Should be σ * H_obj + λ[1] * H_cons
64+
@test H_lag H_obj + [4.0 0.0; 0.0 4.0]
65+
end
66+
67+
@testset "Two constraints with σ = 0" begin
68+
optf = OptimizationFunction(
69+
rosenbrock,
70+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
71+
cons = cons2
72+
)
73+
74+
optprob = OptimizationBase.instantiate_function(
75+
optf, x0,
76+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
77+
nothing, 2,
78+
g = true, h = true,
79+
cons_j = true,
80+
cons_h = false, # Don't generate cons_h!
81+
lag_h = true # Only generate lag_h!
82+
)
83+
84+
# Test with σ = 0
85+
H_lag = Array{Float64}(undef, 2, 2)
86+
λ = [1.5, -0.5]
87+
σ = 0.0
88+
89+
# This should compute H = λ[1] * ∇²c₁ + λ[2] * ∇²c₂
90+
optprob.lag_h(H_lag, x0, σ, λ)
91+
92+
# Expected constraint Hessians:
93+
# ∇²c₁ = [2 0; 0 2] for c₁(x) = x₁² + x₂²
94+
# ∇²c₂ = [-sin(x₁)*x₂ cos(x₁); cos(x₁) 0] for c₂(x) = x₂*sin(x₁) - x₁
95+
# At x0 = [0.5, 0.5]:
96+
H_c2 = zeros(2, 2)
97+
H_c2[1, 1] = -sin(x0[1]) * x0[2]
98+
H_c2[1, 2] = cos(x0[1])
99+
H_c2[2, 1] = cos(x0[1])
100+
H_c2[2, 2] = 0.0
101+
102+
expected = λ[1] * [2.0 0.0; 0.0 2.0] + λ[2] * H_c2
103+
@test H_lag expected rtol=1e-6
104+
end
105+
106+
@testset "Different AD backends with σ = 0" begin
107+
# Test with AutoReverseDiff
108+
optf = OptimizationFunction(
109+
rosenbrock,
110+
SecondOrder(AutoForwardDiff(), AutoReverseDiff()),
111+
cons = cons1
112+
)
113+
114+
optprob = OptimizationBase.instantiate_function(
115+
optf, x0,
116+
SecondOrder(AutoForwardDiff(), AutoReverseDiff()),
117+
nothing, 1,
118+
g = true, h = true,
119+
cons_j = true,
120+
cons_h = false,
121+
lag_h = true
122+
)
123+
124+
H_lag = Array{Float64}(undef, 2, 2)
125+
λ = [3.0]
126+
σ = 0.0
127+
128+
optprob.lag_h(H_lag, x0, σ, λ)
129+
@test H_lag [6.0 0.0; 0.0 6.0] # 3.0 * [2 0; 0 2]
130+
131+
# Test with AutoZygote
132+
optf = OptimizationFunction(
133+
rosenbrock,
134+
SecondOrder(AutoForwardDiff(), AutoZygote()),
135+
cons = cons1
136+
)
137+
138+
optprob = OptimizationBase.instantiate_function(
139+
optf, x0,
140+
SecondOrder(AutoForwardDiff(), AutoZygote()),
141+
nothing, 1,
142+
g = true, h = true,
143+
cons_j = true,
144+
cons_h = false,
145+
lag_h = true
146+
)
147+
148+
H_lag = Array{Float64}(undef, 2, 2)
149+
λ = [0.5]
150+
σ = 0.0
151+
152+
optprob.lag_h(H_lag, x0, σ, λ)
153+
@test H_lag [1.0 0.0; 0.0 1.0] # 0.5 * [2 0; 0 2]
154+
end
155+
156+
@testset "Edge cases" begin
157+
optf = OptimizationFunction(
158+
rosenbrock,
159+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
160+
cons = cons2
161+
)
162+
163+
optprob = OptimizationBase.instantiate_function(
164+
optf, x0,
165+
SecondOrder(AutoForwardDiff(), AutoForwardDiff()),
166+
nothing, 2,
167+
g = true, h = true,
168+
cons_j = true,
169+
cons_h = false,
170+
lag_h = true
171+
)
172+
173+
H_lag = Array{Float64}(undef, 2, 2)
174+
175+
# Test with all λ = 0 and σ = 0 (should give zero matrix)
176+
λ = [0.0, 0.0]
177+
σ = 0.0
178+
optprob.lag_h(H_lag, x0, σ, λ)
179+
@test all(H_lag .≈ 0.0)
180+
181+
# Test with some λ = 0 (should skip those constraints)
182+
λ = [2.0, 0.0]
183+
σ = 0.0
184+
optprob.lag_h(H_lag, x0, σ, λ)
185+
@test H_lag [4.0 0.0; 0.0 4.0] # Only first constraint contributes
186+
end
187+
end

lib/OptimizationBase/test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ using Test
66
include("cvxtest.jl")
77
include("matrixvalued.jl")
88
include("solver_missing_error_messages.jl")
9+
include("lag_h_sigma_zero_test.jl")
910
end

0 commit comments

Comments
 (0)