From b9a4068bba84effa187407915ec35243d86f5c47 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 3 Mar 2026 15:21:33 +1300 Subject: [PATCH] [Bridges] fix bug with open intervals in SemiToBinaryBridge --- .../Constraint/bridges/SemiToBinaryBridge.jl | 14 ++++++++- .../Constraint/test_SemiToBinaryBridge.jl | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Bridges/Constraint/bridges/SemiToBinaryBridge.jl b/src/Bridges/Constraint/bridges/SemiToBinaryBridge.jl index df3c86c3e6..055122163f 100644 --- a/src/Bridges/Constraint/bridges/SemiToBinaryBridge.jl +++ b/src/Bridges/Constraint/bridges/SemiToBinaryBridge.jl @@ -75,6 +75,12 @@ function bridge_constraint( f::MOI.VariableIndex, s::S, ) where {T<:Real,S<:Union{MOI.Semicontinuous{T},MOI.Semiinteger{T}}} + if !isfinite(s.lower) || !isfinite(s.upper) + # We could be cleverer than this, but this is a simple check that should + # be sufficient for most cases. We can revisit if anyone complains. + msg = "Cannot add constraint $f-in-$s because the set has an open interval. To fix, make sure the lower and upper bounds are both finite." + throw(MOI.AddConstraintNotAllowed{MOI.VariableIndex,S}(msg)) + end binary, binary_con = MOI.add_constrained_variable(model, MOI.ZeroOne()) # var - LB * bin >= 0 lb = MOI.Utilities.operate(*, T, -s.lower, binary) @@ -147,10 +153,16 @@ end function MOI.set( model::MOI.ModelLike, - ::MOI.ConstraintSet, + attr::MOI.ConstraintSet, bridge::SemiToBinaryBridge{T,S}, set::S, ) where {T,S} + if !isfinite(set.lower) || !isfinite(set.upper) + # We could be cleverer than this, but this is a simple check that should + # be sufficient for most cases. We can revisit if anyone complains. + msg = "Cannot modify the set $set because the set has an open interval. To fix, make sure the lower and upper bounds are both finite." + throw(MOI.SetAttributeNotAllowed(attr, msg)) + end bridge.semi_set = set MOI.modify( model, diff --git a/test/Bridges/Constraint/test_SemiToBinaryBridge.jl b/test/Bridges/Constraint/test_SemiToBinaryBridge.jl index 250799b496..6d70c65819 100644 --- a/test/Bridges/Constraint/test_SemiToBinaryBridge.jl +++ b/test/Bridges/Constraint/test_SemiToBinaryBridge.jl @@ -304,6 +304,36 @@ function test_runtests() return end +function test_open_interval() + for set in Any[ + MOI.Semicontinuous(1.0, Inf), + MOI.Semicontinuous(-1.0, Inf), + MOI.Semicontinuous(-Inf, 1.0), + MOI.Semicontinuous(-Inf, -1.0), + MOI.Semiinteger(1.0, Inf), + MOI.Semiinteger(-1.0, Inf), + MOI.Semiinteger(-Inf, 1.0), + MOI.Semiinteger(-Inf, -1.0), + ] + model = MOI.Utilities.Model{Float64}() + bridged = MOI.Bridges.Constraint.SemiToBinary{Float64}(model) + x = MOI.add_variable(bridged) + @test_throws( + MOI.AddConstraintNotAllowed{MOI.VariableIndex,typeof(set)}, + MOI.add_constraint(bridged, x, set) + ) + model = MOI.Utilities.Model{Float64}() + bridged = MOI.Bridges.Constraint.SemiToBinary{Float64}(model) + x = MOI.add_variable(bridged) + c = MOI.add_constraint(bridged, x, typeof(set)(1.0, 2.0)) + @test_throws( + MOI.SetAttributeNotAllowed, + MOI.set(bridged, MOI.ConstraintSet(), c, set), + ) + end + return +end + end # module TestConstraintSemiToBinary.runtests()