Skip to content

Commit 85a1db5

Browse files
authored
Allow SetMapBridge to use bridge value (#2509)
1 parent ad45651 commit 85a1db5

File tree

4 files changed

+313
-35
lines changed

4 files changed

+313
-35
lines changed

docs/src/submodules/Bridges/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Bridges.debug_supports
9090
## [SetMap API](@id constraint_set_map)
9191

9292
```@docs
93+
Bridges.MapNotInvertible
9394
Bridges.map_set
9495
Bridges.inverse_map_set
9596
Bridges.map_function

src/Bridges/Constraint/set_map.jl

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,29 @@ end
9696

9797
# Attributes, Bridge acting as a constraint
9898

99+
# MapNotInvertible is thrown if the bridge does not support inverting the
100+
# function. The user doesn't need to know this, only that they cannot get the
101+
# attribute. Throwing `GetAttributeNotAllowed` allows `CachingOptimizer` to fall
102+
# back to using the cache.
103+
function _not_invertible_error_message(attr, message)
104+
return "Cannot get `$attr` as the constraint is reformulated through a linear transformation that is not invertible. $message"
105+
end
106+
99107
function MOI.get(
100108
model::MOI.ModelLike,
101109
attr::MOI.ConstraintFunction,
102110
bridge::MultiSetMapBridge{T,S1,G},
103111
) where {T,S1,G}
104112
mapped_func = MOI.get(model, attr, bridge.constraint)
105-
func = MOI.Bridges.inverse_map_function(typeof(bridge), mapped_func)
113+
func = try
114+
MOI.Bridges.inverse_map_function(bridge, mapped_func)
115+
catch err
116+
if err isa MOI.Bridges.MapNotInvertible
117+
msg = _not_invertible_error_message(attr, err.message)
118+
throw(MOI.GetAttributeNotAllowed(attr, msg))
119+
end
120+
rethrow(err)
121+
end
106122
return MOI.Utilities.convert_approx(G, func)
107123
end
108124

@@ -123,7 +139,7 @@ function MOI.get(
123139
bridge::MultiSetMapBridge,
124140
)
125141
set = MOI.get(model, attr, bridge.constraint)
126-
return MOI.Bridges.inverse_map_set(typeof(bridge), set)
142+
return MOI.Bridges.inverse_map_set(bridge, set)
127143
end
128144

129145
function MOI.set(
@@ -132,7 +148,7 @@ function MOI.set(
132148
bridge::MultiSetMapBridge{T,S1},
133149
set::S1,
134150
) where {T,S1}
135-
new_set = MOI.Bridges.map_set(typeof(bridge), set)
151+
new_set = MOI.Bridges.map_set(bridge, set)
136152
MOI.set(model, attr, bridge.constraint, new_set)
137153
return
138154
end
@@ -146,7 +162,18 @@ function MOI.get(
146162
if value === nothing
147163
return nothing
148164
end
149-
return MOI.Bridges.inverse_map_function(typeof(bridge), value)
165+
try
166+
return MOI.Bridges.inverse_map_function(bridge, value)
167+
catch err
168+
# MapNotInvertible is thrown if the bridge does not support inverting
169+
# the function. The user doesn't need to know this, only that they
170+
# cannot get the attribute.
171+
if err isa MOI.Bridges.MapNotInvertible
172+
msg = _not_invertible_error_message(attr, err.message)
173+
throw(MOI.GetAttributeNotAllowed(attr, msg))
174+
end
175+
rethrow(err)
176+
end
150177
end
151178

152179
function MOI.set(
@@ -158,7 +185,7 @@ function MOI.set(
158185
if value === nothing
159186
MOI.set(model, attr, bridge.constraint, nothing)
160187
else
161-
mapped_value = MOI.Bridges.map_function(typeof(bridge), value)
188+
mapped_value = MOI.Bridges.map_function(bridge, value)
162189
MOI.set(model, attr, bridge.constraint, mapped_value)
163190
end
164191
return
@@ -173,7 +200,7 @@ function MOI.get(
173200
if value === nothing
174201
return nothing
175202
end
176-
return MOI.Bridges.adjoint_map_function(typeof(bridge), value)
203+
return MOI.Bridges.adjoint_map_function(bridge, value)
177204
end
178205

179206
function MOI.set(
@@ -185,7 +212,15 @@ function MOI.set(
185212
if value === nothing
186213
MOI.set(model, attr, bridge.constraint, nothing)
187214
else
188-
mapped_value = MOI.Bridges.inverse_adjoint_map_function(BT, value)
215+
mapped_value = try
216+
MOI.Bridges.inverse_adjoint_map_function(bridge, value)
217+
catch err
218+
if err isa MOI.Bridges.MapNotInvertible
219+
msg = _not_invertible_error_message(attr, err.message)
220+
throw(MOI.SetAttributeNotAllowed(attr, msg))
221+
end
222+
rethrow(err)
223+
end
189224
MOI.set(model, attr, bridge.constraint, mapped_value)
190225
end
191226
return

src/Bridges/set_map.jl

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,69 @@
55
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66

77
"""
8+
struct MapNotInvertible <: Exception
9+
message::String
10+
end
11+
12+
An error thrown by [`inverse_map_function`](@ref) or
13+
[`inverse_adjoint_map_function`](@ref) indicating that the linear map `A`
14+
defined in [`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref)
15+
is not invertible.
16+
"""
17+
struct MapNotInvertible <: Exception
18+
message::String
19+
end
20+
21+
"""
22+
map_set(bridge::MOI.Bridges.AbstractBridge, set)
823
map_set(::Type{BT}, set) where {BT}
924
1025
Return the image of `set` through the linear map `A` defined in
11-
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
12-
used for bridging the constraint and setting
13-
the [`MOI.ConstraintSet`](@ref).
26+
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
27+
28+
This function is used for bridging the constraint and setting the
29+
[`MOI.ConstraintSet`](@ref).
1430
"""
15-
function map_set end
31+
map_set(bridge::AbstractBridge, set) = map_set(typeof(bridge), set)
1632

1733
"""
34+
inverse_map_set(bridge::MOI.Bridges.AbstractBridge, set)
1835
inverse_map_set(::Type{BT}, set) where {BT}
1936
2037
Return the preimage of `set` through the linear map `A` defined in
21-
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
22-
used for getting the [`MOI.ConstraintSet`](@ref).
38+
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
39+
40+
This function is used for getting the [`MOI.ConstraintSet`](@ref).
41+
42+
The method can alternatively be defined on the bridge type. This legacy
43+
interface is kept for backward compatibility.
2344
"""
24-
function inverse_map_set end
45+
function inverse_map_set(bridge::AbstractBridge, set)
46+
return inverse_map_set(typeof(bridge), set)
47+
end
2548

2649
"""
50+
map_function(bridge::MOI.Bridges.AbstractBridge, func)
2751
map_function(::Type{BT}, func) where {BT}
2852
2953
Return the image of `func` through the linear map `A` defined in
30-
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
31-
used for getting the [`MOI.ConstraintPrimal`](@ref) of variable
54+
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
55+
56+
This function is used for getting the [`MOI.ConstraintPrimal`](@ref) of variable
3257
bridges. For constraint bridges, this is used for bridging the constraint,
33-
setting the [`MOI.ConstraintFunction`](@ref) and
34-
[`MOI.ConstraintPrimalStart`](@ref) and
35-
modifying the function with [`MOI.modify`](@ref).
58+
setting the [`MOI.ConstraintFunction`](@ref) and [`MOI.ConstraintPrimalStart`](@ref)
59+
and modifying the function with [`MOI.modify`](@ref).
3660
61+
The default implementation of [`Constraint.bridge_constraint`](@ref) uses
62+
[`map_function`](@ref) with the bridge type so if this function is defined
63+
on the bridge type, [`Constraint.bridge_constraint`](@ref) does not need
64+
to be implemented.
65+
"""
66+
function map_function(bridge::AbstractBridge, func)
67+
return map_function(typeof(bridge), func)
68+
end
69+
70+
"""
3771
map_function(::Type{BT}, func, i::IndexInVector) where {BT}
3872
3973
Return the scalar function at the `i`th index of the vector function that
@@ -42,42 +76,65 @@ would be returned by `map_function(BT, func)` except that it may compute the
4276
the [`MOI.VariablePrimal`](@ref) and
4377
[`MOI.VariablePrimalStart`](@ref) of variable bridges.
4478
"""
45-
function map_function end
46-
4779
function map_function(::Type{BT}, func, i::IndexInVector) where {BT}
4880
return MOI.Utilities.eachscalar(map_function(BT, func))[i.value]
4981
end
5082

5183
"""
84+
inverse_map_function(bridge::MOI.Bridges.AbstractBridge, func)
5285
inverse_map_function(::Type{BT}, func) where {BT}
5386
5487
Return the image of `func` through the inverse of the linear map `A` defined in
55-
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
56-
used by [`Variable.unbridged_map`](@ref) and for setting the
57-
[`MOI.VariablePrimalStart`](@ref) of variable bridges
58-
and for getting the [`MOI.ConstraintFunction`](@ref),
59-
the [`MOI.ConstraintPrimal`](@ref) and the
88+
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
89+
90+
This function is used by [`Variable.unbridged_map`](@ref) and for setting the
91+
[`MOI.VariablePrimalStart`](@ref) of variable bridges and for getting the
92+
[`MOI.ConstraintFunction`](@ref), the [`MOI.ConstraintPrimal`](@ref) and the
6093
[`MOI.ConstraintPrimalStart`](@ref) of constraint bridges.
94+
95+
If the linear map `A` is not invertible, the error [`MapNotInvertible`](@ref) is
96+
thrown.
97+
98+
The method can alternatively be defined on the bridge type. This legacy
99+
interface is kept for backward compatibility.
61100
"""
62-
function inverse_map_function end
101+
function inverse_map_function(bridge::AbstractBridge, func)
102+
return inverse_map_function(typeof(bridge), func)
103+
end
63104

64105
"""
106+
adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
65107
adjoint_map_function(::Type{BT}, func) where {BT}
66108
67109
Return the image of `func` through the adjoint of the linear map `A` defined in
68-
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
69-
used for getting the [`MOI.ConstraintDual`](@ref) and
110+
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
111+
112+
This function is used for getting the [`MOI.ConstraintDual`](@ref) and
70113
[`MOI.ConstraintDualStart`](@ref) of constraint bridges.
114+
115+
The method can alternatively be defined on the bridge type. This legacy
116+
interface is kept for backward compatibility.
71117
"""
72-
function adjoint_map_function end
118+
function adjoint_map_function(bridge::AbstractBridge, func)
119+
return adjoint_map_function(typeof(bridge), func)
120+
end
73121

74122
"""
123+
inverse_adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
75124
inverse_adjoint_map_function(::Type{BT}, func) where {BT}
76125
77126
Return the image of `func` through the inverse of the adjoint of the linear map
78-
`A` defined in [`Variable.SetMapBridge`](@ref) and
79-
[`Constraint.SetMapBridge`](@ref). This is used for getting the
80-
[`MOI.ConstraintDual`](@ref) of variable bridges and setting the
81-
[`MOI.ConstraintDualStart`](@ref) of constraint bridges.
127+
`A` defined in [`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
128+
129+
This function is used for getting the [`MOI.ConstraintDual`](@ref) of variable
130+
bridges and setting the [`MOI.ConstraintDualStart`](@ref) of constraint bridges.
131+
132+
If the linear map `A` is not invertible, the error [`MapNotInvertible`](@ref) is
133+
thrown.
134+
135+
The method can alternatively be defined on the bridge type. This legacy
136+
interface is kept for backward compatibility.
82137
"""
83-
function inverse_adjoint_map_function end
138+
function inverse_adjoint_map_function(bridge::AbstractBridge, func)
139+
return inverse_adjoint_map_function(typeof(bridge), func)
140+
end

0 commit comments

Comments
 (0)