Skip to content

add nuclear norm and spectral norm cone sets (for general/nonsymmetric matrices) #976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/src/apimanual.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,13 @@ sets recognized by the solver.
* **[`DualPowerCone(exponent)`](@ref MathOptInterface.DualPowerCone)**:
``\{ (u,v,w) \in \mathbb{R}^3 : \frac{u}{\mbox{exponent}}^\mbox{exponent}
\frac{v}{1-\mbox{exponent}}^{1-\mbox{exponent}} \ge |w|, u,v \ge 0 \}``
* **[`NormSpectralCone(row_dim, column_dim)`](@ref MathOptInterface.NormSpectralCone)**:
``\{ (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} \}``
* **[`NormNuclearCone(row_dim, column_dim)`](@ref MathOptInterface.NormNuclearCone)**:
``\{ (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} \}``
* **[`PositiveSemidefiniteConeTriangle(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeTriangle)**:
``\{ X \in \mathbb{R}^{\mbox{dimension}(\mbox{dimension}+1)/2} : X \mbox{is
the upper triangle of a PSD matrix }\}``
the upper triangle of a PSD matrix} \}``
* **[`PositiveSemidefiniteConeSquare(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeSquare)**:
``\{ X \in \mathbb{R}^{\mbox{dimension}^2} : X \mbox{is a PSD matrix} \}``
* **[`LogDetConeTriangle(dimension)`](@ref MathOptInterface.LogDetConeTriangle)**:
Expand Down
7 changes: 4 additions & 3 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ ListOfVariableAttributesSet
ListOfConstraintAttributesSet
```


## Optimizers

```@docs
Expand Down Expand Up @@ -330,7 +329,6 @@ Semicontinuous
Semiinteger
```


### Vector sets

List of recognized vector sets.
Expand All @@ -348,6 +346,8 @@ ExponentialCone
DualExponentialCone
PowerCone
DualPowerCone
NormSpectralCone
NormNuclearCone
SOS1
SOS2
IndicatorSet
Expand Down Expand Up @@ -603,7 +603,6 @@ MOI.Bridges.unbridged_variable_function(bridged_model, inner_variables[1])
MOI.ScalarAffineFunction{Float64}(MOI.ScalarAffineTerm{Float64}[ScalarAffineTerm{Float64}(1.0, VariableIndex(-1))], -1.0)
```


!!! note
A notable exception is with [`Bridges.Variable.ZerosBridge`](@ref) where no
variable is created in the underlying model as the variables are simply
Expand Down Expand Up @@ -754,6 +753,8 @@ Bridges.Constraint.QuadtoSOCBridge
Bridges.Constraint.NormInfinityBridge
Bridges.Constraint.NormOneBridge
Bridges.Constraint.GeoMeanBridge
Bridges.Constraint.NormSpectralBridge
Bridges.Constraint.NormNuclearBridge
Bridges.Constraint.SquareBridge
Bridges.Constraint.RootDetBridge
Bridges.Constraint.LogDetBridge
Expand Down
5 changes: 5 additions & 0 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBri
const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT}
include("geomean.jl")
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
include("norm_spec_nuc_to_psd.jl")
const NormSpectral{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormSpectralBridge{T}, OT}
const NormNuclear{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormNuclearBridge{T}, OT}
include("square.jl")
const Square{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SquareBridge{T}, OT}
include("det.jl")
Expand Down Expand Up @@ -80,6 +83,8 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
MOIB.add_bridge(bridged_model, NormInfinityBridge{T})
MOIB.add_bridge(bridged_model, NormOneBridge{T})
MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
MOIB.add_bridge(bridged_model, NormSpectralBridge{T})
MOIB.add_bridge(bridged_model, NormNuclearBridge{T})
MOIB.add_bridge(bridged_model, SquareBridge{T})
MOIB.add_bridge(bridged_model, LogDetBridge{T})
MOIB.add_bridge(bridged_model, RootDetBridge{T})
Expand Down
199 changes: 199 additions & 0 deletions src/Bridges/Constraint/norm_spec_nuc_to_psd.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"""
NormSpectralBridge{T}

The `NormSpectralCone` is representable with a PSD constraint, since
``t \\ge \\sigma_1(X)`` if and only if ``[tI X^\\top; X tI] \\succ 0``.
"""
struct NormSpectralBridge{T, F, G} <: AbstractBridge
row_dim::Int # row dimension of X
column_dim::Int # column dimension of X
psd_index::CI{F, MOI.PositiveSemidefiniteConeTriangle}
end
function bridge_constraint(::Type{NormSpectralBridge{T, F, G}}, model::MOI.ModelLike, f::G, s::MOI.NormSpectralCone) where {T, F, G}
f_scalars = MOIU.eachscalar(f)
t = f_scalars[1]
row_dim = s.row_dim
column_dim = s.column_dim
side_dim = row_dim + column_dim
psd_set = MOI.PositiveSemidefiniteConeTriangle(side_dim)
psd_func = MOIU.zero_with_output_dimension(F, MOI.dimension(psd_set))
for i in 1:side_dim
MOIU.operate_output_index!(+, T, trimap(i, i), psd_func, t)
end
X_idx = 2
for j in 1:column_dim, i in (column_dim + 1):side_dim
MOIU.operate_output_index!(+, T, trimap(i, j), psd_func, f_scalars[X_idx])
X_idx += 1
end
psd_index = MOI.add_constraint(model, psd_func, psd_set)
return NormSpectralBridge{T, F, G}(row_dim, column_dim, psd_index)
end

