@@ -154,85 +154,6 @@ function _pass_attribute(
154
154
return
155
155
end
156
156
157
- """
158
- _try_constrain_variables_on_creation(
159
- dest::MOI.ModelLike,
160
- src::MOI.ModelLike,
161
- index_map::IndexMap,
162
- ::Type{S},
163
- ) where {S<:MOI.AbstractVectorSet}
164
-
165
- Copy the constraints of type `MOI.VectorOfVariables`-in-`S` from the model `src`
166
- to the model `dest` and fill `index_map` accordingly. The copy is only done when
167
- the variables to be copied are not already keys of `index_map`.
168
-
169
- It returns a list of the constraints that were not added.
170
- """
171
- function _try_constrain_variables_on_creation (
172
- dest:: MOI.ModelLike ,
173
- src:: MOI.ModelLike ,
174
- index_map:: IndexMap ,
175
- :: Type{S} ,
176
- ) where {S<: MOI.AbstractVectorSet }
177
- not_added = MOI. ConstraintIndex{MOI. VectorOfVariables,S}[]
178
- for ci_src in
179
- MOI. get (src, MOI. ListOfConstraintIndices {MOI.VectorOfVariables,S} ())
180
- f_src = MOI. get (src, MOI. ConstraintFunction (), ci_src)
181
- if ! allunique (f_src. variables)
182
- # Can't add it because there are duplicate variables
183
- push! (not_added, ci_src)
184
- elseif any (vi -> haskey (index_map, vi), f_src. variables)
185
- # Can't add it because it contains a variable previously added
186
- push! (not_added, ci_src)
187
- else
188
- set = MOI. get (src, MOI. ConstraintSet (), ci_src):: S
189
- vis_dest, ci_dest = MOI. add_constrained_variables (dest, set)
190
- index_map[ci_src] = ci_dest
191
- for (vi_src, vi_dest) in zip (f_src. variables, vis_dest)
192
- index_map[vi_src] = vi_dest
193
- end
194
- end
195
- end
196
- return not_added
197
- end
198
-
199
- """
200
- _try_constrain_variables_on_creation(
201
- dest::MOI.ModelLike,
202
- src::MOI.ModelLike,
203
- index_map::IndexMap,
204
- ::Type{S},
205
- ) where {S<:MOI.AbstractScalarSet}
206
-
207
- Copy the constraints of type `MOI.VariableIndex`-in-`S` from the model `src` to
208
- the model `dest` and fill `index_map` accordingly. The copy is only done when the
209
- variables to be copied are not already keys of `index_map`.
210
-
211
- It returns a list of the constraints that were not added.
212
- """
213
- function _try_constrain_variables_on_creation (
214
- dest:: MOI.ModelLike ,
215
- src:: MOI.ModelLike ,
216
- index_map:: IndexMap ,
217
- :: Type{S} ,
218
- ) where {S<: MOI.AbstractScalarSet }
219
- not_added = MOI. ConstraintIndex{MOI. VariableIndex,S}[]
220
- for ci_src in
221
- MOI. get (src, MOI. ListOfConstraintIndices {MOI.VariableIndex,S} ())
222
- f_src = MOI. get (src, MOI. ConstraintFunction (), ci_src)
223
- if haskey (index_map, f_src)
224
- # Can't add it because it contains a variable previously added
225
- push! (not_added, ci_src)
226
- else
227
- set = MOI. get (src, MOI. ConstraintSet (), ci_src):: S
228
- vi_dest, ci_dest = MOI. add_constrained_variable (dest, set)
229
- index_map[ci_src] = ci_dest
230
- index_map[f_src] = vi_dest
231
- end
232
- end
233
- return not_added
234
- end
235
-
236
157
"""
237
158
_copy_constraints(
238
159
dest::MOI.ModelLike,
@@ -344,22 +265,6 @@ function _pass_constraints(
344
265
return
345
266
end
346
267
347
- function _copy_free_variables (dest:: MOI.ModelLike , index_map:: IndexMap , vis_src)
348
- if length (vis_src) == length (index_map. var_map)
349
- return # All variables already added
350
- end
351
- x = MOI. add_variables (dest, length (vis_src) - length (index_map. var_map))
352
- i = 1
353
- for vi in vis_src
354
- if ! haskey (index_map, vi)
355
- index_map[vi] = x[i]
356
- i += 1
357
- end
358
- end
359
- @assert i == length (x) + 1
360
- return
361
- end
362
-
363
268
_is_variable_function (:: Type{MOI.VariableIndex} ) = true
364
269
_is_variable_function (:: Type{MOI.VectorOfVariables} ) = true
365
270
_is_variable_function (:: Any ) = false
@@ -478,25 +383,8 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
478
383
error (" Model $(typeof (dest)) does not support copy_to." )
479
384
end
480
385
MOI. empty! (dest)
481
- vis_src = MOI. get (src, MOI. ListOfVariableIndices ())
482
- index_map = IndexMap ()
483
- # The `NLPBlock` assumes that the order of variables does not change (#849)
484
- # Therefore, all VariableIndex and VectorOfVariable constraints are added
485
- # seprately, and no variables constrained-on-creation are added.
486
- has_nlp = MOI. NLPBlock () in MOI. get (src, MOI. ListOfModelAttributesSet ())
487
- constraints_not_added = if has_nlp
488
- Any[
489
- MOI. get (src, MOI. ListOfConstraintIndices {F,S} ()) for
490
- (F, S) in MOI. get (src, MOI. ListOfConstraintTypesPresent ()) if
491
- _is_variable_function (F)
492
- ]
493
- else
494
- Any[
495
- _try_constrain_variables_on_creation (dest, src, index_map, S)
496
- for S in sorted_variable_sets_by_cost (dest, src)
497
- ]
498
- end
499
- _copy_free_variables (dest, index_map, vis_src)
386
+ index_map, vis_src, constraints_not_added =
387
+ _copy_variables_with_set (dest, src)
500
388
# Copy variable attributes
501
389
pass_attributes (dest, src, index_map, vis_src)
502
390
# Copy model attributes
@@ -507,6 +395,169 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
507
395
return index_map
508
396
end
509
397
398
+ struct _CopyVariablesWithSetCache
399
+ variable_to_column:: Dict{MOI.VariableIndex,Int}
400
+ constraints_not_added:: Vector{Any}
401
+ variables_with_domain:: Set{MOI.VariableIndex}
402
+ variable_cones:: Vector{Tuple{Vector{MOI.VariableIndex},Any}}
403
+ function _CopyVariablesWithSetCache ()
404
+ return new (
405
+ Dict {MOI.VariableIndex,Int} (),
406
+ Any[],
407
+ Set {MOI.VariableIndex} (),
408
+ Tuple{Vector{MOI. VariableIndex},Any}[],
409
+ )
410
+ end
411
+ end
412
+
413
+ function _build_copy_variables_with_set_cache (
414
+ src:: MOI.ModelLike ,
415
+ cache:: _CopyVariablesWithSetCache ,
416
+ :: Type{S} ,
417
+ ) where {S<: MOI.AbstractScalarSet }
418
+ F = MOI. VariableIndex
419
+ indices = MOI. ConstraintIndex{F,S}[]
420
+ for ci in MOI. get (src, MOI. ListOfConstraintIndices {F,S} ())
421
+ x = MOI. get (src, MOI. ConstraintFunction (), ci)
422
+ if x in cache. variables_with_domain
423
+ # `x` is already assigned to a domain. Add this constraint via
424
+ # `add_constraint`.
425
+ push! (indices, ci)
426
+ else
427
+ # `x` is not assigned to a domain. Choose to add this constraint via
428
+ # `x, ci = add_constraint_variable(model, set)`
429
+ push! (cache. variables_with_domain, x)
430
+ push! (cache. variable_cones, ([x], ci))
431
+ end
432
+ end
433
+ if ! isempty (indices)
434
+ # If indices is not empty, then we have some constraints to add.
435
+ push! (cache. constraints_not_added, indices)
436
+ end
437
+ return
438
+ end
439
+
440
+ # This function is a heuristic that checks whether `f` should be added via
441
+ # `MOI.add_constrained_variables`.
442
+ function _is_variable_cone (
443
+ cache:: _CopyVariablesWithSetCache ,
444
+ f:: MOI.VectorOfVariables ,
445
+ )
446
+ if isempty (f. variables)
447
+ # If the dimension is `0`, `f` cannot be added via
448
+ # `add_constrained_variables`
449
+ return false
450
+ end
451
+ offset = cache. variable_to_column[f. variables[1 ]] - 1
452
+ for (i, xi) in enumerate (f. variables)
453
+ if xi in cache. variables_with_domain
454
+ # The function contains at least one element that is already
455
+ # assigned to a domain. We can't add `f` via
456
+ # `add_constrained_variables`
457
+ return false
458
+ elseif cache. variable_to_column[xi] != offset + i
459
+ # The variables in the function are not contiguous in their column
460
+ # ordering. In theory, we could add `f` via `add_constrained_variables`,
461
+ # but this would introduce a permutation so we choose not to.
462
+ return false
463
+ end
464
+ end
465
+ return true
466
+ end
467
+
468
+ function _build_copy_variables_with_set_cache (
469
+ src:: MOI.ModelLike ,
470
+ cache:: _CopyVariablesWithSetCache ,
471
+ :: Type{S} ,
472
+ ) where {S<: MOI.AbstractVectorSet }
473
+ F = MOI. VectorOfVariables
474
+ indices = MOI. ConstraintIndex{F,S}[]
475
+ for ci in MOI. get (src, MOI. ListOfConstraintIndices {F,S} ())
476
+ f = MOI. get (src, MOI. ConstraintFunction (), ci)
477
+ if _is_variable_cone (cache, f)
478
+ for fi in f. variables
479
+ # We need to assign each variable in `f` to a domain
480
+ push! (cache. variables_with_domain, fi)
481
+ end
482
+ # And we need to add the variables via `add_constrained_variables`.
483
+ push! (cache. variable_cones, (f. variables, ci))
484
+ else
485
+ # Not a variable cone, so add via `add_constraint`.
486
+ push! (indices, ci)
487
+ end
488
+ end
489
+ if ! isempty (indices)
490
+ # If indices is not empty, then we have some constraints to add.
491
+ push! (cache. constraints_not_added, indices)
492
+ end
493
+ return
494
+ end
495
+
496
+ function _add_variable_with_domain (
497
+ dest,
498
+ src,
499
+ index_map,
500
+ f,
501
+ ci:: MOI.ConstraintIndex{MOI.VariableIndex,<:MOI.AbstractScalarSet} ,
502
+ )
503
+ set = MOI. get (src, MOI. ConstraintSet (), ci)
504
+ dest_x, dest_ci = MOI. add_constrained_variable (dest, set)
505
+ index_map[only (f)] = dest_x
506
+ index_map[ci] = dest_ci
507
+ return
508
+ end
509
+
510
+ function _add_variable_with_domain (
511
+ dest,
512
+ src,
513
+ index_map,
514
+ f,
515
+ ci:: MOI.ConstraintIndex{MOI.VectorOfVariables,<:MOI.AbstractVectorSet} ,
516
+ )
517
+ set = MOI. get (src, MOI. ConstraintSet (), ci)
518
+ dest_x, dest_ci = MOI. add_constrained_variables (dest, set)
519
+ for (fi, xi) in zip (f, dest_x)
520
+ index_map[fi] = xi
521
+ end
522
+ index_map[ci] = dest_ci
523
+ return
524
+ end
525
+
526
+ function _copy_variables_with_set (dest, src)
527
+ index_map = IndexMap ()
528
+ vis_src = MOI. get (src, MOI. ListOfVariableIndices ())
529
+ cache = _CopyVariablesWithSetCache ()
530
+ for (i, v) in enumerate (vis_src)
531
+ cache. variable_to_column[v] = i
532
+ end
533
+ for S in sorted_variable_sets_by_cost (dest, src)
534
+ _build_copy_variables_with_set_cache (src, cache, S)
535
+ end
536
+ column (x:: MOI.VariableIndex ) = cache. variable_to_column[x]
537
+ start_column (x) = column (first (x[1 ]))
538
+ current_column = 0
539
+ sort! (cache. variable_cones; by = start_column)
540
+ for (f, ci) in cache. variable_cones
541
+ offset = column (first (f)) - current_column - 1
542
+ if offset > 0
543
+ dest_x = MOI. add_variables (dest, offset)
544
+ for i in 1 : offset
545
+ index_map[vis_src[current_column+ i]] = dest_x[i]
546
+ end
547
+ end
548
+ _add_variable_with_domain (dest, src, index_map, f, ci)
549
+ current_column = column (last (f))
550
+ end
551
+ offset = length (cache. variable_to_column) - current_column
552
+ if offset > 0
553
+ dest_x = MOI. add_variables (dest, offset)
554
+ for i in 1 : offset
555
+ index_map[vis_src[current_column+ i]] = dest_x[i]
556
+ end
557
+ end
558
+ return index_map, vis_src, cache. constraints_not_added
559
+ end
560
+
510
561
"""
511
562
ModelFilter(filter::Function, model::MOI.ModelLike)
512
563
0 commit comments