@@ -513,29 +513,47 @@ function _build_copy_variables_with_set_cache(
513513 F = MOI. VariableIndex
514514 indices = MOI. ConstraintIndex{F,S}[]
515515 for ci in MOI. get (src, MOI. ListOfConstraintIndices {F,S} ())
516- f = MOI. get (src, MOI. ConstraintFunction (), ci)
517- if f in cache. variables_with_domain
516+ x = MOI. get (src, MOI. ConstraintFunction (), ci)
517+ if x in cache. variables_with_domain
518+ # `x` is already assigned to a domain. Add this constraint via
519+ # `add_constraint`.
518520 push! (indices, ci)
519521 else
520- push! (cache. variables_with_domain, f)
521- push! (cache. variable_cones, ([f], ci))
522+ # `x` is not assigned to a domain. Choose to add this constraint via
523+ # `x, ci = add_constraint_variable(model, set)`
524+ push! (cache. variables_with_domain, x)
525+ push! (cache. variable_cones, ([x], ci))
522526 end
523527 end
524528 if ! isempty (indices)
529+ # If indices is not empty, then we have some constraints to add.
525530 push! (cache. constraints_not_added, indices)
526531 end
527532 return
528533end
529534
530- function _is_variable_cone (cache, f:: MOI.VectorOfVariables )
535+ # This function is a heuristic that checks whether `f` should be added via
536+ # `MOI.add_constrained_variables`.
537+ function _is_variable_cone (
538+ cache:: _CopyVariablesWithSetCache ,
539+ f:: MOI.VectorOfVariables ,
540+ )
531541 if isempty (f. variables)
542+ # If the dimension is `0`, `f` cannot be added via
543+ # `add_constrained_variables`
532544 return false
533545 end
534546 offset = cache. variable_to_column[f. variables[1 ]] - 1
535547 for (i, xi) in enumerate (f. variables)
536548 if xi in cache. variables_with_domain
549+ # The function contains at least one element that is already
550+ # assigned to a domain. We can't add `f` via
551+ # `add_constrained_variables`
537552 return false
538553 elseif cache. variable_to_column[xi] != offset + i
554+ # The variables in the function are not contiguous in their column
555+ # ordering. In theory, we could add `f` via `add_constrained_variables`,
556+ # but this would introduce a permutation so we choose not to.
539557 return false
540558 end
541559 end
@@ -553,19 +571,53 @@ function _build_copy_variables_with_set_cache(
553571 f = MOI. get (src, MOI. ConstraintFunction (), ci)
554572 if _is_variable_cone (cache, f)
555573 for fi in f. variables
574+ # We need to assign each variable in `f` to a domain
556575 push! (cache. variables_with_domain, fi)
557576 end
577+ # And we need to add the variables via `add_constrained_variables`.
558578 push! (cache. variable_cones, (f. variables, ci))
559579 else
580+ # Not a variable cone, so add via `add_constraint`.
560581 push! (indices, ci)
561582 end
562583 end
563584 if ! isempty (indices)
585+ # If indices is not empty, then we have some constraints to add.
564586 push! (cache. constraints_not_added, indices)
565587 end
566588 return
567589end
568590
591+ function _add_variable_with_domain (
592+ dest,
593+ src,
594+ index_map,
595+ f,
596+ ci:: MOI.ConstraintIndex{MOI.VariableIndex,<:MOI.AbstractScalarSet} ,
597+ )
598+ set = MOI. get (src, MOI. ConstraintSet (), ci)
599+ dest_x, dest_ci = MOI. add_constrained_variable (dest, set)
600+ index_map[only (f)] = dest_x
601+ index_map[ci] = dest_ci
602+ return
603+ end
604+
605+ function _add_variable_with_domain (
606+ dest,
607+ src,
608+ index_map,
609+ f,
610+ ci:: MOI.ConstraintIndex{MOI.VectorOfVariables,<:MOI.AbstractVectorSet} ,
611+ )
612+ set = MOI. get (src, MOI. ConstraintSet (), ci)
613+ dest_x, dest_ci = MOI. add_constrained_variables (dest, set)
614+ for (fi, xi) in zip (f, dest_x)
615+ index_map[fi] = xi
616+ end
617+ index_map[ci] = dest_ci
618+ return
619+ end
620+
569621function _copy_variables_with_set (dest, src)
570622 index_map = IndexMap ()
571623 vis_src = MOI. get (src, MOI. ListOfVariableIndices ())
@@ -579,26 +631,16 @@ function _copy_variables_with_set(dest, src)
579631 column (x:: MOI.VariableIndex ) = cache. variable_to_column[x]
580632 start_column (x) = column (first (x[1 ]))
581633 current_column = 0
582- for (f, ci) in sort! (cache. variable_cones; by = start_column)
634+ sort! (cache. variable_cones; by = start_column)
635+ for (f, ci) in cache. variable_cones
583636 offset = column (first (f)) - current_column - 1
584637 if offset > 0
585638 dest_x = MOI. add_variables (dest, offset)
586639 for i in 1 : offset
587640 index_map[vis_src[current_column+ i]] = dest_x[i]
588641 end
589642 end
590- set = MOI. get (src, MOI. ConstraintSet (), ci)
591- if set isa MOI. AbstractScalarSet
592- dest_x, dest_ci = MOI. add_constrained_variable (dest, set)
593- index_map[only (f)] = dest_x
594- index_map[ci] = dest_ci
595- else
596- dest_x, dest_ci = MOI. add_constrained_variables (dest, set)
597- for (fi, xi) in zip (f, dest_x)
598- index_map[fi] = xi
599- end
600- index_map[ci] = dest_ci
601- end
643+ _add_variable_with_domain (dest, src, index_map, f, ci)
602644 current_column = column (last (f))
603645 end
604646 offset = length (cache. variable_to_column) - current_column
0 commit comments