Skip to content

Commit c6db50d

Browse files
authored
Merge pull request #976 from chriscoey/addspectralnormcone
add nuclear norm and spectral norm cone sets (for general/nonsymmetric matrices)
2 parents 80098c0 + 395583b commit c6db50d

File tree

18 files changed

+725
-21
lines changed

18 files changed

+725
-21
lines changed

docs/src/apimanual.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,13 @@ sets recognized by the solver.
108108
* **[`DualPowerCone(exponent)`](@ref MathOptInterface.DualPowerCone)**:
109109
``\{ (u,v,w) \in \mathbb{R}^3 : \frac{u}{\mbox{exponent}}^\mbox{exponent}
110110
\frac{v}{1-\mbox{exponent}}^{1-\mbox{exponent}} \ge |w|, u,v \ge 0 \}``
111+
* **[`NormSpectralCone(row_dim, column_dim)`](@ref MathOptInterface.NormSpectralCone)**:
112+
``\{ (t, X) \in \mathbb{R}^{1 + \mbox{row_dim} \times \mbox{column_dim} : t \ge \sigma_1(X), X \mbox{is a matrix with row_dim rows and column_dim columns} \}``
113+
* **[`NormNuclearCone(row_dim, column_dim)`](@ref MathOptInterface.NormNuclearCone)**:
114+
``\{ (t, X) \in \mathbb{R}^{1 + \mbox{row_dim} \times \mbox{column_dim} : t \ge \sum_i \sigma_i(X), X \mbox{is a matrix with row_dim rows and column_dim columns} \}``
111115
* **[`PositiveSemidefiniteConeTriangle(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeTriangle)**:
112116
``\{ X \in \mathbb{R}^{\mbox{dimension}(\mbox{dimension}+1)/2} : X \mbox{is
113-
the upper triangle of a PSD matrix }\}``
117+
the upper triangle of a PSD matrix} \}``
114118
* **[`PositiveSemidefiniteConeSquare(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeSquare)**:
115119
``\{ X \in \mathbb{R}^{\mbox{dimension}^2} : X \mbox{is a PSD matrix} \}``
116120
* **[`LogDetConeTriangle(dimension)`](@ref MathOptInterface.LogDetConeTriangle)**:

docs/src/apireference.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ ListOfVariableAttributesSet
115115
ListOfConstraintAttributesSet
116116
```
117117

118-
119118
## Optimizers
120119

121120
```@docs
@@ -330,7 +329,6 @@ Semicontinuous
330329
Semiinteger
331330
```
332331

333-
334332
### Vector sets
335333

336334
List of recognized vector sets.
@@ -348,6 +346,8 @@ ExponentialCone
348346
DualExponentialCone
349347
PowerCone
350348
DualPowerCone
349+
NormSpectralCone
350+
NormNuclearCone
351351
SOS1
352352
SOS2
353353
IndicatorSet
@@ -603,7 +603,6 @@ MOI.Bridges.unbridged_variable_function(bridged_model, inner_variables[1])
603603
MOI.ScalarAffineFunction{Float64}(MOI.ScalarAffineTerm{Float64}[ScalarAffineTerm{Float64}(1.0, VariableIndex(-1))], -1.0)
604604
```
605605

606-
607606
!!! note
608607
A notable exception is with [`Bridges.Variable.ZerosBridge`](@ref) where no
609608
variable is created in the underlying model as the variables are simply
@@ -754,6 +753,8 @@ Bridges.Constraint.QuadtoSOCBridge
754753
Bridges.Constraint.NormInfinityBridge
755754
Bridges.Constraint.NormOneBridge
756755
Bridges.Constraint.GeoMeanBridge
756+
Bridges.Constraint.NormSpectralBridge
757+
Bridges.Constraint.NormNuclearBridge
757758
Bridges.Constraint.SquareBridge
758759
Bridges.Constraint.RootDetBridge
759760
Bridges.Constraint.LogDetBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBri
4646
const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT}
4747
include("geomean.jl")
4848
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
49+
include("norm_spec_nuc_to_psd.jl")
50+
const NormSpectral{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormSpectralBridge{T}, OT}
51+
const NormNuclear{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormNuclearBridge{T}, OT}
4952
include("square.jl")
5053
const Square{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SquareBridge{T}, OT}
5154
include("det.jl")
@@ -80,6 +83,8 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
8083
MOIB.add_bridge(bridged_model, NormInfinityBridge{T})
8184
MOIB.add_bridge(bridged_model, NormOneBridge{T})
8285
MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
86+
MOIB.add_bridge(bridged_model, NormSpectralBridge{T})
87+
MOIB.add_bridge(bridged_model, NormNuclearBridge{T})
8388
MOIB.add_bridge(bridged_model, SquareBridge{T})
8489
MOIB.add_bridge(bridged_model, LogDetBridge{T})
8590
MOIB.add_bridge(bridged_model, RootDetBridge{T})
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
"""
2+
NormSpectralBridge{T}
3+
4+
The `NormSpectralCone` is representable with a PSD constraint, since
5+
``t \\ge \\sigma_1(X)`` if and only if ``[tI X^\\top; X tI] \\succ 0``.
6+
"""
7+
struct NormSpectralBridge{T, F, G} <: AbstractBridge
8+
row_dim::Int # row dimension of X
9+
column_dim::Int # column dimension of X
10+
psd_index::CI{F, MOI.PositiveSemidefiniteConeTriangle}
11+
end
12+
function bridge_constraint(::Type{NormSpectralBridge{T, F, G}}, model::MOI.ModelLike, f::G, s::MOI.NormSpectralCone) where {T, F, G}
13+
f_scalars = MOIU.eachscalar(f)
14+
t = f_scalars[1]
15+
row_dim = s.row_dim
16+
column_dim = s.column_dim
17+
side_dim = row_dim + column_dim
18+
psd_set = MOI.PositiveSemidefiniteConeTriangle(side_dim)
19+
psd_func = MOIU.zero_with_output_dimension(F, MOI.dimension(psd_set))
20+
for i in 1:side_dim
21+
MOIU.operate_output_index!(+, T, trimap(i, i), psd_func, t)
22+
end
23+
X_idx = 2
24+
for j in 1:column_dim, i in (column_dim + 1):side_dim
25+
MOIU.operate_output_index!(+, T, trimap(i, j), psd_func, f_scalars[X_idx])
26+
X_idx += 1
27+
end
28+
psd_index = MOI.add_constraint(model, psd_func, psd_set)
29+
return NormSpectralBridge{T, F, G}(row_dim, column_dim, psd_index)
30+
end
31+
32+
MOI.supports_constraint(::Type{NormSpectralBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormSpectralCone}) where T = true
33+
MOIB.added_constrained_variable_types(::Type{<:NormSpectralBridge}) = Tuple{DataType}[]
34+
MOIB.added_constraint_types(::Type{NormSpectralBridge{T, F, G}}) where {T, F, G} = [(F, MOI.PositiveSemidefiniteConeTriangle)]
35+
function concrete_bridge_type(::Type{<:NormSpectralBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormSpectralCone}) where T
36+
F = MOIU.promote_operation(vcat, T, MOIU.scalar_type(G), T)
37+
return NormSpectralBridge{T, F, G}
38+
end
39+
40+
# Attributes, Bridge acting as a model
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+
44+
# References
45+
MOI.delete(model::MOI.ModelLike, bridge::NormSpectralBridge) = MOI.delete(model, bridge.psd_index)
46+
47+
# Attributes, Bridge acting as a constraint
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+
t = psd_func[1]
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+
return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, X))
54+
end
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)
59+
t = primal[1]
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]]
62+
return vcat(t, X)
63+
end
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
80+
# constraint is (tr(U) + tr(V), 2X) in NormNuclearCone.
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
85+
t = sum(dual[trimap(i, i)] for i in 1:side_dim)
86+
X = 2 * dual[[trimap(i, j) for j in 1:column_dim for i in (column_dim + 1):side_dim]]
87+
return vcat(t, X)
88+
end
89+
90+
"""
91+
NormNuclearBridge{T}
92+
93+
The `NormNuclearCone` is representable with an SDP constraint and extra variables,
94+
since ``t \\ge \\sum_i \\sigma_i (X)`` if and only if there exists symmetric
95+
matrices ``U, V`` such that ``[U X^\\top; X V] \\succ 0`` and ``t \\ge (tr(U) + tr(V)) / 2``.
96+
"""
97+
struct NormNuclearBridge{T, F, G, H} <: AbstractBridge
98+
row_dim::Int # row dimension of X
99+
column_dim::Int # column dimension of X
100+
U::Vector{MOI.VariableIndex}
101+
V::Vector{MOI.VariableIndex}
102+
ge_index::CI{F, MOI.GreaterThan{T}}
103+
psd_index::CI{G, MOI.PositiveSemidefiniteConeTriangle}
104+
end
105+
function bridge_constraint(::Type{NormNuclearBridge{T, F, G, H}}, model::MOI.ModelLike, f::H, s::MOI.NormNuclearCone) where {T, F, G, H}
106+
f_scalars = MOIU.eachscalar(f)
107+
row_dim = s.row_dim
108+
column_dim = s.column_dim
109+
side_dim = row_dim + column_dim
110+
U_dim = div(column_dim * (column_dim + 1), 2)
111+
V_dim = div(row_dim * (row_dim + 1), 2)
112+
U = MOI.add_variables(model, U_dim)
113+
V = MOI.add_variables(model, V_dim)
114+
diag_vars = vcat([U[trimap(i, i)] for i in 1:column_dim], [V[trimap(i, i)] for i in 1:row_dim])
115+
ge_index = MOIU.normalize_and_add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate!(/, T, MOIU.operate(sum, T, diag_vars), T(2))), MOI.GreaterThan(zero(T)), allow_modify_function=true)
116+
psd_set = MOI.PositiveSemidefiniteConeTriangle(side_dim)
117+
psd_func = MOI.VectorOfVariables(U)
118+
nuc_dim = 1 + row_dim * column_dim
119+
for i in 1:row_dim
120+
row_i = (1 + i):row_dim:nuc_dim
121+
psd_func = MOIU.operate(vcat, T, psd_func, f_scalars[row_i])
122+
psd_func = MOIU.operate(vcat, T, psd_func, MOI.VectorOfVariables(V[[trimap(i, j) for j in 1:i]]))
123+
end
124+
psd_index = MOI.add_constraint(model, psd_func, psd_set)
125+
return NormNuclearBridge{T, F, G, H}(row_dim, column_dim, U, V, ge_index, psd_index)
126+
end
127+
128+
MOI.supports_constraint(::Type{NormNuclearBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormNuclearCone}) where T = true
129+
MOIB.added_constrained_variable_types(::Type{<:NormNuclearBridge}) = Tuple{DataType}[]
130+
MOIB.added_constraint_types(::Type{NormNuclearBridge{T, F, G, H}}) where {T, F, G, H} = [(F, MOI.GreaterThan{T}), (G, MOI.PositiveSemidefiniteConeTriangle)]
131+
function concrete_bridge_type(::Type{<:NormNuclearBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormNuclearCone}) where T
132+
S = MOIU.scalar_type(H)
133+
F = MOIU.promote_operation(-, T, S, MOIU.promote_operation(/, T, MOIU.promote_operation(+, T, T, T), T))
134+
G = MOIU.promote_operation(vcat, T, MOI.VectorOfVariables, H)
135+
return NormNuclearBridge{T, F, G, H}
136+
end
137+
138+
# Attributes, Bridge acting as a model
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]
145+
146+
# References
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)
152+
end
153+
154+
# Attributes, Bridge acting as a constraint
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
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)))
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]]
163+
return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, X))
164+
end
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
171+
t = ge_primal + sum(psd_primal[trimap(i, i)] for i in 1:side_dim) / 2
172+
X = psd_primal[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
173+
return vcat(t, X)
174+
end
175+
# Given t is dual on GreaterThan constraint and [U X'; X V] is dual on PSD constraint,
176+
# the dual on NormNuclearCone constraint is (t, 2X) in NormNuclearCone.
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]]
182+
return vcat(t, X)
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

