Skip to content

Commit 65e4a61

Browse files
authored
Throw GetAttributeNotAllowed in AbstractBridge getter (#2988)
1 parent 0057d65 commit 65e4a61

File tree

5 files changed

+119
-2
lines changed

5 files changed

+119
-2
lines changed

src/Bridges/Bridges.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ _runtests_error_handler(err, ::Bool) = rethrow(err)
239239
function _runtests_error_handler(
240240
err::Union{
241241
MOI.GetAttributeNotAllowed{MOI.ConstraintFunction},
242+
MOI.GetAttributeNotAllowed{MOI.ConstraintSet},
242243
MOI.GetAttributeNotAllowed{MOI.ConstraintPrimalStart},
243244
},
244245
cannot_unbridge::Bool,

src/Bridges/bridge.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ function MOI.get(
146146
bridge::AbstractBridge,
147147
)
148148
message = _attribute_error_message(attr, typeof(bridge), "accessing")
149-
return throw(ArgumentError(message))
149+
return throw(MOI.GetAttributeNotAllowed(attr, message))
150150
end
151151

152152
function MOI.get(
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
# Test with a bridge for which the map is defined on the bridge value and not
8+
# the bridge type
9+
module TestAbstractBridge
10+
11+
using Test
12+
13+
import MathOptInterface as MOI
14+
15+
# A minimal constraint bridge that doesn't implement ConstraintFunction getter.
16+
# Used to test that `cannot_unbridge = true` works with the default bridge
17+
# `MOI.get` (which throws `GetAttributeNotAllowed`).
18+
struct _NoUnbridgeTestBridge{T} <: MOI.Bridges.Constraint.AbstractBridge
19+
ci::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}
20+
end
21+
22+
function MOI.Bridges.Constraint.bridge_constraint(
23+
::Type{_NoUnbridgeTestBridge{T}},
24+
model::MOI.ModelLike,
25+
f::MOI.ScalarAffineFunction{T},
26+
::MOI.GreaterThan{T},
27+
) where {T}
28+
ci = MOI.add_constraint(model, f, MOI.EqualTo(zero(T)))
29+
return _NoUnbridgeTestBridge{T}(ci)
30+
end
31+
32+
function MOI.supports_constraint(
33+
::Type{_NoUnbridgeTestBridge{T}},
34+
::Type{MOI.ScalarAffineFunction{T}},
35+
::Type{MOI.GreaterThan{T}},
36+
) where {T}
37+
return true
38+
end
39+
40+
function MOI.Bridges.Constraint.concrete_bridge_type(
41+
::Type{_NoUnbridgeTestBridge{T}},
42+
::Type{MOI.ScalarAffineFunction{T}},
43+
::Type{MOI.GreaterThan{T}},
44+
) where {T}
45+
return _NoUnbridgeTestBridge{T}
46+
end
47+
48+
function MOI.Bridges.added_constraint_types(
49+
::Type{_NoUnbridgeTestBridge{T}},
50+
) where {T}
51+
return Tuple{Type,Type}[(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T})]
52+
end
53+
54+
function MOI.Bridges.added_constrained_variable_types(
55+
::Type{<:_NoUnbridgeTestBridge},
56+
)
57+
return Tuple{Type}[]
58+
end
59+
60+
function MOI.get(
61+
::_NoUnbridgeTestBridge{T},
62+
::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}},
63+
) where {T}
64+
return 1
65+
end
66+
67+
function MOI.get(
68+
bridge::_NoUnbridgeTestBridge{T},
69+
::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}},
70+
) where {T}
71+
return [bridge.ci]
72+
end
73+
74+
function MOI.delete(model::MOI.ModelLike, bridge::_NoUnbridgeTestBridge)
75+
MOI.delete(model, bridge.ci)
76+
return
77+
end
78+
79+
# Note: ConstraintFunction and ConstraintSet getters are NOT implemented.
80+
# The default MOI.get on AbstractBridge throws GetAttributeNotAllowed.
81+
82+
function test_cannot_unbridge_with_default_getter()
83+
# This test verifies that `cannot_unbridge = true` works when a constraint
84+
# bridge does not implement `ConstraintFunction` getter. Before the fix,
85+
# the default bridge `MOI.get` threw `ArgumentError` which was not caught
86+
# by `_runtests_error_handler`.
87+
MOI.Bridges.runtests(
88+
_NoUnbridgeTestBridge,
89+
model -> begin
90+
x = MOI.add_variable(model)
91+
MOI.add_constraint(model, 1.0 * x, MOI.GreaterThan(0.0))
92+
end,
93+
model -> begin
94+
x = MOI.add_variable(model)
95+
MOI.add_constraint(model, 1.0 * x, MOI.EqualTo(0.0))
96+
end;
97+
cannot_unbridge = true,
98+
)
99+
return
100+
end
101+
102+
function runtests()
103+
for name in names(@__MODULE__; all = true)
104+
if startswith("$(name)", "test_")
105+
@testset "$(name)" begin
106+
getfield(@__MODULE__, name)()
107+
end
108+
end
109+
end
110+
return
111+
end
112+
113+
end
114+
115+
TestAbstractBridge.runtests()

test/Bridges/General/test_bridge_optimizer.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ function test_unsupported_constraint_attribute()
250250
@test !MOI.Bridges.is_bridged(bridged_mock, ci)
251251
@test MOI.Bridges.is_bridged(bridged_mock.model, ci)
252252
@test !MOI.supports(bridged_mock, attr, typeof(ci))
253-
err = ArgumentError(message("accessing"))
253+
err = MOI.GetAttributeNotAllowed(attr, message("accessing"))
254254
@test_throws err MOI.get(bridged_mock, attr, ci)
255255
err = MOI.UnsupportedAttribute(attr, message("setting a value for"))
256256
@test_throws err MOI.set(bridged_mock, attr, ci, 1)

test/Bridges/Variable/test_zeros.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function test_bridge_error_handler()
5151
ErrorException("abc") => false,
5252
MOI.GetAttributeNotAllowed(MOI.ObjectiveSense()) => false,
5353
MOI.GetAttributeNotAllowed(MOI.ConstraintFunction()) => true,
54+
MOI.GetAttributeNotAllowed(MOI.ConstraintSet()) => true,
5455
)
5556
@test_throws err try
5657
@assert false

0 commit comments

Comments
 (0)