|
2 | 2 | NormSpectralBridge{T}
|
3 | 3 |
|
4 | 4 | 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``. |
6 | 6 | """
|
7 | 7 | struct NormSpectralBridge{T, F, G} <: AbstractBridge
|
8 | 8 | row_dim::Int # row dimension of X
|
@@ -38,35 +38,52 @@ function concrete_bridge_type(::Type{<:NormSpectralBridge{T}}, G::Type{<:MOI.Abs
|
38 | 38 | end
|
39 | 39 |
|
40 | 40 | # 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] |
43 | 43 |
|
44 | 44 | # 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) |
46 | 46 |
|
47 | 47 | # 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)) |
50 | 50 | 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]] |
53 | 53 | return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, X))
|
54 | 54 | 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) |
58 | 59 | 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]] |
61 | 62 | return vcat(t, X)
|
62 | 63 | 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 |
64 | 80 | # 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 |
68 | 85 | 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]] |
70 | 87 | return vcat(t, X)
|
71 | 88 | end
|
72 | 89 |
|
|
75 | 92 |
|
76 | 93 | The `NormNuclearCone` is representable with an SDP constraint and extra variables,
|
77 | 94 | 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``. |
79 | 96 | """
|
80 | 97 | struct NormNuclearBridge{T, F, G, H} <: AbstractBridge
|
81 | 98 | row_dim::Int # row dimension of X
|
@@ -119,47 +136,64 @@ function concrete_bridge_type(::Type{<:NormNuclearBridge{T}}, H::Type{<:MOI.Abst
|
119 | 136 | end
|
120 | 137 |
|
121 | 138 | # 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] |
128 | 145 |
|
129 | 146 | # 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) |
135 | 152 | end
|
136 | 153 |
|
137 | 154 | # 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 |
143 | 160 | 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]] |
146 | 163 | return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, X))
|
147 | 164 | 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 |
153 | 171 | 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]] |
155 | 173 | return vcat(t, X)
|
156 | 174 | 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, |
158 | 176 | # 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]] |
164 | 182 | return vcat(t, X)
|
165 | 183 | 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 |
0 commit comments