Skip to content

Commit 065e1b0

Browse files
odowblegat
andauthored
Test support for VariablePrimalStart in Bridges.Variable (#2116)
* WIP: test support for VariablePrimalStart in Bridges.Variable * Opt-out for RSOC/SOC bridges * Default supports * False positive supports * Revert "Default supports" This reverts commit 967c9c7. * Fixes * Fix format --------- Co-authored-by: Benoît Legat <[email protected]>
1 parent 5025b50 commit 065e1b0

File tree

7 files changed

+88
-13
lines changed

7 files changed

+88
-13
lines changed

src/Bridges/Bridges.jl

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,13 @@ function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike)
218218
end
219219

220220
"""
221-
runtests(Bridge::Type{<:AbstractBridge}, input::String, output::String)
221+
runtests(
222+
Bridge::Type{<:AbstractBridge},
223+
input::String,
224+
output::String;
225+
variable_start = 1.2,
226+
constraint_start = 1.2,
227+
)
222228
223229
Run a series of tests that check the correctness of `Bridge`.
224230
@@ -246,6 +252,7 @@ function runtests(
246252
Bridge::Type{<:AbstractBridge},
247253
input::String,
248254
output::String;
255+
variable_start = 1.2,
249256
constraint_start = 1.2,
250257
)
251258
# Load model and bridge it
@@ -263,6 +270,19 @@ function runtests(
263270
target = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
264271
MOI.Utilities.loadfromstring!(target, output)
265272
_test_structural_identical(target, inner)
273+
# Test VariablePrimalStart
274+
attr = MOI.VariablePrimalStart()
275+
bridge_supported = all(values(Variable.bridges(model))) do bridge
276+
return MOI.supports(model, attr, typeof(bridge))
277+
end
278+
if MOI.supports(model, attr, MOI.VariableIndex) && bridge_supported
279+
x = MOI.get(model, MOI.ListOfVariableIndices())
280+
MOI.set(model, attr, x, fill(nothing, length(x)))
281+
Test.@test all(isnothing, MOI.get(model, attr, x))
282+
primal_start = fill(constraint_start, length(x))
283+
MOI.set(model, attr, x, primal_start)
284+
Test.@test MOI.get(model, attr, x) primal_start
285+
end
266286
# Test ConstraintPrimalStart and ConstraintDualStart
267287
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
268288
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())

src/Bridges/Variable/bridges/free.jl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,12 @@ function MOI.get(
130130
i::MOI.Bridges.IndexInVector,
131131
) where {T}
132132
n = div(length(bridge.variables), 2)
133-
return MOI.get(model, attr, bridge.variables[i.value]) -
134-
MOI.get(model, attr, bridge.variables[n+i.value])
133+
primal_pos = MOI.get(model, attr, bridge.variables[i.value])
134+
primal_neg = MOI.get(model, attr, bridge.variables[n+i.value])
135+
if primal_pos === nothing || primal_neg === nothing
136+
return nothing
137+
end
138+
return primal_pos - primal_neg
135139
end
136140

137141
function MOI.Bridges.bridged_function(
@@ -177,3 +181,16 @@ function MOI.set(
177181
MOI.set(model, attr, bridge.variables[n+i.value], -min(zero(value), value))
178182
return
179183
end
184+
185+
function MOI.set(
186+
model::MOI.ModelLike,
187+
attr::MOI.VariablePrimalStart,
188+
bridge::FreeBridge,
189+
::Nothing,
190+
i::MOI.Bridges.IndexInVector,
191+
)
192+
n = div(length(bridge.variables), 2)
193+
MOI.set(model, attr, bridge.variables[i.value], nothing)
194+
MOI.set(model, attr, bridge.variables[n+i.value], nothing)
195+
return
196+
end

src/Bridges/Variable/bridges/rsoc_soc.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,12 @@ function MOI.Bridges.inverse_adjoint_map_function(
9393
)
9494
return MOI.Bridges.map_function(BT, func)
9595
end
96+
97+
function MOI.supports(
98+
::MOI.ModelLike,
99+
::MOI.VariablePrimalStart,
100+
::Type{<:RSOCtoSOCBridge},
101+
)
102+
# See https://github.com/jump-dev/MathOptInterface.jl/issues/2117
103+
return false
104+
end

src/Bridges/Variable/bridges/soc_rsoc.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,12 @@ function MOI.Bridges.inverse_adjoint_map_function(
106106
)
107107
return MOI.Bridges.map_function(BT, func)
108108
end
109+
110+
function MOI.supports(
111+
::MOI.ModelLike,
112+
::MOI.VariablePrimalStart,
113+
::Type{<:SOCtoRSOCBridge},
114+
)
115+
# https://github.com/jump-dev/MathOptInterface.jl/issues/2117
116+
return false
117+
end

src/Bridges/Variable/bridges/vectorize.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,24 @@ function MOI.get(
174174
return value + bridge.set_constant
175175
end
176176

177-
function MOI.get(
177+
function MOI.supports(
178178
model::MOI.ModelLike,
179179
attr::MOI.VariablePrimalStart,
180-
bridge::VectorizeBridge,
180+
::Type{<:VectorizeBridge},
181181
)
182-
return MOI.get(model, attr, bridge.variable) + bridge.set_constant
182+
return MOI.supports(model, attr, MOI.VariableIndex)
183183
end
184184

185-
function MOI.supports(
185+
function MOI.get(
186186
model::MOI.ModelLike,
187187
attr::MOI.VariablePrimalStart,
188-
::Type{<:VectorizeBridge},
188+
bridge::VectorizeBridge,
189189
)
190-
return MOI.supports(model, attr, MOI.VariableIndex)
190+
start = MOI.get(model, attr, bridge.variable)
191+
if start === nothing
192+
return nothing
193+
end
194+
return start + bridge.set_constant
191195
end
192196

193197
function MOI.set(
@@ -196,7 +200,11 @@ function MOI.set(
196200
bridge::VectorizeBridge,
197201
value,
198202
)
199-
MOI.set(model, attr, bridge.variable, value - bridge.set_constant)
203+
if value === nothing
204+
MOI.set(model, attr, bridge.variable, value)
205+
else
206+
MOI.set(model, attr, bridge.variable, value - bridge.set_constant)
207+
end
200208
return
201209
end
202210

src/Bridges/Variable/set_map.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ function MOI.get(
168168
i::MOI.Bridges.IndexInVector,
169169
)
170170
value = MOI.get(model, attr, bridge.variables)
171+
if any(isnothing, value)
172+
return nothing
173+
end
171174
return MOI.Bridges.map_function(typeof(bridge), value, i)
172175
end
173176

@@ -186,8 +189,12 @@ function MOI.set(
186189
value,
187190
i::MOI.Bridges.IndexInVector,
188191
)
189-
bridged_value = MOI.Bridges.inverse_map_function(typeof(bridge), value)
190-
MOI.set(model, attr, bridge.variables[i.value], bridged_value)
192+
if value === nothing
193+
MOI.set(model, attr, bridge.variables[i.value], nothing)
194+
else
195+
bridged_value = MOI.Bridges.inverse_map_function(typeof(bridge), value)
196+
MOI.set(model, attr, bridge.variables[i.value], bridged_value)
197+
end
191198
return
192199
end
193200

src/Bridges/bridge_optimizer.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,9 @@ function MOI.get(
11371137
attr::MOI.AbstractVariableAttribute,
11381138
indices::Vector{MOI.VariableIndex},
11391139
)
1140-
if any(index -> is_bridged(b, index), indices)
1140+
# `Variable.has_bridges` is used as a shortcut to speedup in case no variable bridge is used
1141+
if Variable.has_bridges(Variable.bridges(b)) &&
1142+
any(index -> is_bridged(b, index), indices)
11411143
return MOI.get.(b, attr, indices)
11421144
else
11431145
return unbridged_function.(b, MOI.get(b.model, attr, indices))
@@ -1149,6 +1151,9 @@ function MOI.supports(
11491151
attr::MOI.AbstractVariableAttribute,
11501152
::Type{MOI.VariableIndex},
11511153
)
1154+
# `supports` should only return `false` in case `MOI.set` always errors.
1155+
# If some variables are bridged by bridges that do not support `attr`, we
1156+
# should therefore still return `true`.
11521157
return MOI.supports(b.model, attr, MOI.VariableIndex)
11531158
end
11541159

0 commit comments

Comments
 (0)