Skip to content

Commit 395583b

Browse files
committed
add primalstart for spectral and dualstart for nuclear
1 parent ead2edd commit 395583b

File tree

2 files changed

+114
-59
lines changed

2 files changed

+114
-59
lines changed

src/Bridges/Constraint/norm_spec_nuc_to_psd.jl

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
NormSpectralBridge{T}
33
44
The `NormSpectralCone` is representable with a PSD constraint, since
5-
``t \\ge \\sigma_1(X)`` if and only if ``[tI X; X^\\top tI] \\succ 0``.
5+
``t \\ge \\sigma_1(X)`` if and only if ``[tI X^\\top; X tI] \\succ 0``.
66
"""
77
struct NormSpectralBridge{T, F, G} <: AbstractBridge
88
row_dim::Int # row dimension of X
@@ -38,35 +38,52 @@ function concrete_bridge_type(::Type{<:NormSpectralBridge{T}}, G::Type{<:MOI.Abs
3838
end
3939

4040
# Attributes, Bridge acting as a model
41-
MOI.get(b::NormSpectralBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G} = 1
42-
MOI.get(b::NormSpectralBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G} = [b.psd_index]
41+
MOI.get(bridge::NormSpectralBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G} = 1
42+
MOI.get(bridge::NormSpectralBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G} = [bridge.psd_index]
4343

4444
# References
45-
MOI.delete(model::MOI.ModelLike, c::NormSpectralBridge) = MOI.delete(model, c.psd_index)
45+
MOI.delete(model::MOI.ModelLike, bridge::NormSpectralBridge) = MOI.delete(model, bridge.psd_index)
4646

4747
# Attributes, Bridge acting as a constraint
48-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormSpectralBridge{T, F, G}) where {T, F, G}
49-
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.psd_index))
48+
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::NormSpectralBridge{T, F, G}) where {T, F, G}
49+
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), bridge.psd_index))
5050
t = psd_func[1]
51-
side_dim = c.row_dim + c.column_dim
52-
X = psd_func[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
51+
side_dim = bridge.row_dim + bridge.column_dim
52+
X = psd_func[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
5353
return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, X))
5454
end
55-
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormSpectralBridge) = MOI.NormSpectralCone(c.row_dim, c.column_dim)
56-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormSpectralBridge)
57-
primal = MOI.get(model, MOI.ConstraintPrimal(), c.psd_index)
55+
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, bridge::NormSpectralBridge) = MOI.NormSpectralCone(bridge.row_dim, bridge.column_dim)
56+
MOI.supports(::MOI.ModelLike, ::MOI.ConstraintPrimalStart, ::Type{<:NormSpectralBridge}) = true
57+
function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart}, bridge::NormSpectralBridge)
58+
primal = MOI.get(model, attr, bridge.psd_index)
5859
t = primal[1]
59-
side_dim = c.row_dim + c.column_dim
60-
X = primal[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
60+
side_dim = bridge.row_dim + bridge.column_dim
61+
X = primal[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
6162
return vcat(t, X)
6263
end
63-
# Given [U X; X' V] is dual on PSD constraint, the dual on NormSpectralCone
64+
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintPrimalStart, bridge::NormSpectralBridge{T}, value) where T
65+
column_dim = bridge.column_dim
66+
side_dim = bridge.row_dim + column_dim
67+
primal = zeros(T, div(side_dim * (side_dim + 1), 2))
68+
for i in 1:side_dim
69+
primal[trimap(i, i)] = value[1]
70+
end
71+
X_idx = 2
72+
for j in 1:column_dim, i in (column_dim + 1):side_dim
73+
primal[trimap(i, j)] = value[X_idx]
74+
X_idx += 1
75+
end
76+
MOI.set(model, MOI.ConstraintPrimalStart(), bridge.psd_index, primal)
77+
return
78+
end
79+
# Given [U X'; X V] is dual on PSD constraint, the dual on NormSpectralCone
6480
# constraint is (tr(U) + tr(V), 2X) in NormNuclearCone.
65-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormSpectralBridge)
66-
dual = MOI.get(model, MOI.ConstraintDual(), c.psd_index)
67-
side_dim = c.row_dim + c.column_dim
81+
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, bridge::NormSpectralBridge)
82+
dual = MOI.get(model, MOI.ConstraintDual(), bridge.psd_index)
83+
column_dim = bridge.column_dim
84+
side_dim = bridge.row_dim + column_dim
6885
t = sum(dual[trimap(i, i)] for i in 1:side_dim)
69-
X = 2 * dual[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
86+
X = 2 * dual[[trimap(i, j) for j in 1:column_dim for i in (column_dim + 1):side_dim]]
7087
return vcat(t, X)
7188
end
7289

@@ -75,7 +92,7 @@ end
7592
7693
The `NormNuclearCone` is representable with an SDP constraint and extra variables,
7794
since ``t \\ge \\sum_i \\sigma_i (X)`` if and only if there exists symmetric
78-
matrices ``U, V`` such that ``[U X; X' V] \\succ 0`` and ``t \\ge (tr(U) + tr(V)) / 2``.
95+
matrices ``U, V`` such that ``[U X^\\top; X V] \\succ 0`` and ``t \\ge (tr(U) + tr(V)) / 2``.
7996
"""
8097
struct NormNuclearBridge{T, F, G, H} <: AbstractBridge
8198
row_dim::Int # row dimension of X
@@ -119,47 +136,64 @@ function concrete_bridge_type(::Type{<:NormNuclearBridge{T}}, H::Type{<:MOI.Abst
119136
end
120137

121138
# Attributes, Bridge acting as a model
122-
MOI.get(b::NormNuclearBridge, ::MOI.NumberOfVariables) = length(b.U) + length(b.V)
123-
MOI.get(b::NormNuclearBridge, ::MOI.ListOfVariableIndices) = vcat(b.U, b.V)
124-
MOI.get(b::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1
125-
MOI.get(b::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = 1
126-
MOI.get(b::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [b.ge_index]
127-
MOI.get(b::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = [b.psd_index]
139+
MOI.get(bridge::NormNuclearBridge, ::MOI.NumberOfVariables) = length(bridge.U) + length(bridge.V)
140+
MOI.get(bridge::NormNuclearBridge, ::MOI.ListOfVariableIndices) = vcat(bridge.U, bridge.V)
141+
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1
142+
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = 1
143+
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [bridge.ge_index]
144+
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = [bridge.psd_index]
128145

129146
# References
130-
function MOI.delete(model::MOI.ModelLike, c::NormNuclearBridge)
131-
MOI.delete(model, c.ge_index)
132-
MOI.delete(model, c.psd_index)
133-
MOI.delete(model, c.U)
134-
MOI.delete(model, c.V)
147+
function MOI.delete(model::MOI.ModelLike, bridge::NormNuclearBridge)
148+
MOI.delete(model, bridge.ge_index)
149+
MOI.delete(model, bridge.psd_index)
150+
MOI.delete(model, bridge.U)
151+
MOI.delete(model, bridge.V)
135152
end
136153

137154
# Attributes, Bridge acting as a constraint
138-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormNuclearBridge{T, F, G, H}) where {T, F, G, H}
139-
ge_func = MOI.get(model, MOI.ConstraintFunction(), c.ge_index)
140-
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.psd_index))
141-
column_dim = c.column_dim
142-
side_dim = c.row_dim + column_dim
155+
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::NormNuclearBridge{T, F, G, H}) where {T, F, G, H}
156+
ge_func = MOI.get(model, MOI.ConstraintFunction(), bridge.ge_index)
157+
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), bridge.psd_index))
158+
column_dim = bridge.column_dim
159+
side_dim = bridge.row_dim + column_dim
143160
t = MOIU.operate(+, T, ge_func, MOIU.operate(/, T, MOIU.operate(+, T, [psd_func[trimap(i, i)] for i in 1:side_dim]...), T(2)))
144-
t = MOIU.remove_variable(MOIU.remove_variable(t, c.U), c.V)
145-
X = psd_func[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
161+
t = MOIU.remove_variable(MOIU.remove_variable(t, bridge.U), bridge.V)
162+
X = psd_func[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
146163
return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, X))
147164
end
148-
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormNuclearBridge) = MOI.NormNuclearCone(c.row_dim, c.column_dim)
149-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormNuclearBridge)
150-
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index)
151-
psd_primal = MOI.get(model, MOI.ConstraintPrimal(), c.psd_index)
152-
side_dim = c.row_dim + c.column_dim
165+
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, bridge::NormNuclearBridge) = MOI.NormNuclearCone(bridge.row_dim, bridge.column_dim)
166+
MOI.supports(::MOI.ModelLike, ::MOI.ConstraintDualStart, ::Type{<:NormNuclearBridge}) = true
167+
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, bridge::NormNuclearBridge)
168+
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), bridge.ge_index)
169+
psd_primal = MOI.get(model, MOI.ConstraintPrimal(), bridge.psd_index)
170+
side_dim = bridge.row_dim + bridge.column_dim
153171
t = ge_primal + sum(psd_primal[trimap(i, i)] for i in 1:side_dim) / 2
154-
X = psd_primal[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
172+
X = psd_primal[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
155173
return vcat(t, X)
156174
end
157-
# Given t is dual on GreaterThan constraint and [U X; X' V] is dual on PSD constraint,
175+
# Given t is dual on GreaterThan constraint and [U X'; X V] is dual on PSD constraint,
158176
# the dual on NormNuclearCone constraint is (t, 2X) in NormNuclearCone.
159-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormNuclearBridge)
160-
t = MOI.get(model, MOI.ConstraintDual(), c.ge_index)
161-
psd_dual = MOI.get(model, MOI.ConstraintDual(), c.psd_index)
162-
side_dim = c.row_dim + c.column_dim
163-
X = 2 * psd_dual[[trimap(i, j) for j in 1:c.column_dim for i in (c.column_dim + 1):side_dim]]
177+
function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintDual, MOI.ConstraintDualStart}, bridge::NormNuclearBridge)
178+
t = MOI.get(model, attr, bridge.ge_index)
179+
psd_dual = MOI.get(model, attr, bridge.psd_index)
180+
side_dim = bridge.row_dim + bridge.column_dim
181+
X = 2 * psd_dual[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
164182
return vcat(t, X)
165183
end
184+
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart, bridge::NormNuclearBridge{T}, value) where T
185+
MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, value[1])
186+
column_dim = bridge.column_dim
187+
side_dim = bridge.row_dim + column_dim
188+
dual = zeros(T, div(side_dim * (side_dim + 1), 2))
189+
for i in 1:side_dim
190+
dual[trimap(i, i)] = value[1]
191+
end
192+
X_idx = 2
193+
for j in 1:column_dim, i in (column_dim + 1):side_dim
194+
dual[trimap(i, j)] = value[X_idx] / 2
195+
X_idx += 1
196+
end
197+
MOI.set(model, MOI.ConstraintDualStart(), bridge.psd_index, dual)
198+
return
199+
end

