Skip to content

Commit 9f2e2f0

Browse files
committed
merge in master
2 parents cb20ac7 + c6db50d commit 9f2e2f0

File tree

22 files changed

+817
-46
lines changed

22 files changed

+817
-46
lines changed

docs/make.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ using Documenter, MathOptInterface
22

33
makedocs(
44
sitename = "MathOptInterface",
5-
# See https://github.com/JuliaDocs/Documenter.jl/issues/868
6-
format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"),
5+
format = Documenter.HTML(
6+
# See https://github.com/JuliaDocs/Documenter.jl/issues/868
7+
prettyurls = get(ENV, "CI", nothing) == "true",
8+
mathengine = Documenter.MathJax()
9+
),
710
# See https://github.com/JuliaOpt/JuMP.jl/issues/1576
811
strict = true,
912
pages = [

docs/src/apimanual.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,13 @@ sets recognized by the solver.
114114
\frac{v}{1-\mbox{exponent}}^{1-\mbox{exponent}} \ge |w|, u,v \ge 0 \}``
115115
* **[`RelativeEntropyCone(dimension)`](@ref MathOptInterface.RelativeEntropyCone)**:
116116
``\{ (u, v, w) \\in \\mathbb{R}^\mbox{dimension} : u \\ge \\sum_i w_i \\log (\\frac{w_i}{v_i}), v_i \\ge 0, w_i \\ge 0 \}``
117+
* **[`NormSpectralCone(row_dim, column_dim)`](@ref MathOptInterface.NormSpectralCone)**:
118+
``\{ (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} \}``
119+
* **[`NormNuclearCone(row_dim, column_dim)`](@ref MathOptInterface.NormNuclearCone)**:
120+
``\{ (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} \}``
117121
* **[`PositiveSemidefiniteConeTriangle(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeTriangle)**:
118122
``\{ X \in \mathbb{R}^{\mbox{dimension}(\mbox{dimension}+1)/2} : X \mbox{is
119-
the upper triangle of a PSD matrix }\}``
123+
the upper triangle of a PSD matrix} \}``
120124
* **[`PositiveSemidefiniteConeSquare(dimension)`](@ref MathOptInterface.PositiveSemidefiniteConeSquare)**:
121125
``\{ X \in \mathbb{R}^{\mbox{dimension}^2} : X \mbox{is a PSD matrix} \}``
122126
* **[`LogDetConeTriangle(dimension)`](@ref MathOptInterface.LogDetConeTriangle)**:
@@ -1106,8 +1110,7 @@ appropriate constraint bridges for unsupported constraints.
11061110

11071111
### Solver-specific attributes
11081112

1109-
Solver-specific attributes should either be passed to the optimizer on creation,
1110-
e.g., `MyPackage.Optimizer(PrintLevel = 0)`, or through a sub-type of
1113+
Solver-specific attributes should be specified by creating an
11111114
[`AbstractOptimizerAttribute`](@ref). For example, inside `MyPackage`, we could
11121115
add the following:
11131116
```julia

docs/src/apireference.md

Lines changed: 5 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
@@ -197,6 +196,7 @@ ConstraintIndex
197196
is_valid
198197
throw_if_not_valid
199198
delete(::ModelLike, ::Index)
199+
delete(::ModelLike, ::Vector{<:Index})
200200
```
201201

202202
### Variables
@@ -329,7 +329,6 @@ Semicontinuous
329329
Semiinteger
330330
```
331331

332-
333332
### Vector sets
334333

335334
List of recognized vector sets.
@@ -348,6 +347,8 @@ DualExponentialCone
348347
PowerCone
349348
DualPowerCone
350349
RelativeEntropyCone
350+
NormSpectralCone
351+
NormNuclearCone
351352
SOS1
352353
SOS2
353354
IndicatorSet
@@ -603,7 +604,6 @@ MOI.Bridges.unbridged_variable_function(bridged_model, inner_variables[1])
603604
MOI.ScalarAffineFunction{Float64}(MOI.ScalarAffineTerm{Float64}[ScalarAffineTerm{Float64}(1.0, VariableIndex(-1))], -1.0)
604605
```
605606

606-
607607
!!! note
608608
A notable exception is with [`Bridges.Variable.ZerosBridge`](@ref) where no
609609
variable is created in the underlying model as the variables are simply
@@ -755,6 +755,8 @@ Bridges.Constraint.NormInfinityBridge
755755
Bridges.Constraint.NormOneBridge
756756
Bridges.Constraint.GeoMeanBridge
757757
Bridges.Constraint.RelativeEntropyBridge
758+
Bridges.Constraint.NormSpectralBridge
759+
Bridges.Constraint.NormNuclearBridge
758760
Bridges.Constraint.SquareBridge
759761
Bridges.Constraint.RootDetBridge
760762
Bridges.Constraint.LogDetBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ include("geomean.jl")
4848
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
4949
include("relentr_to_exp.jl")
5050
const RelativeEntropy{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RelativeEntropyBridge{T}, OT}
51+
include("norm_spec_nuc_to_psd.jl")
52+
const NormSpectral{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormSpectralBridge{T}, OT}
53+
const NormNuclear{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormNuclearBridge{T}, OT}
5154
include("square.jl")
5255
const Square{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SquareBridge{T}, OT}
5356
include("det.jl")
@@ -83,6 +86,8 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
8386
MOIB.add_bridge(bridged_model, NormOneBridge{T})
8487
MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
8588
MOIB.add_bridge(bridged_model, RelativeEntropyBridge{T})
89+
MOIB.add_bridge(bridged_model, NormSpectralBridge{T})
90+
MOIB.add_bridge(bridged_model, NormNuclearBridge{T})
8691
MOIB.add_bridge(bridged_model, SquareBridge{T})
8792
MOIB.add_bridge(bridged_model, LogDetBridge{T})
8893
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/Bridges/Constraint/norm_to_lp.jl

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,64 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge)
127127
dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2)
128128
return MOI.NormOneCone(dim)
129129
end
130-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge)
131-
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index)
132-
nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index)
130+
131+
function MOI.supports(
132+
::MOI.ModelLike,
133+
::Union{MOI.ConstraintPrimalStart, MOI.ConstraintDualStart},
134+
::Type{<:NormOneBridge})
135+
136+
return true
137+
end
138+
function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart,
139+
bridge::NormOneBridge{T}, value) where T
140+
x_value = value[1 .+ (1:length(bridge.y))]
141+
y_value = abs.(x_value)
142+
for i in eachindex(bridge.y)
143+
MOI.set(model, MOI.VariablePrimalStart(), bridge.y[i], y_value[i])
144+
end
145+
MOI.set(model, attr, bridge.nn_index, [y_value - x_value; y_value + x_value])
146+
MOI.set(model, attr, bridge.ge_index, value[1] - reduce(+, y_value, init=zero(T)))
147+
return
148+
end
149+
function MOI.get(model::MOI.ModelLike,
150+
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintPrimalStart},
151+
bridge::NormOneBridge)
152+
ge_primal = MOI.get(model, attr, bridge.ge_index)
153+
nn_primal = MOI.get(model, attr, bridge.nn_index)
133154
t = ge_primal + sum(nn_primal) / 2
134-
d = length(c.y)
155+
d = length(bridge.y)
135156
x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2
136157
return vcat(t, x)
137158
end
138159
# Given a_i is dual on y_i - x_i >= 0 and b_i is dual on y_i + x_i >= 0 and c is dual on t - sum(y) >= 0,
139160
# the dual on (t, x) in NormOneCone is (u, v) in NormInfinityCone, where
140161
# v_i = -a_i + b_i and u = c.
141-
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormOneBridge)
142-
t = MOI.get(model, MOI.ConstraintDual(), c.ge_index)
143-
nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index)
144-
d = div(length(nn_dual), 2)
145-
x = (nn_dual[(d + 1):end] - nn_dual[1:d])
162+
function MOI.get(model::MOI.ModelLike,
163+
attr::Union{MOI.ConstraintDual, MOI.ConstraintDualStart},
164+
bridge::NormOneBridge)
165+
t = MOI.get(model, attr, bridge.ge_index)
166+
nn_dual = MOI.get(model, attr, bridge.nn_index)
167+
d = length(bridge.y)
168+
x = nn_dual[(d + 1):end] - nn_dual[1:d]
146169
return vcat(t, x)
147170
end
171+
# value[1 + i] = nn_dual[d + i] - nn_dual[i]
172+
# and `nn_dual` is nonnegative. By complementarity slackness, only one of each
173+
# `nn_dual` can be nonzero (except if `x = 0`) so we can set
174+
# depending on the sense of `value[1 + i]`.
175+
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintDualStart,
176+
bridge::NormOneBridge, value)
177+
t = MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, value[1])
178+
d = length(bridge.y)
179+
nn_dual = zeros(eltype(value), 2d)
180+
for i in eachindex(bridge.y)
181+
v = value[1 + i]
182+
if v < 0
183+
nn_dual[i] = -v
184+
else
185+
nn_dual[d + i] = v
186+
end
187+
end
188+
MOI.set(model, MOI.ConstraintDualStart(), bridge.nn_index, nn_dual)
189+
return
190+
end

0 commit comments

Comments
 (0)