MOI.supports_constraint(::Type{NormSpectralBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormSpectralCone}) where T = true
MOIB.added_constrained_variable_types(::Type{<:NormSpectralBridge}) = Tuple{DataType}[]
MOIB.added_constraint_types(::Type{NormSpectralBridge{T, F, G}}) where {T, F, G} = [(F, MOI.PositiveSemidefiniteConeTriangle)]
function concrete_bridge_type(::Type{<:NormSpectralBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormSpectralCone}) where T
F = MOIU.promote_operation(vcat, T, MOIU.scalar_type(G), T)
return NormSpectralBridge{T, F, G}
end

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

# References
MOI.delete(model::MOI.ModelLike, bridge::NormSpectralBridge) = MOI.delete(model, bridge.psd_index)

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::NormSpectralBridge{T, F, G}) where {T, F, G}
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), bridge.psd_index))
t = psd_func[1]
side_dim = bridge.row_dim + bridge.column_dim
X = psd_func[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, X))
end
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, bridge::NormSpectralBridge) = MOI.NormSpectralCone(bridge.row_dim, bridge.column_dim)
MOI.supports(::MOI.ModelLike, ::MOI.ConstraintPrimalStart, ::Type{<:NormSpectralBridge}) = true
function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart}, bridge::NormSpectralBridge)
primal = MOI.get(model, attr, bridge.psd_index)
t = primal[1]
side_dim = bridge.row_dim + bridge.column_dim
X = primal[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
return vcat(t, X)
end
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintPrimalStart, bridge::NormSpectralBridge{T}, value) where T
column_dim = bridge.column_dim
side_dim = bridge.row_dim + column_dim
primal = zeros(T, div(side_dim * (side_dim + 1), 2))
for i in 1:side_dim
primal[trimap(i, i)] = value[1]
end
X_idx = 2
for j in 1:column_dim, i in (column_dim + 1):side_dim
primal[trimap(i, j)] = value[X_idx]
X_idx += 1
end
MOI.set(model, MOI.ConstraintPrimalStart(), bridge.psd_index, primal)
return
end
# Given [U X'; X V] is dual on PSD constraint, the dual on NormSpectralCone
# constraint is (tr(U) + tr(V), 2X) in NormNuclearCone.
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, bridge::NormSpectralBridge)
dual = MOI.get(model, MOI.ConstraintDual(), bridge.psd_index)
column_dim = bridge.column_dim
side_dim = bridge.row_dim + column_dim
t = sum(dual[trimap(i, i)] for i in 1:side_dim)
X = 2 * dual[[trimap(i, j) for j in 1:column_dim for i in (column_dim + 1):side_dim]]
return vcat(t, X)
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ConstraintDualStart, I would set all trimap(i, i) to the dual of t divided by 2side_dim. It might not be the only possibility but it works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK should I make another PR for that?

Copy link
Member