test/Bridges/Constraint/norm_spec_nuc_to_psd.jl

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ config = MOIT.TestConfig()
2828

2929
MOIT.normspec1test(bridged_mock, config)
3030

31+
var_names = ["t"]
32+
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names)
33+
34+
psd = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle}())
35+
3136
@testset "Test mock model" begin
32-
var_names = ["t"]
33-
MOI.set(mock, MOI.VariableName(), MOI.get(mock, MOI.ListOfVariableIndices()), var_names)
34-
psd = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle}())
3537
@test length(psd) == 1
3638
MOI.set(mock, MOI.ConstraintName(), psd[1], "psd")
3739

@@ -46,8 +48,6 @@ config = MOIT.TestConfig()
4648
end
4749

4850
@testset "Test bridged model" begin
49-
var_names = ["t"]
50-
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names)
5151
spec = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone}())
5252
@test length(spec) == 1
5353
MOI.set(bridged_mock, MOI.ConstraintName(), spec[1], "spec")
@@ -63,6 +63,16 @@ config = MOIT.TestConfig()
6363
end
6464

6565
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone}()))
66+
67+
attr = MOI.ConstraintPrimalStart()
68+
@testset "$attr" begin
69+
@test MOI.supports(bridged_mock, attr, typeof(ci))
70+
value = Float64[4, 1, 1, 1, -1, 0, 1]
71+
MOI.set(bridged_mock, attr, ci, value)
72+
@test MOI.get(bridged_mock, attr, ci) value
73+
@test MOI.get(mock, attr, psd[1]) == Float64[4, 0, 4, 0, 0, 4, 1, 1, 0, 4, 1, -1, 1, 0, 4]
74+
end
75+
6676
test_delete_bridge(bridged_mock, ci, 1, ((MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle, 0),))
6777
end
6878

