From 42500e719a038a1d7b8ad149b931ab35f24e3971 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Apr 2024 14:26:21 +1200 Subject: [PATCH 1/3] [Bridges] restrict some bridges to acting on real-valued functions --- src/Bridges/Constraint/bridges/functionize.jl | 2 +- src/Bridges/Constraint/bridges/vectorize.jl | 6 +- src/Bridges/Constraint/set_map.jl | 12 ++-- src/Utilities/functions.jl | 34 +++++++++++ test/Bridges/lazy_bridge_optimizer.jl | 58 +++++++++++++++++++ 5 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/Bridges/Constraint/bridges/functionize.jl b/src/Bridges/Constraint/bridges/functionize.jl index 7da6c4c025..c311dbd0e8 100644 --- a/src/Bridges/Constraint/bridges/functionize.jl +++ b/src/Bridges/Constraint/bridges/functionize.jl @@ -280,7 +280,7 @@ function MOI.supports_constraint( ::Type{G}, ::Type{<:MOI.AbstractSet}, ) where {T,F,G<:MOI.AbstractFunction} - return isfinite(conversion_cost(F, G)) + return MOI.Utilities.is_maybe_real(G) && isfinite(conversion_cost(F, G)) end function concrete_bridge_type( diff --git a/src/Bridges/Constraint/bridges/vectorize.jl b/src/Bridges/Constraint/bridges/vectorize.jl index 142badcc49..d314d5f55b 100644 --- a/src/Bridges/Constraint/bridges/vectorize.jl +++ b/src/Bridges/Constraint/bridges/vectorize.jl @@ -61,10 +61,10 @@ end function MOI.supports_constraint( ::Type{VectorizeBridge{T}}, - ::Type{<:MOI.AbstractScalarFunction}, + ::Type{F}, ::Type{<:MOI.Utilities.ScalarLinearSet{T}}, -) where {T} - return true +) where {T,F<:MOI.AbstractScalarFunction} + return MOI.Utilities.is_maybe_real(F) end function MOI.Bridges.added_constrained_variable_types(::Type{<:VectorizeBridge}) diff --git a/src/Bridges/Constraint/set_map.jl b/src/Bridges/Constraint/set_map.jl index ddf89d52d5..590f8244f9 100644 --- a/src/Bridges/Constraint/set_map.jl +++ b/src/Bridges/Constraint/set_map.jl @@ -48,18 +48,18 @@ end function MOI.supports_constraint( ::Type{<:MultiSetMapBridge{T,S1}}, - ::Type{<:MOI.AbstractScalarFunction}, + ::Type{F}, ::Type{S1}, -) where {T,S1<:MOI.AbstractScalarSet} - return true +) where {T,S1<:MOI.AbstractScalarSet,F<:MOI.AbstractScalarFunction} + return MOI.Utilities.is_maybe_real(F) end function MOI.supports_constraint( ::Type{<:MultiSetMapBridge{T,S1}}, - ::Type{<:MOI.AbstractVectorFunction}, + ::Type{F}, ::Type{S1}, -) where {T,S1<:MOI.AbstractVectorSet} - return true +) where {T,S1<:MOI.AbstractVectorSet,F<:MOI.AbstractVectorFunction} + return MOI.Utilities.is_maybe_real(F) end function MOI.Bridges.added_constrained_variable_types( diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 2bade074b0..5ba53b1e63 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -2492,3 +2492,37 @@ function constant_vector( ) return f.constants end + +""" + is_maybe_real(::Type{<:MOI.AbstractFunction}) + +Return `true` if the function type may return `<:Real` values. It returns false +for Complex-valued functions. + +This function defaults to returning a false positive (`true`). If your new +function explicitly returns complex values, opt-out by defining a new method. + +This function is mostly intended for use in the `MOI.Bridges` submodule to +identify when bridges are not applicable (because the function is +complex-valued). + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> MOI.Utilities._is_maybe_real(MOI.VariableIndex) +true + +julia> MOI.Utilities._is_maybe_real(MOI.ScalarAffineFunction{Complex{Int}}) +false + +julia> MOI.Utilities._is_maybe_real(MOI.ScalarNonlinearFunction) +true +``` +""" +is_maybe_real(::Type{<:MOI.AbstractFunction}) = true +is_maybe_real(::Type{<:MOI.ScalarAffineFunction{<:Complex}}) = false +is_maybe_real(::Type{<:MOI.VectorAffineFunction{<:Complex}}) = false +is_maybe_real(::Type{<:MOI.ScalarQuadraticFunction{<:Complex}}) = false +is_maybe_real(::Type{<:MOI.VectorQuadraticFunction{<:Complex}}) = false diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 0e3106d250..3b5822800c 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2131,6 +2131,64 @@ function test_delete_index_in_vector(T::Type = Float64) return end +function test_bridge_complex_nonnegtononpos() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + g = MOI.Utilities.operate(vcat, Complex{Float64}, f) + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, g, MOI.Nonnegatives(1)), + ) + return +end + +function test_bridge_complex_nonpostononneg() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + g = MOI.Utilities.operate(vcat, Complex{Float64}, f) + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, g, MOI.Nonpositives(1)), + ) + return +end + +function test_bridge_complex_greatertoless() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, f, MOI.LessThan(1.0)), + ) + return +end + +function test_bridge_complex_greatertoless() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, f, MOI.GreaterThan(1.0)) + ) + return +end + end # module TestBridgesLazyBridgeOptimizer.runtests() From 40bd46a261b6f62b6f8382fdb9e5e09b8ac53d7a Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Apr 2024 14:31:34 +1200 Subject: [PATCH 2/3] Update --- src/Bridges/Constraint/bridges/norm_one.jl | 6 +-- test/Bridges/lazy_bridge_optimizer.jl | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Bridges/Constraint/bridges/norm_one.jl b/src/Bridges/Constraint/bridges/norm_one.jl index 2ba7180430..5bc0655ac2 100644 --- a/src/Bridges/Constraint/bridges/norm_one.jl +++ b/src/Bridges/Constraint/bridges/norm_one.jl @@ -53,10 +53,10 @@ end function MOI.supports_constraint( ::Type{NormOneBridge{T}}, - ::Type{<:MOI.AbstractVectorFunction}, + ::Type{F}, ::Type{MOI.NormOneCone}, -) where {T} - return true +) where {T,F<:MOI.AbstractVectorFunction} + return MOI.Utilities.is_maybe_real(F) end function MOI.Bridges.added_constrained_variable_types(::Type{<:NormOneBridge}) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 3b5822800c..1d382fc78b 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2131,6 +2131,54 @@ function test_delete_index_in_vector(T::Type = Float64) return end +function test_bridge_complex_norminfinitycone() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + t = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + g = MOI.Utilities.operate(vcat, Complex{Float64}, t, f) + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, g, MOI.NormInfinityCone(2)), + ) + return +end + +function test_bridge_complex_normonecone() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + t = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + g = MOI.Utilities.operate(vcat, Complex{Float64}, t, f) + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, g, MOI.NormOneCone(2)), + ) + return +end + +function test_bridge_complex_secondorder() + model = MOI.instantiate( + MOI.Utilities.Model{Float64}; + with_bridge_type = Float64, + ) + x = MOI.add_variable(model) + t = MOI.add_variable(model) + f = (1.0 + 0.0im) * x + 2.0 * im + g = MOI.Utilities.operate(vcat, Complex{Float64}, t, f) + @test_throws( + MOI.UnsupportedConstraint, + MOI.add_constraint(model, g, MOI.SecondOrderCone(2)), + ) + return +end + function test_bridge_complex_nonnegtononpos() model = MOI.instantiate( MOI.Utilities.Model{Float64}; From d8ef9edeffce27d8356847ff2ec2053b3ab0ba26 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Apr 2024 14:39:42 +1200 Subject: [PATCH 3/3] Fix --- src/Utilities/functions.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 5ba53b1e63..183c4dad9b 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -2511,13 +2511,13 @@ complex-valued). ```jldoctest julia> import MathOptInterface as MOI -julia> MOI.Utilities._is_maybe_real(MOI.VariableIndex) +julia> MOI.Utilities.is_maybe_real(MOI.VariableIndex) true -julia> MOI.Utilities._is_maybe_real(MOI.ScalarAffineFunction{Complex{Int}}) +julia> MOI.Utilities.is_maybe_real(MOI.ScalarAffineFunction{Complex{Int}}) false -julia> MOI.Utilities._is_maybe_real(MOI.ScalarNonlinearFunction) +julia> MOI.Utilities.is_maybe_real(MOI.ScalarNonlinearFunction) true ``` """