@blegat blegat Jan 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wrong, that won't make the matrix PSD.
In fact, it's easier to discuss the ConstraintPrimalStart of NormNuclearBridge.
So suppose you have X and t set.
[A X'; X B] >= 0 is equivalent to A >= X' inv(B) X. If X = U S V', then, it's equivalent to
V' * A * V <= S * U' * inv(B) * U * S
then, by choosing A = V * S * V' and B = U * S * U', you get
S <= S * inv(S) * S which is satisfied.
As the trace must sum up to t, you set A = V * S * V' * (t / tr(S)) and B = U * S * U' * (t / tr(S).
The matrix [A X'; X B] >= will be PSD iff t >= sum sigma_i(X) so it seems like a good candidate for starting value :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that works for constraint primal start of NormNuclear then should it also work for constraint dual start of NormSpectral?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was just easier to reason in the primal than in the dual ^^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK rather than having you explain the details to me, it's probably easier if you implement those new start functions

"""
NormNuclearBridge{T}

The `NormNuclearCone` is representable with an SDP constraint and extra variables,
since ``t \\ge \\sum_i \\sigma_i (X)`` if and only if there exists symmetric
matrices ``U, V`` such that ``[U X^\\top; X V] \\succ 0`` and ``t \\ge (tr(U) + tr(V)) / 2``.
"""
struct NormNuclearBridge{T, F, G, H} <: AbstractBridge
row_dim::Int # row dimension of X
column_dim::Int # column dimension of X
U::Vector{MOI.VariableIndex}
V::Vector{MOI.VariableIndex}
ge_index::CI{F, MOI.GreaterThan{T}}
psd_index::CI{G, MOI.PositiveSemidefiniteConeTriangle}
end
function bridge_constraint(::Type{NormNuclearBridge{T, F, G, H}}, model::MOI.ModelLike, f::H, s::MOI.NormNuclearCone) where {T, F, G, H}
f_scalars = MOIU.eachscalar(f)
row_dim = s.row_dim
column_dim = s.column_dim
side_dim = row_dim + column_dim
U_dim = div(column_dim * (column_dim + 1), 2)
V_dim = div(row_dim * (row_dim + 1), 2)
U = MOI.add_variables(model, U_dim)
V = MOI.add_variables(model, V_dim)
diag_vars = vcat([U[trimap(i, i)] for i in 1:column_dim], [V[trimap(i, i)] for i in 1:row_dim])
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)
psd_set = MOI.PositiveSemidefiniteConeTriangle(side_dim)
psd_func = MOI.VectorOfVariables(U)
nuc_dim = 1 + row_dim * column_dim
for i in 1:row_dim
row_i = (1 + i):row_dim:nuc_dim
psd_func = MOIU.operate(vcat, T, psd_func, f_scalars[row_i])
psd_func = MOIU.operate(vcat, T, psd_func, MOI.VectorOfVariables(V[[trimap(i, j) for j in 1:i]]))
end
psd_index = MOI.add_constraint(model, psd_func, psd_set)
return NormNuclearBridge{T, F, G, H}(row_dim, column_dim, U, V, ge_index, psd_index)
end

MOI.supports_constraint(::Type{NormNuclearBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormNuclearCone}) where T = true
MOIB.added_constrained_variable_types(::Type{<:NormNuclearBridge}) = Tuple{DataType}[]
MOIB.added_constraint_types(::Type{NormNuclearBridge{T, F, G, H}}) where {T, F, G, H} = [(F, MOI.GreaterThan{T}), (G, MOI.PositiveSemidefiniteConeTriangle)]
function concrete_bridge_type(::Type{<:NormNuclearBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormNuclearCone}) where T
S = MOIU.scalar_type(H)
F = MOIU.promote_operation(-, T, S, MOIU.promote_operation(/, T, MOIU.promote_operation(+, T, T, T), T))
G = MOIU.promote_operation(vcat, T, MOI.VectorOfVariables, H)
return NormNuclearBridge{T, F, G, H}
end

# Attributes, Bridge acting as a model
MOI.get(bridge::NormNuclearBridge, ::MOI.NumberOfVariables) = length(bridge.U) + length(bridge.V)
MOI.get(bridge::NormNuclearBridge, ::MOI.ListOfVariableIndices) = vcat(bridge.U, bridge.V)
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = 1
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [bridge.ge_index]
MOI.get(bridge::NormNuclearBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.PositiveSemidefiniteConeTriangle}) where {T, F, G, H} = [bridge.psd_index]

# References
function MOI.delete(model::MOI.ModelLike, bridge::NormNuclearBridge)
MOI.delete(model, bridge.ge_index)
MOI.delete(model, bridge.psd_index)
MOI.delete(model, bridge.U)
MOI.delete(model, bridge.V)
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, bridge::NormNuclearBridge{T, F, G, H}) where {T, F, G, H}
ge_func = MOI.get(model, MOI.ConstraintFunction(), bridge.ge_index)
psd_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), bridge.psd_index))
column_dim = bridge.column_dim
side_dim = bridge.row_dim + column_dim
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)))
t = MOIU.remove_variable(MOIU.remove_variable(t, bridge.U), bridge.V)
X = psd_func[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, X))
end
MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, bridge::NormNuclearBridge) = MOI.NormNuclearCone(bridge.row_dim, bridge.column_dim)
MOI.supports(::MOI.ModelLike, ::MOI.ConstraintDualStart, ::Type{<:NormNuclearBridge}) = true
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, bridge::NormNuclearBridge)
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), bridge.ge_index)
psd_primal = MOI.get(model, MOI.ConstraintPrimal(), bridge.psd_index)
side_dim = bridge.row_dim + bridge.column_dim
t = ge_primal + sum(psd_primal[trimap(i, i)] for i in 1:side_dim) / 2
X = psd_primal[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
return vcat(t, X)
end
# Given t is dual on GreaterThan constraint and [U X'; X V] is dual on PSD constraint,
# the dual on NormNuclearCone constraint is (t, 2X) in NormNuclearCone.
function MOI.get(model::MOI.ModelLike, attr::Union{MOI.ConstraintDual, MOI.ConstraintDualStart}, bridge::NormNuclearBridge)
t = MOI.get(model, attr, bridge.ge_index)
psd_dual = MOI.get(model, attr, bridge.psd_index)
side_dim = bridge.row_dim + bridge.column_dim
X = 2 * psd_dual[[trimap(i, j) for j in 1:bridge.column_dim for i in (bridge.column_dim + 1):side_dim]]
return vcat(t, X)
end
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart, bridge::NormNuclearBridge{T}, value) where T
MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, value[1])
column_dim = bridge.column_dim
side_dim = bridge.row_dim + column_dim
dual = zeros(T, div(side_dim * (side_dim + 1), 2))
for i in 1:side_dim
dual[trimap(i, i)] = value[1]
end
X_idx = 2
for j in 1:column_dim, i in (column_dim + 1):side_dim
dual[trimap(i, j)] = value[X_idx] / 2
X_idx += 1
end
MOI.set(model, MOI.ConstraintDualStart(), bridge.psd_index, dual)
return
end
8 changes: 3 additions & 5 deletions src/FileFormats/MOF/MOF.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ MOI.Utilities.@model(InnerModel,
(MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval,
MOI.Semicontinuous, MOI.Semiinteger),
(MOI.Reals, MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone,
MOI.GeometricMeanCone,
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.ExponentialCone, MOI.DualExponentialCone, MOI.NormOneCone,
MOI.NormInfinityCone, MOI.NormSpectralCone, MOI.NormNuclearCone,
MOI.RootDetConeTriangle, MOI.RootDetConeSquare,
MOI.LogDetConeTriangle, MOI.LogDetConeSquare,
MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare,
MOI.ExponentialCone, MOI.DualExponentialCone, MOI.NormOneCone,
MOI.NormInfinityCone),
MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare),
(MOI.PowerCone, MOI.DualPowerCone, MOI.SOS1, MOI.SOS2),
(Nonlinear,),
(MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction),
Expand Down
2 changes: 2 additions & 0 deletions src/FileFormats/MOF/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ end
set_info(::Type{Val{:GeometricMeanCone}}) = (MOI.GeometricMeanCone, "dimension")
set_info(::Type{Val{:NormOneCone}}) = (MOI.NormOneCone, "dimension")
set_info(::Type{Val{:NormInfinityCone}}) = (MOI.NormInfinityCone, "dimension")
set_info(::Type{Val{:NormSpectralCone}}) = (MOI.NormSpectralCone, "row_dim", "column_dim")
set_info(::Type{Val{:NormNuclearCone}}) = (MOI.NormNuclearCone, "row_dim", "column_dim")
function set_info(::Type{Val{:RootDetConeTriangle}})
return MOI.RootDetConeTriangle, "side_dimension"
end
Expand Down
34 changes: 34 additions & 0 deletions src/FileFormats/MOF/v0.4.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,40 @@
"minimum": 2
}
}
}, {
"description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}",
"examples": ["{\"head\": \"NormSpectralCone\", \"row_dim\": 1, \"column_dim\": 2}"],
"required": ["row_dim", "column_dim"],
"properties": {
"head": {
"const": "NormSpectralCone"
},
"row_dim": {
"type": "integer",
"minimum": 1
},
"column_dim": {
"type": "integer",
"minimum": 1
}
}
}, {
"description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}",
"examples": ["{\"head\": \"NormNuclearCone\", \"row_dim\": 1, \"column_dim\": 2}"],
"required": ["row_dim", "column_dim"],
"properties": {
"head": {
"const": "NormNuclearCone"
},
"row_dim": {
"type": "integer",
"minimum": 1
},
"column_dim": {
"type": "integer",
"minimum": 1
}
}
}]
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/FileFormats/MOF/write.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ head_name(::Type{MOI.ExponentialCone}) = "ExponentialCone"
head_name(::Type{MOI.DualExponentialCone}) = "DualExponentialCone"
head_name(::Type{MOI.NormOneCone}) = "NormOneCone"
head_name(::Type{MOI.NormInfinityCone}) = "NormInfinityCone"
head_name(::Type{MOI.NormSpectralCone}) = "NormSpectralCone"
head_name(::Type{MOI.NormNuclearCone}) = "NormNuclearCone"
head_name(::Type{MOI.RootDetConeTriangle}) = "RootDetConeTriangle"
head_name(::Type{MOI.RootDetConeSquare}) = "RootDetConeSquare"
head_name(::Type{MOI.LogDetConeTriangle}) = "LogDetConeTriangle"
Expand Down
Loading