@@ -88,13 +98,14 @@ end
8898

8999
MOIT.normnuc1test(bridged_mock, config)
90100

101+
greater = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())
102+
psd = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle}())
103+
91104
@testset "Test mock model" begin
92105
var_names = ["t", "U11", "U12", "U22", "U31", "U32", "U33", "V11", "V12", "V22"]
93106
MOI.set(mock, MOI.VariableName(), MOI.get(mock, MOI.ListOfVariableIndices()), var_names)
94-
greater = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())
95107
@test length(greater) == 1
96108
MOI.set(mock, MOI.ConstraintName(), greater[1], "greater")
97-
psd = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle}())
98109
@test length(psd) == 1
99110
MOI.set(mock, MOI.ConstraintName(), psd[1], "psd")
100111

@@ -110,8 +121,7 @@ end
110121
end
111122

112123
@testset "Test bridged model" begin
113-
var_names = ["t"]
114-
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names)
124+
MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), ["t"])
115125
nuc = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone}())
116126
@test length(nuc) == 1
117127
MOI.set(bridged_mock, MOI.ConstraintName(), nuc[1], "nuc")
@@ -123,10 +133,21 @@ end
123133
"""
124134
model = MOIU.Model{Float64}()
125135
MOIU.loadfromstring!(model, s)
126-
MOIU.test_models_equal(bridged_mock, model, var_names, ["nuc"])
136+
MOIU.test_models_equal(bridged_mock, model, ["t"], ["nuc"])
127137
end
128138

129139
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone}()))
140+
141+
attr = MOI.ConstraintDualStart()
142+
@testset "$attr" begin
143+
@test MOI.supports(bridged_mock, attr, typeof(ci))
144+
value = Float64[4, 1, 1, 1, -1, 0, 1]
145+
MOI.set(bridged_mock, attr, ci, value)
146+
@test MOI.get(bridged_mock, attr, ci) value
147+
@test MOI.get(mock, attr, greater[1]) == 4
148+
@test MOI.get(mock, attr, psd[1]) == Float64[4, 0, 4, 0, 0, 4, 0.5, 0.5, 0, 4, 0.5, -0.5, 0.5, 0, 4]
149+
end
150+
130151
test_delete_bridge(bridged_mock, ci, 1, (
131152
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0),
132153
(MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle, 0)))

0 commit comments

Comments
 (0)