Skip to content

Commit 23578ac

Browse files
committed
Add final_touch for bridges
1 parent b760048 commit 23578ac

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

src/Bridges/Constraint/Constraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Constraint
88

99
import LinearAlgebra
1010
import MathOptInterface
11-
import OrderedCollections: OrderedDict
11+
import OrderedCollections: OrderedDict, OrderedSet
1212
import SparseArrays
1313

1414
const MOI = MathOptInterface

src/Bridges/Constraint/map.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +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}
2324

2425
function Map()
2526
return new(
2627
Union{Nothing,AbstractBridge}[],
2728
Tuple{Type,Type}[],
2829
OrderedDict{Tuple{Int64,Type},AbstractBridge}(),
30+
OrderedDict{Type,OrderedSet}(),
2931
)
3032
end
3133
end
@@ -48,6 +50,7 @@ function Base.empty!(map::Map)
4850
empty!(map.bridges)
4951
empty!(map.constraint_types)
5052
empty!(map.single_variable_constraints)
53+
empty!(map.needs_final_touch)
5154
return map
5255
end
5356

@@ -83,6 +86,7 @@ function Base.getindex(
8386
end
8487

8588
function Base.delete!(map::Map, ci::MOI.ConstraintIndex)
89+
_unregister_for_final_touch(map, map.bridges[_index(ci)])
8690
map.bridges[_index(ci)] = nothing
8791
return map
8892
end
@@ -91,6 +95,8 @@ function Base.delete!(
9195
map::Map,
9296
ci::MOI.ConstraintIndex{MOI.VariableIndex,S},
9397
) where {S}
98+
bridge = map.single_variable_constraints[(ci.value, S)]
99+
_unregister_for_final_touch(map, bridge)
94100
delete!(map.single_variable_constraints, (ci.value, S))
95101
return map
96102
end
@@ -278,6 +284,7 @@ function add_key_for_bridge(
278284
::F,
279285
::S,
280286
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
287+
_register_for_final_touch(map, bridge)
281288
push!(map.bridges, bridge)
282289
push!(map.constraint_types, (F, S))
283290
return _index(length(map.bridges), F, S)
@@ -289,10 +296,40 @@ function add_key_for_bridge(
289296
func::MOI.VariableIndex,
290297
::S,
291298
) where {S<:MOI.AbstractScalarSet}
299+
_register_for_final_touch(map, bridge)
292300
map.single_variable_constraints[(func.value, S)] = bridge
293301
return MOI.ConstraintIndex{MOI.VariableIndex,S}(func.value)
294302
end
295303

304+
function _register_for_final_touch(map::Map, bridge)
305+
if MOI.Bridges.needs_final_touch(bridge)
306+
if !haskey(map.needs_final_touch, typeof(bridge))
307+
map.needs_final_touch[typeof(bridge)] = OrderedSet{typeof(bridge)}()
308+
end
309+
push!(map.needs_final_touch[typeof(bridge)], bridge)
310+
end
311+
return
312+
end
313+
function _unregister_for_final_touch(b::Map, bridge)
314+
if MOI.Bridges.needs_final_touch(bridge)
315+
delete!(b.needs_final_touch[typeof(bridge)], bridge)
316+
end
317+
return
318+
end
319+
320+
# function barrier to iterate over bridges of the same type in an efficient way
321+
function _final_touch(bridges)
322+
for bridge in bridges
323+
MOI.Utilities.final_touch(bridge)
324+
end
325+
end
326+
327+
function MOI.Utilities.final_touch(map::Map)
328+
for bridges in values(map.needs_final_touch)
329+
_final_touch(bridges)
330+
end
331+
end
332+
296333
"""
297334
EmptyMap <: AbstractDict{MOI.ConstraintIndex, AbstractBridge}
298335
@@ -315,3 +352,5 @@ Base.values(::EmptyMap) = MOI.Utilities.EmptyVector{AbstractBridge}()
315352
has_bridges(::EmptyMap) = false
316353

317354
number_of_type(::EmptyMap, ::Type{<:MOI.ConstraintIndex}) = 0
355+
356+
MOI.Utilities.final_touch(::EmptyMap) = nothing

src/Bridges/bridge.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,14 @@ MathOptInterface.ScalarAffineFunction{Float64}
253253
```
254254
"""
255255
function set_objective_function_type end
256+
257+
"""
258+
needs_final_touch(bridge::AbstractBridge)
259+
260+
Return whether [`Utilities.final_touch`](@ref) needs to be called on `bridge`.
261+
For instance, if the correctness of `bridge` depends on bounds of variable
262+
or the fact that variables are integer, then the bridging is only valid
263+
if these do not change. In that case, the bridge can check in `final_touch`
264+
that they did not change and change the bridging in case it did.
265+
"""
266+
needs_final_touch(::AbstractBridge) = false

src/Bridges/bridge_optimizer.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,11 @@ end
374374
# Implementation of the MOI interface for AbstractBridgeOptimizer
375375

376376
# By convention, the model should be stored in a `model` field
377-
MOI.optimize!(b::AbstractBridgeOptimizer) = MOI.optimize!(b.model)
377+
function MOI.optimize!(b::AbstractBridgeOptimizer)
378+
MOI.Utilities.final_touch(Constraint.bridges(b))
379+
MOI.optimize!(b.model)
380+
return
381+
end
378382

379383
function MOI.is_empty(b::AbstractBridgeOptimizer)
380384
return isempty(Variable.bridges(b)) &&
@@ -454,6 +458,7 @@ function MOI.supports_incremental_interface(b::AbstractBridgeOptimizer)
454458
return MOI.supports_incremental_interface(b.model)
455459
end
456460
function MOI.Utilities.final_touch(uf::AbstractBridgeOptimizer, index_map)
461+
MOI.Utilities.final_touch(Constraint.bridges(b))
457462
return MOI.Utilities.final_touch(uf.model, index_map)
458463
end
459464

0 commit comments

Comments
 (0)