Skip to content

Commit 3745059

Browse files
committed
[Utilities] maintain order of variables in default_copy_to
1 parent 08d3400 commit 3745059

File tree

1 file changed

+115
-19
lines changed

1 file changed

+115
-19
lines changed

src/Utilities/copy.jl

Lines changed: 115 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -478,25 +478,8 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
478478
error("Model $(typeof(dest)) does not support copy_to.")
479479
end
480480
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)
481+
index_map, vis_src, constraints_not_added =
482+
_copy_variables_with_set(dest, src)
500483
# Copy variable attributes
501484
pass_attributes(dest, src, index_map, vis_src)
502485
# Copy model attributes
@@ -507,6 +490,119 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
507490
return index_map
508491
end
509492

493+
struct _CopyVariablesWithSetCache
494+
variable_to_column::Dict{MOI.VariableIndex,Int}
495+
constraints_not_added::Vector{Any}
496+
variables_with_domain::Set{MOI.VariableIndex}
497+
variable_cones::Vector{Tuple{Vector{MOI.VariableIndex},Any}}
498+
function _CopyVariablesWithSetCache()
499+
return new(
500+
Dict{MOI.VariableIndex,Int}(),
501+
Any[],
502+
Set{MOI.VariableIndex}(),
503+
Tuple{Vector{MOI.VariableIndex},Any}[]
504+
)
505+
end
506+
end
507+
508+
function _build_copy_variables_with_set_cache(
509+
src::MOI.ModelLike,
510+
cache::_CopyVariablesWithSetCache,
511+
::Type{S},
512+
) where {S<:MOI.AbstractScalarSet}
513+
F = MOI.VariableIndex
514+
for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
515+
f = MOI.get(src, MOI.ConstraintFunction(), ci)
516+
if f in cache.variables_with_domain
517+
push!(cache.constraints_not_added, ci)
518+
else
519+
push!(cache.variables_with_domain, f)
520+
push!(cache.variable_cones, ([f], ci))
521+
end
522+
end
523+
return
524+
end
525+
526+
function _is_variable_cone(cache, f::MOI.VectorOfVariables)
527+
if isempty(f.variables)
528+
return false
529+
end
530+
offset = cache.variable_to_column[f.variables[1]] - 1
531+
for (i, xi) in enumerate(f.variables)
532+
if xi in cache.variables_with_domain
533+
return false
534+
elseif cache.variable_to_column[xi] != offset + i
535+
return false
536+
end
537+
end
538+
return true
539+
end
540+
541+
function _build_copy_variables_with_set_cache(
542+
src::MOI.ModelLike,
543+
cache::_CopyVariablesWithSetCache,
544+
::Type{S},
545+
) where {S<:MOI.AbstractVectorSet}
546+
F = MOI.VectorOfVariables
547+
for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
548+
f = MOI.get(src, MOI.ConstraintFunction())
549+
if _is_variable_cone(cache, f)
550+
for fi in f.variables
551+
push!(cache.variables_with_domain, fi)
552+
end
553+
push!(cache.variable_cones, (f.variables, ci))
554+
else
555+
push!(cache.constraints_not_added, ci)
556+
end
557+
end
558+
return
559+
end
560+
561+
function _copy_variables_with_set(dest, src)
562+
vis_src = MOI.get(src, MOI.ListOfVariableIndices())
563+
index_map = IndexMap()
564+
cache = _CopyVariablesWithSetCache()
565+
for (i, v) in enumerate(vis_src)
566+
cache.variable_to_column[v] = i
567+
end
568+
for S in sorted_variable_sets_by_cost(dest, src)
569+
_build_copy_variables_with_set_cache(src, cache, S)
570+
end
571+
column(x::MOI.VariableIndex) = cache.variable_to_column[x]
572+
start_column(x) = column(first(x[1]))
573+
current_column = 0
574+
for (f, ci) in sort!(cache.variable_cones; by = start_column)
575+
offset = column(first(f)) - current_column - 1
576+
if offset > 0
577+
dest_x = MOI.add_variables(dest, offset)
578+
for i in 1:offset
579+
index_map[vis_src[current_column + i]] = dest_x[i]
580+
end
581+
end
582+
set = MOI.get(src, MOI.ConstraintSet(), ci)
583+
if set isa MOI.AbstractScalarSet
584+
dest_x, dest_ci = MOI.add_constrained_variable(dest, set)
585+
index_map[only(f)] = dest_x
586+
index_map[ci] = dest_ci
587+
else
588+
dest_x, dest_ci = MOI.add_constrained_variables(dest, set)
589+
for (fi, xi) in zip(f, dest_x)
590+
index_map[fi] = xi
591+
end
592+
index_map[ci] = dest_ci
593+
end
594+
current_column = column(last(f))
595+
end
596+
offset = length(cache.variable_to_column) - current_column
597+
if offset > 0
598+
dest_x = MOI.add_variables(dest, offset)
599+
for i in 1:offset
600+
index_map[vis_src[current_column + i]] = dest_x[i]
601+
end
602+
end
603+
return index_map, vis_src, cache.constraints_not_added
604+
end
605+
510606
"""
511607
ModelFilter(filter::Function, model::MOI.ModelLike)
512608

0 commit comments

Comments
 (0)