Skip to content

Commit 9580dd0

Browse files
authored
Add NormCone for representing the epigraph of a p-norm (#2119)
1 parent aba4828 commit 9580dd0

File tree

14 files changed

+558
-16
lines changed

14 files changed

+558
-16
lines changed

docs/src/manual/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ The vector-valued set types implemented in MathOptInterface.jl are:
7979
| [`NormInfinityCone(d)`](@ref MathOptInterface.NormInfinityCone) | ``\{ (t,x) \in \mathbb{R}^{d} : t \ge \max_i \lvert x_i \rvert \}`` |
8080
| [`RelativeEntropyCone(d)`](@ref MathOptInterface.RelativeEntropyCone) | ``\{ (u, v, w) \in \mathbb{R}^{d} : u \ge \sum_i w_i \log (\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}`` |
8181
| [`HyperRectangle(l, u)`](@ref MathOptInterface.HyperRectangle) | ``\{x \in \bar{\mathbb{R}}^d: x_i \in [l_i, u_i] \forall i=1,\ldots,d\}`` |
82+
| [`NormCone(p, d)`](@ref MathOptInterface.NormCone) | ``\{ (t,x) \in \mathbb{R}^{d} : t \ge \left(\sum\limits_i |x_i|^p\right)^{\frac{1}{p}} \}`` |
8283

8384
## Matrix cones
8485

docs/src/reference/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Nonnegatives
8282
Nonpositives
8383
NormInfinityCone
8484
NormOneCone
85+
NormCone
8586
SecondOrderCone
8687
RotatedSecondOrderCone
8788
GeometricMeanCone

docs/src/submodules/Bridges/list_of_bridges.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ Bridges.Constraint.SOCtoPSDBridge
4141
Bridges.Constraint.RSOCtoPSDBridge
4242
Bridges.Constraint.NormInfinityBridge
4343
Bridges.Constraint.NormOneBridge
44+
Bridges.Constraint.NormToPowerBridge
45+
Bridges.Constraint.NormOneConeToNormConeBridge
46+
Bridges.Constraint.SecondOrderConeToNormConeBridge
47+
Bridges.Constraint.NormInfinityConeToNormConeBridge
4448
Bridges.Constraint.GeoMeantoRelEntrBridge
4549
Bridges.Constraint.GeoMeanToPowerBridge
4650
Bridges.Constraint.GeoMeanBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ include("bridges/interval.jl")
3939
include("bridges/ltgt_to_interval.jl")
4040
include("bridges/norm_infinity.jl")
4141
include("bridges/norm_one.jl")
42+
include("bridges/norm_to_power.jl")
43+
include("bridges/norm_special_case.jl")
4244
include("bridges/norm_spec_nuc_to_psd.jl")
4345
include("bridges/number_conversion.jl")
4446
include("bridges/quad_to_soc.jl")
@@ -93,6 +95,10 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
9395
MOI.Bridges.add_bridge(bridged_model, GeoMeantoRelEntrBridge{T})
9496
MOI.Bridges.add_bridge(bridged_model, GeoMeanBridge{T})
9597
MOI.Bridges.add_bridge(bridged_model, GeoMeanToPowerBridge{T})
98+
MOI.Bridges.add_bridge(bridged_model, NormToPowerBridge{T})
99+
MOI.Bridges.add_bridge(bridged_model, NormOneConeToNormConeBridge{T})
100+
MOI.Bridges.add_bridge(bridged_model, SecondOrderConeToNormConeBridge{T})
101+
MOI.Bridges.add_bridge(bridged_model, NormInfinityConeToNormConeBridge{T})
96102
MOI.Bridges.add_bridge(bridged_model, RelativeEntropyBridge{T})
97103
MOI.Bridges.add_bridge(bridged_model, NormSpectralBridge{T})
98104
MOI.Bridges.add_bridge(bridged_model, NormNuclearBridge{T})
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
struct NormSpecialCaseBridge{S,T,F} <: SetMapBridge{T,MOI.NormCone,S,F,F}
8+
constraint::MOI.ConstraintIndex{F,MOI.NormCone}
9+
end
10+
11+
MOI.Bridges.map_function(::Type{<:NormSpecialCaseBridge}, f) = f
12+
13+
MOI.Bridges.inverse_map_function(::Type{<:NormSpecialCaseBridge}, f) = f
14+
15+
MOI.Bridges.adjoint_map_function(::Type{<:NormSpecialCaseBridge}, f) = f
16+
17+
MOI.Bridges.inverse_adjoint_map_function(::Type{<:NormSpecialCaseBridge}, f) = f
18+
19+
function MOI.Bridges.map_set(
20+
::Type{<:NormSpecialCaseBridge{S}},
21+
set::S,
22+
) where {S}
23+
return MOI.NormCone(set)
24+
end
25+
26+
function MOI.Bridges.inverse_map_set(
27+
::Type{<:NormSpecialCaseBridge{S}},
28+
set::MOI.NormCone,
29+
) where {S}
30+
return S(MOI.dimension(set))
31+
end
32+
33+
function concrete_bridge_type(
34+
::Type{<:NormSpecialCaseBridge{S,T}},
35+
F::Type{<:MOI.AbstractVectorFunction},
36+
::Type{S},
37+
) where {T,S<:Union{MOI.NormOneCone,MOI.SecondOrderCone,MOI.NormInfinityCone}}
38+
return NormSpecialCaseBridge{S,T,F}
39+
end
40+
41+
"""
42+
NormOneConeToNormConeBridge{T,F} <: Bridges.Constraint.AbstractBridge
43+
44+
`NormOneConeToNormConeBridge` implements the following reformulations:
45+
46+
* ``(t, x) in NormOneCone(d)`` into ``(t, x) in NormCone(1, d)``
47+
48+
## Source node
49+
50+
`NormOneConeToNormConeBridge` supports:
51+
52+
* `F` in [`MOI.NormOneCone`](@ref)
53+
54+
## Target nodes
55+
56+
`NormOneConeToNormConeBridge` creates:
57+
58+
* `F` in [`MOI.NormCone`](@ref)
59+
"""
60+
const NormOneConeToNormConeBridge{T,F} =
61+
NormSpecialCaseBridge{MOI.NormOneCone,T,F}
62+
63+
const NormOneConeToNormCone{T,OT<:MOI.ModelLike} =
64+
SingleBridgeOptimizer{NormOneConeToNormConeBridge{T},OT}
65+
66+
"""
67+
SecondOrderConeToNormConeBridge{T,F} <: Bridges.Constraint.AbstractBridge
68+
69+
`SecondOrderConeToNormConeBridge` implements the following reformulations:
70+
71+
* ``(t, x) in SecondOrderCone(d)`` into ``(t, x) in NormCone(2, d)``
72+
73+
## Source node
74+
75+
`SecondOrderConeToNormConeBridge` supports:
76+
77+
* `F` in [`MOI.SecondOrderCone`](@ref)
78+
79+
## Target nodes
80+
81+
`SecondOrderConeToNormConeBridge` creates:
82+
83+
* `F` in [`MOI.NormCone`](@ref)
84+
"""
85+
const SecondOrderConeToNormConeBridge{T,F} =
86+
NormSpecialCaseBridge{MOI.SecondOrderCone,T,F}
87+
88+
const SecondOrderConeToNormCone{T,OT<:MOI.ModelLike} =
89+
SingleBridgeOptimizer{SecondOrderConeToNormConeBridge{T},OT}
90+
91+
"""
92+
NormInfinityConeToNormConeBridge{T,F} <: Bridges.Constraint.AbstractBridge
93+
94+
`NormInfinityConeToNormConeBridge` implements the following reformulations:
95+
96+
* ``(t, x) in NormInfinityCone(d)`` into ``(t, x) in NormCone(Inf, d)``
97+
98+
## Source node
99+
100+
`NormInfinityConeToNormConeBridge` supports:
101+
102+
* `F` in [`MOI.NormInfinityCone`](@ref)
103+
104+
## Target nodes
105+
106+
`NormInfinityConeToNormConeBridge` creates:
107+
108+
* `F` in [`MOI.NormCone`](@ref)
109+
"""
110+
const NormInfinityConeToNormConeBridge{T,F} =
111+
NormSpecialCaseBridge{MOI.NormInfinityCone,T,F}
112+
113+
const NormInfinityConeToNormCone{T,OT<:MOI.ModelLike} =
114+
SingleBridgeOptimizer{NormInfinityConeToNormConeBridge{T},OT}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
NormToPowerBridge{T,F} <: Bridges.Constraint.AbstractBridge
9+
10+
`NormToPowerBridge` implements the following reformulation:
11+
12+
* ``(t, x) \\in NormCone(p, 1+d)`` into ``(r_i, t, x_i) \\in PowerCone(1 / p)``
13+
for all ``i``, and ``\\sum\\limits_i r_i == t``.
14+
15+
For details, see Alizadeh, F., and Goldfarb, D. (2001). "Second-order cone
16+
programming." Mathematical Programming, Series B, 95:3-51.
17+
18+
## Source node
19+
20+
`NormToPowerBridge` supports:
21+
22+
* `F` in [`MOI.NormCone`](@ref)
23+
24+
## Target nodes
25+
26+
`NormToPowerBridge` creates:
27+
28+
* `F` in [`MOI.PowerCone{T}`](@ref)
29+
* [`MOI.ScalarAffineFunction`](@ref) in [`MOI.EqualTo`](@ref)
30+
"""
31+
struct NormToPowerBridge{T,F} <: AbstractBridge
32+
power::Vector{MOI.ConstraintIndex{F,MOI.PowerCone{T}}}
33+
r::Vector{MOI.VariableIndex}
34+
equal::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}
35+
set::MOI.NormCone
36+
end
37+
38+
const NormToPower{T,OT<:MOI.ModelLike} =
39+
SingleBridgeOptimizer{NormToPowerBridge{T},OT}
40+
41+
function bridge_constraint(
42+
::Type{NormToPowerBridge{T,F}},
43+
model::MOI.ModelLike,
44+
f::F,
45+
s::MOI.NormCone,
46+
) where {T,F}
47+
d = MOI.dimension(s)
48+
fi_s = MOI.Utilities.eachscalar(f)
49+
r = MOI.add_variables(model, d - 1)
50+
power_ci = MOI.ConstraintIndex{F,MOI.PowerCone{T}}[
51+
MOI.add_constraint(
52+
model,
53+
MOI.Utilities.operate(vcat, T, r[i], fi_s[1], fi_s[i+1]),
54+
MOI.PowerCone(T(1 / s.p)),
55+
) for i in 1:length(r)
56+
]
57+
f = zero(MOI.ScalarAffineFunction{T})
58+
for ri in r
59+
f = MOI.Utilities.operate!(+, T, f, ri)
60+
end
61+
MOI.Utilities.operate!(-, T, f, fi_s[1])
62+
equal_ci = MOI.add_constraint(model, f, MOI.EqualTo(zero(T)))
63+
return NormToPowerBridge{T,F}(power_ci, r, equal_ci, s)
64+
end
65+
66+
function MOI.supports_constraint(
67+
::Type{<:NormToPowerBridge{T}},
68+
::Type{<:MOI.AbstractVectorFunction},
69+
::Type{MOI.NormCone},
70+
) where {T}
71+
return true
72+
end
73+
74+
function MOI.Bridges.added_constrained_variable_types(
75+
::Type{<:NormToPowerBridge},
76+
)
77+
return Tuple{Type}[(MOI.Reals,)]
78+
end
79+
80+
function MOI.Bridges.added_constraint_types(
81+
::Type{<:NormToPowerBridge{T,F}},
82+
) where {T,F}
83+
return Tuple{Type,Type}[
84+
(F, MOI.PowerCone{T}),
85+
(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}),
86+
]
87+
end
88+
89+
function concrete_bridge_type(
90+
::Type{<:NormToPowerBridge{T}},
91+
F::Type{<:MOI.AbstractVectorFunction},
92+
::Type{MOI.NormCone},
93+
) where {T}
94+
return NormToPowerBridge{T,F}
95+
end
96+
97+
function MOI.get(bridge::NormToPowerBridge, ::MOI.NumberOfVariables)::Int64
98+
return length(bridge.r)
99+
end
100+
101+
function MOI.get(bridge::NormToPowerBridge, ::MOI.ListOfVariableIndices)
102+
return copy(bridge.r)
103+
end
104+
105+
function MOI.get(
106+
bridge::NormToPowerBridge{T,F},
107+
::MOI.NumberOfConstraints{F,MOI.PowerCone{T}},
108+
)::Int64 where {T,F}
109+
return length(bridge.power)
110+
end
111+
112+
function MOI.get(
113+
bridge::NormToPowerBridge{T,F},
114+
::MOI.ListOfConstraintIndices{F,MOI.PowerCone{T}},
115+
) where {T,F}
116+
return copy(bridge.power)
117+
end
118+
119+
function MOI.get(
120+
bridge::NormToPowerBridge{T},
121+
::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}},
122+
)::Int64 where {T}
123+
return 1
124+
end
125+
126+
function MOI.get(
127+
bridge::NormToPowerBridge{T},
128+
::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}},
129+
) where {T}
130+
return [bridge.equal]
131+
end
132+
133+
function MOI.delete(model::MOI.ModelLike, bridge::NormToPowerBridge)
134+
MOI.delete(model, bridge.power)
135+
MOI.delete(model, bridge.equal)
136+
MOI.delete(model, bridge.r)
137+
return
138+
end
139+
140+
function MOI.get(
141+
model::MOI.ModelLike,
142+
::MOI.ConstraintFunction,
143+
bridge::NormToPowerBridge{T,F},
144+
) where {T,F}
145+
elements = MOI.Utilities.scalar_type(F)[]
146+
for ci in bridge.power
147+
f = MOI.get(model, MOI.ConstraintFunction(), ci)
148+
fi_s = MOI.Utilities.eachscalar(f)
149+
if isempty(elements)
150+
push!(elements, fi_s[2])
151+
end
152+
push!(elements, fi_s[3])
153+
end
154+
return MOI.Utilities.operate(vcat, T, elements...)
155+
end
156+
157+
function MOI.get(
158+
::MOI.ModelLike,
159+
::MOI.ConstraintSet,
160+
bridge::NormToPowerBridge,
161+
)
162+
return bridge.set
163+
end

src/Test/test_basic_constraint.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ _set(::Type{MOI.Nonpositives}) = MOI.Nonpositives(2)
8888
_set(::Type{MOI.Nonnegatives}) = MOI.Nonnegatives(2)
8989
_set(::Type{MOI.NormInfinityCone}) = MOI.NormInfinityCone(3)
9090
_set(::Type{MOI.NormOneCone}) = MOI.NormOneCone(3)
91+
_set(::Type{MOI.NormCone}) = MOI.NormCone(4.0, 3)
9192
_set(::Type{MOI.SecondOrderCone}) = MOI.SecondOrderCone(3)
9293
_set(::Type{MOI.RotatedSecondOrderCone}) = MOI.RotatedSecondOrderCone(3)
9394
_set(::Type{MOI.GeometricMeanCone}) = MOI.GeometricMeanCone(3)
@@ -278,6 +279,7 @@ for s in [
278279
:Nonnegatives,
279280
:NormInfinityCone,
280281
:NormOneCone,
282+
:NormCone,
281283
:SecondOrderCone,
282284
:RotatedSecondOrderCone,
283285
:GeometricMeanCone,

src/Test/test_conic.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7023,3 +7023,45 @@ function version_added(
70237023
)
70247024
return v"1.7.0"
70257025
end
7026+
7027+
"""
7028+
test_conic_NormCone(model::MOI.ModelLike, config::Config)
7029+
7030+
Test [`MOI.NormCone`](@ref).
7031+
"""
7032+
function test_conic_NormCone(model::MOI.ModelLike, config::Config{T}) where {T}
7033+
@requires _supports(config, MOI.optimize!)
7034+
@requires MOI.supports_constraint(
7035+
model,
7036+
MOI.VectorOfVariables,
7037+
MOI.NormCone,
7038+
)
7039+
t = MOI.add_variable(model)
7040+
x = MOI.add_variables(model, 4)
7041+
x0 = T[1, 2, 3, 4]
7042+
MOI.add_constraint.(model, x, MOI.EqualTo.(x0))
7043+
f = MOI.VectorOfVariables([t; x])
7044+
MOI.add_constraint(model, f, MOI.NormCone(4, 5))
7045+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
7046+
MOI.set(model, MOI.ObjectiveFunction{typeof(t)}(), t)
7047+
MOI.optimize!(model)
7048+
target = LinearAlgebra.norm(x0, 4)
7049+
@test (MOI.get(model, MOI.VariablePrimal(), t), target, config)
7050+
return
7051+
end
7052+
7053+
function setup_test(
7054+
::typeof(test_conic_NormCone),
7055+
model::MOIU.MockOptimizer,
7056+
::Config{T},
7057+
) where {T}
7058+
x0 = T[1, 2, 3, 4]
7059+
MOIU.set_mock_optimize!(
7060+
model,
7061+
(mock::MOIU.MockOptimizer) ->
7062+
MOIU.mock_optimize!(mock, T[LinearAlgebra.norm(x0, 4); x0]),
7063+
)
7064+
return
7065+
end
7066+
7067+
version_added(::typeof(test_conic_NormCone)) = v"1.14.0"

src/Utilities/model.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ const LessThanIndicatorZero{T} =
780780
MOI.Complements,
781781
MOI.NormInfinityCone,
782782
MOI.NormOneCone,
783+
MOI.NormCone,
783784
MOI.SecondOrderCone,
784785
MOI.RotatedSecondOrderCone,
785786
MOI.GeometricMeanCone,

0 commit comments

Comments
 (0)