src/FileFormats/MOF/MOF.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,11 @@ MOI.Utilities.@model(InnerModel,
3030
(MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval,
3131
MOI.Semicontinuous, MOI.Semiinteger),
3232
(MOI.Reals, MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
33-
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone,
34-
MOI.GeometricMeanCone,
33+
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.ExponentialCone, MOI.DualExponentialCone, MOI.NormOneCone,
34+
MOI.NormInfinityCone, MOI.NormSpectralCone, MOI.NormNuclearCone,
3535
MOI.RootDetConeTriangle, MOI.RootDetConeSquare,
3636
MOI.LogDetConeTriangle, MOI.LogDetConeSquare,
37-
MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare,
38-
MOI.ExponentialCone, MOI.DualExponentialCone, MOI.NormOneCone,
39-
MOI.NormInfinityCone),
37+
MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare),
4038
(MOI.PowerCone, MOI.DualPowerCone, MOI.SOS1, MOI.SOS2),
4139
(Nonlinear,),
4240
(MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction),

src/FileFormats/MOF/read.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ end
307307
set_info(::Type{Val{:GeometricMeanCone}}) = (MOI.GeometricMeanCone, "dimension")
308308
set_info(::Type{Val{:NormOneCone}}) = (MOI.NormOneCone, "dimension")
309309
set_info(::Type{Val{:NormInfinityCone}}) = (MOI.NormInfinityCone, "dimension")
310+
set_info(::Type{Val{:NormSpectralCone}}) = (MOI.NormSpectralCone, "row_dim", "column_dim")
311+
set_info(::Type{Val{:NormNuclearCone}}) = (MOI.NormNuclearCone, "row_dim", "column_dim")
310312
function set_info(::Type{Val{:RootDetConeTriangle}})
311313
return MOI.RootDetConeTriangle, "side_dimension"
312314
end

