Skip to content

Commit b4c1a83

Browse files
authored
[Bridges] fix support for nested final_touch bridges (#3027)
1 parent f5d3b2c commit b4c1a83

3 files changed

Lines changed: 355 additions & 32 deletions

File tree

src/Bridges/Constraint/map.jl

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ struct Map <: AbstractDict{MOI.ConstraintIndex,AbstractBridge}
2020
# of creation so we need `OrderedDict` and not `Dict`.
2121
# For `VariableIndex` constraints: (variable, set type) -> bridge
2222
single_variable_constraints::OrderedDict{Tuple{Int64,Type},AbstractBridge}
23-
needs_final_touch::OrderedDict{Type,OrderedSet}
23+
needs_final_touch::Vector{Any}
2424

2525
function Map()
2626
return new(
2727
Union{Nothing,AbstractBridge}[],
2828
Tuple{Type,Type}[],
2929
OrderedDict{Tuple{Int64,Type},AbstractBridge}(),
30-
OrderedDict{Type,OrderedSet}(),
30+
Any[],
3131
)
3232
end
3333
end
@@ -78,6 +78,14 @@ function Base.getindex(
7878
return map.single_variable_constraints[(ci.value, S)]
7979
end
8080

81+
function _unregister_for_final_touch(b::Map, bridge)
82+
if MOI.Bridges.needs_final_touch(bridge)
83+
i = findfirst(==(bridge), b.needs_final_touch)
84+
deleteat!(b.needs_final_touch, i)
85+
end
86+
return
87+
end
88+
8189
function Base.delete!(map::Map, ci::MOI.ConstraintIndex)
8290
_unregister_for_final_touch(map, map.bridges[_index(ci)]::AbstractBridge)
8391
map.bridges[_index(ci)] = nothing
@@ -298,7 +306,6 @@ function add_key_for_bridge(
298306
::S,
299307
is_available::Function,
300308
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
301-
_register_for_final_touch(map, bridge)
302309
_ensure_available(map, F, S, is_available)
303310
push!(map.bridges, bridge)
304311
push!(map.constraint_types, (F, S))
@@ -312,39 +319,17 @@ function add_key_for_bridge(
312319
::S,
313320
::Function,
314321
) where {S<:MOI.AbstractScalarSet}
315-
_register_for_final_touch(map, bridge)
316322
map.single_variable_constraints[(func.value, S)] = bridge
317323
return MOI.ConstraintIndex{MOI.VariableIndex,S}(func.value)
318324
end
319325

320-
function _register_for_final_touch(map::Map, bridge::BT) where {BT}
321-
if MOI.Bridges.needs_final_touch(bridge)
322-
if !haskey(map.needs_final_touch, BT)
323-
map.needs_final_touch[BT] = OrderedSet{BT}()
324-
end
325-
push!(map.needs_final_touch[BT], bridge)
326-
end
327-
return
328-
end
329-
330-
function _unregister_for_final_touch(b::Map, bridge::BT) where {BT}
331-
if MOI.Bridges.needs_final_touch(bridge)
332-
delete!(b.needs_final_touch[BT], bridge)
333-
end
334-
return
335-
end
336-
337-
# Function barrier to iterate over bridges of the same type in an efficient way.
338-
function _final_touch(bridges, model)
339-
for bridge in bridges
340-
MOI.Bridges.final_touch(bridge, model)
341-
end
342-
return
343-
end
344-
345326
function MOI.Bridges.final_touch(map::Map, model::MOI.ModelLike)
346-
for bridges in values(map.needs_final_touch)
347-
_final_touch(bridges, model)
327+
# A bridge's `final_touch` may add a new bridge that needs `final_touch`,
328+
# but that's okay because it will push a new element to the end of
329+
# `map.needs_final_touch` and this iterator will keep going until it reaches
330+
# the end (because it's a `Base.Vector{Any}`).
331+
for bridge in map.needs_final_touch
332+
MOI.Bridges.final_touch(bridge, model)
348333
end
349334
return
350335
end

src/Bridges/bridge_optimizer.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1961,14 +1961,29 @@ function MOI.supports_constraint(
19611961
end
19621962

19631963
function add_bridged_constraint(b, BridgeType, f, s)
1964+
map = Constraint.bridges(b)::Constraint.Map
1965+
# Okay, this part is a bit tricky. `Constraint.bridge_constraint` might add
1966+
# new constraints which also need `final_touch`. But we need the return
1967+
# value `bridge` to come _before_ them in the `needs_final_touch` vector.
1968+
# To make this happen, we remember how many bridges are already in the
1969+
# vector, and then we insert the bridge into the vector. This is an O(N)
1970+
# operation in the number of new bridges, but it's a pretty rare edge-case,
1971+
# and the number of new bridges should be small. (And most of the time, N=0,
1972+
# because most bridges that need final touch don't add any bridges that
1973+
# themselves need final touch. Good examples are the ToMILP bridges that
1974+
# need variable bounds.)
1975+
n_final_touch = length(map.needs_final_touch)
19641976
bridge = Constraint.bridge_constraint(BridgeType, recursive_model(b), f, s)
1977+
if MOI.Bridges.needs_final_touch(bridge)
1978+
insert!(map.needs_final_touch, n_final_touch + 1, bridge)
1979+
end
19651980
# `MOI.VectorOfVariables` constraint indices have negative indices
19661981
# to distinguish between the indices of the inner model.
19671982
# However, they can clash between the indices created by the variable
19681983
# so we use the last argument to inform the constraint bridge mapping about
19691984
# indices already taken by variable bridges.
19701985
ci = Constraint.add_key_for_bridge(
1971-
Constraint.bridges(b)::Constraint.Map,
1986+
map,
19721987
bridge,
19731988
f,
19741989
s,

0 commit comments

Comments
 (0)