@@ -154,85 +154,6 @@ function _pass_attribute(
154154 return
155155end
156156
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-
236157"""
237158 _copy_constraints(
238159 dest::MOI.ModelLike,
@@ -344,22 +265,6 @@ function _pass_constraints(
344265 return
345266end
346267
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-
363268_is_variable_function (:: Type{MOI.VariableIndex} ) = true
364269_is_variable_function (:: Type{MOI.VectorOfVariables} ) = true
365270_is_variable_function (:: Any ) = false
@@ -478,25 +383,8 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
478383 error (" Model $(typeof (dest)) does not support copy_to." )
479384 end
480385 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)
500388 # Copy variable attributes
501389 pass_attributes (dest, src, index_map, vis_src)
502390 # Copy model attributes
@@ -507,6 +395,169 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
507395 return index_map
508396end
509397
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+
510561"""
511562 ModelFilter(filter::Function, model::MOI.ModelLike)
512563
0 commit comments