src/FileFormats/MOF/v0.4.0.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,40 @@
812812
"minimum": 2
813813
}
814814
}
815+
}, {
816+
"description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}",
817+
"examples": ["{\"head\": \"NormSpectralCone\", \"row_dim\": 1, \"column_dim\": 2}"],
818+
"required": ["row_dim", "column_dim"],
819+
"properties": {
820+
"head": {
821+
"const": "NormSpectralCone"
822+
},
823+
"row_dim": {
824+
"type": "integer",
825+
"minimum": 1
826+
},
827+
"column_dim": {
828+
"type": "integer",
829+
"minimum": 1
830+
}
831+
}
832+
}, {
833+
"description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}",
834+
"examples": ["{\"head\": \"NormNuclearCone\", \"row_dim\": 1, \"column_dim\": 2}"],
835+
"required": ["row_dim", "column_dim"],
836+
"properties": {
837+
"head": {
838+
"const": "NormNuclearCone"
839+
},
840+
"row_dim": {
841+
"type": "integer",
842+
"minimum": 1
843+
},
844+
"column_dim": {
845+
"type": "integer",
846+
"minimum": 1
847+
}
848+
}
815849
}]
816850
}
817851
}

src/FileFormats/MOF/write.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ head_name(::Type{MOI.ExponentialCone}) = "ExponentialCone"
238238
head_name(::Type{MOI.DualExponentialCone}) = "DualExponentialCone"
239239
head_name(::Type{MOI.NormOneCone}) = "NormOneCone"
240240
head_name(::Type{MOI.NormInfinityCone}) = "NormInfinityCone"
241+
head_name(::Type{MOI.NormSpectralCone}) = "NormSpectralCone"
242+
head_name(::Type{MOI.NormNuclearCone}) = "NormNuclearCone"
241243
head_name(::Type{MOI.RootDetConeTriangle}) = "RootDetConeTriangle"
242244
head_name(::Type{MOI.RootDetConeSquare}) = "RootDetConeSquare"
243245
head_name(::Type{MOI.LogDetConeTriangle}) = "LogDetConeTriangle"

0 commit comments

Comments
 (0)