Skip to content

Commit d966e88

Browse files
authored
Fix Bridge test for non-invertible constraint bridge (#2713)
1 parent 1dcd8a4 commit d966e88

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

docs/src/reference/errors.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The different [`UnsupportedError`](@ref) and [`NotAllowedError`](@ref) are the
6666
following errors:
6767
```@docs
6868
UnsupportedAttribute
69+
GetAttributeNotAllowed
6970
SetAttributeNotAllowed
7071
AddVariableNotAllowed
7172
UnsupportedConstraint

src/Bridges/Bridges.jl

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ MOI.attribute_value_type(::ListOfNonstandardBridges) = Vector
142142

143143
MOI.is_copyable(::ListOfNonstandardBridges) = false
144144

145-
MOI.get_fallback(model::MOI.ModelLike, ::ListOfNonstandardBridges) = Type[]
145+
MOI.get_fallback(::MOI.ModelLike, ::ListOfNonstandardBridges) = Type[]
146146

147147
function _test_structural_identical(
148148
a::MOI.ModelLike,
@@ -191,12 +191,8 @@ function _test_structural_identical(
191191
f_b = try
192192
MOI.get(b, MOI.ConstraintFunction(), ci)
193193
catch err
194-
if cannot_unbridge &&
195-
err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction}
196-
continue
197-
else
198-
rethrow(err)
199-
end
194+
_runtests_error_handler(err, cannot_unbridge)
195+
continue
200196
end
201197
f_b = MOI.Utilities.map_indices(x_map, f_b)
202198
s_b = MOI.get(b, MOI.ConstraintSet(), ci)
@@ -227,7 +223,10 @@ end
227223
_runtests_error_handler(err, ::Bool) = rethrow(err)
228224

229225
function _runtests_error_handler(
230-
err::MOI.GetAttributeNotAllowed{MOI.ConstraintFunction},
226+
err::Union{
227+
MOI.GetAttributeNotAllowed{MOI.ConstraintFunction},
228+
MOI.GetAttributeNotAllowed{MOI.ConstraintPrimalStart},
229+
},
231230
cannot_unbridge::Bool,
232231
)
233232
if cannot_unbridge
@@ -252,9 +251,11 @@ Run a series of tests that check the correctness of `Bridge`.
252251
`input_fn` and `output_fn` are functions such that `input_fn(model)`
253252
and `output_fn(model)` load the corresponding model into `model`.
254253
255-
Set `cannot_unbridge` to `true` if the bridge is a variable bridge
256-
for which [`Variable.unbridged_map`](@ref) returns `nothing` so that
257-
the tests allow errors that can be raised due to this.
254+
Set `cannot_unbridge` to `true` if the bridge transformation is not invertible.
255+
If `Bridge` is a variable bridge this allows [`Variable.unbridged_map`](@ref)
256+
to returns `nothing` so that the tests allow errors that can be raised due to this.
257+
If `Bridge` is a constraint bridge this allows the getter of [`MOI.ConstraintFunction`](@ref)
258+
and [`MOI.ConstraintPrimalStart`](@ref) to throw [`MOI.GetAttributeNotAllowed`](@ref).
258259
259260
## Example
260261
@@ -328,7 +329,19 @@ function runtests(
328329
Test.@test MOI.get(model, attr, ci) === nothing
329330
start = _fake_start(constraint_start, set)
330331
MOI.set(model, attr, ci, start)
331-
Test.@test MOI.get(model, attr, ci) start
332+
returned_start = try
333+
MOI.get(model, attr, ci)
334+
catch err
335+
# For a Constraint bridge for which the map is not invertible, the constraint primal cannot
336+
# be inverted
337+
_runtests_error_handler(
338+
err,
339+
Bridge <: MOI.Bridges.Constraint.AbstractBridge &&
340+
cannot_unbridge,
341+
)
342+
continue
343+
end
344+
Test.@test returned_start start
332345
end
333346
end
334347
end
@@ -416,7 +429,7 @@ _fake_start(value, ::MOI.AbstractScalarSet) = value
416429

417430
_fake_start(value, set::MOI.AbstractVectorSet) = fill(value, MOI.dimension(set))
418431

419-
_fake_start(value::AbstractVector, set::MOI.AbstractVectorSet) = value
432+
_fake_start(value::AbstractVector, ::MOI.AbstractVectorSet) = value
420433

421434
function _bridged_model(Bridge::Type{<:Constraint.AbstractBridge}, inner)
422435
return Constraint.SingleBridgeOptimizer{Bridge}(inner)

test/Bridges/set_map.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ function MOI.Bridges.inverse_map_set(
9898
return bridge.set
9999
end
100100

101+
# Otherwise, it fails Bridges.runtests for `NOT_INVERTIBLE`
102+
function MOI.supports(
103+
::MOI.ModelLike,
104+
::MOI.ConstraintDualStart,
105+
::Type{<:ConstraintSwapBridge},
106+
)
107+
return false
108+
end
109+
101110
const SwapBridge{T} = Union{VariableSwapBridge{T},ConstraintSwapBridge{T}}
102111

103112
function MOI.Bridges.map_function(bridge::SwapBridge, func)
@@ -214,6 +223,22 @@ function test_runtests()
214223
MOI.add_constraint(model, func, set)
215224
end,
216225
)
226+
MOI.Bridges.runtests(
227+
ConstraintSwapBridge,
228+
model -> begin
229+
x = MOI.add_variables(model, 2)
230+
func = MOI.VectorOfVariables(x)
231+
set = SwapSet(do_swap, NOT_INVERTIBLE)
232+
MOI.add_constraint(model, func, set)
233+
end,
234+
model -> begin
235+
x = MOI.add_variables(model, 2)
236+
func = MOI.VectorOfVariables(swap(x, do_swap))
237+
set = MOI.Nonnegatives(2)
238+
MOI.add_constraint(model, func, set)
239+
end,
240+
cannot_unbridge = true,
241+
)
217242
MOI.Bridges.runtests(
218243
VariableSwapBridge,
219244
model -> begin

0 commit comments

Comments
 (0)