diff --git a/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl b/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl index d4df1922ef..3007539f14 100644 --- a/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl @@ -61,6 +61,12 @@ end const SOS1ToMILP{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOS1ToMILPBridge{T},OT} +# We need to weight this bridge such that `VectorAffineFunction-in-SOS1` is +# bridged (if possible) by `VectorSlack + VectorOfVariables-in-SOS1`. +# +# See MathOptInterface#2722 for details. +MOI.Bridges.bridging_cost(::Type{<:SOS1ToMILPBridge}) = 10.0 + function bridge_constraint( ::Type{SOS1ToMILPBridge{T,F}}, model::MOI.ModelLike, diff --git a/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl b/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl index 3d0e07611f..a368522d96 100644 --- a/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl @@ -61,6 +61,12 @@ end const SOS2ToMILP{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOS2ToMILPBridge{T},OT} +# We need to weight this bridge such that `VectorAffineFunction-in-SOS2` is +# bridged (if possible) by `VectorSlack + VectorOfVariables-in-SOS2`. +# +# See MathOptInterface#2722 for details. +MOI.Bridges.bridging_cost(::Type{<:SOS2ToMILPBridge}) = 10.0 + function bridge_constraint( ::Type{SOS2ToMILPBridge{T,F}}, model::MOI.ModelLike, diff --git a/test/Bridges/Constraint/SOS1ToMILPBridge.jl b/test/Bridges/Constraint/SOS1ToMILPBridge.jl index b9a774e12a..029f4873cf 100644 --- a/test/Bridges/Constraint/SOS1ToMILPBridge.jl +++ b/test/Bridges/Constraint/SOS1ToMILPBridge.jl @@ -134,6 +134,38 @@ function test_delete_before_final_touch() return end +MOI.Utilities.@model( + Model2722, + (), + (MOI.EqualTo,), + (MOI.Zeros,), + (MOI.SOS1,), + (), + (MOI.ScalarAffineFunction,), + (MOI.VectorOfVariables,), + (MOI.VectorAffineFunction,), +) + +function MOI.supports_constraint( + ::Model2722{T}, + ::Type{MOI.VectorAffineFunction{T}}, + ::Type{MOI.SOS1{T}}, +) where {T} + return false +end + +function test_bridge_does_not_apply_if_vector_slack_exists() + inner = Model2722{Float64}() + model = MOI.Bridges.full_bridge_optimizer(inner, Float64) + x = MOI.add_variables(model, 3) + f = MOI.Utilities.vectorize(1.0 .* x) + c = MOI.add_constraint(model, f, MOI.SOS1([1.0, 2.0, 3.0])) + @test model.constraint_map[c] isa MOI.Bridges.Constraint.VectorSlackBridge + F, S = MOI.VectorOfVariables, MOI.SOS1{Float64} + @test (F, S) in MOI.get(inner, MOI.ListOfConstraintTypesPresent()) + return +end + end # module TestConstraintSOS1ToMILP.runtests() diff --git a/test/Bridges/Constraint/SOS2ToMILPBridge.jl b/test/Bridges/Constraint/SOS2ToMILPBridge.jl index 5a62cacbbf..8725015b1e 100644 --- a/test/Bridges/Constraint/SOS2ToMILPBridge.jl +++ b/test/Bridges/Constraint/SOS2ToMILPBridge.jl @@ -144,6 +144,38 @@ function test_delete_before_final_touch() return end +MOI.Utilities.@model( + Model2722, + (), + (MOI.EqualTo,), + (MOI.Zeros,), + (MOI.SOS2,), + (), + (MOI.ScalarAffineFunction,), + (MOI.VectorOfVariables,), + (MOI.VectorAffineFunction,), +) + +function MOI.supports_constraint( + ::Model2722{T}, + ::Type{MOI.VectorAffineFunction{T}}, + ::Type{MOI.SOS2{T}}, +) where {T} + return false +end + +function test_bridge_does_not_apply_if_vector_slack_exists() + inner = Model2722{Float64}() + model = MOI.Bridges.full_bridge_optimizer(inner, Float64) + x = MOI.add_variables(model, 3) + f = MOI.Utilities.vectorize(1.0 .* x) + c = MOI.add_constraint(model, f, MOI.SOS2([1.0, 2.0, 3.0])) + @test model.constraint_map[c] isa MOI.Bridges.Constraint.VectorSlackBridge + F, S = MOI.VectorOfVariables, MOI.SOS2{Float64} + @test (F, S) in MOI.get(inner, MOI.ListOfConstraintTypesPresent()) + return +end + end # module TestConstraintSOS2ToMILP.runtests()