Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Utilities/copy/index_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,20 @@ Base.length(map::IndexMap) = length(map.var_map) + length(map.con_map)
function Base.iterate(map::IndexMap, args...)
return iterate(Base.Iterators.flatten((map.var_map, map.con_map)), args...)
end

# Objects that may contain `MOI.VariableIndex` but no `MOI.ConstraintIndex`.
const _OnlyVariableIndices = Union{
MOI.VariableIndex,
MOI.AbstractFunction,
MOI.AbstractFunctionModification,
}

# The variable map is often the identity, for example, when the destination
# model of `copy_to` creates its variables in the same order as the source
# model. In that case, there is no need to map the variables of `x`.
function map_indices(map::IndexMap, x::X)::X where {X<:_OnlyVariableIndices}
if CleverDicts.is_identity(map.var_map)
return x
end
return map_indices(Base.Fix1(getindex, map), x)
end
6 changes: 6 additions & 0 deletions src/Utilities/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ map_indices(::Any, ::MOI.RawOptimizerAttribute, x) = x
)::X where {T<:MOI.Index,X}

Shortcut for `map_indices(vi -> variable_map[vi], x)`.

!!! note
The returned value is not guaranteed to be a copy of `x`. For example, if
`variable_map` is an [`IndexMap`](@ref) whose variable map is the
identity, `x` may be returned without being copied. Therefore, it should
not be modified unless `x` is not used anymore.
"""
function map_indices(
variable_map::AbstractDict{T,T},
Expand Down
22 changes: 22 additions & 0 deletions test/Utilities/test_copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,28 @@ function test_copy_names_unsupported()
return
end

function test_map_indices_identity_index_map()
src = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(src, 2)
dest = MOI.Utilities.Model{Float64}()
index_map = MOI.copy_to(dest, src)
@test MOI.Utilities.CleverDicts.is_identity(index_map.var_map)
f = 1.0 * x[1] + 2.0 * x[2]
@test MOI.Utilities.map_indices(index_map, f) === f
attr = MOI.ObjectiveFunction{typeof(f)}()
@test MOI.Utilities.map_indices(index_map, attr, f) === f
change = MOI.ScalarCoefficientChange(x[1], 2.0)
@test MOI.Utilities.map_indices(index_map, change) === change
index_map[x[1]] = x[2]
index_map[x[2]] = x[1]
@test !MOI.Utilities.CleverDicts.is_identity(index_map.var_map)
g = MOI.Utilities.map_indices(index_map, f)
@test isapprox(g, 1.0 * x[2] + 2.0 * x[1])
@test MOI.Utilities.map_indices(index_map, change) ==
MOI.ScalarCoefficientChange(x[2], 2.0)
return
end

end # module

TestCopy.runtests()
Loading