From d30c2d805156304848496a01ae4ef7e7783cf63e Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 22 Oct 2025 20:34:18 +1300 Subject: [PATCH 1/3] [Bridges] fix ConstraintConflictStatus in added_constrained_variable_types --- src/Bridges/bridge_optimizer.jl | 14 +++++++++ test/Bridges/lazy_bridge_optimizer.jl | 41 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/Bridges/bridge_optimizer.jl b/src/Bridges/bridge_optimizer.jl index a45fbdc185..8a55d8d335 100644 --- a/src/Bridges/bridge_optimizer.jl +++ b/src/Bridges/bridge_optimizer.jl @@ -1749,6 +1749,9 @@ function MOI.set( return throw(MOI.SettingVariableIndexNotAllowed()) end +_f_type(::Type{S}) where {S<:MOI.AbstractScalarSet} = MOI.VariableIndex +_f_type(::Type{S}) where {S<:MOI.AbstractVectorSet} = MOI.VectorOfVariables + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintConflictStatus, @@ -1765,6 +1768,17 @@ function MOI.get( end end end + for (S,) in MOI.Bridges.added_constrained_variable_types(typeof(bridge)) + F = _f_type(S) + for ci in MOI.get(bridge, MOI.ListOfConstraintIndices{F,S}()) + status = MOI.get(model, attr, ci) + if status == MOI.IN_CONFLICT + return status + elseif status == MOI.MAYBE_IN_CONFLICT + ret = status + end + end + end return ret end diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index e9a56e7531..149c1927c5 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2328,6 +2328,47 @@ function test_issue_2838() return end +MOI.Utilities.@model( + Model2870, + (), + (MOI.EqualTo,), + (), + (), + (), + (MOI.ScalarAffineFunction,), + (), + () +) + +function test_issue_2870() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.ScalarSlack{Float64}(inner) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, 2.0 * x, MOI.Interval(1.0, -1.0)) + F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64} + ci_eq = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())) + F, S = MOI.VariableIndex, MOI.Interval{Float64} + ci_iv = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())) + function cmp(a, b) + if a == MOI.IN_CONFLICT || b == MOI.IN_CONFLICT + return MOI.IN_CONFLICT + elseif a == MOI.MAYBE_IN_CONFLICT || b == MOI.MAYBE_IN_CONFLICT + return MOI.MAYBE_IN_CONFLICT + else + return MOI.NOT_IN_CONFLICT + end + end + list = (MOI.NOT_IN_CONFLICT, MOI.IN_CONFLICT, MOI.MAYBE_IN_CONFLICT) + for a in list, b in list + MOI.set(inner, MOI.ConflictCount(), 1) + MOI.set(inner, MOI.ConstraintConflictStatus(), ci_eq, a) + MOI.set(inner, MOI.ConstraintConflictStatus(), ci_iv, b) + MOI.compute_conflict!(model) + @test MOI.get(model, MOI.ConstraintConflictStatus(), c) == cmp(a, b) + end + return +end + end # module TestBridgesLazyBridgeOptimizer.runtests() From 80e8234de3f8582b62721e93ad610be53a1e08ba Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 22 Oct 2025 20:35:32 +1300 Subject: [PATCH 2/3] Update --- test/Bridges/lazy_bridge_optimizer.jl | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 149c1927c5..7846baeb01 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2328,18 +2328,6 @@ function test_issue_2838() return end -MOI.Utilities.@model( - Model2870, - (), - (MOI.EqualTo,), - (), - (), - (), - (MOI.ScalarAffineFunction,), - (), - () -) - function test_issue_2870() inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) model = MOI.Bridges.Constraint.ScalarSlack{Float64}(inner) From a377327ba92c70c505e5bc4feef27c5c37cf2748 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 23 Oct 2025 08:30:43 +1300 Subject: [PATCH 3/3] Update --- test/Bridges/lazy_bridge_optimizer.jl | 93 +++++++++++++++------------ 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 7846baeb01..8da73884b8 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2300,60 +2300,71 @@ MOI.Utilities.@model( () ) +function _test_conflicts(model, inner, c) + ci = MOI.ConstraintIndex[] + for (F, S) in MOI.get(inner, MOI.ListOfConstraintTypesPresent()) + append!(ci, MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())) + end + list = (MOI.NOT_IN_CONFLICT, MOI.IN_CONFLICT, MOI.MAYBE_IN_CONFLICT) + for a in Iterators.product(ntuple(i -> list, length(ci))...) + MOI.set(inner, MOI.ConflictCount(), 1) + MOI.set.(inner, MOI.ConstraintConflictStatus(), ci, a) + MOI.compute_conflict!(model) + status = MOI.get(model, MOI.ConstraintConflictStatus(), c) + @test status == reduce(_cmp, a) + end + return +end + +function _cmp( + a::MOI.ConflictParticipationStatusCode, + b::MOI.ConflictParticipationStatusCode, +) + if a == MOI.IN_CONFLICT || b == MOI.IN_CONFLICT + return MOI.IN_CONFLICT + elseif a == MOI.MAYBE_IN_CONFLICT || b == MOI.MAYBE_IN_CONFLICT + return MOI.MAYBE_IN_CONFLICT + else + return MOI.NOT_IN_CONFLICT + end +end + function test_issue_2838() inner = MOI.Utilities.MockOptimizer(Model2838{Float64}()) model = MOI.Bridges.full_bridge_optimizer(inner, Float64) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Float64, (1.0 * x)...) c = MOI.add_constraint(model, f, MOI.Nonnegatives(2)) - F, S = MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64} - ci = MOI.get(inner, MOI.ListOfConstraintIndices{F,S}()) - function cmp(a, b) - if a == MOI.IN_CONFLICT || b == MOI.IN_CONFLICT - return MOI.IN_CONFLICT - elseif a == MOI.MAYBE_IN_CONFLICT || b == MOI.MAYBE_IN_CONFLICT - return MOI.MAYBE_IN_CONFLICT - else - return MOI.NOT_IN_CONFLICT - end - end - list = (MOI.NOT_IN_CONFLICT, MOI.IN_CONFLICT, MOI.MAYBE_IN_CONFLICT) - for a in list, b in list - MOI.set(inner, MOI.ConflictCount(), 1) - MOI.set(inner, MOI.ConstraintConflictStatus(), ci[1], a) - MOI.set(inner, MOI.ConstraintConflictStatus(), ci[2], b) - MOI.compute_conflict!(model) - @test MOI.get(model, MOI.ConstraintConflictStatus(), c) == cmp(a, b) - end + _test_conflicts(model, inner, c) return end -function test_issue_2870() +function test_issue_2870_scalar_slack() inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) model = MOI.Bridges.Constraint.ScalarSlack{Float64}(inner) x = MOI.add_variable(model) c = MOI.add_constraint(model, 2.0 * x, MOI.Interval(1.0, -1.0)) - F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64} - ci_eq = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())) - F, S = MOI.VariableIndex, MOI.Interval{Float64} - ci_iv = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}())) - function cmp(a, b) - if a == MOI.IN_CONFLICT || b == MOI.IN_CONFLICT - return MOI.IN_CONFLICT - elseif a == MOI.MAYBE_IN_CONFLICT || b == MOI.MAYBE_IN_CONFLICT - return MOI.MAYBE_IN_CONFLICT - else - return MOI.NOT_IN_CONFLICT - end - end - list = (MOI.NOT_IN_CONFLICT, MOI.IN_CONFLICT, MOI.MAYBE_IN_CONFLICT) - for a in list, b in list - MOI.set(inner, MOI.ConflictCount(), 1) - MOI.set(inner, MOI.ConstraintConflictStatus(), ci_eq, a) - MOI.set(inner, MOI.ConstraintConflictStatus(), ci_iv, b) - MOI.compute_conflict!(model) - @test MOI.get(model, MOI.ConstraintConflictStatus(), c) == cmp(a, b) - end + _test_conflicts(model, inner, c) + return +end + +function test_issue_2870_geomean_to_power() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.GeoMeanToPower{Float64}(inner) + x = MOI.add_variables(model, 4) + f = MOI.VectorOfVariables(x) + c = MOI.add_constraint(model, f, MOI.GeometricMeanCone(4)) + _test_conflicts(model, inner, c) + return +end + +function test_issue_2870_relative_entropy() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.RelativeEntropy{Float64}(inner) + x = MOI.add_variables(model, 5) + f = MOI.VectorOfVariables(x) + c = MOI.add_constraint(model, f, MOI.RelativeEntropyCone(5)) + _test_conflicts(model, inner, c) return end