From 8781063d685706e7bc9eca91967f7f0a093d218c Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 5 Mar 2024 17:21:20 +1300 Subject: [PATCH 1/7] WIP: add bridge for Complex NormInfinityCone --- src/Bridges/Constraint/Constraint.jl | 2 + .../bridges/complex_norm_infinity.jl | 146 ++++++++++++++++++ .../Constraint/bridges/norm_infinity.jl | 8 + .../Constraint/complex_norm_infinity.jl | 76 +++++++++ 4 files changed, 232 insertions(+) create mode 100644 src/Bridges/Constraint/bridges/complex_norm_infinity.jl create mode 100644 test/Bridges/Constraint/complex_norm_infinity.jl diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index f15adf6cd7..76339333f5 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -21,6 +21,7 @@ include("bridges/all_different.jl") include("bridges/all_different_reif.jl") include("bridges/bin_packing.jl") include("bridges/circuit.jl") +include("bridges/complex_norm_infinity.jl") include("bridges/count_at_least.jl") include("bridges/count_belongs.jl") include("bridges/count_distinct.jl") @@ -107,6 +108,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOI.Bridges.add_bridge(bridged_model, NormOneConeToNormConeBridge{T}) MOI.Bridges.add_bridge(bridged_model, SecondOrderConeToNormConeBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormInfinityConeToNormConeBridge{T}) + MOI.Bridges.add_bridge(bridged_model, ComplexNormInfinityToSecondOrderConeBridge{T}) MOI.Bridges.add_bridge(bridged_model, RelativeEntropyBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormSpectralBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormNuclearBridge{T}) diff --git a/src/Bridges/Constraint/bridges/complex_norm_infinity.jl b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl new file mode 100644 index 0000000000..6cb831c9eb --- /dev/null +++ b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl @@ -0,0 +1,146 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +""" + ComplexNormInfinityToSecondOrderConeBridge{T} <: Bridges.Constraint.AbstractBridge + +`ComplexNormInfinityToSecondOrderConeBridge` implements the following +reformulation: + + * ``(t, x) \\in NormInfinity(1+d)`` into ``(t, real(x_i), imag(x_i)) \\in SecondOrderCone()`` + for all ``i``. + +## Source node + +`ComplexNormInfinityToSecondOrderConeBridge` supports: + + * [`MOI.VectorAffineFunction{Complex{T}}`](@ref) in [`MOI.NormInfinityCone`](@ref) + +## Target nodes + +`ComplexNormInfinityToSecondOrderConeBridge` creates: + + * [`MOI.VectorAffineFunction{T}`](@ref) in [`MOI.SecondOrderCone`](@ref) +""" +struct ComplexNormInfinityToSecondOrderConeBridge{T} <: AbstractBridge + ci::Vector{ + MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.SecondOrderCone} + } +end + +const ComplexNormInfinityToSecondOrderCone{T,OT<:MOI.ModelLike} = + SingleBridgeOptimizer{ComplexNormInfinityToSecondOrderConeBridge{T},OT} + +function bridge_constraint( + ::Type{ComplexNormInfinityToSecondOrderConeBridge{T}}, + model::MOI.ModelLike, + f::MOI.VectorAffineFunction{Complex{T}}, + s::MOI.NormInfinityCone, +) where {T} + fi_s = MOI.Utilities.scalarize(f) + t = real(fi_s[1]) + cis = MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.SecondOrderCone}[] + for fi in fi_s[2:end] + ci = MOI.add_constraint( + model, + MOI.Utilities.operate(vcat, T, t, real(fi), imag(fi)), + MOI.SecondOrderCone(3), + ) + push!(cis, ci) + end + return ComplexNormInfinityToSecondOrderConeBridge{T}(cis) +end + +function MOI.supports_constraint( + ::Type{<:ComplexNormInfinityToSecondOrderConeBridge{T}}, + ::Type{MOI.VectorAffineFunction{Complex{T}}}, + ::Type{MOI.NormInfinityCone}, +) where {T} + return true +end + +function MOI.Bridges.added_constrained_variable_types( + ::Type{<:ComplexNormInfinityToSecondOrderConeBridge}, +) + return Tuple{Type}[] +end + +function MOI.Bridges.added_constraint_types( + ::Type{<:ComplexNormInfinityToSecondOrderConeBridge{T}}, +) where {T} + return Tuple{Type,Type}[(MOI.VectorAffineFunction{T}, MOI.SecondOrderCone)] +end + +function concrete_bridge_type( + ::Type{<:ComplexNormInfinityToSecondOrderConeBridge{T}}, + ::Type{MOI.VectorAffineFunction{Complex{T}}}, + ::Type{MOI.NormInfinityCone}, +) where {T} + return ComplexNormInfinityToSecondOrderConeBridge{T} +end + +function MOI.get( + ::ComplexNormInfinityToSecondOrderConeBridge, + ::MOI.NumberOfVariables, +)::Int64 + return 0 +end + +function MOI.get( + bridge::ComplexNormInfinityToSecondOrderConeBridge{T}, + ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T},MOI.SecondOrderCone}, +)::Int64 where {T} + return length(bridge.ci) +end + +function MOI.get( + bridge::ComplexNormInfinityToSecondOrderConeBridge{T}, + ::MOI.ListOfConstraintIndices{ + MOI.VectorAffineFunction{T}, + MOI.SecondOrderCone, + }, +) where {T} + return copy(bridge.ci) +end + +function MOI.delete( + model::MOI.ModelLike, + bridge::ComplexNormInfinityToSecondOrderConeBridge, +) + MOI.delete(model, bridge.ci) + return +end + +function MOI.get( + model::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::ComplexNormInfinityToSecondOrderConeBridge{T}, +) where {T} + elements = MOI.ScalarAffineFunction{Complex{T}}[] + for ci in bridge.ci + f = MOI.get(model, MOI.ConstraintFunction(), ci) + fi_s = MOI.Utilities.scalarize(f) + if isempty(elements) + push!(elements, fi_s[1]) + end + fi = MOI.Utilities.operate( + +, + Complex{T}, + (one(T) + zero(T)*im) * fi_s[2], + (zero(T) + one(T)*im) * fi_s[3], + ) + push!(elements, fi) + end + return MOI.Utilities.operate(vcat, Complex{T}, elements...) +end + +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintSet, + bridge::ComplexNormInfinityToSecondOrderConeBridge, +) + return MOI.NormInfinityCone(1 + length(bridge.ci)) +end diff --git a/src/Bridges/Constraint/bridges/norm_infinity.jl b/src/Bridges/Constraint/bridges/norm_infinity.jl index d540cdcae5..80bea28361 100644 --- a/src/Bridges/Constraint/bridges/norm_infinity.jl +++ b/src/Bridges/Constraint/bridges/norm_infinity.jl @@ -31,6 +31,14 @@ end const NormInfinity{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T},OT} +function MOI.supports_constraint( + ::Type{<:NormInfinityBridge{T}}, + ::Type{MOI.VectorAffineFunction{Complex{T}}}, + ::Type{MOI.NormInfinityCone}, +) where {T} + return false +end + function concrete_bridge_type( ::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, diff --git a/test/Bridges/Constraint/complex_norm_infinity.jl b/test/Bridges/Constraint/complex_norm_infinity.jl new file mode 100644 index 0000000000..91620420d4 --- /dev/null +++ b/test/Bridges/Constraint/complex_norm_infinity.jl @@ -0,0 +1,76 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +module TestConstraintComplexNormInfinityToSecondOrderCone + +using Test + +import MathOptInterface as MOI + +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + +function test_runtests() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, + """ + variables: t, x + ::Complex{Float64}: [t, (1 + 2im) * x + (3 + 4im)] in NormInfinityCone(2) + """, + """ + variables: t, x + ::Float64: [t, 1 * x + 3, 2 * x + 4] in SecondOrderCone(3) + """, + ) + MOI.Bridges.runtests( + MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, + """ + variables: t, x, y + ::Complex{Float64}: [2.0 * t + 3.0, x + im * y] in NormInfinityCone(2) + """, + """ + variables: t, x, y + ::Float64: [2.0 * t + 3.0, 1.0 * x, 1.0 * y] in SecondOrderCone(3) + """, + ) + # MOI.Bridges.runtests( + # MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, + # """ + # variables: x + # ::Complex{Float64}: [(0 + 2im) * x + (0 + 4im)] in Zeros(1) + # """, + # """ + # variables: x + # ::Float64: [2 * x + 4] in Zeros(1) + # """; + # constraint_start = 0.0 + 1.2im, + # ) + # MOI.Bridges.runtests( + # MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, + # """ + # variables: x + # ::Complex{Float64}: [(2 + 0im) * x + (4 + 0im)] in Zeros(1) + # """, + # """ + # variables: x + # ::Float64: [2 * x + 4] in Zeros(1) + # """; + # constraint_start = 1.2 + 0.0im, + # ) + return +end + +end # module + +TestConstraintComplexNormInfinityToSecondOrderCone.runtests() From 8eebab3b82e2eb657f60c033844ffb136b59b64c Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 9 Apr 2024 12:09:53 +1200 Subject: [PATCH 2/7] Update --- docs/src/submodules/Bridges/list_of_bridges.md | 1 + src/Bridges/Constraint/bridges/norm_infinity.jl | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/src/submodules/Bridges/list_of_bridges.md b/docs/src/submodules/Bridges/list_of_bridges.md index 0a4c0e52af..a4a61e4205 100644 --- a/docs/src/submodules/Bridges/list_of_bridges.md +++ b/docs/src/submodules/Bridges/list_of_bridges.md @@ -44,6 +44,7 @@ Bridges.Constraint.QuadtoSOCBridge Bridges.Constraint.SOCtoPSDBridge Bridges.Constraint.RSOCtoPSDBridge Bridges.Constraint.NormInfinityBridge +Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge Bridges.Constraint.NormOneBridge Bridges.Constraint.NormToPowerBridge Bridges.Constraint.NormOneConeToNormConeBridge diff --git a/src/Bridges/Constraint/bridges/norm_infinity.jl b/src/Bridges/Constraint/bridges/norm_infinity.jl index 80bea28361..d540cdcae5 100644 --- a/src/Bridges/Constraint/bridges/norm_infinity.jl +++ b/src/Bridges/Constraint/bridges/norm_infinity.jl @@ -31,14 +31,6 @@ end const NormInfinity{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T},OT} -function MOI.supports_constraint( - ::Type{<:NormInfinityBridge{T}}, - ::Type{MOI.VectorAffineFunction{Complex{T}}}, - ::Type{MOI.NormInfinityCone}, -) where {T} - return false -end - function concrete_bridge_type( ::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, From a4f9954868ac2310124127377ee7011ad597dfce Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 9 Apr 2024 12:19:38 +1200 Subject: [PATCH 3/7] Fix format --- src/Bridges/Constraint/Constraint.jl | 5 ++++- src/Bridges/Constraint/bridges/complex_norm_infinity.jl | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 76339333f5..6f6e7bec07 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -108,7 +108,10 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOI.Bridges.add_bridge(bridged_model, NormOneConeToNormConeBridge{T}) MOI.Bridges.add_bridge(bridged_model, SecondOrderConeToNormConeBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormInfinityConeToNormConeBridge{T}) - MOI.Bridges.add_bridge(bridged_model, ComplexNormInfinityToSecondOrderConeBridge{T}) + MOI.Bridges.add_bridge( + bridged_model, + ComplexNormInfinityToSecondOrderConeBridge{T}, + ) MOI.Bridges.add_bridge(bridged_model, RelativeEntropyBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormSpectralBridge{T}) MOI.Bridges.add_bridge(bridged_model, NormNuclearBridge{T}) diff --git a/src/Bridges/Constraint/bridges/complex_norm_infinity.jl b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl index 6cb831c9eb..c55c9bbfc8 100644 --- a/src/Bridges/Constraint/bridges/complex_norm_infinity.jl +++ b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl @@ -27,7 +27,7 @@ reformulation: """ struct ComplexNormInfinityToSecondOrderConeBridge{T} <: AbstractBridge ci::Vector{ - MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.SecondOrderCone} + MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.SecondOrderCone}, } end @@ -129,8 +129,8 @@ function MOI.get( fi = MOI.Utilities.operate( +, Complex{T}, - (one(T) + zero(T)*im) * fi_s[2], - (zero(T) + one(T)*im) * fi_s[3], + (one(T) + zero(T) * im) * fi_s[2], + (zero(T) + one(T) * im) * fi_s[3], ) push!(elements, fi) end From aaf93f72ce47209506f913d54a8909aebc926fb5 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 9 Apr 2024 12:21:08 +1200 Subject: [PATCH 4/7] Update --- .../Constraint/complex_norm_infinity.jl | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/test/Bridges/Constraint/complex_norm_infinity.jl b/test/Bridges/Constraint/complex_norm_infinity.jl index 91620420d4..e1969d7a4d 100644 --- a/test/Bridges/Constraint/complex_norm_infinity.jl +++ b/test/Bridges/Constraint/complex_norm_infinity.jl @@ -44,30 +44,6 @@ function test_runtests() ::Float64: [2.0 * t + 3.0, 1.0 * x, 1.0 * y] in SecondOrderCone(3) """, ) - # MOI.Bridges.runtests( - # MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, - # """ - # variables: x - # ::Complex{Float64}: [(0 + 2im) * x + (0 + 4im)] in Zeros(1) - # """, - # """ - # variables: x - # ::Float64: [2 * x + 4] in Zeros(1) - # """; - # constraint_start = 0.0 + 1.2im, - # ) - # MOI.Bridges.runtests( - # MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridge, - # """ - # variables: x - # ::Complex{Float64}: [(2 + 0im) * x + (4 + 0im)] in Zeros(1) - # """, - # """ - # variables: x - # ::Float64: [2 * x + 4] in Zeros(1) - # """; - # constraint_start = 1.2 + 0.0im, - # ) return end From 72a3efc84d14a05ca453bcc6ecf165d28fb91b15 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 9 Apr 2024 13:01:50 +1200 Subject: [PATCH 5/7] Update lazy_bridge_optimizer.jl --- test/Bridges/lazy_bridge_optimizer.jl | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 1d382fc78b..aea3ba985c 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -2131,22 +2131,6 @@ 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}; From d8346c64ba34025c6552f0d885726cb94bac8f19 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 10 Apr 2024 10:00:15 +1200 Subject: [PATCH 6/7] Add test --- .../Constraint/bridges/complex_norm_infinity.jl | 6 ++++++ .../Bridges/Constraint/complex_norm_infinity.jl | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Bridges/Constraint/bridges/complex_norm_infinity.jl b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl index c55c9bbfc8..8a989883a2 100644 --- a/src/Bridges/Constraint/bridges/complex_norm_infinity.jl +++ b/src/Bridges/Constraint/bridges/complex_norm_infinity.jl @@ -41,6 +41,12 @@ function bridge_constraint( s::MOI.NormInfinityCone, ) where {T} fi_s = MOI.Utilities.scalarize(f) + if !iszero(imag(fi_s[1])) + error( + "The epigraph variable `t` in `[t; x] in NormInfinityCone()` " * + "must be real. It is: $(fi_s[1])", + ) + end t = real(fi_s[1]) cis = MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.SecondOrderCone}[] for fi in fi_s[2:end] diff --git a/test/Bridges/Constraint/complex_norm_infinity.jl b/test/Bridges/Constraint/complex_norm_infinity.jl index e1969d7a4d..b6801e636c 100644 --- a/test/Bridges/Constraint/complex_norm_infinity.jl +++ b/test/Bridges/Constraint/complex_norm_infinity.jl @@ -47,6 +47,23 @@ function test_runtests() return end +function test_imag_t() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderCone{Float64}(inner) + x = MOI.add_variables(model, 2) + f_t = (1.0 + 2.0im) * x[1] + f_x = (1.0 + 2.0im) * x[2] + (3.0 + 4.0im) + f = MOI.Utilities.operate(vcat, Complex{Float64}, f_t, f_x) + @test_throws( + ErrorException( + "The epigraph variable `t` in `[t; x] in NormInfinityCone()` " * + "must be real. It is: $f_t", + ), + MOI.add_constraint(model, f, MOI.NormInfinityCone(2)) + ) + return +end + end # module TestConstraintComplexNormInfinityToSecondOrderCone.runtests() From 98f3fa9018ff4318b9dd403e4f7c900dfd896e20 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 10 Apr 2024 10:28:53 +1200 Subject: [PATCH 7/7] Update test/Bridges/Constraint/complex_norm_infinity.jl --- test/Bridges/Constraint/complex_norm_infinity.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Bridges/Constraint/complex_norm_infinity.jl b/test/Bridges/Constraint/complex_norm_infinity.jl index b6801e636c..4c4c48dbaa 100644 --- a/test/Bridges/Constraint/complex_norm_infinity.jl +++ b/test/Bridges/Constraint/complex_norm_infinity.jl @@ -49,7 +49,10 @@ end function test_imag_t() inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) - model = MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderCone{Float64}(inner) + model = + MOI.Bridges.Constraint.ComplexNormInfinityToSecondOrderCone{Float64}( + inner, + ) x = MOI.add_variables(model, 2) f_t = (1.0 + 2.0im) * x[1] f_x = (1.0 + 2.0im) * x[2] + (3.0 + 4.0im)