Skip to content

Commit 3bbca2a

Browse files
authored
[Bridges] improve test coverage (#2661)
1 parent c0197d3 commit 3bbca2a

23 files changed

+373
-38
lines changed

src/Bridges/Constraint/bridges/IndicatorToMILPBridge.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,6 @@ function MOI.delete(
126126
model::MOI.ModelLike,
127127
bridge::IndicatorToMILPBridge{T},
128128
) where {T}
129-
if bridge.slack === nothing
130-
return # final_touch not called, so we can safely skip
131-
end
132129
MOI.delete.(model, bridge.slack_bounds)
133130
MOI.delete(model, bridge.constraint)
134131
MOI.delete(model, bridge.slack::MOI.VariableIndex)

src/Bridges/Constraint/bridges/RSOCtoSOCBridge.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ end
5959

6060
function MOI.Bridges.map_function(::Type{<:RSOCtoSOCBridge{T}}, func) where {T}
6161
scalars = MOI.Utilities.eachscalar(func)
62+
# We cannot construct MOI.RotatedSecondOrderCone(1)
63+
@assert length(scalars) >= 2
6264
t, u, x = scalars[1], scalars[2], scalars[3:end]
6365
ts = MOI.Utilities.operate!(/, T, t, sqrt(T(2)))
6466
us = MOI.Utilities.operate!(/, T, u, sqrt(T(2)))

src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,9 @@ end
256256

257257
function MOI.Bridges.map_function(::Type{<:RSOCtoPSDBridge{T}}, func) where {T}
258258
scalars = MOI.Utilities.eachscalar(func)
259-
if length(scalars) < 2
260-
error(
261-
"Unable to bridge RotatedSecondOrderCone to PSD because the ",
262-
"dimension is too small: got $(length(scalars)), expected >= 2.",
263-
)
264-
elseif length(scalars) == 2
259+
# We cannot construct MOI.RotatedSecondOrderCone(1)
260+
@assert length(scalars) >= 2
261+
if length(scalars) == 2
265262
return func
266263
end
267264
# Input is (t, u, x), and we need [t x'; x 2uI]

src/Bridges/Constraint/bridges/SOCtoRSOCBridge.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,6 @@ function MOI.Bridges.map_set(
5050
::Type{<:SOCtoRSOCBridge},
5151
set::MOI.SecondOrderCone,
5252
)
53-
if MOI.dimension(set) == 1
54-
error(
55-
"Unable to reformulate a `SecondOrderCone` into a " *
56-
"`RotatedSecondOrderCone` because the dimension of `1` is too " *
57-
"small",
58-
)
59-
end
6053
return MOI.RotatedSecondOrderCone(MOI.dimension(set))
6154
end
6255

@@ -69,6 +62,14 @@ end
6962

7063
function MOI.Bridges.map_function(::Type{<:SOCtoRSOCBridge{T}}, func) where {T}
7164
scalars = MOI.Utilities.eachscalar(func)
65+
if length(scalars) < 2
66+
err = DimensionMismatch(
67+
"Unable to reformulate a `SecondOrderCone` into a " *
68+
"`RotatedSecondOrderCone` because the output dimension is too " *
69+
"small",
70+
)
71+
throw(err)
72+
end
7273
t, u, x = scalars[1], scalars[2], scalars[3:end]
7374
ts = MOI.Utilities.operate!(/, T, t, sqrt(T(2)))
7475
us = MOI.Utilities.operate!(/, T, u, sqrt(T(2)))

src/Bridges/Constraint/bridges/SOS1ToMILPBridge.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,6 @@ function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::SOS1ToMILPBridge)
116116
end
117117

118118
function MOI.delete(model::MOI.ModelLike, bridge::SOS1ToMILPBridge)
119-
if isempty(bridge.variables)
120-
return
121-
end
122119
MOI.delete(model, bridge.equal_to)
123120
for ci in bridge.less_than
124121
MOI.delete(model, ci)

src/Bridges/Constraint/bridges/SOS2ToMILPBridge.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,6 @@ function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::SOS2ToMILPBridge)
116116
end
117117

118118
function MOI.delete(model::MOI.ModelLike, bridge::SOS2ToMILPBridge)
119-
if isempty(bridge.variables)
120-
return
121-
end
122119
MOI.delete(model, bridge.equal_to)
123120
for ci in bridge.less_than
124121
MOI.delete(model, ci)

