Skip to content

Commit 6e3a9ea

Browse files
authored
Add MOI interface for complex PSD cone (#315)
1 parent d046016 commit 6e3a9ea

File tree

5 files changed

+336
-4
lines changed

5 files changed

+336
-4
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "2.1.0"
55

66
[deps]
77
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
8+
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
89
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
910
SCS_jll = "f4f2fc5b-1d94-523c-97ea-2ab488bedf4b"
1011
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
@@ -19,6 +20,7 @@ SCSSCS_MKL_jllExt = ["SCS_MKL_jll"]
1920

2021
[compat]
2122
MathOptInterface = "1.20"
23+
MutableArithmetics = "1"
2224
Pkg = "<0.0.1, ^1.6"
2325
PrecompileTools = "1"
2426
SCS_GPU_jll = "=3.2.8"

src/MOI_wrapper/MOI_wrapper.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
55

66
include("scaled_psd_cone_bridge.jl")
7+
include("hermitian_complex_psd_cone_bridge.jl")
8+
include("scaled_complex_psd_cone_bridge.jl")
79

810
MOI.Utilities.@product_of_sets(
911
_Cones,
1012
MOI.Zeros,
1113
MOI.Nonnegatives,
1214
MOI.SecondOrderCone,
1315
ScaledPSDCone,
16+
ScaledComplexPSDCone,
1417
MOI.ExponentialCone,
1518
MOI.DualExponentialCone,
1619
MOI.PowerCone{T},
@@ -132,7 +135,11 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
132135
end
133136

134137
function MOI.get(::Optimizer, ::MOI.Bridges.ListOfNonstandardBridges)
135-
return Type[ScaledPSDConeBridge{Cdouble}]
138+
return Type[
139+
ScaledPSDConeBridge{Cdouble},
140+
ScaledComplexPSDConeBridge{Cdouble},
141+
HermitianComplexPSDConeBridge{Cdouble},
142+
]
136143
end
137144

138145
MOI.get(::Optimizer, ::MOI.SolverName) = "SCS"
@@ -239,6 +246,7 @@ function MOI.supports_constraint(
239246
MOI.Nonnegatives,
240247
MOI.SecondOrderCone,
241248
ScaledPSDCone,
249+
ScaledComplexPSDCone,
242250
MOI.ExponentialCone,
243251
MOI.DualExponentialCone,
244252
MOI.PowerCone{Cdouble},
@@ -390,9 +398,9 @@ function MOI.optimize!(
390398
Float64[], # # placeholder: bl
391399
_map_sets(MOI.dimension, T, Ab, MOI.SecondOrderCone),
392400
_map_sets(MOI.side_dimension, T, Ab, ScaledPSDCone),
393-
Int[], # placeholder complex PSD
394-
div(Ab.sets.num_rows[5] - Ab.sets.num_rows[4], 3),
401+
_map_sets(MOI.side_dimension, T, Ab, ScaledComplexPSDCone),
395402
div(Ab.sets.num_rows[6] - Ab.sets.num_rows[5], 3),
403+
div(Ab.sets.num_rows[7] - Ab.sets.num_rows[6], 3),
396404
vcat(
397405
_map_sets(set -> set.exponent, Float64, Ab, MOI.PowerCone{Cdouble}),
398406
_map_sets(
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Copyright (c) 2014: SCS.jl contributors
2+
#
3+
# Use of this source code is governed by an MIT-style license that can be found
4+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
5+
6+
"""
7+
struct ComplexPositiveSemidefiniteConeTriangle <: MOI.AbstractVectorSet
8+
side_dimension::Int
9+
end
10+
11+
Similar to `HermitianPositiveSemidefiniteConeTriangle` but its
12+
vectorization interleaves real and imaginary parts of the off-diagonal.
13+
"""
14+
struct ComplexPositiveSemidefiniteConeTriangle <: MOI.AbstractVectorSet
15+
side_dimension::Int
16+
end
17+
18+
function MOI.Utilities.set_with_dimension(
19+
::Type{ComplexPositiveSemidefiniteConeTriangle},
20+
dim,
21+
)
22+
return ComplexPositiveSemidefiniteConeTriangle(isqrt(dim))
23+
end
24+
25+
function Base.copy(x::ComplexPositiveSemidefiniteConeTriangle)
26+
return ComplexPositiveSemidefiniteConeTriangle(x.side_dimension)
27+
end
28+
29+
function MOI.side_dimension(x::ComplexPositiveSemidefiniteConeTriangle)
30+
return x.side_dimension
31+
end
32+
33+
function MOI.dimension(x::ComplexPositiveSemidefiniteConeTriangle)
34+
return x.side_dimension^2
35+
end
36+
37+
import MutableArithmetics as MA
38+
39+
function MOI.Utilities.set_dot(
40+
x::AbstractVector{S},
41+
y::AbstractVector{T},
42+
set::ComplexPositiveSemidefiniteConeTriangle,
43+
) where {S,T}
44+
U = MA.promote_operation(MA.add_mul, S, T)
45+
result = zero(U)
46+
d = set.side_dimension
47+
k = 0
48+
for j in 1:d
49+
for i in 1:j-1
50+
k += 1
51+
result = MA.add_mul!!(result, 2, x[k], y[k])
52+
k += 1
53+
result = MA.add_mul!!(result, 2, x[k], y[k])
54+
end
55+
k += 1
56+
result = MA.add_mul!!(result, x[k], y[k])
57+
end
58+
return result
59+
end
60+
61+
function MOI.Utilities.dot_coefficients(
62+
a::AbstractVector,
63+
set::ComplexPositiveSemidefiniteConeTriangle,
64+
)
65+
d = set.side_dimension
66+
b = copy(a)
67+
k = 0
68+
for j in 1:d
69+
for i in 1:j-1
70+
k += 1
71+
b[k] /= 2
72+
k += 1
73+
b[k] /= 2
74+
end
75+
k += 1
76+
end
77+
return b
78+
end
79+
80+
function MOI.is_set_dot_scaled(::Type{ComplexPositiveSemidefiniteConeTriangle})
81+
return true
82+
end
83+
84+
struct HermitianComplexPSDConeBridge{T,F} <:
85+
MOI.Bridges.Constraint.SetMapBridge{
86+
T,
87+
ComplexPositiveSemidefiniteConeTriangle,
88+
MOI.HermitianPositiveSemidefiniteConeTriangle,
89+
F,
90+
F,
91+
}
92+
constraint::MOI.ConstraintIndex{F,ComplexPositiveSemidefiniteConeTriangle}
93+
end
94+
95+
MOI.Bridges.bridging_cost(::Type{<:HermitianComplexPSDConeBridge}) = 0.1
96+
97+
function MOI.Bridges.Constraint.concrete_bridge_type(
98+
::Type{HermitianComplexPSDConeBridge{T}},
99+
::Type{F},
100+
::Type{MOI.HermitianPositiveSemidefiniteConeTriangle},
101+
) where {T,F<:MOI.AbstractVectorFunction}
102+
return HermitianComplexPSDConeBridge{T,F}
103+
end
104+
105+
function MOI.Bridges.map_set(
106+
::Type{<:HermitianComplexPSDConeBridge},
107+
set::MOI.HermitianPositiveSemidefiniteConeTriangle,
108+
)
109+
return ComplexPositiveSemidefiniteConeTriangle(MOI.side_dimension(set))
110+
end
111+
112+
function MOI.Bridges.inverse_map_set(
113+
::Type{<:HermitianComplexPSDConeBridge},
114+
set::ComplexPositiveSemidefiniteConeTriangle,
115+
)
116+
return MOI.HermitianPositiveSemidefiniteConeTriangle(set.side_dimension)
117+
end
118+
119+
function _hermitian_to_complex(func)
120+
vals = MOI.Utilities.eachscalar(func)
121+
dim = length(vals)
122+
side = isqrt(dim)
123+
k_re = 1
124+
k_im = div(side * (side + 1), 2) + 1
125+
l = 1
126+
perm = zeros(Int, dim)
127+
for i in 1:side
128+
for j in 1:(i-1)
129+
perm[l] = k_re
130+
perm[l+1] = k_im
131+
k_re += 1
132+
k_im += 1
133+
l += 2
134+
end
135+
perm[l] = k_re
136+
k_re += 1
137+
l += 1
138+
end
139+
return vals[perm]
140+
end
141+
142+
function _complex_to_hermitian(func)
143+
vals = MOI.Utilities.eachscalar(func)
144+
dim = length(vals)
145+
side = isqrt(dim)
146+
k_re = 1
147+
k_im = div(side * (side + 1), 2) + 1
148+
l = 1
149+
perm = zeros(Int, dim)
150+
for i in 1:side
151+
for j in 1:(i-1)
152+
perm[k_re] = l
153+
perm[k_im] = l + 1
154+
k_re += 1
155+
k_im += 1
156+
l += 2
157+
end
158+
perm[k_re] = l
159+
k_re += 1
160+
l += 1
161+
end
162+
return vals[perm]
163+
end
164+
165+
# Map ConstraintFunction from Hermitian -> Complex
166+
function MOI.Bridges.map_function(::Type{<:HermitianComplexPSDConeBridge}, f)
167+
return _hermitian_to_complex(f)
168+
end
169+
170+
# Used to map the ConstraintPrimal from Complex -> Hermitian
171+
function MOI.Bridges.inverse_map_function(
172+
::Type{<:HermitianComplexPSDConeBridge},
173+
f,
174+
)
175+
return _complex_to_hermitian(f)
176+
end
177+
178+
# Used to map the ConstraintDual from Complex -> Hermitian
179+
function MOI.Bridges.adjoint_map_function(
180+
::Type{<:HermitianComplexPSDConeBridge},
181+
f,
182+
)
183+
return _complex_to_hermitian(f)
184+
end
185+
186+
# Used to set ConstraintDualStart
187+
function MOI.Bridges.inverse_adjoint_map_function(
188+
::Type{<:HermitianComplexPSDConeBridge},
189+
f,
190+
)
191+
return _hermitian_to_complex(f)
192+
end
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Copyright (c) 2014: SCS.jl contributors
2+
#
3+
# Use of this source code is governed by an MIT-style license that can be found
4+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
5+
6+
"""
7+
struct ScaledComplexPSDCone <: MOI.AbstractVectorSet
8+
side_dimension::Int
9+
end
10+
11+
Similar to `MOI.Scaled{ComplexPositiveSemidefiniteConeTriangle}` but its
12+
vectorization is the lower triangular part column-wise.
13+
"""
14+
struct ScaledComplexPSDCone <: MOI.AbstractVectorSet
15+
side_dimension::Int
16+
end
17+
18+
function MOI.Utilities.set_with_dimension(::Type{ScaledComplexPSDCone}, dim)
19+
return ScaledComplexPSDCone(isqrt(dim))
20+
end
21+
22+
Base.copy(x::ScaledComplexPSDCone) = ScaledComplexPSDCone(x.side_dimension)
23+
24+
MOI.side_dimension(x::ScaledComplexPSDCone) = x.side_dimension
25+
26+
function MOI.dimension(x::ScaledComplexPSDCone)
27+
return x.side_dimension^2
28+
end
29+
30+
struct ScaledComplexPSDConeBridge{T,F} <: MOI.Bridges.Constraint.SetMapBridge{
31+
T,
32+
ScaledComplexPSDCone,
33+
MOI.Scaled{ComplexPositiveSemidefiniteConeTriangle},
34+
F,
35+
F,
36+
}
37+
constraint::MOI.ConstraintIndex{F,ScaledComplexPSDCone}
38+
end
39+
40+
MOI.Bridges.bridging_cost(::Type{<:ScaledComplexPSDConeBridge}) = 0.1
41+
42+
function MOI.Bridges.Constraint.concrete_bridge_type(
43+
::Type{ScaledComplexPSDConeBridge{T}},
44+
::Type{F},
45+
::Type{MOI.Scaled{ComplexPositiveSemidefiniteConeTriangle}},
46+
) where {T,F<:MOI.AbstractVectorFunction}
47+
return ScaledComplexPSDConeBridge{T,F}
48+
end
49+
50+
function MOI.Bridges.map_set(
51+
::Type{<:ScaledComplexPSDConeBridge},
52+
set::MOI.Scaled{ComplexPositiveSemidefiniteConeTriangle},
53+
)
54+
return ScaledComplexPSDCone(MOI.side_dimension(set))
55+
end
56+
57+
function MOI.Bridges.inverse_map_set(
58+
::Type{<:ScaledComplexPSDConeBridge},
59+
set::ScaledComplexPSDCone,
60+
)
61+
return MOI.Scaled(
62+
ComplexPositiveSemidefiniteConeTriangle(set.side_dimension),
63+
)
64+
end
65+
66+
function _complex_to_scs(func)
67+
vals = MOI.Utilities.eachscalar(func)
68+
d = isqrt(length(vals))
69+
c = 0
70+
perm = zeros(Int, length(vals))
71+
for i in 1:d
72+
c += 1
73+
perm[c] = i^2
74+
for j in i+1:d
75+
triidx = 2i - 1 + (j - 1)^2
76+
c += 1
77+
perm[c] = triidx
78+
c += 1
79+
perm[c] = triidx + 1
80+
end
81+
end
82+
return vals[perm]
83+
end
84+
85+
function _scs_to_complex(func)
86+
vals = MOI.Utilities.eachscalar(func)
87+
d = isqrt(length(vals))
88+
c = 0
89+
perm = zeros(Int, length(vals))
90+
for i in 1:d
91+
c += 1
92+
perm[i^2] = c
93+
for j in i+1:d
94+
triidx = 2i - 1 + (j - 1)^2
95+
c += 1
96+
perm[triidx] = c
97+
c += 1
98+
perm[triidx+1] = c
99+
end
100+
end
101+
return vals[perm]
102+
end
103+
104+
# Map ConstraintFunction from MOI -> SCS
105+
function MOI.Bridges.map_function(::Type{<:ScaledComplexPSDConeBridge}, f)
106+
return _complex_to_scs(f)
107+
end
108+
109+
# Used to map the ConstraintPrimal from SCS -> MOI
110+
function MOI.Bridges.inverse_map_function(
111+
::Type{<:ScaledComplexPSDConeBridge},
112+
f,
113+
)
114+
return _scs_to_complex(f)
115+
end
116+
117+
# Used to map the ConstraintDual from SCS -> MOI
118+
function MOI.Bridges.adjoint_map_function(
119+
::Type{<:ScaledComplexPSDConeBridge},
120+
f,
121+
)
122+
return _scs_to_complex(f)
123+
end
124+
125+
# Used to set ConstraintDualStart
126+
function MOI.Bridges.inverse_adjoint_map_function(
127+
::Type{<:ScaledComplexPSDConeBridge},
128+
f,
129+
)
130+
return _complex_to_scs(f)
131+
end

test/MOI_wrapper.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ function _test_runtests(linear_solver)
6161
# Unexpected failures:
6262
# TODO(odow): looks like a tolerance issue?
6363
"test_linear_add_constraints",
64-
"test_conic_HermitianPositiveSemidefiniteConeTriangle_2",
6564
# Expected test failures:
6665
# TODO(odow): get not supported for primal/dual starts
6766
"test_model_ModelFilter_AbstractConstraintAttribute",

0 commit comments

Comments
 (0)