diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index afc38fc224..306e4ff261 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -14,30 +14,36 @@ optimizer bridges should be used instead. mutable struct UniversalFallback{MT} <: MOI.ModelLike model::MT objective::Union{MOI.AbstractScalarFunction,Nothing} - # See https://github.com/jump-dev/JuMP.jl/issues/1152 and https://github.com/jump-dev/JuMP.jl/issues/2238 for why we use an `OrderedDict` + # See https://github.com/jump-dev/JuMP.jl/issues/1152 and + # https://github.com/jump-dev/JuMP.jl/issues/2238 for why we use an + # `OrderedDict` single_variable_constraints::OrderedDict{DataType,OrderedDict} constraints::OrderedDict{Tuple{DataType,DataType},VectorOfConstraints} - con_to_name::Dict{CI,String} + con_to_name::Dict{MOI.ConstraintIndex,String} name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} optattr::Dict{MOI.AbstractOptimizerAttribute,Any} modattr::Dict{MOI.AbstractModelAttribute,Any} varattr::Dict{MOI.AbstractVariableAttribute,Dict{VI,Any}} - conattr::Dict{MOI.AbstractConstraintAttribute,Dict{CI,Any}} + conattr::Dict{MOI.AbstractConstraintAttribute,Dict{MOI.ConstraintIndex,Any}} function UniversalFallback{MT}(model::MOI.ModelLike) where {MT} return new{typeof(model)}( model, nothing, OrderedDict{Tuple{DataType,DataType},OrderedDict}(), OrderedDict{Tuple{DataType,DataType},VectorOfConstraints}(), - Dict{CI,String}(), + Dict{MOI.ConstraintIndex,String}(), nothing, Dict{MOI.AbstractOptimizerAttribute,Any}(), Dict{MOI.AbstractModelAttribute,Any}(), Dict{MOI.AbstractVariableAttribute,Dict{VI,Any}}(), - Dict{MOI.AbstractConstraintAttribute,Dict{CI,Any}}(), + Dict{ + MOI.AbstractConstraintAttribute, + Dict{MOI.ConstraintIndex,Any}, + }(), ) end end + function UniversalFallback(model::MOI.ModelLike) return UniversalFallback{typeof(model)}(model) end @@ -73,6 +79,7 @@ function MOI.is_empty(uf::UniversalFallback) isempty(uf.varattr) && isempty(uf.conattr) end + function MOI.empty!(uf::UniversalFallback) MOI.empty!(uf.model) uf.objective = nothing @@ -137,30 +144,32 @@ function final_touch(uf::UniversalFallback, index_map) end # References -function MOI.is_valid(uf::UniversalFallback, idx::MOI.VariableIndex) - return MOI.is_valid(uf.model, idx) +function MOI.is_valid(uf::UniversalFallback, vi::MOI.VariableIndex) + return MOI.is_valid(uf.model, vi) end + function MOI.is_valid( uf::UniversalFallback, - idx::CI{MOI.SingleVariable,S}, + ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, ) where {S} if MOI.supports_constraint(uf.model, MOI.SingleVariable, S) - return MOI.is_valid(uf.model, idx) - else - return haskey(uf.single_variable_constraints, S) && - haskey(uf.single_variable_constraints[S], idx) + return MOI.is_valid(uf.model, ci) end + return haskey(uf.single_variable_constraints, S) && + haskey(uf.single_variable_constraints[S], ci) end + function MOI.is_valid( uf::UniversalFallback, - idx::MOI.ConstraintIndex{F,S}, + ci::MOI.ConstraintIndex{F,S}, ) where {F,S} if !MOI.supports_constraint(uf.model, F, S) && !haskey(uf.constraints, (F, S)) return false end - return MOI.is_valid(constraints(uf, idx), idx) + return MOI.is_valid(constraints(uf, ci), ci) end + function _delete( uf::UniversalFallback, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, @@ -173,10 +182,12 @@ function _delete( end return end + function _delete(uf::UniversalFallback, ci::MOI.ConstraintIndex) MOI.delete(constraints(uf, ci), ci) return end + function MOI.delete( uf::UniversalFallback, ci::MOI.ConstraintIndex{F,S}, @@ -191,16 +202,16 @@ function MOI.delete( end return end + function _remove_variable( uf::UniversalFallback, - constraints::OrderedDict{<:CI{MOI.SingleVariable}}, + constraints::OrderedDict{<:MOI.ConstraintIndex{MOI.SingleVariable}}, vi::MOI.VariableIndex, ) - return MOI.delete( - uf, - [ci for ci in keys(constraints) if ci.value == vi.value], - ) + MOI.delete(uf, [ci for ci in keys(constraints) if ci.value == vi.value]) + return end + function MOI.delete(uf::UniversalFallback, vi::MOI.VariableIndex) vis = [vi] for constraints in values(uf.constraints) @@ -227,6 +238,7 @@ function MOI.delete(uf::UniversalFallback, vi::MOI.VariableIndex) end return end + function MOI.delete(uf::UniversalFallback, vis::Vector{MOI.VariableIndex}) fast_in_vis = Set(vis) for constraints in values(uf.constraints) @@ -259,26 +271,40 @@ function MOI.delete(uf::UniversalFallback, vis::Vector{MOI.VariableIndex}) end # Attributes -_get(uf, attr::MOI.AbstractOptimizerAttribute) = uf.optattr[attr] -_get(uf, attr::MOI.AbstractModelAttribute) = uf.modattr[attr] -function _get(uf, attr::MOI.AbstractVariableAttribute, vi::VI) +function _get(uf::UniversalFallback, attr::MOI.AbstractOptimizerAttribute) + return uf.optattr[attr] +end + +_get(uf::UniversalFallback, attr::MOI.AbstractModelAttribute) = uf.modattr[attr] + +function _get( + uf::UniversalFallback, + attr::MOI.AbstractVariableAttribute, + vi::MOI.VariableIndex, +) attribute_dict = get(uf.varattr, attr, nothing) if attribute_dict === nothing # It means the attribute is not set to any variable so in particular, it # is not set for `vi` - return nothing + return end return get(attribute_dict, vi, nothing) end -function _get(uf, attr::MOI.AbstractConstraintAttribute, ci::CI) + +function _get( + uf::UniversalFallback, + attr::MOI.AbstractConstraintAttribute, + ci::MOI.ConstraintIndex, +) attribute_dict = get(uf.conattr, attr, nothing) if attribute_dict === nothing # It means the attribute is not set to any constraint so in particular, # it is not set for `ci` - return nothing + return end return get(attribute_dict, ci, nothing) end + function _get( uf, attr::MOI.CanonicalConstraintFunction, @@ -286,58 +312,60 @@ function _get( ) return MOI.get_fallback(uf, attr, ci) end + function MOI.get( uf::UniversalFallback, attr::Union{MOI.AbstractOptimizerAttribute,MOI.AbstractModelAttribute}, ) if !MOI.is_copyable(attr) || MOI.supports(uf.model, attr) return MOI.get(uf.model, attr) - else - return _get(uf, attr) end + return _get(uf, attr) end + function MOI.get( uf::UniversalFallback, attr::MOI.AbstractConstraintAttribute, - idx::MOI.ConstraintIndex{F,S}, + ci::MOI.ConstraintIndex{F,S}, ) where {F,S} if MOI.supports_constraint(uf.model, F, S) && - (!MOI.is_copyable(attr) || MOI.supports(uf.model, attr, typeof(idx))) - return MOI.get(uf.model, attr, idx) - else - return _get(uf, attr, idx) + (!MOI.is_copyable(attr) || MOI.supports(uf.model, attr, typeof(ci))) + return MOI.get(uf.model, attr, ci) end + return _get(uf, attr, ci) end + function MOI.get( uf::UniversalFallback, attr::MOI.AbstractVariableAttribute, - idx::MOI.VariableIndex, + vi::MOI.VariableIndex, ) - if !MOI.is_copyable(attr) || MOI.supports(uf.model, attr, typeof(idx)) - return MOI.get(uf.model, attr, idx) - else - return _get(uf, attr, idx) + if !MOI.is_copyable(attr) || MOI.supports(uf.model, attr, MOI.VariableIndex) + return MOI.get(uf.model, attr, vi) end + return _get(uf, attr, vi) end + function MOI.get( uf::UniversalFallback, attr::MOI.NumberOfConstraints{MOI.SingleVariable,S}, ) where {S} - F = MOI.SingleVariable - if MOI.supports_constraint(uf.model, F, S) + if MOI.supports_constraint(uf.model, MOI.SingleVariable, S) return MOI.get(uf.model, attr) + elseif !haskey(uf.single_variable_constraints, S) + return 0 else - return length( - get(uf.single_variable_constraints, S, OrderedDict{CI{F,S},S}()), - ) + return length(uf.single_variable_constraints[S]) end end + function MOI.get( uf::UniversalFallback, attr::MOI.NumberOfConstraints{F,S}, ) where {F,S} return MOI.get(constraints(uf, F, S), attr) end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfConstraintIndices{MOI.SingleVariable,S}, @@ -345,24 +373,22 @@ function MOI.get( F = MOI.SingleVariable if MOI.supports_constraint(uf.model, F, S) return MOI.get(uf.model, listattr) - else - return collect( - keys( - get( - uf.single_variable_constraints, - S, - OrderedDict{CI{F,S},S}(), - ), - ), - ) end + indices = get( + uf.single_variable_constraints, + S, + OrderedDict{MOI.ConstraintIndex{F,S},S}(), + ) + return collect(keys(indices)) end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfConstraintIndices{F,S}, ) where {F,S} return MOI.get(constraints(uf, F, S), listattr) end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfConstraintTypesPresent, @@ -380,6 +406,7 @@ function MOI.get( end return list end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfOptimizerAttributesSet, @@ -390,6 +417,7 @@ function MOI.get( end return list end + function MOI.get(uf::UniversalFallback, listattr::MOI.ListOfModelAttributesSet) list = MOI.get(uf.model, listattr) if uf.objective !== nothing @@ -400,6 +428,7 @@ function MOI.get(uf::UniversalFallback, listattr::MOI.ListOfModelAttributesSet) end return list end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfVariableAttributesSet, @@ -410,6 +439,7 @@ function MOI.get( end return list end + function MOI.get( uf::UniversalFallback, listattr::MOI.ListOfConstraintAttributesSet{F,S}, @@ -433,23 +463,24 @@ function MOI.set( MOI.set(uf.model, attr, sense) return end + function MOI.get(uf::UniversalFallback, attr::MOI.ObjectiveFunctionType) if uf.objective === nothing return MOI.get(uf.model, attr) - else - return typeof(uf.objective) end + return typeof(uf.objective) end + function MOI.get( uf::UniversalFallback, attr::MOI.ObjectiveFunction{F}, )::F where {F} if uf.objective === nothing return MOI.get(uf.model, attr) - else - return uf.objective end + return uf.objective end + function MOI.set( uf::UniversalFallback, attr::MOI.ObjectiveFunction, @@ -487,7 +518,7 @@ end function MOI.set( uf::UniversalFallback, attr::MOI.ConstraintName, - ci::CI{F,S}, + ci::MOI.ConstraintIndex{F,S}, name::String, ) where {F,S} if MOI.supports_constraint(uf.model, F, S) @@ -519,13 +550,12 @@ end function MOI.get( uf::UniversalFallback, attr::MOI.ConstraintName, - ci::CI{F,S}, + ci::MOI.ConstraintIndex{F,S}, ) where {F,S} if MOI.supports_constraint(uf.model, F, S) return MOI.get(uf.model, attr, ci) - else - return get(uf.con_to_name, ci, EMPTYSTRING) end + return get(uf.con_to_name, ci, EMPTYSTRING) end function MOI.get(uf::UniversalFallback, ::Type{VI}, name::String) @@ -533,6 +563,7 @@ function MOI.get(uf::UniversalFallback, ::Type{VI}, name::String) end check_type_and_multiple_names(::Type, ::Nothing, ::Nothing, name) = nothing + function check_type_and_multiple_names( ::Type{T}, value::T, @@ -541,9 +572,11 @@ function check_type_and_multiple_names( ) where {T} return value end + function check_type_and_multiple_names(::Type, ::Any, ::Nothing, name) where {T} return nothing end + function check_type_and_multiple_names( ::Type{T}, ::Nothing, @@ -552,76 +585,114 @@ function check_type_and_multiple_names( ) where {T} return value end + function check_type_and_multiple_names(::Type, ::Nothing, ::Any, name) where {T} return nothing end + function check_type_and_multiple_names(T::Type, ::Any, ::Any, name) return throw_multiple_name_error(T, name) end + function MOI.get( uf::UniversalFallback, - ::Type{CI{F,S}}, + ::Type{MOI.ConstraintIndex{F,S}}, name::String, ) where {F,S} if uf.name_to_con === nothing uf.name_to_con = build_name_to_con_map(uf.con_to_name) end - if MOI.supports_constraint(uf.model, F, S) - ci = MOI.get(uf.model, CI{F,S}, name) + ci = if MOI.supports_constraint(uf.model, F, S) + MOI.get(uf.model, MOI.ConstraintIndex{F,S}, name) else # There is no `F`-in-`S` constraint in `b.model`, `ci` is only queried # to check for duplicate names. - ci = MOI.get(uf.model, CI, name) + MOI.get(uf.model, MOI.ConstraintIndex, name) end ci_uf = get(uf.name_to_con, name, nothing) throw_if_multiple_with_name(ci_uf, name) - return check_type_and_multiple_names(CI{F,S}, ci_uf, ci, name) + c = check_type_and_multiple_names(MOI.ConstraintIndex{F,S}, ci_uf, ci, name) + return c end -function MOI.get(uf::UniversalFallback, ::Type{CI}, name::String) + +function MOI.get( + uf::UniversalFallback, + ::Type{MOI.ConstraintIndex}, + name::String, +) if uf.name_to_con === nothing uf.name_to_con = build_name_to_con_map(uf.con_to_name) end ci_uf = get(uf.name_to_con, name, nothing) throw_if_multiple_with_name(ci_uf, name) return check_type_and_multiple_names( - CI, + MOI.ConstraintIndex, ci_uf, - MOI.get(uf.model, CI, name), + MOI.get(uf.model, MOI.ConstraintIndex, name), name, ) end -_set(uf, attr::MOI.AbstractOptimizerAttribute, value) = uf.optattr[attr] = value -_set(uf, attr::MOI.AbstractModelAttribute, value) = uf.modattr[attr] = value -function _set(uf, attr::MOI.AbstractVariableAttribute, vi::VI, value) +function _set( + uf::UniversalFallback, + attr::MOI.AbstractOptimizerAttribute, + value, +) + uf.optattr[attr] = value + return +end + +function _set(uf::UniversalFallback, attr::MOI.AbstractModelAttribute, value) + uf.modattr[attr] = value + return +end + +function _set( + uf, + attr::MOI.AbstractVariableAttribute, + vi::MOI.VariableIndex, + value, +) if !haskey(uf.varattr, attr) - uf.varattr[attr] = Dict{VI,Any}() + uf.varattr[attr] = Dict{MOI.VariableIndex,Any}() end - return uf.varattr[attr][vi] = value + uf.varattr[attr][vi] = value + return end -function _set(uf, attr::MOI.AbstractConstraintAttribute, ci::CI, value) + +function _set( + uf, + attr::MOI.AbstractConstraintAttribute, + ci::MOI.ConstraintIndex, + value, +) if !haskey(uf.conattr, attr) - uf.conattr[attr] = Dict{CI,Any}() + uf.conattr[attr] = Dict{MOI.ConstraintIndex,Any}() end - return uf.conattr[attr][ci] = value + uf.conattr[attr][ci] = value + return end + function MOI.supports( ::UniversalFallback, ::Union{MOI.AbstractModelAttribute,MOI.AbstractOptimizerAttribute}, ) return true end + function MOI.set( uf::UniversalFallback, attr::Union{MOI.AbstractOptimizerAttribute,MOI.AbstractModelAttribute}, value, ) if MOI.supports(uf.model, attr) - return MOI.set(uf.model, attr, value) + MOI.set(uf.model, attr, value) else - return _set(uf, attr, value) + _set(uf, attr, value) end + return end + function MOI.supports( ::UniversalFallback, ::Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute}, @@ -629,40 +700,45 @@ function MOI.supports( ) return true end + function MOI.set( uf::UniversalFallback, attr::MOI.AbstractVariableAttribute, - idx::VI, + vi::MOI.VariableIndex, value, ) - if MOI.supports(uf.model, attr, typeof(idx)) - return MOI.set(uf.model, attr, idx, value) + if MOI.supports(uf.model, attr, MOI.VariableIndex) + MOI.set(uf.model, attr, vi, value) else - return _set(uf, attr, idx, value) + _set(uf, attr, vi, value) end + return end + function MOI.set( uf::UniversalFallback, attr::MOI.AbstractConstraintAttribute, - idx::CI{F,S}, + ci::MOI.ConstraintIndex{F,S}, value, ) where {F,S} if MOI.supports_constraint(uf.model, F, S) && - MOI.supports(uf.model, attr, CI{F,S}) - return MOI.set(uf.model, attr, idx, value) + MOI.supports(uf.model, attr, MOI.ConstraintIndex{F,S}) + MOI.set(uf.model, attr, ci, value) else - return _set(uf, attr, idx, value) + _set(uf, attr, ci, value) end + return end # Constraints function MOI.supports_constraint( - uf::UniversalFallback, + ::UniversalFallback, ::Type{<:MOI.AbstractFunction}, ::Type{<:MOI.AbstractSet}, ) return true end + function constraints( uf::UniversalFallback, ::Type{F}, @@ -671,12 +747,12 @@ function constraints( ) where {F,S} if MOI.supports_constraint(uf.model, F, S) return uf.model - else - return getter(uf.constraints, (F, S)) do - return VectorOfConstraints{F,S}() - end::VectorOfConstraints{F,S} end + return getter(uf.constraints, (F, S)) do + return VectorOfConstraints{F,S}() + end::VectorOfConstraints{F,S} end + function constraints( uf::UniversalFallback, ci::MOI.ConstraintIndex{F,S}, @@ -686,6 +762,7 @@ function constraints( end return constraints(uf, F, S) end + function MOI.add_constraint( uf::UniversalFallback, func::MOI.SingleVariable, @@ -693,21 +770,21 @@ function MOI.add_constraint( ) where {S<:MOI.AbstractScalarSet} if MOI.supports_constraint(uf.model, MOI.SingleVariable, S) return MOI.add_constraint(uf.model, func, set) - else - constraints = get!( - uf.single_variable_constraints, - S, - ) do - return OrderedDict{ - CI{MOI.SingleVariable,S}, - S, - }() - end::OrderedDict{CI{MOI.SingleVariable,S},S} - ci = MOI.ConstraintIndex{MOI.SingleVariable,S}(func.variable.value) - constraints[ci] = set - return ci end + constraints = get!( + uf.single_variable_constraints, + S, + ) do + return OrderedDict{ + MOI.ConstraintIndex{MOI.SingleVariable,S}, + S, + }() + end::OrderedDict{MOI.ConstraintIndex{MOI.SingleVariable,S},S} + ci = MOI.ConstraintIndex{MOI.SingleVariable,S}(func.variable.value) + constraints[ci] = set + return ci end + function MOI.add_constraint( uf::UniversalFallback, func::MOI.AbstractFunction, @@ -719,6 +796,7 @@ function MOI.add_constraint( set, ) end + function MOI.modify( uf::UniversalFallback, ci::MOI.ConstraintIndex, @@ -748,32 +826,33 @@ end function MOI.get( uf::UniversalFallback, ::MOI.ConstraintFunction, - ci::CI{MOI.SingleVariable}, + ci::MOI.ConstraintIndex{MOI.SingleVariable}, ) MOI.throw_if_not_valid(uf, ci) return MOI.SingleVariable(MOI.VariableIndex(ci.value)) end + function MOI.get( uf::UniversalFallback, ::MOI.ConstraintSet, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, ) where {S} if MOI.supports_constraint(uf.model, MOI.SingleVariable, S) - MOI.get(uf.model, MOI.ConstraintSet(), ci) - else - MOI.throw_if_not_valid(uf, ci) - return uf.single_variable_constraints[S][ci] + return MOI.get(uf.model, MOI.ConstraintSet(), ci) end + MOI.throw_if_not_valid(uf, ci) + return uf.single_variable_constraints[S][ci] end function MOI.set( - uf::UniversalFallback, - attr::MOI.ConstraintFunction, - ci::MOI.ConstraintIndex{MOI.SingleVariable}, - func::MOI.SingleVariable, + ::UniversalFallback, + ::MOI.ConstraintFunction, + ::MOI.ConstraintIndex{MOI.SingleVariable}, + ::MOI.SingleVariable, ) return throw(MOI.SettingSingleVariableFunctionNotAllowed()) end + function MOI.set( uf::UniversalFallback, ::MOI.ConstraintSet, @@ -790,5 +869,7 @@ function MOI.set( end # Variables + MOI.add_variable(uf::UniversalFallback) = MOI.add_variable(uf.model) + MOI.add_variables(uf::UniversalFallback, n) = MOI.add_variables(uf.model, n)