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
1 change: 1 addition & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ Utilities.mode
The following utilities are available for functions:
```@docs
Utilities.eval_variables
Utilities.map_indices
Utilities.substitute_variables
Utilities.remove_variable
Utilities.all_coefficients
Expand Down
4 changes: 2 additions & 2 deletions src/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ function bridged_function(b::AbstractBridgeOptimizer,
end
# Shortcut to avoid `Variable.throw_if_cannot_unbridge(Variable.bridges(b))`
function bridge_function(
::AbstractBridgeOptimizer, value::MOIU.ObjectOrArrayWithoutIndex)
::AbstractBridgeOptimizer, value::MOIU.ObjectOrTupleOrArrayWithoutIndex)
return value
end

Expand Down Expand Up @@ -983,7 +983,7 @@ function unbridged_function(bridge::AbstractBridgeOptimizer,
end
# Shortcut to avoid `Variable.throw_if_cannot_unbridge(Variable.bridges(b))`
function unbridged_function(
::AbstractBridgeOptimizer, value::MOIU.ObjectOrArrayWithoutIndex)
::AbstractBridgeOptimizer, value::MOIU.ObjectOrTupleOrArrayWithoutIndex)
return value
end

Expand Down
62 changes: 30 additions & 32 deletions src/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function reset_optimizer(m::CachingOptimizer, optimizer::MOI.AbstractOptimizer)
m.state = EMPTY_OPTIMIZER
for attr in MOI.get(m.model_cache, MOI.ListOfOptimizerAttributesSet())
value = MOI.get(m.model_cache, attr)
optimizer_value = attribute_value_map(m.model_to_optimizer_map, value)
optimizer_value = map_indices(m.model_to_optimizer_map, value)
MOI.set(m.optimizer, attr, optimizer_value)
end
return
Expand Down Expand Up @@ -236,7 +236,7 @@ function MOI.add_constraint(m::CachingOptimizer, func::MOI.AbstractFunction, set
if m.state == ATTACHED_OPTIMIZER
if m.mode == AUTOMATIC
try
cindex_optimizer = MOI.add_constraint(m.optimizer, mapvariables(m.model_to_optimizer_map, func), set)
cindex_optimizer = MOI.add_constraint(m.optimizer, map_indices(m.model_to_optimizer_map, func), set)
catch err
if err isa MOI.NotAllowedError
# It could be MOI.AddConstraintNotAllowed{F', S'} with F' != F
Expand All @@ -248,7 +248,7 @@ function MOI.add_constraint(m::CachingOptimizer, func::MOI.AbstractFunction, set
end
end
else
cindex_optimizer = MOI.add_constraint(m.optimizer, mapvariables(m.model_to_optimizer_map, func), set)
cindex_optimizer = MOI.add_constraint(m.optimizer, map_indices(m.model_to_optimizer_map, func), set)
end
end
cindex = MOI.add_constraint(m.model_cache, func, set)
Expand All @@ -262,7 +262,7 @@ end
function MOI.modify(m::CachingOptimizer, cindex::CI, change::MOI.AbstractFunctionModification)
if m.state == ATTACHED_OPTIMIZER
cindex_optimizer = m.model_to_optimizer_map[cindex]
change_optimizer = mapvariables(m.model_to_optimizer_map, change)
change_optimizer = map_indices(m.model_to_optimizer_map, change)
if m.mode == AUTOMATIC
try
MOI.modify(m.optimizer, cindex_optimizer, change_optimizer)
Expand All @@ -285,10 +285,11 @@ end
# the third and fourth arguments of the set methods so that we only support
# setting the same type of set or function.
function replace_constraint_function_or_set(m::CachingOptimizer, attr, cindex, replacement)
replacement_optimizer = map_indices(m.model_to_optimizer_map, replacement)
if m.state == ATTACHED_OPTIMIZER
if m.mode == AUTOMATIC
try
MOI.set(m.optimizer, attr, m.model_to_optimizer_map[cindex], replacement)
MOI.set(m.optimizer, attr, m.model_to_optimizer_map[cindex], replacement_optimizer)
catch err
if err isa MOI.NotAllowedError
reset_optimizer(m)
Expand All @@ -297,7 +298,7 @@ function replace_constraint_function_or_set(m::CachingOptimizer, attr, cindex, r
end
end
else
MOI.set(m.optimizer, attr, m.model_to_optimizer_map[cindex], replacement)
MOI.set(m.optimizer, attr, m.model_to_optimizer_map[cindex], replacement_optimizer)
end
end
MOI.set(m.model_cache, attr, cindex, replacement)
Expand All @@ -307,13 +308,13 @@ function MOI.set(m::CachingOptimizer, ::MOI.ConstraintSet, cindex::CI{F,S}, set:
replace_constraint_function_or_set(m, MOI.ConstraintSet(), cindex, set)
end

function MOI.set(m::CachingOptimizer, ::MOI.ConstraintFunction, cindex::CI{F,S}, func::F) where {F,S}
function MOI.set(m::CachingOptimizer, ::MOI.ConstraintFunction, cindex::CI{F}, func::F) where F
replace_constraint_function_or_set(m, MOI.ConstraintFunction(), cindex, func)
end

function MOI.modify(m::CachingOptimizer, obj::MOI.ObjectiveFunction, change::MOI.AbstractFunctionModification)
if m.state == ATTACHED_OPTIMIZER
change_optimizer = mapvariables(m.model_to_optimizer_map, change)
change_optimizer = map_indices(m.model_to_optimizer_map, change)
if m.mode == AUTOMATIC
try
MOI.modify(m.optimizer, obj, change_optimizer)
Expand Down Expand Up @@ -368,16 +369,13 @@ end

## CachingOptimizer get and set attributes

# Attributes are mapped through attribute_value_map (defined in copy.jl) before
# Attributes are mapped through `map_indices` (defined in functions.jl) before
# they are sent to the optimizer and when they are returned from the optimizer.
# This map currently only translates indices on MOI.AbstractFunction objects
# between the optimizer indices and the (user-facing) model_cache indices. As a result,
# all MOI.AbstractFunctions must implement mapvariables. Other attributes that
# store indices need to be handled with care.
# As a result, values of attributes must implement `map_indices`.

function MOI.set(m::CachingOptimizer, attr::MOI.AbstractModelAttribute, value)
if m.state == ATTACHED_OPTIMIZER
optimizer_value = attribute_value_map(m.model_to_optimizer_map, value)
optimizer_value = map_indices(m.model_to_optimizer_map, value)
if m.mode == AUTOMATIC
try
MOI.set(m.optimizer, attr, optimizer_value)
Expand All @@ -401,7 +399,7 @@ function MOI.set(m::CachingOptimizer,
index::MOI.Index, value)
if m.state == ATTACHED_OPTIMIZER
optimizer_index = m.model_to_optimizer_map[index]
optimizer_value = attribute_value_map(m.model_to_optimizer_map, value)
optimizer_value = map_indices(m.model_to_optimizer_map, value)
if m.mode == AUTOMATIC
try
MOI.set(m.optimizer, attr, optimizer_index, optimizer_value)
Expand Down Expand Up @@ -448,8 +446,8 @@ function MOI.get(model::CachingOptimizer, attr::MOI.AbstractModelAttribute)
" optimizer is attached.")
end
end
return attribute_value_map(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr))
return map_indices(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr))
else
return MOI.get(model.model_cache, attr)
end
Expand All @@ -463,9 +461,9 @@ function MOI.get(model::CachingOptimizer,
error("Cannot query $(attr) from caching optimizer because no " *
"optimizer is attached.")
end
return attribute_value_map(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr,
model.model_to_optimizer_map[index]))
return map_indices(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr,
model.model_to_optimizer_map[index]))
else
return MOI.get(model.model_cache, attr, index)
end
Expand All @@ -479,10 +477,10 @@ function MOI.get(model::CachingOptimizer,
error("Cannot query $(attr) from caching optimizer because no " *
"optimizer is attached.")
end
return attribute_value_map(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr,
map(index -> model.model_to_optimizer_map[index],
indices)))
return map_indices(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr,
map(index -> model.model_to_optimizer_map[index],
indices)))
else
return MOI.get(model.model_cache, attr, indices)
end
Expand Down Expand Up @@ -521,7 +519,7 @@ end

function MOI.set(model::CachingOptimizer, attr::MOI.AbstractOptimizerAttribute,
value)
optimizer_value = attribute_value_map(model.model_to_optimizer_map, value)
optimizer_value = map_indices(model.model_to_optimizer_map, value)
if model.optimizer !== nothing
MOI.set(model.optimizer, attr, optimizer_value)
end
Expand All @@ -537,8 +535,8 @@ function MOI.get(model::CachingOptimizer, attr::MOI.AbstractOptimizerAttribute)
error("Cannot query $(attr) from caching optimizer because no " *
"optimizer is attached.")
end
return attribute_value_map(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr))
return map_indices(model.optimizer_to_model_map,
MOI.get(model.optimizer, attr))
end

# Force users to specify whether the attribute should be queried from the
Expand Down Expand Up @@ -566,17 +564,17 @@ end

function MOI.get(m::CachingOptimizer, attr::AttributeFromOptimizer{T}) where {T <: MOI.AbstractModelAttribute}
@assert m.state == ATTACHED_OPTIMIZER
return attribute_value_map(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr))
return map_indices(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr))
end

function MOI.get(m::CachingOptimizer, attr::AttributeFromOptimizer{T}, idx::MOI.Index) where {T <: Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute}}
@assert m.state == ATTACHED_OPTIMIZER
return attribute_value_map(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr, m.model_to_optimizer_map[idx]))
return map_indices(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr, m.model_to_optimizer_map[idx]))
end

function MOI.get(m::CachingOptimizer, attr::AttributeFromOptimizer{T}, idx::Vector{<:MOI.Index}) where {T <: Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute}}
@assert m.state == ATTACHED_OPTIMIZER
return attribute_value_map(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr, getindex.(m.model_to_optimizer_map,idx)))
return map_indices(m.optimizer_to_model_map,MOI.get(m.optimizer, attr.attr, getindex.(m.model_to_optimizer_map,idx)))
end

function MOI.set(m::CachingOptimizer, attr::AttributeFromModelCache{T}, v) where {T <: MOI.AbstractModelAttribute}
Expand All @@ -589,15 +587,15 @@ end

function MOI.set(m::CachingOptimizer, attr::AttributeFromOptimizer{T}, v) where {T <: MOI.AbstractModelAttribute}
@assert m.state == ATTACHED_OPTIMIZER
return MOI.set(m.optimizer, attr.attr, attribute_value_map(m.model_to_optimizer_map,v))
return MOI.set(m.optimizer, attr.attr, map_indices(m.model_to_optimizer_map, v))
end

# Map vector of indices into vector of indices or one index into one index
map_indices_to_optimizer(m::CachingOptimizer, idx::MOI.Index) = m.model_to_optimizer_map[idx]
map_indices_to_optimizer(m::CachingOptimizer, indices::Vector{<:MOI.Index}) = getindex.(Ref(m.model_to_optimizer_map), indices)
function MOI.set(m::CachingOptimizer, attr::AttributeFromOptimizer{T}, idx, v) where {T <: Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute}}
@assert m.state == ATTACHED_OPTIMIZER
return MOI.set(m.optimizer, attr.attr, map_indices_to_optimizer(m, idx), attribute_value_map(m.model_to_optimizer_map,v))
return MOI.set(m.optimizer, attr.attr, map_indices_to_optimizer(m, idx), map_indices(m.model_to_optimizer_map, v))
end

function MOI.supports(m::CachingOptimizer, attr::AttributeFromModelCache{T}) where {T <: MOI.AbstractModelAttribute}
Expand Down
12 changes: 5 additions & 7 deletions src/Utilities/copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ error in case `copy_to` is called with `copy_names` equal to `true`.
"""
supports_default_copy_to(model::MOI.ModelLike, copy_names::Bool) = false

struct IndexMap
struct IndexMap <: AbstractDict{MOI.Index, MOI.Index}
varmap::Dict{MOI.VariableIndex, MOI.VariableIndex}
conmap::Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}
end
Expand Down Expand Up @@ -141,7 +141,7 @@ function _pass_attributes(dest::MOI.ModelLike, src::MOI.ModelLike,
end
value = MOI.get(src, attr, get_args...)
if value !== nothing
mapped_value = attribute_value_map(idxmap, value)
mapped_value = map_indices(idxmap, value)
pass_attr!(dest, attr, set_args..., mapped_value)
end
end
Expand Down Expand Up @@ -220,7 +220,7 @@ function copy_constraints(dest::MOI.ModelLike, src::MOI.ModelLike,
idxmap::IndexMap,
cis_src::Vector{<:MOI.ConstraintIndex})
f_src = MOI.get(src, MOI.ConstraintFunction(), cis_src)
f_dest = mapvariables.(Ref(idxmap), f_src)
f_dest = map_indices.(Ref(idxmap), f_src)
s = MOI.get(src, MOI.ConstraintSet(), cis_src)
cis_dest = MOI.add_constraints(dest, f_dest, s)
for (ci_src, ci_dest) in zip(cis_src, cis_dest)
Expand Down Expand Up @@ -264,8 +264,6 @@ function pass_constraints(
end
end

attribute_value_map(idxmap, f::MOI.AbstractFunction) = mapvariables(idxmap, f)
attribute_value_map(idxmap, attribute_value) = attribute_value
function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike)
Base.depwarn("default_copy_to(dest, src) is deprecated, use default_copy_to(dest, src, true) instead or default_copy_to(dest, src, false) if you do not want to copy names.", :default_copy_to)
default_copy_to(dest, src, true)
Expand Down Expand Up @@ -600,7 +598,7 @@ function allocate_constraints(dest::MOI.ModelLike, src::MOI.ModelLike,
for ci_src in cis_src
f_src = MOI.get(src, MOI.ConstraintFunction(), ci_src)
s = MOI.get(src, MOI.ConstraintSet(), ci_src)
f_dest = mapvariables(idxmap, f_src)
f_dest = map_indices(idxmap, f_src)
ci_dest = allocate_constraint(dest, f_dest, s)
idxmap.conmap[ci_src] = ci_dest
end
Expand All @@ -611,7 +609,7 @@ function load_constraints(dest::MOI.ModelLike, src::MOI.ModelLike,
for ci_src in cis_src
ci_dest = idxmap[ci_src]
f_src = MOI.get(src, MOI.ConstraintFunction(), ci_src)
f_dest = mapvariables(idxmap, f_src)
f_dest = map_indices(idxmap, f_src)
s = MOI.get(src, MOI.ConstraintSet(), ci_src)
load_constraint(dest, ci_dest, f_dest, s)
end
Expand Down
Loading