-
Notifications
You must be signed in to change notification settings - Fork 99
Expand file tree
/
Copy pathvector_of_constraints.jl
More file actions
332 lines (295 loc) · 9.17 KB
/
vector_of_constraints.jl
File metadata and controls
332 lines (295 loc) · 9.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# Copyright (c) 2017: Miles Lubin and contributors
# Copyright (c) 2017: Google Inc.
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
## Storage of constraints
#
# All `F`-in-`S` constraints are stored in a vector of `ConstraintEntry{F, S}`.
# The index in this vector of a constraint of index
# `ci::MOI.ConstraintIndex{F, S}` is given by `model.constrmap[ci.value]`. The
# advantage of this representation is that it does not require any dictionary
# hence it never needs to compute a hash.
#
# It may seem redundant to store the constraint index `ci` as well as the
# function and sets in the tuple but it is used to efficiently implement the
# getter for `MOI.ListOfConstraintIndices{F, S}`. It is also used to implement
# `MOI.delete`. Indeed, when a constraint is deleted, it is removed from the
# vector hence the index in the vector of all the functions that were stored
# after must be decreased by one. As the constraint index is stored in the
# vector, it readily gives the entries of `model.constrmap` that need to be
# updated.
"""
mutable struct VectorOfConstraints{
F<:MOI.AbstractFunction,
S<:MOI.AbstractSet,
} <: MOI.ModelLike
constraints::CleverDicts.CleverDict{
MOI.ConstraintIndex{F,S},
Tuple{F,S},
typeof(CleverDicts.key_to_index),
typeof(CleverDicts.index_to_key),
}
end
A struct storing `F`-in-`S` constraints as a mapping between the constraint
indices to the corresponding tuple of function and set.
"""
mutable struct VectorOfConstraints{
F<:MOI.AbstractFunction,
S<:MOI.AbstractSet,
} <: MOI.ModelLike
constraints::CleverDicts.CleverDict{
MOI.ConstraintIndex{F,S},
Tuple{F,S},
typeof(CleverDicts.key_to_index),
typeof(CleverDicts.index_to_key),
}
function VectorOfConstraints{F,S}() where {F,S}
return new{F,S}(
CleverDicts.CleverDict{MOI.ConstraintIndex{F,S},Tuple{F,S}}(),
)
end
end
MOI.is_empty(v::VectorOfConstraints) = isempty(v.constraints)
MOI.empty!(v::VectorOfConstraints) = empty!(v.constraints)
function MOI.supports_constraint(
::VectorOfConstraints{F,S},
::Type{F},
::Type{S},
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
return true
end
function MOI.add_constraint(
v::VectorOfConstraints{F,S},
func::F,
set::S,
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
# We canonicalize the constraint so that solvers can avoid having to
# canonicalize it most of the time (they can check if they need to with
# `is_canonical`.
# Note that the canonicalization is not guaranteed if for instance
# `modify` is called and adds a new term.
# See https://github.com/jump-dev/MathOptInterface.jl/pull/1118
return CleverDicts.add_item(v.constraints, (canonical(func), copy(set)))
end
function MOI.is_valid(
v::VectorOfConstraints{F,S},
ci::MOI.ConstraintIndex{F,S},
) where {F,S}
return haskey(v.constraints, ci)
end
function MOI.delete(
v::VectorOfConstraints{F,S},
ci::MOI.ConstraintIndex{F,S},
) where {F,S}
MOI.throw_if_not_valid(v, ci)
delete!(v.constraints, ci)
return
end
function MOI.get(
v::VectorOfConstraints{F,S},
::UnsafeConstraintFunction,
ci::MOI.ConstraintIndex{F,S},
) where {F,S}
MOI.throw_if_not_valid(v, ci)
f, _ = v.constraints[ci]::Tuple{F,S}
return f
end
function MOI.get(
v::VectorOfConstraints{F,S},
::MOI.ConstraintFunction,
ci::MOI.ConstraintIndex{F,S},
) where {F,S}
return copy(MOI.get(v, UnsafeConstraintFunction(), ci))
end
function MOI.get(
v::VectorOfConstraints{F,S},
::MOI.ConstraintSet,
ci::MOI.ConstraintIndex{F,S},
) where {F,S}
MOI.throw_if_not_valid(v, ci)
_, s = v.constraints[ci]::Tuple{F,S}
return s
end
function MOI.set(
v::VectorOfConstraints{F,S},
::MOI.ConstraintFunction,
ci::MOI.ConstraintIndex{F,S},
func::F,
) where {F,S}
MOI.throw_if_not_valid(v, ci)
_, s = v.constraints[ci]::Tuple{F,S}
v.constraints[ci] = (func, s)
return
end
function MOI.set(
v::VectorOfConstraints{F,S},
::MOI.ConstraintSet,
ci::MOI.ConstraintIndex{F,S},
set::S,
) where {F,S}
MOI.throw_if_not_valid(v, ci)
f, _ = v.constraints[ci]::Tuple{F,S}
v.constraints[ci] = (f, set)
return
end
function MOI.get(
v::VectorOfConstraints{F,S},
::MOI.ListOfConstraintTypesPresent,
)::Vector{Tuple{Type,Type}} where {F,S}
return isempty(v.constraints) ? [] : [(F, S)]
end
function MOI.get(
v::VectorOfConstraints{F,S},
::MOI.NumberOfConstraints{F,S},
)::Int64 where {F,S}
return length(v.constraints)
end
function MOI.get(
v::VectorOfConstraints{F,S},
::MOI.ListOfConstraintIndices{F,S},
) where {F,S}
return collect(keys(v.constraints))
end
function MOI.get(
::VectorOfConstraints{F,S},
::MOI.ListOfConstraintAttributesSet{F,S},
) where {F,S}
return MOI.AbstractConstraintAttribute[]
end
function MOI.modify(
v::VectorOfConstraints{F,S},
ci::MOI.ConstraintIndex{F,S},
change::MOI.AbstractFunctionModification,
) where {F,S}
func, set = v.constraints[ci]::Tuple{F,S}
v.constraints[ci] = (modify_function!(func, change), set)
return
end
function _add_variable(::VectorOfConstraints) end
function _add_variables(::VectorOfConstraints, ::Int64) end
# Deletion of variables in vector of variables
"""
remove_variable(
f::MOI.AbstractFunction,
s::MOI.AbstractSet,
vi::MOI.VariableIndex,
)
Return a tuple `(g, t)` representing the constraint `f`-in-`s` with the
variable `vi` removed. That is, the terms containing the variable `vi` in the
function `f` are removed and the dimension of the set `s` is updated if
needed (for example, when `f` is a `VectorOfVariables` with `vi` being one of the
variables).
"""
remove_variable(f, s, vi::MOI.VariableIndex) = remove_variable(f, vi), s
function remove_variable(f::MOI.VectorOfVariables, s, vi::MOI.VariableIndex)
g = remove_variable(f, vi)
if length(g.variables) != length(f.variables)
return g, MOI.update_dimension(s, length(g.variables))
end
return g, s
end
function _remove_variable(v::VectorOfConstraints, vi::MOI.VariableIndex)
CleverDicts.map_values!(v.constraints) do (f, s)
return remove_variable(f, s, vi)
end
return
end
function filter_variables(keep::F, f, s) where {F<:Function}
return filter_variables(keep, f), s
end
function filter_variables(
keep::F,
f::MOI.VectorOfVariables,
s,
) where {F<:Function}
g = filter_variables(keep, f)
if length(g.variables) != length(f.variables)
return g, MOI.update_dimension(s, length(g.variables))
end
return g, s
end
function _filter_variables(keep::Function, v::VectorOfConstraints)
CleverDicts.map_values!(v.constraints) do (f, s)
return filter_variables(keep, f, s)
end
return
end
function throw_delete_variable_in_vov(vi::MOI.VariableIndex)
message = string(
"Cannot delete variable as it is constrained with other",
" variables in a `MOI.VectorOfVariables`.",
)
return throw(MOI.DeleteNotAllowed(vi, message))
end
# Nothing to do as it's not `VectorOfVariables` constraints
_throw_if_cannot_delete(::VectorOfConstraints, vis, fast_in_vis) = nothing
_fast_in(vi1::MOI.VariableIndex, vi2::MOI.VariableIndex) = vi1 == vi2
_fast_in(vi::MOI.VariableIndex, vis::Set{MOI.VariableIndex}) = vi in vis
function _throw_if_cannot_delete(
v::VectorOfConstraints{MOI.VectorOfVariables,S},
vis,
fast_in_vis,
) where {S<:MOI.AbstractVectorSet}
if MOI.supports_dimension_update(S) || MOI.is_empty(v)
return
end
for fs in values(v.constraints)
f = fs[1]::MOI.VectorOfVariables
if length(f.variables) > 1 && f.variables != vis
for vi in f.variables
if _fast_in(vi, fast_in_vis)
# If `supports_dimension_update(S)` then the variable
# will be removed in `_filter_variables`.
throw_delete_variable_in_vov(vi)
end
end
end
end
return
end
function _delete_variables(
::Function,
::VectorOfConstraints,
::Vector{MOI.VariableIndex},
)
return # Nothing to do as it's not `VectorOfVariables` constraints
end
function _delete_variables(
callback::Function,
v::VectorOfConstraints{MOI.VectorOfVariables,<:MOI.AbstractVectorSet},
vis::Vector{MOI.VariableIndex},
)
filter!(v.constraints) do p
f = p.second[1]
del = if length(f.variables) == 1
first(f.variables) in vis
else
vis == f.variables
end
if del
callback(p.first)
end
return !del
end
return
end
function _deleted_constraints(
callback::Function,
v::VectorOfConstraints,
vi::MOI.VariableIndex,
)
_delete_variables(callback, v, [vi])
_remove_variable(v, vi)
return
end
function _deleted_constraints(
callback::Function,
v::VectorOfConstraints,
vis::Vector{MOI.VariableIndex},
)
_delete_variables(callback, v, vis)
removed = Set(vis)
_filter_variables(vi -> !(vi in removed), v)
return
end