src/Bridges/Constraint/bridges/ScalarSlackBridge.jl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@ function MOI.get(
2525
return 1
2626
end
2727

28-
function MOI.get(
29-
::_AbstractSlackBridge{T,VF,ZS,F,S},
30-
::MOI.NumberOfConstraints{VF,S},
31-
)::Int64 where {T,VF,ZS,F,S}
32-
return 1
33-
end
34-
3528
function MOI.get(
3629
bridge::_AbstractSlackBridge{T,VF,ZS,F},
3730
::MOI.ListOfConstraintIndices{F,ZS},
3831
) where {T,VF,ZS,F}
3932
return [bridge.equality]
4033
end
4134

35+
function MOI.get(
36+
::_AbstractSlackBridge{T,VF,ZS,F,S},
37+
::MOI.NumberOfConstraints{VF,S},
38+
)::Int64 where {T,VF,ZS,F,S}
39+
return 1
40+
end
41+
4242
function MOI.get(
4343
bridge::_AbstractSlackBridge{T,VF,ZS,F,S},
4444
::MOI.ListOfConstraintIndices{VF,S},
@@ -55,20 +55,20 @@ function MOI.get(
5555
# that the original set was the same as the slacked set.
5656
return error(
5757
"Internal error: this method should never be called because it " *
58-
"represents and invalid state. Please open an issue to report.",
58+
"represents an invalid state. Please open an issue to report.",
5959
)
6060
end
6161

6262
function MOI.get(
6363
bridge::_AbstractSlackBridge{T,VF,S,F,S},
64-
::MOI.ListOfConstraintIndices{F,S},
64+
::MOI.ListOfConstraintIndices{VF,S},
6565
) where {T,VF,S,F}
6666
# This method is needed to resolve a possible ambiguity reported by
6767
# Test.detect_ambiguities. It can't happen in practice because it would mean
6868
# that the original set was the same as the slacked set.
6969
return error(
7070
"Internal error: this method should never be called because it " *
71-
"represents and invalid state. Please open an issue to report.",
71+
"represents an invalid state. Please open an issue to report.",
7272
)
7373
end
7474

src/Bridges/Objective/bridge.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,13 @@ end
9595

9696
function MOI.set(
9797
::MOI.ModelLike,
98-
::MOI.ObjectiveSense,
98+
attr::MOI.ObjectiveSense,
9999
bridge::AbstractBridge,
100100
::MOI.OptimizationSense,
101101
)
102102
return throw(
103-
ArgumentError(
103+
MOI.SetAttributeNotAllowed(
104+
attr,
104105
"Objective bridge of type `$(typeof(bridge))` does not support " *
105106
"modifying the objective sense. As a workaround, set the sense " *
106107
"to `MOI.FEASIBILITY_SENSE` to clear the objective function and " *
@@ -111,11 +112,12 @@ end
111112

112113
function MOI.get(
113114
::MOI.ModelLike,
114-
::MOI.ObjectiveFunction,
115+
attr::MOI.ObjectiveFunction,
115116
bridge::AbstractBridge,
116117
)
117118
return throw(
118-
ArgumentError(
119+
MOI.GetAttributeNotAllowed(
120+
attr,
119121
"ObjectiveFunction bridge of type `$(typeof(bridge))` does not" *
120122
" support getting the objective function.",
121123
),

src/Bridges/bridge_optimizer.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@ function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
625625
else
626626
MOI.delete(b.model, vis)
627627
end
628+
return
628629
end
629630

630631
function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex)

test/Bridges/Constraint/RSOCBridge.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,26 @@ function test_runtests()
179179
return
180180
end
181181

182+
function test_dimension_mismatch_SOCR()
183+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
184+
model = MOI.Bridges.Constraint.SOCR{Float64}(inner)
185+
@test_throws(
186+
DimensionMismatch,
187+
MOI.add_constrained_variables(model, MOI.SecondOrderCone(1)),
188+
)
189+
return
190+
end
191+
192+
function test_dimension_mismatch_RSOC()
193+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
194+
model = MOI.Bridges.Constraint.RSOC{Float64}(inner)
195+
@test_throws(
196+
DimensionMismatch,
197+
MOI.add_constrained_variables(model, MOI.RotatedSecondOrderCone(1)),
198+
)
199+
return
200+
end
201+
182202
end # module
183203

184204
TestConstraintRSOC.runtests()

test/Bridges/Constraint/SOCtoPSDBridge.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,16 @@ function test_bridging_cost_RSOCtoPSD()
231231
return
232232
end
233233

234+
function test_dimension_mismatch_RSOC_to_PSD()
235+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
236+
model = MOI.Bridges.Constraint.RSOCtoPSD{Float64}(inner)
237+
@test_throws(
238+
DimensionMismatch,
239+
MOI.add_constrained_variables(model, MOI.RotatedSecondOrderCone(1)),
240+
)
241+
return
242+
end
243+
234244
end # module
235245

236246
TestConstraintSOCtoPSD.runtests()

test/Bridges/Constraint/ScalarSlackBridge.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,47 @@ function test_runtests()
411411
return
412412
end
413413

414+
function test_basis_status()
415+
inner = MOI.Utilities.MockOptimizer(
416+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
417+
)
418+
model = MOI.Bridges.Constraint.ScalarSlack{Float64}(inner)
419+
x = MOI.add_variable(model)
420+
c = MOI.add_constraint(model, 1.0 * x, MOI.GreaterThan(1.0))
421+
y = MOI.get(inner, MOI.ListOfVariableIndices())
422+
MOI.set.(inner, MOI.VariableBasisStatus(), y, MOI.BASIC)
423+
@test MOI.get(model, MOI.ConstraintBasisStatus(), c) == MOI.BASIC
424+
d = MOI.add_constraint(model, 1.0 * x, MOI.Interval(1.0, 2.0))
425+
z = last(MOI.get(inner, MOI.ListOfVariableIndices()))
426+
MOI.set(inner, MOI.VariableBasisStatus(), z, MOI.SUPER_BASIC)
427+
@test MOI.get(model, MOI.ConstraintBasisStatus(), d) == MOI.SUPER_BASIC
428+
return
429+
end
430+
431+
function test_internal_error()
432+
F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}
433+
BT = MOI.Bridges.Constraint.ScalarSlackBridge{Float64,F,S}
434+
model = MOI.Utilities.Model{Float64}()
435+
x = MOI.add_variable(model)
436+
set = MOI.EqualTo(1.0)
437+
bridge = MOI.Bridges.Constraint.bridge_constraint(BT, model, 1.0 * x, set)
438+
@test_throws(
439+
ErrorException(
440+
"Internal error: this method should never be called because it " *
441+
"represents an invalid state. Please open an issue to report.",
442+
),
443+
MOI.get(bridge, MOI.NumberOfConstraints{MOI.VariableIndex,S}()),
444+
)
445+
@test_throws(
446+
ErrorException(
447+
"Internal error: this method should never be called because it " *
448+
"represents an invalid state. Please open an issue to report.",
449+
),
450+
MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VariableIndex,S}()),
451+
)
452+
return
453+
end
454+
414455
end # module
415456

416457
TestConstraintSlack.runtests()

test/Bridges/Constraint/SplitIntervalBridge.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,33 @@ function test_runtests_vector()
499499
return
500500
end
501501

502+
function test_get_function()
503+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
504+
model = MOI.Bridges.Constraint.SplitInterval{Float64}(inner)
505+
x = MOI.add_variable(model)
506+
c = MOI.add_constraint(model, 1.0 * x, MOI.Interval(-Inf, Inf))
507+
@test MOI.get(model, MOI.ConstraintFunction(), c) 1.0 * x
508+
return
509+
end
510+
511+
function test_modify_set()
512+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
513+
model = MOI.Bridges.Constraint.SplitInterval{Float64}(inner)
514+
x = MOI.add_variable(model)
515+
c = MOI.add_constraint(model, 1.0 * x, MOI.Interval(-Inf, Inf))
516+
for set in [
517+
MOI.Interval(-Inf, Inf),
518+
MOI.Interval(1.0, 2.0),
519+
MOI.Interval(2.0, 3.0),
520+
MOI.Interval(-Inf, Inf),
521+
]
522+
MOI.set(model, MOI.ConstraintSet(), c, set)
523+
@test MOI.get(model, MOI.ConstraintSet(), c) == set
524+
@test MOI.get(model, MOI.ConstraintFunction(), c) 1.0 * x
525+
end
526+
return
527+
end
528+
502529
end # module
503530

504531
TestConstraintSplitInterval.runtests()

test/Bridges/Constraint/VectorizeBridge.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,22 @@ function test_VectorNonlinearFunction()
285285
return
286286
end
287287

288+
function test_constraint_primal_ray()
289+
inner = MOI.Utilities.MockOptimizer(
290+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
291+
)
292+
model = MOI.Bridges.Constraint.Vectorize{Float64}(inner)
293+
x = MOI.add_variable(model)
294+
c = MOI.add_constraint(model, 1.0 * x, MOI.EqualTo(3.0))
295+
MOI.set(inner, MOI.PrimalStatus(), MOI.INFEASIBILITY_CERTIFICATE)
296+
y = only(MOI.get(inner, MOI.ListOfVariableIndices()))
297+
MOI.set(inner, MOI.VariablePrimal(), y, 1.23)
298+
@test MOI.get(model, MOI.ConstraintPrimal(), c) == 1.23
299+
MOI.set(inner, MOI.PrimalStatus(), MOI.FEASIBLE_POINT)
300+
@test MOI.get(model, MOI.ConstraintPrimal(), c) == 1.23
301+
return
302+
end
303+
288304
end # module
289305

290306
TestConstraintVectorize.runtests()

test/Bridges/Objective/SlackBridge.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function test_SlackBridge_ObjectiveSense_modify()
4646
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
4747
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
4848
@test_throws(
49-
ArgumentError,
49+
MOI.SetAttributeNotAllowed,
5050
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE),
5151
)
5252
MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
@@ -328,7 +328,8 @@ function test_original()
328328
("y", MOI.GreaterThan{Float64}(2.0)),
329329
],
330330
)
331-
err = ArgumentError(
331+
err = MOI.SetAttributeNotAllowed(
332+
MOI.ObjectiveSense(),
332333
"Objective bridge of type `$(MOI.Bridges.Objective.SlackBridge{Float64,MOI.ScalarQuadraticFunction{Float64},MOI.ScalarQuadraticFunction{Float64}})`" *
333334
" does not support modifying the objective sense. As a workaround, set" *
334335
" the sense to `MOI.FEASIBILITY_SENSE` to clear the objective function" *

test/Bridges/Variable/HermitianToSymmetricPSDBridge.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,28 @@ function test_runtests()
113113
return
114114
end
115115

116+
function test_delete()
117+
inner = MOI.Utilities.Model{Float64}()
118+
model = MOI.Bridges.Variable.HermitianToSymmetricPSD{Float64}(inner)
119+
set = MOI.HermitianPositiveSemidefiniteConeTriangle(2)
120+
x, _ = MOI.add_constrained_variables(model, set)
121+
MOI.delete(model, x)
122+
@test MOI.is_empty(inner)
123+
return
124+
end
125+
126+
function test_set_variable_primal_start()
127+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
128+
model = MOI.Bridges.Variable.HermitianToSymmetricPSD{Float64}(inner)
129+
set = MOI.HermitianPositiveSemidefiniteConeTriangle(2)
130+
x, _ = MOI.add_constrained_variables(model, set)
131+
MOI.set(model, MOI.VariablePrimalStart(), x[1], 1.0)
132+
@test MOI.get(model, MOI.VariablePrimalStart(), x[1]) == 1.0
133+
MOI.set(model, MOI.VariablePrimalStart(), x[1], nothing)
134+
@test MOI.get(model, MOI.VariablePrimalStart(), x[1]) === nothing
135+
return
136+
end
137+
116138
end # module
117139

118140
TestVariableHermitianToSymmetricPSD.runtests()

test/Bridges/Variable/NonposToNonnegBridge.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,14 @@ function test_runtests()
199199
return
200200
end
201201

202+
function test_adjoint_map_function()
203+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
204+
model = MOI.Bridges.Variable.NonposToNonneg{Float64}(inner)
205+
x, _ = MOI.add_constrained_variables(model, MOI.Nonpositives(1))
206+
@test MOI.Bridges.adjoint_map_function(model.map[only(x)], 1.23) == -1.23
207+
return
208+
end
209+
202210
end # module
203211

204212
TestVariableFlipSign.runtests()

0 commit comments

Comments
 (0)