From 83666cb341057ff455dd762429cf94756bc1ef20 Mon Sep 17 00:00:00 2001 From: "Klamkin, Michael" Date: Wed, 7 Jan 2026 18:41:12 -0500 Subject: [PATCH 1/6] fix NLP bounds --- src/NonLinearProgram/NonLinearProgram.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NonLinearProgram/NonLinearProgram.jl b/src/NonLinearProgram/NonLinearProgram.jl index f7a04b1e..c31eb2e7 100644 --- a/src/NonLinearProgram/NonLinearProgram.jl +++ b/src/NonLinearProgram/NonLinearProgram.jl @@ -574,13 +574,13 @@ function DiffOpt.reverse_differentiate!(model::Model; tol = 1e-6) end end for (i, var_idx) in enumerate(cache.primal_vars[cache.has_low]) - idx = form.constraint_lower_bounds[var_idx.value].value + idx = form.constraint_lower_bounds[var_idx.value] if haskey(model.input_cache.dy, idx) Δdual[num_constraints+i] = model.input_cache.dy[idx] end end for (i, var_idx) in enumerate(cache.primal_vars[cache.has_up]) - idx = form.constraint_upper_bounds[var_idx.value].value + idx = form.constraint_upper_bounds[var_idx.value] if haskey(model.input_cache.dy, idx) Δdual[num_constraints+num_low+i] = model.input_cache.dy[idx] end From 603f76a1b7846dde8779a8ab717acc8df8261b2a Mon Sep 17 00:00:00 2001 From: "Klamkin, Michael" Date: Thu, 8 Jan 2026 06:09:35 -0500 Subject: [PATCH 2/6] add test --- test/nlp_program.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/nlp_program.jl b/test/nlp_program.jl index c9dbbc73..8e00a333 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -977,6 +977,22 @@ function test_changing_factorization() ) end +function test_reverse_bounds() + model = DiffOpt.nonlinear_diff_model(Ipopt.Optimizer) + set_silent(model) + @variable(model, x[1:3] >= 0) # x[3] ≥ 0 is active + @variable(model, p in MOI.Parameter(4.5)) + @constraint(model, 6x[1] + 3x[2] + 2x[3] == p) + @constraint(model, x[1] + x[2] - x[3] == 1) + @objective(model, Min, sum(x.^2)) + optimize!(model) + MOI.set(model, DiffOpt.ReverseConstraintDual(), LowerBoundRef(x[3]), 1.0) + DiffOpt.reverse_differentiate!(model) + dp = MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)).value + @test abs(dp) > 0.0 # test contribution is not ignored + @test isapprox(dp, -2.88888; atol = 1e-4) +end + end # module TestNLPProgram.runtests() From 2935dc8d9e1683cd45e641c7b41369a017f138de Mon Sep 17 00:00:00 2001 From: Michael Klamkin Date: Thu, 8 Jan 2026 07:58:26 -0500 Subject: [PATCH 3/6] Remove redundant test --- test/nlp_program.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/nlp_program.jl b/test/nlp_program.jl index 8e00a333..b0b9a445 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -989,7 +989,6 @@ function test_reverse_bounds() MOI.set(model, DiffOpt.ReverseConstraintDual(), LowerBoundRef(x[3]), 1.0) DiffOpt.reverse_differentiate!(model) dp = MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)).value - @test abs(dp) > 0.0 # test contribution is not ignored @test isapprox(dp, -2.88888; atol = 1e-4) end From 73186f4428cd4d95c33b5a64ad35f8724be2932a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 9 Jan 2026 11:16:29 +0100 Subject: [PATCH 4/6] Fix format --- test/nlp_program.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/nlp_program.jl b/test/nlp_program.jl index b0b9a445..c08b925e 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -984,7 +984,7 @@ function test_reverse_bounds() @variable(model, p in MOI.Parameter(4.5)) @constraint(model, 6x[1] + 3x[2] + 2x[3] == p) @constraint(model, x[1] + x[2] - x[3] == 1) - @objective(model, Min, sum(x.^2)) + @objective(model, Min, sum(x .^ 2)) optimize!(model) MOI.set(model, DiffOpt.ReverseConstraintDual(), LowerBoundRef(x[3]), 1.0) DiffOpt.reverse_differentiate!(model) From 199bfac483884be321947663a74ab1928200eb51 Mon Sep 17 00:00:00 2001 From: "Klamkin, Michael" Date: Sun, 18 Jan 2026 14:21:54 -0500 Subject: [PATCH 5/6] get full coverage --- test/nlp_program.jl | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/nlp_program.jl b/test/nlp_program.jl index c08b925e..f8c6e718 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -977,7 +977,7 @@ function test_changing_factorization() ) end -function test_reverse_bounds() +function test_reverse_bounds_lower() model = DiffOpt.nonlinear_diff_model(Ipopt.Optimizer) set_silent(model) @variable(model, x[1:3] >= 0) # x[3] ≥ 0 is active @@ -992,6 +992,21 @@ function test_reverse_bounds() @test isapprox(dp, -2.88888; atol = 1e-4) end +function test_reverse_bounds_upper() + model = DiffOpt.nonlinear_diff_model(Ipopt.Optimizer) + set_silent(model) + @variable(model, x[1:3] <= 0) # x[3] ≤ 0 is active + @variable(model, p in MOI.Parameter(4.5)) + @constraint(model, 6x[1] + 3x[2] + 2x[3] == -p) + @constraint(model, x[1] + x[2] - x[3] == -1) + @objective(model, Min, sum(x.^2)) + optimize!(model) + MOI.set(model, DiffOpt.ReverseConstraintDual(), UpperBoundRef(x[3]), 1.0) + DiffOpt.reverse_differentiate!(model) + dp = MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)).value + @test isapprox(dp, 2.88888; atol = 1e-4) +end + end # module TestNLPProgram.runtests() From 2f93d6390c5a31289d48e9db7469fb42a58444cf Mon Sep 17 00:00:00 2001 From: Michael Klamkin Date: Sun, 18 Jan 2026 16:48:59 -0500 Subject: [PATCH 6/6] Apply suggestion from @klamike --- test/nlp_program.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/nlp_program.jl b/test/nlp_program.jl index f8c6e718..c266c715 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -999,7 +999,7 @@ function test_reverse_bounds_upper() @variable(model, p in MOI.Parameter(4.5)) @constraint(model, 6x[1] + 3x[2] + 2x[3] == -p) @constraint(model, x[1] + x[2] - x[3] == -1) - @objective(model, Min, sum(x.^2)) + @objective(model, Min, sum(x .^ 2)) optimize!(model) MOI.set(model, DiffOpt.ReverseConstraintDual(), UpperBoundRef(x[3]), 1.0) DiffOpt.reverse_differentiate!(model)