From 842e3c928711a37ece5bcb7bd41473f8979bdb8a Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 23 May 2025 12:55:15 +1200 Subject: [PATCH 1/2] [Bridges] improve error thrown in ToMILPBridge when variable is not bounded --- src/Bridges/Bridges.jl | 65 +++++++++++++++++++ .../bridges/BinPackingToMILPBridge.jl | 5 +- .../bridges/CountBelongsToMILPBridge.jl | 6 +- .../bridges/CountDistinctToMILPBridge.jl | 10 +-- .../bridges/CountGreaterThanToMILPBridge.jl | 5 +- .../bridges/IndicatorToMILPBridge.jl | 5 +- .../ReifiedCountDistinctToMILPBridge.jl | 5 +- .../Constraint/bridges/SOS1ToMILPBridge.jl | 5 +- .../Constraint/bridges/SOS2ToMILPBridge.jl | 5 +- .../Constraint/BinPackingToMILPBridge.jl | 23 ++----- .../Constraint/CountBelongsToMILPBridge.jl | 25 ++----- .../Constraint/CountDistinctToMILPBridge.jl | 18 +++-- .../CountGreaterThanToMILPBridge.jl | 25 ++----- .../Constraint/IndicatorToMILPBridge.jl | 17 ++--- .../ReifiedCountDistinctToMILPBridge.jl | 17 ++--- test/Bridges/Constraint/SOS1ToMILPBridge.jl | 17 ++--- test/Bridges/Constraint/SOS2ToMILPBridge.jl | 17 ++--- 17 files changed, 131 insertions(+), 139 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 797428d02f..f7c67308aa 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -512,4 +512,69 @@ function _general_bridge_tests(bridge::B) where {B<:AbstractBridge} return end +""" + BridgeRequiresFiniteDomainError{ + B<:AbstractBridge, + F<:MOI.AbstractFunction, + } <: Exception + +An error thrown when the bridge requires the input function to have a finite +variable domain. +""" +struct BridgeRequiresFiniteDomainError{ + B<:AbstractBridge, + F<:MOI.AbstractFunction, +} <: Exception + bridge::B + f::F end + +function Base.showerror(io::IO, err::BridgeRequiresFiniteDomainError) + return print( + io, + """ + $(typeof(err)): + + There was an error reformulating your model into a form supported by the + solver because one of the bridges requires that all variables have a + finite domain. + + To fix this error, add a lower and upper bound to all variables in your + model. + + If you have double checked that all variables have finite bounds and you + are still encountering this issue, please open a GitHub issue at + https://github.com/jump-dev/MathOptInterface.jl + + ## Common mistakes + + A common mistake is to add the variable bounds as affine constraints. For + example, if you are using JuMP, do not use `@constraint` to add variable + bounds: + ```julia + using JuMP + model = Model() + @variable(model, x) + @constraint(model, x >= 0) + @constraint(model, x <= 1) + ``` + do instead: + ```julia + using JuMP + model = Model() + @variable(model, 0 <= x <= 1) + ``` + + ## Large bound values + + Do not add arbitrarily large variable bounds to fix this error. Doing so + will likely result in a reformulation that takes a long time to build + and solve. Use domain knowledge to find the tightest valid bounds. + + Alternatively, use a solver that has native support for the constraint + types you are using so that you do not need to use the bridging system. + """, + ) +end + +end # module Bridges diff --git a/src/Bridges/Constraint/bridges/BinPackingToMILPBridge.jl b/src/Bridges/Constraint/bridges/BinPackingToMILPBridge.jl index 5c450bda9f..73141f1365 100644 --- a/src/Bridges/Constraint/bridges/BinPackingToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/BinPackingToMILPBridge.jl @@ -205,10 +205,7 @@ function MOI.Bridges.final_touch( x = scalars[i] ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use $(typeof(bridge)) because an element in the " * - "function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/CountBelongsToMILPBridge.jl b/src/Bridges/Constraint/bridges/CountBelongsToMILPBridge.jl index 51604a3db9..45eb08fec6 100644 --- a/src/Bridges/Constraint/bridges/CountBelongsToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/CountBelongsToMILPBridge.jl @@ -210,11 +210,7 @@ function _unit_expansion( for i in 1:length(f) ret = MOI.Utilities.get_bounds(model, bounds, f[i]) if ret === nothing - BT = typeof(bridge) - error( - "Unable to use $BT because an element in the function has a " * - "non-finite domain: $(f[i])", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, f[i])) end if length(bridge.bounds) < i # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/CountDistinctToMILPBridge.jl b/src/Bridges/Constraint/bridges/CountDistinctToMILPBridge.jl index 3e3facdc1f..72c8a998bd 100644 --- a/src/Bridges/Constraint/bridges/CountDistinctToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/CountDistinctToMILPBridge.jl @@ -277,10 +277,7 @@ function _final_touch_not_equal_case( x = scalars[i] ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use CountDistinctToMILPBridge because element $i " * - "in the function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i - 1 # This is the first time calling final_touch @@ -353,10 +350,7 @@ function _final_touch_general_case( x = scalars[i] ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use CountDistinctToMILPBridge because element $i " * - "in the function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i - 1 # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/CountGreaterThanToMILPBridge.jl b/src/Bridges/Constraint/bridges/CountGreaterThanToMILPBridge.jl index 8d7370ab8a..c49282b3d3 100644 --- a/src/Bridges/Constraint/bridges/CountGreaterThanToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/CountGreaterThanToMILPBridge.jl @@ -192,10 +192,7 @@ function _add_unit_expansion( ) where {T,F} ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use $(typeof(bridge)) because an element in the " * - "function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/IndicatorToMILPBridge.jl b/src/Bridges/Constraint/bridges/IndicatorToMILPBridge.jl index 7aeb012c16..c109256e37 100644 --- a/src/Bridges/Constraint/bridges/IndicatorToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/IndicatorToMILPBridge.jl @@ -197,10 +197,7 @@ function MOI.Bridges.final_touch( fi = scalars[2] ret = MOI.Utilities.get_bounds(model, bounds, fi) if ret === nothing - error( - "Unable to use IndicatorToMILPBridge because element 2 " * - "in the function has a non-finite domain: $fi", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, fi)) end if bridge.slack === nothing # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/ReifiedCountDistinctToMILPBridge.jl b/src/Bridges/Constraint/bridges/ReifiedCountDistinctToMILPBridge.jl index ffaf89f1d2..dbdb81fd6b 100644 --- a/src/Bridges/Constraint/bridges/ReifiedCountDistinctToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/ReifiedCountDistinctToMILPBridge.jl @@ -257,10 +257,7 @@ function MOI.Bridges.final_touch( x = scalars[i] ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use ReifiedCountDistinctToMILPBridge because " * - "element $i in the function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i - 2 # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl b/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl index 3007539f14..baae693cbc 100644 --- a/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl @@ -203,10 +203,7 @@ function MOI.Bridges.final_touch( for (i, x) in enumerate(scalars) ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use SOS1ToMILPBridge because element $i " * - "in the function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i # This is the first time calling final_touch diff --git a/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl b/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl index a368522d96..d9db185454 100644 --- a/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl +++ b/src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl @@ -203,10 +203,7 @@ function MOI.Bridges.final_touch( for (i, x) in enumerate(scalars) ret = MOI.Utilities.get_bounds(model, bounds, x) if ret === nothing - error( - "Unable to use SOS2ToMILPBridge because element $i " * - "in the function has a non-finite domain: $x", - ) + throw(MOI.Bridges.BridgeRequiresFiniteDomainError(bridge, x)) end if length(bridge.bounds) < i # This is the first time calling final_touch diff --git a/test/Bridges/Constraint/BinPackingToMILPBridge.jl b/test/Bridges/Constraint/BinPackingToMILPBridge.jl index 3869a9dc7f..d97ca34a16 100644 --- a/test/Bridges/Constraint/BinPackingToMILPBridge.jl +++ b/test/Bridges/Constraint/BinPackingToMILPBridge.jl @@ -118,14 +118,10 @@ function test_runtests_error_variable() model = MOI.Bridges.Constraint.BinPackingToMILP{Int}(inner) x = MOI.add_variables(model, 3) f = MOI.VectorOfVariables(x) - MOI.add_constraint(model, f, MOI.BinPacking(3, [1, 2, 3])) - BT = - MOI.Bridges.Constraint.BinPackingToMILPBridge{Int,MOI.VectorOfVariables} + c = MOI.add_constraint(model, f, MOI.BinPacking(3, [1, 2, 3])) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use $BT because an element in the " * - "function has a non-finite domain: $(x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -136,16 +132,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.BinPackingToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.BinPacking(3, [1, 2, 3])) - BT = MOI.Bridges.Constraint.BinPackingToMILPBridge{ - Int, - MOI.VectorAffineFunction{Int}, - } + c = MOI.add_constraint(model, f, MOI.BinPacking(3, [1, 2, 3])) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use $BT because an element in the " * - "function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/CountBelongsToMILPBridge.jl b/test/Bridges/Constraint/CountBelongsToMILPBridge.jl index a471649aec..cc7d0a2871 100644 --- a/test/Bridges/Constraint/CountBelongsToMILPBridge.jl +++ b/test/Bridges/Constraint/CountBelongsToMILPBridge.jl @@ -103,16 +103,10 @@ function test_runtests_error_variable() model = MOI.Bridges.Constraint.CountBelongsToMILP{Int}(inner) x = MOI.add_variables(model, 3) f = MOI.VectorOfVariables(x) - MOI.add_constraint(model, f, MOI.CountBelongs(3, Set([2, 4]))) - BT = MOI.Bridges.Constraint.CountBelongsToMILPBridge{ - Int, - MOI.VectorOfVariables, - } + c = MOI.add_constraint(model, f, MOI.CountBelongs(3, Set([2, 4]))) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use $BT because an element in " * - "the function has a non-finite domain: $(x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -123,16 +117,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.CountBelongsToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.CountBelongs(3, Set([2, 4]))) - BT = MOI.Bridges.Constraint.CountBelongsToMILPBridge{ - Int, - MOI.VectorAffineFunction{Int}, - } + c = MOI.add_constraint(model, f, MOI.CountBelongs(3, Set([2, 4]))) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use $BT because an element in " * - "the function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/CountDistinctToMILPBridge.jl b/test/Bridges/Constraint/CountDistinctToMILPBridge.jl index d06cd33b20..60163ae6b3 100644 --- a/test/Bridges/Constraint/CountDistinctToMILPBridge.jl +++ b/test/Bridges/Constraint/CountDistinctToMILPBridge.jl @@ -144,12 +144,11 @@ function test_runtests_error_variable() inner = MOI.Utilities.Model{Int}() model = MOI.Bridges.Constraint.CountDistinctToMILP{Int}(inner) x = MOI.add_variables(model, 3) - MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.CountDistinct(3)) + f = MOI.VectorOfVariables(x) + c = MOI.add_constraint(model, f, MOI.CountDistinct(3)) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use CountDistinctToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -160,12 +159,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.CountDistinctToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.CountDistinct(3)) + c = MOI.add_constraint(model, f, MOI.CountDistinct(3)) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use CountDistinctToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/CountGreaterThanToMILPBridge.jl b/test/Bridges/Constraint/CountGreaterThanToMILPBridge.jl index 1615f270bd..4ae4599e8c 100644 --- a/test/Bridges/Constraint/CountGreaterThanToMILPBridge.jl +++ b/test/Bridges/Constraint/CountGreaterThanToMILPBridge.jl @@ -120,16 +120,10 @@ function test_runtests_error_variable() model = MOI.Bridges.Constraint.CountGreaterThanToMILP{Int}(inner) x = MOI.add_variables(model, 3) f = MOI.VectorOfVariables(x) - MOI.add_constraint(model, f, MOI.CountGreaterThan(3)) - BT = MOI.Bridges.Constraint.CountGreaterThanToMILPBridge{ - Int, - MOI.VectorOfVariables, - } + c = MOI.add_constraint(model, f, MOI.CountGreaterThan(3)) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use $BT because an element in " * - "the function has a non-finite domain: $(x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -140,16 +134,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.CountGreaterThanToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, x[1], 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.CountGreaterThan(3)) - BT = MOI.Bridges.Constraint.CountGreaterThanToMILPBridge{ - Int, - MOI.VectorAffineFunction{Int}, - } + c = MOI.add_constraint(model, f, MOI.CountGreaterThan(3)) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use $BT because an element in " * - "the function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/IndicatorToMILPBridge.jl b/test/Bridges/Constraint/IndicatorToMILPBridge.jl index d7f3a87db6..95e7a6bfa5 100644 --- a/test/Bridges/Constraint/IndicatorToMILPBridge.jl +++ b/test/Bridges/Constraint/IndicatorToMILPBridge.jl @@ -253,16 +253,14 @@ function test_runtests_error_variable() model = MOI.Bridges.Constraint.IndicatorToMILP{Int}(inner) x = MOI.add_variables(model, 2) MOI.add_constraint(model, x[1], MOI.ZeroOne()) - MOI.add_constraint( + c = MOI.add_constraint( model, MOI.VectorOfVariables(x), MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.GreaterThan(2)), ) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use IndicatorToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -273,16 +271,15 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.IndicatorToMILP{Int}(inner) x = MOI.add_variables(model, 2) MOI.add_constraint(model, x[1], MOI.ZeroOne()) - MOI.add_constraint( + c = MOI.add_constraint( model, MOI.Utilities.operate(vcat, Int, x[1], 2 * x[2]), MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.GreaterThan(2)), ) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use IndicatorToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(2 * x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/ReifiedCountDistinctToMILPBridge.jl b/test/Bridges/Constraint/ReifiedCountDistinctToMILPBridge.jl index 06b913df32..361ae9a730 100644 --- a/test/Bridges/Constraint/ReifiedCountDistinctToMILPBridge.jl +++ b/test/Bridges/Constraint/ReifiedCountDistinctToMILPBridge.jl @@ -136,16 +136,14 @@ function test_runtests_error_variable() inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Int}()) model = MOI.Bridges.Constraint.ReifiedCountDistinctToMILP{Int}(inner) x = MOI.add_variables(model, 4) - MOI.add_constraint( + c = MOI.add_constraint( model, MOI.VectorOfVariables(x), MOI.Reified(MOI.CountDistinct(3)), ) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use ReifiedCountDistinctToMILPBridge because element " * - "3 in the function has a non-finite domain: $(x[3])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -156,12 +154,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.ReifiedCountDistinctToMILP{Int}(inner) x = MOI.add_variables(model, 3) f = MOI.Utilities.operate(vcat, Int, x[1], 1, x[2], x[3]) - MOI.add_constraint(model, f, MOI.Reified(MOI.CountDistinct(3))) + c = MOI.add_constraint(model, f, MOI.Reified(MOI.CountDistinct(3))) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use ReifiedCountDistinctToMILPBridge because element " * - "3 in the function has a non-finite domain: $(1 * x[2])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/SOS1ToMILPBridge.jl b/test/Bridges/Constraint/SOS1ToMILPBridge.jl index 029f4873cf..b25b91086d 100644 --- a/test/Bridges/Constraint/SOS1ToMILPBridge.jl +++ b/test/Bridges/Constraint/SOS1ToMILPBridge.jl @@ -92,12 +92,10 @@ function test_runtests_error_variable() inner = MOI.Utilities.Model{Int}() model = MOI.Bridges.Constraint.SOS1ToMILP{Int}(inner) x = MOI.add_variables(model, 3) - MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SOS1([1, 2, 3])) + c = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SOS1([1, 2, 3])) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use SOS1ToMILPBridge because element 1 in " * - "the function has a non-finite domain: $(x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -108,12 +106,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.SOS1ToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.SOS1([1, 2, 3])) + c = MOI.add_constraint(model, f, MOI.SOS1([1, 2, 3])) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use SOS1ToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return diff --git a/test/Bridges/Constraint/SOS2ToMILPBridge.jl b/test/Bridges/Constraint/SOS2ToMILPBridge.jl index 8725015b1e..3d1ce6273b 100644 --- a/test/Bridges/Constraint/SOS2ToMILPBridge.jl +++ b/test/Bridges/Constraint/SOS2ToMILPBridge.jl @@ -102,12 +102,10 @@ function test_runtests_error_variable() inner = MOI.Utilities.Model{Int}() model = MOI.Bridges.Constraint.SOS2ToMILP{Int}(inner) x = MOI.add_variables(model, 3) - MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SOS2([1, 2, 3])) + c = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SOS2([1, 2, 3])) + BT = typeof(model.map[c]) @test_throws( - ErrorException( - "Unable to use SOS2ToMILPBridge because element 1 in " * - "the function has a non-finite domain: $(x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,MOI.VariableIndex}, MOI.Bridges.final_touch(model), ) return @@ -118,12 +116,11 @@ function test_runtests_error_affine() model = MOI.Bridges.Constraint.SOS2ToMILP{Int}(inner) x = MOI.add_variables(model, 2) f = MOI.Utilities.operate(vcat, Int, 2, 1 * x[1], x[2]) - MOI.add_constraint(model, f, MOI.SOS2([1, 2, 3])) + c = MOI.add_constraint(model, f, MOI.SOS2([1, 2, 3])) + BT = typeof(model.map[c]) + F = MOI.ScalarAffineFunction{Int} @test_throws( - ErrorException( - "Unable to use SOS2ToMILPBridge because element 2 in " * - "the function has a non-finite domain: $(1 * x[1])", - ), + MOI.Bridges.BridgeRequiresFiniteDomainError{BT,F}, MOI.Bridges.final_touch(model), ) return From abdddccf99a9b8dde5ac1ab609385c5df2ea6811 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 23 May 2025 13:50:02 +1200 Subject: [PATCH 2/2] Update --- test/Bridges/bridge_optimizer.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index da8a26a4b1..f42a12ee1e 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -1452,6 +1452,17 @@ function test_issue_2714() return end +function test_BridgeRequiresFiniteDomainError() + inner = MOI.Utilities.Model{Int}() + model = MOI.Bridges.Constraint.SOS1ToMILP{Int}(inner) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SOS1([1, 2, 3])) + err = MOI.Bridges.BridgeRequiresFiniteDomainError(model.map[c], x[1]) + contents = sprint(showerror, err) + @test occursin("To fix this error, add a lower and upper bound", contents) + return +end + end # module TestBridgeOptimizer.runtests()