Skip to content

Commit e57373b

Browse files
authored
Add bridge from Hermitian PSD to complex function in Symmetric PSD (#2724)
* Add bridge from Hermitian PSD to complex function in Symmetric PSD * Fix docstring * Add to add_all_bridges * Fix format * Fix * Add tests * Add adjoint * Add test * Fix SetDotScaling * Add SingleBridgeOptimizer
1 parent 3a70ff0 commit e57373b

File tree

5 files changed

+257
-3
lines changed

5 files changed

+257
-3
lines changed

src/Bridges/Constraint/Constraint.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function add_all_bridges(model, ::Type{T}) where {T}
5656
end
5757
MOI.Bridges.add_bridge(model, GreaterToLessBridge{T})
5858
MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T})
59+
MOI.Bridges.add_bridge(model, HermitianToComplexSymmetricBridge{T})
5960
MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T})
6061
MOI.Bridges.add_bridge(model, IndicatorGreaterToLessThanBridge{T})
6162
MOI.Bridges.add_bridge(model, IndicatorLessToGreaterThanBridge{T})
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
HermitianToComplexSymmetricBridge{T,F,G} <: Bridges.Constraint.AbstractBridge
9+
10+
`HermitianToSymmetricBridge` implements the following reformulation:
11+
12+
* Hermitian positive semidefinite `n x n` represented as a vector of real
13+
entries with real and imaginary parts on different entries to a vector
14+
of complex entries.
15+
16+
See also [`MOI.Bridges.Constraint.HermitianToSymmetricPSDBridge`](@ref).
17+
18+
## Source node
19+
20+
`HermitianToComplexSymmetricBridge` supports:
21+
22+
* `G` in [`MOI.HermitianPositiveSemidefiniteConeTriangle`](@ref)
23+
24+
## Target node
25+
26+
`HermitianToComplexSymmetricBridge` creates:
27+
28+
* `F` in [`MOI.PositiveSemidefiniteConeTriangle`](@ref)
29+
30+
Note that if `G` is `MOI.VectorAffineFunction{T}` then `F` will be
31+
`MOI.VectorAffineFunction{Complex{T}}`
32+
"""
33+
struct HermitianToComplexSymmetricBridge{T,F,G} <: SetMapBridge{
34+
T,
35+
MOI.PositiveSemidefiniteConeTriangle,
36+
MOI.HermitianPositiveSemidefiniteConeTriangle,
37+
F,
38+
G,
39+
}
40+
constraint::MOI.ConstraintIndex{F,MOI.PositiveSemidefiniteConeTriangle}
41+
end
42+
43+
const HermitianToComplexSymmetric{T,OT<:MOI.ModelLike} =
44+
SingleBridgeOptimizer{HermitianToComplexSymmetricBridge{T},OT}
45+
46+
# Should be favored over `HermitianToSymmetricPSDBridge`
47+
MOI.Bridges.bridging_cost(::Type{<:HermitianToComplexSymmetricBridge}) = 0.5
48+
49+
function _promote_complex_vcat(::Type{T}, ::Type{G}) where {T<:Real,G}
50+
S = MOI.Utilities.scalar_type(G)
51+
if S === T
52+
M = Complex{T}
53+
elseif S <: MOI.Utilities.TypedLike{T}
54+
M = MOI.Utilities.similar_type(S, Complex{T})
55+
else
56+
M = MOI.Utilities.promote_operation(*, Complex{T}, Complex{T}, S)
57+
end
58+
return MOI.Utilities.promote_operation(vcat, Complex{T}, M)
59+
end
60+
61+
function concrete_bridge_type(
62+
::Type{<:HermitianToComplexSymmetricBridge{T}},
63+
G::Type{<:MOI.AbstractVectorFunction},
64+
::Type{MOI.HermitianPositiveSemidefiniteConeTriangle},
65+
) where {T}
66+
F = _promote_complex_vcat(T, G)
67+
return HermitianToComplexSymmetricBridge{T,F,G}
68+
end
69+
70+
function MOI.Bridges.map_set(
71+
::Type{<:HermitianToComplexSymmetricBridge},
72+
set::MOI.HermitianPositiveSemidefiniteConeTriangle,
73+
)
74+
return MOI.PositiveSemidefiniteConeTriangle(set.side_dimension)
75+
end
76+
77+
function MOI.Bridges.inverse_map_set(
78+
::Type{<:HermitianToComplexSymmetricBridge},
79+
set::MOI.PositiveSemidefiniteConeTriangle,
80+
)
81+
return MOI.HermitianPositiveSemidefiniteConeTriangle(set.side_dimension)
82+
end
83+
84+
function MOI.Bridges.map_function(
85+
::Type{<:HermitianToComplexSymmetricBridge{T}},
86+
func,
87+
) where {T}
88+
complex_scalars = MOI.Utilities.eachscalar(func)
89+
S = MOI.Utilities.scalar_type(_promote_complex_vcat(T, typeof(func)))
90+
complex_dim = length(complex_scalars)
91+
complex_set = MOI.Utilities.set_with_dimension(
92+
MOI.HermitianPositiveSemidefiniteConeTriangle,
93+
complex_dim,
94+
)
95+
n = complex_set.side_dimension
96+
real_set = MOI.PositiveSemidefiniteConeTriangle(n)
97+
real_dim = MOI.dimension(real_set)
98+
real_scalars = Vector{S}(undef, real_dim)
99+
real_index = 0
100+
imag_index = real_dim
101+
for j in 1:n
102+
for i in 1:j
103+
real_index += 1
104+
if i == j
105+
real_scalars[real_index] = complex_scalars[real_index]
106+
else
107+
imag_index += 1
108+
real_scalars[real_index] =
109+
one(Complex{T}) * complex_scalars[real_index] +
110+
(one(T) * im) * complex_scalars[imag_index]
111+
end
112+
end
113+
end
114+
@assert length(real_scalars) == real_index
115+
@assert length(complex_scalars) == imag_index
116+
return MOI.Utilities.vectorize(real_scalars)
117+
end
118+
119+
function MOI.Bridges.inverse_adjoint_map_function(
120+
BT::Type{<:HermitianToComplexSymmetricBridge},
121+
func,
122+
)
123+
return MOI.Bridges.map_function(BT, func)
124+
end
125+
126+
function MOI.Bridges.inverse_map_function(
127+
::Type{<:HermitianToComplexSymmetricBridge},
128+
func,
129+
)
130+
real_scalars = MOI.Utilities.eachscalar(func)
131+
real_set = MOI.Utilities.set_with_dimension(
132+
MOI.PositiveSemidefiniteConeTriangle,
133+
length(real_scalars),
134+
)
135+
n = real_set.side_dimension
136+
complex_set = MOI.HermitianPositiveSemidefiniteConeTriangle(n)
137+
complex_scalars = Vector{
138+
MA.promote_operation(real, MOI.Utilities.scalar_type(typeof(func))),
139+
}(
140+
undef,
141+
MOI.dimension(complex_set),
142+
)
143+
real_index = 0
144+
imag_index = MOI.dimension(real_set)
145+
for j in 1:n
146+
for i in 1:j
147+
real_index += 1
148+
complex_scalars[real_index] = real(real_scalars[real_index])
149+
if i != j
150+
imag_index += 1
151+
complex_scalars[imag_index] = imag(real_scalars[real_index])
152+
end
153+
end
154+
end
155+
@assert length(real_scalars) == real_index
156+
@assert length(complex_scalars) == imag_index
157+
return MOI.Utilities.vectorize(complex_scalars)
158+
end
159+
160+
function MOI.Bridges.adjoint_map_function(
161+
BT::Type{<:HermitianToComplexSymmetricBridge},
162+
func,
163+
)
164+
return MOI.Bridges.inverse_map_function(BT, func)
165+
end

src/Bridges/Constraint/bridges/HermitianToSymmetricPSDBridge.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ function MOI.Bridges.map_function(
153153
end
154154

155155
function MOI.Bridges.inverse_map_function(
156-
BT::Type{<:HermitianToSymmetricPSDBridge},
156+
::Type{<:HermitianToSymmetricPSDBridge},
157157
func,
158158
)
159159
real_scalars = MOI.Utilities.eachscalar(func)

src/Bridges/Constraint/bridges/SetDotScalingBridge.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,10 @@ end
194194
# for `SetMapBridge` does not work
195195
function MOI.supports_constraint(
196196
::Type{<:SetDotScalingBridge},
197-
::Type{<:MOI.AbstractVectorFunction},
197+
F::Type{<:MOI.AbstractVectorFunction},
198198
S::Type{<:MOI.AbstractVectorSet},
199199
)
200-
return MOI.is_set_dot_scaled(S)
200+
return !MOI.Utilities.is_complex(F) && MOI.is_set_dot_scaled(S)
201201
end
202202

203203
function MOI.supports_constraint(
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
module TestConstraintHermitianToComplexSymmetric
8+
9+
using Test
10+
11+
import MathOptInterface as MOI
12+
13+
function runtests()
14+
for name in names(@__MODULE__; all = true)
15+
if startswith("$(name)", "test_")
16+
@testset "$(name)" begin
17+
getfield(@__MODULE__, name)()
18+
end
19+
end
20+
end
21+
return
22+
end
23+
24+
function test_dimension_2()
25+
MOI.Bridges.runtests(
26+
MOI.Bridges.Constraint.HermitianToComplexSymmetricBridge,
27+
model -> begin
28+
a, b, c = MOI.add_variables(model, 3)
29+
MOI.add_constraint(
30+
model,
31+
MOI.Utilities.vectorize([
32+
1.0 * a + 2.0 * b,
33+
3.0 * c,
34+
4.0 * b,
35+
5.0 * a,
36+
]),
37+
MOI.HermitianPositiveSemidefiniteConeTriangle(2),
38+
)
39+
end,
40+
model -> begin
41+
a, b, c = MOI.add_variables(model, 3)
42+
MOI.add_constraint(
43+
model,
44+
MOI.Utilities.vectorize([
45+
Complex(1.0) * a + Complex(2.0) * b,
46+
Complex(3.0) * c + 5.0 * im * a,
47+
Complex(4.0) * b,
48+
]),
49+
MOI.PositiveSemidefiniteConeTriangle(2),
50+
)
51+
end,
52+
)
53+
return
54+
end
55+
56+
function test_dimension_3()
57+
MOI.Bridges.runtests(
58+
MOI.Bridges.Constraint.HermitianToComplexSymmetricBridge,
59+
model -> begin
60+
x = MOI.add_variables(model, 9)
61+
MOI.add_constraint(
62+
model,
63+
MOI.VectorOfVariables(x),
64+
MOI.HermitianPositiveSemidefiniteConeTriangle(3),
65+
)
66+
end,
67+
model -> begin
68+
x = MOI.add_variables(model, 9)
69+
MOI.add_constraint(
70+
model,
71+
MOI.Utilities.vectorize([
72+
Complex(1.0) * x[1],
73+
Complex(1.0) * x[2] + 1.0 * im * x[7],
74+
Complex(1.0) * x[3],
75+
Complex(1.0) * x[4] + 1.0 * im * x[8],
76+
Complex(1.0) * x[5] + 1.0 * im * x[9],
77+
Complex(1.0) * x[6],
78+
]),
79+
MOI.PositiveSemidefiniteConeTriangle(3),
80+
)
81+
end,
82+
)
83+
return
84+
end
85+
86+
end # module
87+
88+
TestConstraintHermitianToComplexSymmetric.runtests()

0 commit comments

Comments
 (0)