Skip to content

Commit 3f872f9

Browse files
authored
Merge pull request #1088 from Wikunia/deterministic-constraint-ordering
deterministic ordering of constraints -> variable indices
2 parents e6610e6 + ee98c4b commit 3f872f9

3 files changed

Lines changed: 35 additions & 12 deletions

File tree

src/Utilities/Utilities.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Utilities
22

33
using LinearAlgebra # For dot
4+
using OrderedCollections # for OrderedDict in UniversalFallback
45

56
using MathOptInterface
67
const MOI = MathOptInterface

src/Utilities/universalfallback.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
The `UniversalFallback` can be applied on a [`MathOptInterface.ModelLike`](@ref)
55
`model` to create the model `UniversalFallback(model)` supporting *any*
6-
constaint and attribute. This allows to have a specialized implementation in
6+
constraint and attribute. This allows to have a specialized implementation in
77
`model` for performance critical constraints and attributes while still
88
supporting other attributes with a small performance penalty. Note that `model`
99
is unaware of constraints and attributes stored by `UniversalFallback` so this
@@ -13,7 +13,7 @@ optimizer bridges should be used instead.
1313
"""
1414
mutable struct UniversalFallback{MT} <: MOI.ModelLike
1515
model::MT
16-
constraints::Dict{Tuple{DataType, DataType}, Dict} # See https://github.com/JuliaOpt/JuMP.jl/issues/1152
16+
constraints::OrderedDict{Tuple{DataType, DataType}, OrderedDict} # See https://github.com/JuliaOpt/JuMP.jl/issues/1152 and https://github.com/JuliaOpt/JuMP.jl/issues/2238
1717
nextconstraintid::Int64
1818
con_to_name::Dict{CI, String}
1919
name_to_con::Union{Dict{String, MOI.ConstraintIndex}, Nothing}
@@ -23,7 +23,7 @@ mutable struct UniversalFallback{MT} <: MOI.ModelLike
2323
conattr::Dict{MOI.AbstractConstraintAttribute, Dict{CI, Any}}
2424
function UniversalFallback{MT}(model::MOI.ModelLike) where {MT}
2525
new{typeof(model)}(model,
26-
Dict{Tuple{DataType, DataType}, Dict}(),
26+
OrderedDict{Tuple{DataType, DataType}, OrderedDict}(),
2727
0,
2828
Dict{CI, String}(),
2929
nothing,
@@ -99,7 +99,7 @@ function MOI.delete(uf::UniversalFallback, ci::CI{F, S}) where {F, S}
9999
end
100100
end
101101
function _remove_variable(uf::UniversalFallback,
102-
constraints::Dict{<:CI{MOI.SingleVariable}}, vi::VI)
102+
constraints::OrderedDict{<:CI{MOI.SingleVariable}}, vi::VI)
103103
to_delete = keytype(constraints)[]
104104
for (ci, constraint) in constraints
105105
f::MOI.SingleVariable = constraint[1]
@@ -110,7 +110,7 @@ function _remove_variable(uf::UniversalFallback,
110110
MOI.delete(uf, to_delete)
111111
end
112112
function _remove_variable(uf::UniversalFallback,
113-
constraints::Dict{CI{MOI.VectorOfVariables, S}},
113+
constraints::OrderedDict{CI{MOI.VectorOfVariables, S}},
114114
vi::VI) where S
115115
to_delete = keytype(constraints)[]
116116
for (ci, constraint) in constraints
@@ -129,14 +129,14 @@ function _remove_variable(uf::UniversalFallback,
129129
end
130130
MOI.delete(uf, to_delete)
131131
end
132-
function _remove_variable(::UniversalFallback, constraints::Dict{<:CI}, vi::VI)
132+
function _remove_variable(::UniversalFallback, constraints::OrderedDict{<:CI}, vi::VI)
133133
for (ci, constraint) in constraints
134134
f, s = constraint
135135
constraints[ci] = remove_variable(f, s, vi)
136136
end
137137
end
138138
function _remove_vector_of_variables(
139-
uf::UniversalFallback, constraints::Dict{<:CI{MOI.VectorOfVariables}},
139+
uf::UniversalFallback, constraints::OrderedDict{<:CI{MOI.VectorOfVariables}},
140140
vis::Vector{VI}
141141
)
142142
to_delete = keytype(constraints)[]
@@ -149,7 +149,7 @@ function _remove_vector_of_variables(
149149
MOI.delete(uf, to_delete)
150150
end
151151
function _remove_vector_of_variables(
152-
::UniversalFallback, ::Dict{<:CI}, ::Vector{VI})
152+
::UniversalFallback, ::OrderedDict{<:CI}, ::Vector{VI})
153153
end
154154
function MOI.delete(uf::UniversalFallback, vi::VI)
155155
MOI.delete(uf.model, vi)
@@ -219,15 +219,15 @@ function MOI.get(uf::UniversalFallback,
219219
if MOI.supports_constraint(uf.model, F, S)
220220
return MOI.get(uf.model, attr)
221221
else
222-
return length(get(uf.constraints, (F, S), Dict{CI{F, S}, Tuple{F, S}}()))
222+
return length(get(uf.constraints, (F, S), OrderedDict{CI{F, S}, Tuple{F, S}}()))
223223
end
224224
end
225225
function MOI.get(uf::UniversalFallback,
226226
listattr::MOI.ListOfConstraintIndices{F, S}) where {F, S}
227227
if MOI.supports_constraint(uf.model, F, S)
228228
MOI.get(uf.model, listattr)
229229
else
230-
collect(keys(get(uf.constraints, (F, S), Dict{CI{F, S}, Tuple{F, S}}())))
230+
collect(keys(get(uf.constraints, (F, S), OrderedDict{CI{F, S}, Tuple{F, S}}())))
231231
end
232232
end
233233
function MOI.get(uf::UniversalFallback, listattr::MOI.ListOfConstraints)
@@ -376,8 +376,8 @@ function MOI.add_constraint(uf::UniversalFallback, f::MOI.AbstractFunction, s::M
376376
return MOI.add_constraint(uf.model, f, s)
377377
else
378378
constraints = get!(uf.constraints, (F, S)) do
379-
Dict{CI{F, S}, Tuple{F, S}}()
380-
end::Dict{CI{F, S}, Tuple{F, S}}
379+
OrderedDict{CI{F, S}, Tuple{F, S}}()
380+
end::OrderedDict{CI{F, S}, Tuple{F, S}}
381381
ci = _new_constraint_index(uf, f, s)
382382
constraints[ci] = (f, s)
383383
return ci

test/Utilities/universalfallback.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,28 @@ end
205205
@test_throws Exception MOI.get(uf, MOI.ConstraintIndex, "a name")
206206
end
207207

208+
@testset "Deterministic constraint ordering" begin
209+
F = MOI.ScalarAffineFunction{Float64}
210+
_affine(vi) = convert(F, MOI.SingleVariable(vi))
211+
set1 = MOI.EqualTo(1.0)
212+
set2 = MOI.GreaterThan(1.0)
213+
for sets in [[set1, set2], [set2, set1]]
214+
model = ModelForUniversalFallback{Float64}()
215+
uf = MOIU.UniversalFallback(model)
216+
x = MOI.add_variable(uf)
217+
y = MOI.add_variable(uf)
218+
cx1 = MOI.add_constraint(uf, _affine(x), sets[1])
219+
cx2 = MOI.add_constraint(uf, _affine(x), sets[2])
220+
cy1 = MOI.add_constraint(uf, _affine(y), sets[1])
221+
cy2 = MOI.add_constraint(uf, _affine(y), sets[2])
222+
# check that the constraint types are in the order they were added in
223+
@test MOI.get(uf, MOI.ListOfConstraints()) == [(F, typeof(sets[1])), (F, typeof(sets[2]))]
224+
# check that the constraints given the constraint type are in the order they were added in
225+
@test MOI.get(uf, MOI.ListOfConstraintIndices{F, typeof(sets[1])}()) == [MOI.ConstraintIndex{F, typeof(sets[1])}(1), MOI.ConstraintIndex{F, typeof(sets[1])}(3)]
226+
@test MOI.get(uf, MOI.ListOfConstraintIndices{F, typeof(sets[2])}()) == [MOI.ConstraintIndex{F, typeof(sets[2])}(2), MOI.ConstraintIndex{F, typeof(sets[2])}(4)]
227+
end
228+
end
229+
208230
@testset "Delete" begin
209231
model = ModelForUniversalFallback{Float64}()
210232
uf = MOIU.UniversalFallback(model)

0 commit comments

Comments
 (0)