From e86f5e400fb95fc2e8be96225f3ab7277e508ab2 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 5 Jul 2021 19:06:38 +1200 Subject: [PATCH 01/11] WIP: refactor DoubleDicts.jl --- src/Utilities/DoubleDicts.jl | 377 +++++++++++++++++++--------------- test/Utilities/DoubleDicts.jl | 69 ++++--- 2 files changed, 257 insertions(+), 189 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index a1aec7946e..70bf153175 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -3,33 +3,40 @@ module DoubleDicts import MathOptInterface const MOI = MathOptInterface -const MOIU = MOI.Utilities -const CI = MOI.ConstraintIndex -const AD = AbstractDict -const DataTypePair = Tuple{DataType,DataType} - -abstract type AbstractDoubleDict{K,V,IK,DI<:AD{IK},DO<:AD{K,DI}} <: AD{CI,V} end +abstract type AbstractDoubleDict{ + K, + V, + IK, + DI<:AbstractDict{IK}, + DO<:AbstractDict{K,DI}, +} <: AbstractDict{MOI.ConstraintIndex,V} end """ DoubleDict{V} -Optimized dictionary to map `ConstraintIndex` (`CI`) to values of type `V`. -Works as a `AbstractDict{CI, V}` with minimal differences. -Note that `CI` is not a concrete type, opposed to `CI{MOI.SingleVariable, MOI.Integers}`, -which is a concrete type. +Optimized dictionary to map `MOI.ConstraintIndex` to values of type `V`. + +Works as a `AbstractDict{MOI.ConstraintIndex, V}` with minimal differences. -When optimal performance or type stability is required it is possible to obtain a -fully type stable dictionary with values of type `V` and keys of type -`CI{MOI.SingleVariable, MOI.Integers}` from the dictionary `dict`, for instance: +Note that `MOI.ConstraintIndex` is not a concrete type, opposed to +`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}`, which is a concrete +type. - ``inner = dict[MOI.SingleVariable, MOI.Integers]`` +When optimal performance or type stability is required it is possible to obtain +a fully type stable dictionary with values of type `V` and keys of type +`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` from the dictionary +`dict`, for instance: +```julia +inner = dict[MOI.SingleVariable, MOI.Integers] +``` """ -struct DoubleDict{V,DI,DO} <: AbstractDoubleDict{DataTypePair,V,Int64,DI,DO} +struct DoubleDict{V,DI,DO} <: + AbstractDoubleDict{Tuple{DataType,DataType},V,Int64,DI,DO} dict::DO function DoubleDict{V}() where {V} - return new{V,Dict{Int64,V},Dict{DataTypePair,Dict{Int64,V}}}( - Dict{DataTypePair,Dict{Int64,V}}(), + return new{V,Dict{Int64,V},Dict{Tuple{DataType,DataType},Dict{Int64,V}}}( + Dict{Tuple{DataType,DataType},Dict{Int64,V}}(), ) end end @@ -38,35 +45,50 @@ end IndexDoubleDict Specialized version of `DoubleDict` in which keys and values are of type -`ConstraintIndex` (`CI`). +`ConstraintIndex` + +This is an optimized dictionary to map `MOI.ConstraintIndex` to values of +type `MOI.ConstraintIndex`. + +Works as a `AbstractDict{MOI.ConstraintIndex, V}` with minimal differences. -This is an optimized dictionary to map `ConstraintIndex` (`CI`) to values of type `CI`. -Works as a `AbstractDict{CI, V}` with minimal differences. -Note that `CI` is not a concrete type, opposed to `CI{MOI.SingleVariable, MOI.Integers}`, -which is a concrete type. +Note that `MOI.ConstraintIndex` is not a concrete type, opposed to +`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}`, which is a concrete +type. When optimal performance or type stability is required its possible to obtain a fully type stable dictionary with values of type -`CI{MOI.SingleVariable, MOI.Integers}` and keys of type -`CI{MOI.SingleVariable, MOI.Integers}` from the dictionary `dict`, for instance: - - ``inner = dict[MOI.SingleVariable, MOI.Integers]`` +`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` and keys of type +`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` from the dictionary +`dict`, for instance: +```julia +inner = dict[MOI.SingleVariable, MOI.Integers] +``` """ -struct IndexDoubleDict{DI,DO} <: AbstractDoubleDict{DataTypePair,CI,Int64,DI,DO} +struct IndexDoubleDict{DI,DO} <: AbstractDoubleDict{ + Tuple{DataType,DataType}, + MOI.ConstraintIndex, + Int64, + DI, + DO, +} dict::DO function IndexDoubleDict() - return new{Dict{Int64,Int64},Dict{DataTypePair,Dict{Int64,Int64}}}( - Dict{DataTypePair,Dict{Int64,Int64}}(), + return new{ + Dict{Int64,Int64}, + Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}, + }( + Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}(), ) end end struct FunctionSetDoubleDict{DI,DO} <: - AbstractDoubleDict{DataTypePair,Tuple,Int64,DI,DO} + AbstractDoubleDict{Tuple{DataType,DataType},Tuple,Int64,DI,DO} dict::DO function FunctionSetDoubleDict() - return new{Dict{Int64},Dict{DataTypePair,Dict{Int64}}}( - Dict{DataTypePair,Dict{Int64}}(), + return new{Dict{Int64},Dict{Tuple{DataType,DataType},Dict{Int64}}}( + Dict{Tuple{DataType,DataType},Dict{Int64}}(), ) end end @@ -76,7 +98,7 @@ const MainIndexDoubleDict = IndexDoubleDict{ Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}, } -@inline function typed_value( +@inline function _typed_value( ::DoubleDict{V}, v::V, ::Type{F}, @@ -84,15 +106,17 @@ const MainIndexDoubleDict = IndexDoubleDict{ )::V where {V,F,S} return v end -@inline function typed_value( + +@inline function _typed_value( ::IndexDoubleDict, v::Int64, ::Type{F}, ::Type{S}, -)::CI{F,S} where {F,S} - return CI{F,S}(v) +)::MOI.ConstraintIndex{F,S} where {F,S} + return MOI.ConstraintIndex{F,S}(v) end -@inline function typed_value( + +@inline function _typed_value( ::FunctionSetDoubleDict, v::Tuple{F,S}, ::Type{F}, @@ -102,12 +126,12 @@ end end # reversing IndexDoubleDict is ok because they map CI to CI -function MOIU._reverse_dict( +function MOI.Utilities._reverse_dict( dest::IndexDoubleDict{DI}, src::IndexDoubleDict{DI}, ) where {DI} for (k, v) in src.dict - dest.dict[k] = MOIU._reverse_dict(v) + dest.dict[k] = MOI.Utilities._reverse_dict(v) end end # reversing other double dict types is not ok because the map CI fo K @@ -132,40 +156,51 @@ function Base.length(d::AbstractDoubleDict) return len end -function Base.haskey(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} +function Base.haskey( + dict::AbstractDoubleDict, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} inner = get(dict.dict, (F, S), nothing) return inner !== nothing ? haskey(inner, key.value) : false end -function Base.get(dict::AbstractDoubleDict, key::CI{F,S}, default) where {F,S} +function Base.get( + dict::AbstractDoubleDict, + key::MOI.ConstraintIndex{F,S}, + default, +) where {F,S} inner = get(dict.dict, (F, S), nothing) if inner !== nothing && haskey(inner, key.value) - return typed_value(dict, inner[key.value], F, S) + return _typed_value(dict, inner[key.value], F, S) end return default end -function Base.getindex(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} +function Base.getindex( + dict::AbstractDoubleDict, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} inner = dict.dict[(F, S)] - k_value = key.value::Int64 - return typed_value(dict, inner[k_value], F, S) + return _typed_value(dict, inner[key.value], F, S) end -function init_inner_dict( +function _init_inner_dict( ::AbstractDoubleDict{K,V}, ::Type{F}, ::Type{S}, ) where {K,V,F,S} return Dict{Int64,V}() end -function init_inner_dict( + +function _init_inner_dict( ::FunctionSetDoubleDict, ::Type{F}, ::Type{S}, ) where {F,S} return Dict{Int64,Tuple{F,S}}() end -function init_inner_dict(::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} + +function _init_inner_dict(::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} return Dict{Int64,Int64}() end @@ -179,56 +214,49 @@ function lazy_get( ::Type{F}, ::Type{S}, ) where {K,V,F,S} - inner = get(dict.dict, (F, S), nothing) - if inner === nothing - return dict.dict[(F, S)] = init_inner_dict(dict, F, S) - end - return inner + return get!(() -> _init_inner_dict(dict, F, S), dict.dict, (F, S)) end function Base.setindex!( dict::AbstractDoubleDict{K,V}, value::V, - key::CI{F,S}, + key::MOI.ConstraintIndex{F,S}, ) where {K,V,F,S} - v_value = value - k_value = key.value::Int64 inner = lazy_get(dict, F, S) - inner[k_value] = v_value + inner[key.value] = value return value end + function Base.setindex!( dict::DoubleDict{V}, value::V, - key::CI{F,S}, + key::MOI.ConstraintIndex{F,S}, ) where {V,F,S} - v_value = value - k_value = key.value::Int64 inner = lazy_get(dict, F, S)::Dict{Int64,V} - inner[k_value] = v_value + inner[key.value] = value return value end function Base.setindex!( dict::IndexDoubleDict, - value::CI{F,S}, - key::CI{F,S}, + value::MOI.ConstraintIndex{F,S}, + key::MOI.ConstraintIndex{F,S}, ) where {F,S} - v_value = value.value::Int64 - k_value = key.value::Int64 inner = lazy_get(dict, F, S)::Dict{Int64,Int64} - inner[k_value] = v_value + inner[key.value] = value.value return value end -function Base.empty!(d::AbstractDoubleDict)::Nothing +function Base.empty!(d::AbstractDoubleDict) Base.empty!(d.dict) - return + return d end -function Base.delete!(d::AbstractDoubleDict, key::CI{F,S}) where {F,S} - k_value = key.value::Int64 +function Base.delete!( + d::AbstractDoubleDict, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} inner = lazy_get(d, F, S) - delete!(inner, k_value) + delete!(inner, key.value) return d end @@ -244,25 +272,26 @@ function Base.isempty(d::AbstractDoubleDict) return true end -function Base.values(d::IndexDoubleDict)::Vector{CI} - out = CI[] +function Base.values(d::IndexDoubleDict)::Vector{MOI.ConstraintIndex} + out = MOI.ConstraintIndex[] for ((F, S), inner) in d.dict - append!(out, CI{F,S}.(values(inner))) + append!(out, MOI.ConstraintIndex{F,S}.(values(inner))) end return out end + function Base.values(d::AbstractDoubleDict{K,V})::Vector{V} where {K,V} out = V[] - for ((F, S), inner) in d.dict + for (_, inner) in d.dict append!(out, values(inner)) end return out end function Base.keys(d::AbstractDoubleDict) - out = CI[] + out = MOI.ConstraintIndex[] for ((F, S), inner) in d.dict - append!(out, CI{F,S}.(keys(inner))) + append!(out, MOI.ConstraintIndex{F,S}.(keys(inner))) end return out end @@ -285,9 +314,10 @@ function Base.iterate(d::AbstractDoubleDict) i_next = iterate(inner) end (i_i, i_state) = i_next - return CI{F,S}(i_i[1]) => typed_value(d, i_i[2], F, S), + return MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S), (i_state, (o_i, o_state)) end + function Base.iterate(d::AbstractDoubleDict, state) (i_state, (o_i, o_state)) = state ((F, S), inner) = o_i @@ -302,17 +332,18 @@ function Base.iterate(d::AbstractDoubleDict, state) i_next = iterate(inner) end (i_i, i_state) = i_next - return CI{F,S}(i_i[1]) => typed_value(d, i_i[2], F, S), + return MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S), (i_state, (o_i, o_state)) end -abstract type AbstractWithType{F,S,V,DI,DD} <: AD{CI{F,S},V} end +abstract type AbstractWithType{F,S,V,DI,DD} <: + AbstractDict{MOI.ConstraintIndex{F,S},V} end """ WithType{F,S,V,DI,DD} -Used to specialize methods and iterators for a given contraint type `CI{F,S}` -returning elements of type `V`. +Used to specialize methods and iterators for a given contraint type +`MOI.ConstraintIndex{F,S}` returning elements of type `V`. """ mutable struct WithType{F,S,V,DI,DD} <: AbstractWithType{F,S,V,DI,DD} dict::DD @@ -321,8 +352,8 @@ mutable struct WithType{F,S,V,DI,DD} <: AbstractWithType{F,S,V,DI,DD} d::DD, ) where { V, - DI<:AD{Int64,V}, - DO<:AD{DataTypePair,DI}, + DI<:AbstractDict{Int64,V}, + DO<:AbstractDict{Tuple{DataType,DataType},DI}, DD<:DoubleDict{V,DI,DO}, F, S, @@ -331,22 +362,25 @@ mutable struct WithType{F,S,V,DI,DD} <: AbstractWithType{F,S,V,DI,DD} return new{F,S,V,DI,DD}(d, inner) end end -mutable struct IndexWithType{F,S,V,DI,DD} <: AbstractWithType{F,S,CI{F,S},DI,DD} + +mutable struct IndexWithType{F,S,V,DI,DD} <: + AbstractWithType{F,S,MOI.ConstraintIndex{F,S},DI,DD} dict::DD inner::Union{DI,Nothing} function IndexWithType{F,S}( d::DD, ) where { - DI<:AD{Int64,Int64}, - DO<:AD{DataTypePair,DI}, + DI<:AbstractDict{Int64,Int64}, + DO<:AbstractDict{Tuple{DataType,DataType},DI}, DD<:IndexDoubleDict{DI,DO}, F, S, } inner = get(d.dict, (F, S), nothing)::Union{DI,Nothing} - return new{F,S,CI{F,S},DI,DD}(d, inner) + return new{F,S,MOI.ConstraintIndex{F,S},DI,DD}(d, inner) end end + mutable struct FunctionSetWithType{F,S,V,DI,DD} <: AbstractWithType{F,S,Tuple{F,S},DI,DD} dict::DD @@ -356,8 +390,8 @@ mutable struct FunctionSetWithType{F,S,V,DI,DD} <: ) where { F, S, - DI<:AD{Int64}, - DO<:AD{DataTypePair,DI}, + DI<:AbstractDict{Int64}, + DO<:AbstractDict{Tuple{DataType,DataType},DI}, DD<:FunctionSetDoubleDict{DI,DO}, } inner = get(d.dict, (F, S), nothing)::Union{DI{Tuple{F,S}},Nothing} @@ -372,6 +406,7 @@ function with_type( )::WithType{F,S} where {F,S} return WithType{F,S}(d)::WithType{F,S} end + function with_type( d::IndexDoubleDict, ::Type{F}, @@ -379,6 +414,7 @@ function with_type( )::IndexWithType{F,S} where {F,S} return IndexWithType{F,S}(d)::IndexWithType{F,S} end + function with_type( d::FunctionSetDoubleDict, ::Type{F}, @@ -390,9 +426,11 @@ end function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} return WithType{F,S}(d) end + function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} return IndexWithType{F,S}(d) end + function Base.getindex( d::FunctionSetDoubleDict, ::Type{F}, @@ -401,176 +439,189 @@ function Base.getindex( return FunctionSetWithType{F,S}(d) end -@inline function typed_value(::WithType{F,S,V}, v::V)::V where {V,F,S} +@inline function _typed_value(::WithType{F,S,V}, v::V)::V where {V,F,S} return v end -@inline function typed_value( + +@inline function _typed_value( ::IndexWithType{F,S}, v::Int64, -)::CI{F,S} where {F,S} - return CI{F,S}(v) +)::MOI.ConstraintIndex{F,S} where {F,S} + return MOI.ConstraintIndex{F,S}(v) end -@inline function typed_value( + +@inline function _typed_value( ::FunctionSetWithType{F,S,V}, v::Tuple{F,S}, )::Tuple{F,S} where {V,F,S} return v end -function initialize_inner!(d::AbstractWithType{F,S,V,D}) where {F,S,V,D} +function _initialize_inner!(d::AbstractWithType{F,S,V,D}) where {F,S,V,D} d.inner = D() d.dict.dict[(F, S)] = d.inner return end -inner_is_empty(d::AbstractWithType)::Bool = d.inner === nothing -function inner(d::AbstractWithType{F,S,V,D})::D where {F,S,V,D} +_inner_is_empty(d::AbstractWithType)::Bool = d.inner === nothing + +function _inner(d::AbstractWithType{F,S,V,D}) where {F,S,V,D} return d.inner::D end function Base.sizehint!(d::AbstractWithType{F,S}, n::Integer) where {F,S} - if inner_is_empty(d) - initialize_inner!(d) + if _inner_is_empty(d) + _initialize_inner!(d) end - return sizehint!(inner(d), n) + return sizehint!(_inner(d), n) end function Base.length(d::AbstractWithType{F,S}) where {F,S} - if inner_is_empty(d) + if _inner_is_empty(d) return 0 end - return length(inner(d)) + return length(_inner(d)) end -function Base.haskey(d::AbstractWithType{F,S}, key::CI{F,S}) where {F,S} - if inner_is_empty(d) +function Base.haskey( + d::AbstractWithType{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + if _inner_is_empty(d) return false end - return haskey(inner(d), key.value) + return haskey(_inner(d), key.value) end -function Base.getindex(d::AbstractWithType{F,S}, key::CI{F,S}) where {F,S} - if inner_is_empty(d) +function Base.getindex( + d::AbstractWithType{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + if _inner_is_empty(d) throw(KeyError(key)) end - k_value = key.value::Int64 - return typed_value(d, inner(d)[k_value]) + return _typed_value(d, _inner(d)[key.value]) end function Base.setindex!( d::WithType{F,S,V}, value::V, - key::CI{F,S}, + key::MOI.ConstraintIndex{F,S}, )::V where {F,S,V} - if inner_is_empty(d) - initialize_inner!(d) + if _inner_is_empty(d) + _initialize_inner!(d) end - v_value = value - k_value = key.value::Int64 - inner(d)[k_value] = v_value + inner = _inner(d) + inner[key.value] = value return value end + function Base.setindex!( d::IndexWithType{F,S,V}, - value::CI{F,S}, - key::CI{F,S}, -)::CI{F,S} where {F,S,V} - if inner_is_empty(d) - initialize_inner!(d) - end - _inner = inner(d)::Dict{Int64,Int64} - v_value = value.value::Int64 - k_value = key.value::Int64 - _inner[k_value] = v_value + value::MOI.ConstraintIndex{F,S}, + key::MOI.ConstraintIndex{F,S}, +)::MOI.ConstraintIndex{F,S} where {F,S,V} + if _inner_is_empty(d) + _initialize_inner!(d) + end + inner = _inner(d)::Dict{Int64,Int64} + inner[key.value] = value.value return value end + function Base.setindex!( d::FunctionSetWithType{F,S,V}, value::Tuple{F,S}, - key::CI{F,S}, + key::MOI.ConstraintIndex{F,S}, )::Tuple{F,S} where {F,S,V} - if inner_is_empty(d) - initialize_inner!(d) + if _inner_is_empty(d) + _initialize_inner!(d) end - _inner = inner(d)::Dict{Int64,Tuple{F,S}} - v_value = value - k_value = key.value::Int64 - _inner[k_value] = v_value + inner = _inner(d)::Dict{Int64,Tuple{F,S}} + inner[key.value] = value return value end function Base.empty!(d::AbstractWithType{F,S}) where {F,S} - if inner_is_empty(d) + if _inner_is_empty(d) return d end - return empty!(inner(d)) + return empty!(_inner(d)) end -function Base.delete!(d::AbstractWithType{F,S}, key::CI{F,S}) where {F,S} - if inner_is_empty(d) +function Base.delete!( + d::AbstractWithType{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + if _inner_is_empty(d) return d end k_value = key.value::Int64 - delete!(inner(d), k_value) + delete!(_inner(d), k_value) return d end function Base.isempty(d::AbstractWithType{F,S}) where {F,S} - if inner_is_empty(d) + if _inner_is_empty(d) return true end - return isempty(inner(d)) + return isempty(_inner(d)) end function Base.values(d::WithType{F,S,V})::Vector{V} where {F,S,V} - if inner_is_empty(d) + if _inner_is_empty(d) return V[] end - return collect(values(inner(d))) + return collect(values(_inner(d))) end -function Base.values(d::IndexWithType{F,S})::Vector{CI{F,S}} where {F,S} - if inner_is_empty(d) - return CI{F,S}[] + +function Base.values( + d::IndexWithType{F,S}, +)::Vector{MOI.ConstraintIndex{F,S}} where {F,S} + if _inner_is_empty(d) + return MOI.ConstraintIndex{F,S}[] end - return CI{F,S}.(values(inner(d))) + return MOI.ConstraintIndex{F,S}.(values(_inner(d))) end + function Base.values( d::FunctionSetWithType{F,S}, )::Vector{Tuple{F,S}} where {F,S} - if inner_is_empty(d) + if _inner_is_empty(d) return Tuple{F,S}[] end - return collect(values(inner(d))) + return collect(values(_inner(d))) end function Base.keys(d::AbstractWithType{F,S}) where {F,S} - if inner_is_empty(d) - return CI{F,S}[] + if _inner_is_empty(d) + return MOI.ConstraintIndex{F,S}[] end - return CI{F,S}.(keys(inner(d))) + return MOI.ConstraintIndex{F,S}.(keys(_inner(d))) end function Base.iterate(d::AbstractWithType{F,S}) where {F,S} - inner = d.inner - if inner === nothing - return nothing - else - next = iterate(inner) - if next === nothing - return nothing - end - (i, state) = next - return CI{F,S}(i[1]) => typed_value(d, i[2]), (state, inner) + if d.inner === nothing + return + end + next = iterate(d.inner) + if next === nothing + return end + (i, state) = next + value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) + return value, (state, d.inner) end + function Base.iterate(d::AbstractWithType{F,S}, state) where {F,S} (istate, inner) = state next = iterate(inner, istate) if next === nothing - return nothing + return end (i, state) = next - return CI{F,S}(i[1]) => typed_value(d, i[2]), (state, inner) + value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) + return value, (state, inner) end end diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index ce7fc82f7c..7d7c8ebac9 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -1,16 +1,24 @@ -# include("C:/Users/joaquimgarcia/.julia/dev/MathOptInterface/test/Utilities/DoubleDicts.jl") -# include("C:/Users/joaquimgarcia/.julia/dev/MathOptInterface/src/Utilities/DoubleDicts.jl") +module TestDoubleDicts -using MathOptInterface, Test +using Test + +using MathOptInterface const MOI = MathOptInterface -const CI{F,S} = MOI.ConstraintIndex{F,S} const DoubleDicts = MathOptInterface.Utilities.DoubleDicts -const CI_I = CI{MOI.SingleVariable,MOI.Integer} -const CI_B = CI{MOI.SingleVariable,MOI.ZeroOne} +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end -function test_iterator(dict) +function _test_iterator(dict) kk, vv = [], [] for (k, v) in dict push!(kk, k) @@ -21,20 +29,22 @@ function test_iterator(dict) @test length(vv) == length(values(dict)) @test isempty(setdiff(vv, values(dict))) @test dict == Dict(kk .=> vv) + return end -function basic_functionality(dict, k_values, v_values) +function _test_basic_functionality(dict, k_values, v_values) @test_throws ErrorException sizehint!(dict, 1) @test isempty(dict) @test length(dict) == 0 + _test_iterator(dict) dict[k_values[1]] = v_values[1] delete!(dict, k_values[1]) - test_iterator(dict) + _test_iterator(dict) dict[k_values[3]] = v_values[3] - test_iterator(dict) + _test_iterator(dict) empty!(dict) @test isempty(dict) @@ -55,7 +65,7 @@ function basic_functionality(dict, k_values, v_values) end end - test_iterator(dict) + _test_iterator(dict) delete!(dict, k_values[1]) @test !haskey(dict, k_values[1]) @@ -73,7 +83,7 @@ function basic_functionality(dict, k_values, v_values) dict[k] = v end - test_iterator(dict) + _test_iterator(dict) empty!(dict) @test isempty(dict) @@ -100,11 +110,11 @@ function basic_functionality(dict, k_values, v_values) idict[k_values[2]] = v_values[2] @test haskey(idict, k_values[2]) - test_iterator(idict) + _test_iterator(idict) delete!(idict, k_values[2]) @test !haskey(idict, k_values[2]) - test_iterator(idict) + _test_iterator(idict) @test !isempty(idict) @test isempty(bdict) @@ -117,29 +127,30 @@ function basic_functionality(dict, k_values, v_values) length(bdict) == 1 edict = DoubleDicts.with_type(dict, MOI.SingleVariable, MOI.EqualTo{Bool}) - ek = CI{MOI.SingleVariable,MOI.EqualTo{Bool}}(1) + ek = MOI.ConstraintIndex{MOI.SingleVariable,MOI.EqualTo{Bool}}(1) delete!(edict, ek) @test_throws KeyError edict[ek] sizehint!(edict, 0) @test length(edict) == 0 @test_throws KeyError edict[ek] delete!(edict, ek) - return test_iterator(edict) + _test_iterator(edict) + return end -@testset "DoubleDict" begin +function test_DoubleDict() dict = DoubleDicts.DoubleDict{Float64}() - keys = [CI_I(1), CI_I(2), CI_B(1)] + keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] vals = [1.0, 2.0, 1.0] - basic_functionality(dict, keys, vals) + _test_basic_functionality(dict, keys, vals) + return end -@testset "IndexDoubleDict" begin +function test_IndexDoubleDict() dict = DoubleDicts.IndexDoubleDict() - keys = [CI_I(1), CI_I(2), CI_B(1)] + keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] vals = keys - basic_functionality(dict, keys, vals) - + _test_basic_functionality(dict, keys, vals) src = DoubleDicts.IndexDoubleDict() for (k, v) in zip(keys, vals) dict[k] = v @@ -149,15 +160,21 @@ end for (k, v) in src @test dest[v] == k end + return end -@testset "FunctionSetDoubleDict" begin +function test_FunctionSetDoubleDict() dict = DoubleDicts.FunctionSetDoubleDict() - keys = [CI_I(1), CI_I(2), CI_B(1)] + keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] vals = [ (MOI.SingleVariable(MOI.VariableIndex(1)), MOI.Integer()), (MOI.SingleVariable(MOI.VariableIndex(2)), MOI.Integer()), (MOI.SingleVariable(MOI.VariableIndex(1)), MOI.ZeroOne()), ] - basic_functionality(dict, keys, vals) + _test_basic_functionality(dict, keys, vals) + return end + +end # module + +TestDoubleDicts.runtests() From 4f0149b31c04ae9bb246c2f655368d0f42ed4195 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 7 Jul 2021 16:42:28 +1200 Subject: [PATCH 02/11] Large refactoring of DoubleDicts --- docs/src/submodules/Utilities/overview.md | 14 + docs/src/submodules/Utilities/reference.md | 10 + src/Utilities/DoubleDicts.jl | 641 ++++++++------------- src/Utilities/copy/index_map.jl | 4 +- test/Utilities/DoubleDicts.jl | 18 +- 5 files changed, 267 insertions(+), 420 deletions(-) diff --git a/docs/src/submodules/Utilities/overview.md b/docs/src/submodules/Utilities/overview.md index fbeb038084..449c81ec23 100644 --- a/docs/src/submodules/Utilities/overview.md +++ b/docs/src/submodules/Utilities/overview.md @@ -491,3 +491,17 @@ function MOI.get(model::Optimizer, attr::MOI.ObjectiveFunction) return MOI.Utilities.get_fallback(model, attr) end ``` + +## DoubleDicts + +When writing MOI interfaces, we often need to handle situations in which we map +[`MOI.ConstraintIndex`](@ref)s to different values. For example, to a string +for [`MOI.ConstraintName`](@ref). + +One option is to use a dictionary like `Dict{MOI.ConstraintIndex,String}`. +However, this incurs a performance cost because the key is not a concrete type. + +The DoubleDicts submodule helps this situation by providing two types main +types [`DoubleDict`](@ref) and [`IndexDoubleDict`](@ref). These types act like +normal dictionaries, but internally they use more efficient dictionaries +specialized to the type of the function-set pair. diff --git a/docs/src/submodules/Utilities/reference.md b/docs/src/submodules/Utilities/reference.md index dc5fc69737..a0524e6de3 100644 --- a/docs/src/submodules/Utilities/reference.md +++ b/docs/src/submodules/Utilities/reference.md @@ -198,3 +198,13 @@ The following utilities are useful when working with symmetric matrix cones. Utilities.is_diagonal_vectorized_index Utilities.side_dimension_for_vectorized_dimension ``` + + +## DoubleDicts + +```@docs +DoubleDict +DoubleDictInner +IndexDoubleDict +IndexDoubleDictInner +``` diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 70bf153175..1519cf553d 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -4,106 +4,112 @@ import MathOptInterface const MOI = MathOptInterface -abstract type AbstractDoubleDict{ - K, - V, - IK, - DI<:AbstractDict{IK}, - DO<:AbstractDict{K,DI}, -} <: AbstractDict{MOI.ConstraintIndex,V} end +abstract type AbstractDoubleDict{V} <: AbstractDict{MOI.ConstraintIndex,V} end + +abstract type AbstractDoubleDictInner{F,S,V} <: + AbstractDict{MOI.ConstraintIndex{F,S},V} end """ DoubleDict{V} -Optimized dictionary to map `MOI.ConstraintIndex` to values of type `V`. +An optimized dictionary to map `MOI.ConstraintIndex` to values of type `V`. + +Works as a `AbstractDict{MOI.ConstraintIndex,V}` with minimal differences. -Works as a `AbstractDict{MOI.ConstraintIndex, V}` with minimal differences. +If `V` is also a `MOI.ConstraintIndex`, use [`IndexDoubleDict`](@ref). Note that `MOI.ConstraintIndex` is not a concrete type, opposed to `MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}`, which is a concrete type. -When optimal performance or type stability is required it is possible to obtain -a fully type stable dictionary with values of type `V` and keys of type -`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` from the dictionary -`dict`, for instance: +When looping through multiple keys of the same Function-in-Set type, use ```julia -inner = dict[MOI.SingleVariable, MOI.Integers] +inner = dict[F, S] ``` +to return a type-stable [`DoubleDictInner`](@ref). """ -struct DoubleDict{V,DI,DO} <: - AbstractDoubleDict{Tuple{DataType,DataType},V,Int64,DI,DO} - dict::DO +struct DoubleDict{V} <: AbstractDoubleDict{V} + dict::Dict{Tuple{DataType,DataType},Dict{Int64,V}} function DoubleDict{V}() where {V} - return new{V,Dict{Int64,V},Dict{Tuple{DataType,DataType},Dict{Int64,V}}}( - Dict{Tuple{DataType,DataType},Dict{Int64,V}}(), - ) + return new{V}(Dict{Tuple{DataType,DataType},Dict{Int64,V}}()) end end """ - IndexDoubleDict + DoubleDictInner{F,S,V} -Specialized version of `DoubleDict` in which keys and values are of type -`ConstraintIndex` +A type stable inner dictionary of [`DoubleDict`](@ref). +""" +mutable struct DoubleDictInner{F,S,V} <: AbstractDoubleDictInner{F,S,V} + dict::DoubleDict{V} + inner::Union{Dict{Int64,V},Nothing} + function DoubleDictInner{F,S}(d::DoubleDict{V}) where {F,S,V} + return new{F,S,V}(d, get(d.dict, (F, S), nothing)) + end +end -This is an optimized dictionary to map `MOI.ConstraintIndex` to values of -type `MOI.ConstraintIndex`. +function _inner( + d::DoubleDictInner{F,S,V}; + initialize::Bool = false, +) where {F,S,V} + if initialize && d.inner === nothing + d.inner = Dict{Int64,V}() + d.dict.dict[(F, S)] = d.inner + end + return d.inner::Dict{Int64,V} +end -Works as a `AbstractDict{MOI.ConstraintIndex, V}` with minimal differences. +""" + IndexDoubleDict -Note that `MOI.ConstraintIndex` is not a concrete type, opposed to -`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}`, which is a concrete -type. +A specialized version of [`DoubleDict`] in which the values are of type +`MOI.ConstraintIndex` -When optimal performance or type stability is required its possible to obtain a -fully type stable dictionary with values of type -`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` and keys of type -`MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integers}` from the dictionary -`dict`, for instance: +When looping through multiple keys of the same Function-in-Set type, use ```julia -inner = dict[MOI.SingleVariable, MOI.Integers] +inner = dict[F, S] ``` +to return a type-stable [`IndexDoubleDictInner`](@ref). """ -struct IndexDoubleDict{DI,DO} <: AbstractDoubleDict{ - Tuple{DataType,DataType}, - MOI.ConstraintIndex, - Int64, - DI, - DO, -} - dict::DO +struct IndexDoubleDict <: AbstractDoubleDict{MOI.ConstraintIndex} + dict::Dict{Tuple{DataType,DataType},Dict{Int64,Int64}} function IndexDoubleDict() - return new{ - Dict{Int64,Int64}, - Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}, - }( - Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}(), - ) + return new(Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}()) end end -struct FunctionSetDoubleDict{DI,DO} <: - AbstractDoubleDict{Tuple{DataType,DataType},Tuple,Int64,DI,DO} - dict::DO - function FunctionSetDoubleDict() - return new{Dict{Int64},Dict{Tuple{DataType,DataType},Dict{Int64}}}( - Dict{Tuple{DataType,DataType},Dict{Int64}}(), - ) +""" + IndexDoubleDictInner{F,S} + +A type stable inner dictionary of [`IndexDoubleDict`](@ref). +""" +mutable struct IndexDoubleDictInner{F,S} <: + AbstractDoubleDictInner{F,S,MOI.ConstraintIndex{F,S}} + dict::IndexDoubleDict + inner::Union{Dict{Int64,Int64},Nothing} + function IndexDoubleDictInner{F,S}(d::IndexDoubleDict) where {F,S} + return new{F,S}(d, get(d.dict, (F, S), nothing)) end end -const MainIndexDoubleDict = IndexDoubleDict{ - Dict{Int64,Int64}, - Dict{Tuple{DataType,DataType},Dict{Int64,Int64}}, -} +function _inner( + d::IndexDoubleDictInner{F,S}; + initialize::Bool = false, +) where {F,S} + if initialize && d.inner === nothing + d.inner = Dict{Int64,Int64}() + d.dict.dict[(F, S)] = d.inner + end + return d.inner::Dict{Int64,Int64} +end -@inline function _typed_value( - ::DoubleDict{V}, - v::V, - ::Type{F}, - ::Type{S}, -)::V where {V,F,S} +# _typed_value + +@inline function _typed_value(::DoubleDict{V}, v::V, ::Type, ::Type) where {V} + return v +end + +@inline function _typed_value(::DoubleDictInner{F,S,V}, v::V) where {F,S,V} return v end @@ -112,32 +118,32 @@ end v::Int64, ::Type{F}, ::Type{S}, -)::MOI.ConstraintIndex{F,S} where {F,S} +) where {F,S} return MOI.ConstraintIndex{F,S}(v) end -@inline function _typed_value( - ::FunctionSetDoubleDict, - v::Tuple{F,S}, - ::Type{F}, - ::Type{S}, -)::Tuple{F,S} where {F,S} - return v +@inline function _typed_value(::IndexDoubleDictInner{F,S}, v::Int64) where {F,S} + return MOI.ConstraintIndex{F,S}(v) end +# _reverse_dict + # reversing IndexDoubleDict is ok because they map CI to CI function MOI.Utilities._reverse_dict( - dest::IndexDoubleDict{DI}, - src::IndexDoubleDict{DI}, -) where {DI} + dest::IndexDoubleDict, + src::IndexDoubleDict, +) for (k, v) in src.dict dest.dict[k] = MOI.Utilities._reverse_dict(v) end + return end # reversing other double dict types is not ok because the map CI fo K # so it wont be a double dict anymore, double dict keys are always CIs. # We keep the default fallback +# Base.sizehint! + function Base.sizehint!(::AbstractDoubleDict, ::Integer) return throw( ErrorException( @@ -148,6 +154,13 @@ function Base.sizehint!(::AbstractDoubleDict, ::Integer) ) end +function Base.sizehint!(d::AbstractDoubleDictInner, n::Integer) + inner = _inner(d; initialize = true) + return sizehint!(inner, n) +end + +# Base.length + function Base.length(d::AbstractDoubleDict) len = 0 for inner in values(d.dict) @@ -156,6 +169,15 @@ function Base.length(d::AbstractDoubleDict) return len end +function Base.length(d::AbstractDoubleDictInner) + if d.inner === nothing + return 0 + end + return length(_inner(d)) +end + +# Base.haskey + function Base.haskey( dict::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, @@ -164,6 +186,18 @@ function Base.haskey( return inner !== nothing ? haskey(inner, key.value) : false end +function Base.haskey( + d::AbstractDoubleDictInner{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + if d.inner === nothing + return false + end + return haskey(_inner(d), key.value) +end + +# Base.get + function Base.get( dict::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, @@ -176,6 +210,8 @@ function Base.get( return default end +# Base.getindex + function Base.getindex( dict::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, @@ -184,95 +220,148 @@ function Base.getindex( return _typed_value(dict, inner[key.value], F, S) end -function _init_inner_dict( - ::AbstractDoubleDict{K,V}, - ::Type{F}, - ::Type{S}, -) where {K,V,F,S} - return Dict{Int64,V}() +function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return DoubleDictInner{F,S}(d) end -function _init_inner_dict( - ::FunctionSetDoubleDict, - ::Type{F}, - ::Type{S}, +function Base.getindex( + d::AbstractDoubleDictInner{F,S}, + key::MOI.ConstraintIndex{F,S}, ) where {F,S} - return Dict{Int64,Tuple{F,S}}() + if d.inner === nothing + throw(KeyError(key)) + end + return _typed_value(d, _inner(d)[key.value]) end -function _init_inner_dict(::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return Dict{Int64,Int64}() +function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return IndexDoubleDictInner{F,S}(d) end -""" - lazy_get(dict::DoubleDict, ::Type{F}, ::Type{S}) +# _initialize_and_get -description -""" -function lazy_get( - dict::AbstractDoubleDict{K,V}, +function _initialize_and_get( + dict::AbstractDoubleDict{V}, ::Type{F}, ::Type{S}, -) where {K,V,F,S} - return get!(() -> _init_inner_dict(dict, F, S), dict.dict, (F, S)) +)::Dict{Int64,V} where {F,S,V} + return get!(() -> Dict{Int64,V}(), dict.dict, (F, S)) end +function _initialize_and_get( + dict::IndexDoubleDict, + ::Type{F}, + ::Type{S}, +)::Dict{Int64,Int64} where {F,S} + return get!(() -> Dict{Int64,Int64}(), dict.dict, (F, S)) +end + +# Base.setindex! + function Base.setindex!( - dict::AbstractDoubleDict{K,V}, + dict::AbstractDoubleDict{V}, value::V, key::MOI.ConstraintIndex{F,S}, -) where {K,V,F,S} - inner = lazy_get(dict, F, S) +) where {F,S,V} + inner = _initialize_and_get(dict, F, S) inner[key.value] = value return value end function Base.setindex!( - dict::DoubleDict{V}, + d::DoubleDictInner{F,S,V}, value::V, key::MOI.ConstraintIndex{F,S}, -) where {V,F,S} - inner = lazy_get(dict, F, S)::Dict{Int64,V} +) where {F,S,V} + inner = _inner(d; initialize = true) inner[key.value] = value return value end + function Base.setindex!( dict::IndexDoubleDict, value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = lazy_get(dict, F, S)::Dict{Int64,Int64} + inner = _initialize_and_get(dict, F, S) inner[key.value] = value.value return value end +function Base.setindex!( + d::IndexDoubleDictInner{F,S}, + value::MOI.ConstraintIndex{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + inner = _inner(d; initialize = true) + inner[key.value] = value.value + return value +end + +# Base.empty! + function Base.empty!(d::AbstractDoubleDict) Base.empty!(d.dict) return d end +function Base.empty!(d::AbstractDoubleDictInner) + if d.inner === nothing + return d + end + empty!(_inner(d)) + return d +end + +# Base.delete! + function Base.delete!( d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = lazy_get(d, F, S) + inner = _initialize_and_get(d, F, S) delete!(inner, key.value) return d end +function Base.delete!( + d::AbstractDoubleDictInner{F,S}, + key::MOI.ConstraintIndex{F,S}, +) where {F,S} + if !(d.inner === nothing) + delete!(_inner(d), key.value) + end + return d +end + +# Base.isempty + function Base.isempty(d::AbstractDoubleDict) - if isempty(d.dict) - return true + return isempty(d.dict) || all(isempty, values(d.dict)) +end + +function Base.isempty(d::AbstractDoubleDictInner) + return d.inner === nothing || isempty(_inner(d)) +end + +# Base.values + +function Base.values(d::AbstractDoubleDict{V})::Vector{V} where {V} + out = V[] + for inner in values(d.dict) + append!(out, values(inner)) end - for val in values(d.dict) - if !isempty(val) - return false - end + return out +end + +function Base.values(d::DoubleDictInner{F,S,V})::Vector{V} where {F,S,V} + if d.inner === nothing + return V[] end - return true + return collect(values(_inner(d))) end -function Base.values(d::IndexDoubleDict)::Vector{MOI.ConstraintIndex} +function Base.values(d::IndexDoubleDict) out = MOI.ConstraintIndex[] for ((F, S), inner) in d.dict append!(out, MOI.ConstraintIndex{F,S}.(values(inner))) @@ -280,14 +369,15 @@ function Base.values(d::IndexDoubleDict)::Vector{MOI.ConstraintIndex} return out end -function Base.values(d::AbstractDoubleDict{K,V})::Vector{V} where {K,V} - out = V[] - for (_, inner) in d.dict - append!(out, values(inner)) +function Base.values(d::IndexDoubleDictInner{F,S}) where {F,S} + if d.inner === nothing + return MOI.ConstraintIndex{F,S}[] end - return out + return MOI.ConstraintIndex{F,S}.(values(_inner(d))) end +# Base.keys + function Base.keys(d::AbstractDoubleDict) out = MOI.ConstraintIndex[] for ((F, S), inner) in d.dict @@ -296,10 +386,19 @@ function Base.keys(d::AbstractDoubleDict) return out end +function Base.keys(d::AbstractDoubleDictInner{F,S}) where {F,S} + if d.inner === nothing + return MOI.ConstraintIndex{F,S}[] + end + return MOI.ConstraintIndex{F,S}.(keys(_inner(d))) +end + +# Base.iterate + function Base.iterate(d::AbstractDoubleDict) o_next = iterate(d.dict) if o_next === nothing - return nothing + return end (o_i, o_state) = o_next ((F, S), inner) = o_i @@ -307,15 +406,28 @@ function Base.iterate(d::AbstractDoubleDict) while i_next === nothing o_next = iterate(d.dict, o_state) if o_next === nothing - return nothing + return end (o_i, o_state) = o_next ((F, S), inner) = o_i i_next = iterate(inner) end (i_i, i_state) = i_next - return MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S), - (i_state, (o_i, o_state)) + pair = MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S) + return pair, (i_state, (o_i, o_state)) +end + +function Base.iterate(d::AbstractDoubleDictInner{F,S}) where {F,S} + if d.inner === nothing + return + end + next = iterate(d.inner) + if next === nothing + return + end + (i, state) = next + value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) + return value, (state, d.inner) end function Base.iterate(d::AbstractDoubleDict, state) @@ -332,288 +444,11 @@ function Base.iterate(d::AbstractDoubleDict, state) i_next = iterate(inner) end (i_i, i_state) = i_next - return MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S), - (i_state, (o_i, o_state)) -end - -abstract type AbstractWithType{F,S,V,DI,DD} <: - AbstractDict{MOI.ConstraintIndex{F,S},V} end - -""" - WithType{F,S,V,DI,DD} - -Used to specialize methods and iterators for a given contraint type -`MOI.ConstraintIndex{F,S}` returning elements of type `V`. -""" -mutable struct WithType{F,S,V,DI,DD} <: AbstractWithType{F,S,V,DI,DD} - dict::DD - inner::Union{DI,Nothing} - function WithType{F,S}( - d::DD, - ) where { - V, - DI<:AbstractDict{Int64,V}, - DO<:AbstractDict{Tuple{DataType,DataType},DI}, - DD<:DoubleDict{V,DI,DO}, - F, - S, - } - inner = get(d.dict, (F, S), nothing) - return new{F,S,V,DI,DD}(d, inner) - end -end - -mutable struct IndexWithType{F,S,V,DI,DD} <: - AbstractWithType{F,S,MOI.ConstraintIndex{F,S},DI,DD} - dict::DD - inner::Union{DI,Nothing} - function IndexWithType{F,S}( - d::DD, - ) where { - DI<:AbstractDict{Int64,Int64}, - DO<:AbstractDict{Tuple{DataType,DataType},DI}, - DD<:IndexDoubleDict{DI,DO}, - F, - S, - } - inner = get(d.dict, (F, S), nothing)::Union{DI,Nothing} - return new{F,S,MOI.ConstraintIndex{F,S},DI,DD}(d, inner) - end -end - -mutable struct FunctionSetWithType{F,S,V,DI,DD} <: - AbstractWithType{F,S,Tuple{F,S},DI,DD} - dict::DD - inner::Union{DI,Nothing} - function FunctionSetWithType{F,S}( - d::DD, - ) where { - F, - S, - DI<:AbstractDict{Int64}, - DO<:AbstractDict{Tuple{DataType,DataType},DI}, - DD<:FunctionSetDoubleDict{DI,DO}, - } - inner = get(d.dict, (F, S), nothing)::Union{DI{Tuple{F,S}},Nothing} - return new{F,S,Tuple{F,S},DI{Tuple{F,S}},DD}(d, inner) - end -end - -function with_type( - d::DoubleDict, - ::Type{F}, - ::Type{S}, -)::WithType{F,S} where {F,S} - return WithType{F,S}(d)::WithType{F,S} -end - -function with_type( - d::IndexDoubleDict, - ::Type{F}, - ::Type{S}, -)::IndexWithType{F,S} where {F,S} - return IndexWithType{F,S}(d)::IndexWithType{F,S} -end - -function with_type( - d::FunctionSetDoubleDict, - ::Type{F}, - ::Type{S}, -)::FunctionSetWithType{F,S} where {F,S} - return FunctionSetWithType{F,S}(d)::FunctionSetWithType{F,S} -end - -function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return WithType{F,S}(d) -end - -function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return IndexWithType{F,S}(d) -end - -function Base.getindex( - d::FunctionSetDoubleDict, - ::Type{F}, - ::Type{S}, -) where {F,S} - return FunctionSetWithType{F,S}(d) -end - -@inline function _typed_value(::WithType{F,S,V}, v::V)::V where {V,F,S} - return v -end - -@inline function _typed_value( - ::IndexWithType{F,S}, - v::Int64, -)::MOI.ConstraintIndex{F,S} where {F,S} - return MOI.ConstraintIndex{F,S}(v) -end - -@inline function _typed_value( - ::FunctionSetWithType{F,S,V}, - v::Tuple{F,S}, -)::Tuple{F,S} where {V,F,S} - return v -end - -function _initialize_inner!(d::AbstractWithType{F,S,V,D}) where {F,S,V,D} - d.inner = D() - d.dict.dict[(F, S)] = d.inner - return -end - -_inner_is_empty(d::AbstractWithType)::Bool = d.inner === nothing - -function _inner(d::AbstractWithType{F,S,V,D}) where {F,S,V,D} - return d.inner::D -end - -function Base.sizehint!(d::AbstractWithType{F,S}, n::Integer) where {F,S} - if _inner_is_empty(d) - _initialize_inner!(d) - end - return sizehint!(_inner(d), n) -end - -function Base.length(d::AbstractWithType{F,S}) where {F,S} - if _inner_is_empty(d) - return 0 - end - return length(_inner(d)) -end - -function Base.haskey( - d::AbstractWithType{F,S}, - key::MOI.ConstraintIndex{F,S}, -) where {F,S} - if _inner_is_empty(d) - return false - end - return haskey(_inner(d), key.value) -end - -function Base.getindex( - d::AbstractWithType{F,S}, - key::MOI.ConstraintIndex{F,S}, -) where {F,S} - if _inner_is_empty(d) - throw(KeyError(key)) - end - return _typed_value(d, _inner(d)[key.value]) -end - -function Base.setindex!( - d::WithType{F,S,V}, - value::V, - key::MOI.ConstraintIndex{F,S}, -)::V where {F,S,V} - if _inner_is_empty(d) - _initialize_inner!(d) - end - inner = _inner(d) - inner[key.value] = value - return value -end - -function Base.setindex!( - d::IndexWithType{F,S,V}, - value::MOI.ConstraintIndex{F,S}, - key::MOI.ConstraintIndex{F,S}, -)::MOI.ConstraintIndex{F,S} where {F,S,V} - if _inner_is_empty(d) - _initialize_inner!(d) - end - inner = _inner(d)::Dict{Int64,Int64} - inner[key.value] = value.value - return value -end - -function Base.setindex!( - d::FunctionSetWithType{F,S,V}, - value::Tuple{F,S}, - key::MOI.ConstraintIndex{F,S}, -)::Tuple{F,S} where {F,S,V} - if _inner_is_empty(d) - _initialize_inner!(d) - end - inner = _inner(d)::Dict{Int64,Tuple{F,S}} - inner[key.value] = value - return value -end - -function Base.empty!(d::AbstractWithType{F,S}) where {F,S} - if _inner_is_empty(d) - return d - end - return empty!(_inner(d)) -end - -function Base.delete!( - d::AbstractWithType{F,S}, - key::MOI.ConstraintIndex{F,S}, -) where {F,S} - if _inner_is_empty(d) - return d - end - k_value = key.value::Int64 - delete!(_inner(d), k_value) - return d -end - -function Base.isempty(d::AbstractWithType{F,S}) where {F,S} - if _inner_is_empty(d) - return true - end - return isempty(_inner(d)) -end - -function Base.values(d::WithType{F,S,V})::Vector{V} where {F,S,V} - if _inner_is_empty(d) - return V[] - end - return collect(values(_inner(d))) -end - -function Base.values( - d::IndexWithType{F,S}, -)::Vector{MOI.ConstraintIndex{F,S}} where {F,S} - if _inner_is_empty(d) - return MOI.ConstraintIndex{F,S}[] - end - return MOI.ConstraintIndex{F,S}.(values(_inner(d))) -end - -function Base.values( - d::FunctionSetWithType{F,S}, -)::Vector{Tuple{F,S}} where {F,S} - if _inner_is_empty(d) - return Tuple{F,S}[] - end - return collect(values(_inner(d))) -end - -function Base.keys(d::AbstractWithType{F,S}) where {F,S} - if _inner_is_empty(d) - return MOI.ConstraintIndex{F,S}[] - end - return MOI.ConstraintIndex{F,S}.(keys(_inner(d))) -end - -function Base.iterate(d::AbstractWithType{F,S}) where {F,S} - if d.inner === nothing - return - end - next = iterate(d.inner) - if next === nothing - return - end - (i, state) = next - value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) - return value, (state, d.inner) + pair = MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S) + return pair, (i_state, (o_i, o_state)) end -function Base.iterate(d::AbstractWithType{F,S}, state) where {F,S} +function Base.iterate(d::AbstractDoubleDictInner{F,S}, state) where {F,S} (istate, inner) = state next = iterate(inner, istate) if next === nothing diff --git a/src/Utilities/copy/index_map.jl b/src/Utilities/copy/index_map.jl index dddc54e2ad..a1cffa51ec 100644 --- a/src/Utilities/copy/index_map.jl +++ b/src/Utilities/copy/index_map.jl @@ -5,7 +5,7 @@ struct IndexMap <: AbstractDict{MOI.Index,MOI.Index} typeof(CleverDicts.key_to_index), typeof(CleverDicts.index_to_key), } - con_map::DoubleDicts.MainIndexDoubleDict + con_map::DoubleDicts.IndexDoubleDict end """ @@ -46,7 +46,7 @@ end function _identity_constraints_map( model, - map::MOIU.DoubleDicts.IndexWithType{F,S}, + map::MOIU.DoubleDicts.IndexDoubleDictInner{F,S}, ) where {F,S} for c in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) map[c] = c diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index 7d7c8ebac9..9e5f73f570 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -90,8 +90,8 @@ function _test_basic_functionality(dict, k_values, v_values) @test length(dict) == 0 dict[k_values[1]] = v_values[1] - idict = DoubleDicts.with_type(dict, MOI.SingleVariable, MOI.Integer) - bdict = DoubleDicts.with_type(dict, MOI.SingleVariable, MOI.ZeroOne) + idict = dict[MOI.SingleVariable, MOI.Integer] + bdict = dict[MOI.SingleVariable, MOI.ZeroOne] idict_ = dict[MOI.SingleVariable, MOI.Integer] @test idict.dict === idict_.dict sizehint!(idict, 2) @@ -126,7 +126,7 @@ function _test_basic_functionality(dict, k_values, v_values) bdict[k_values[3]] = v_values[3] length(bdict) == 1 - edict = DoubleDicts.with_type(dict, MOI.SingleVariable, MOI.EqualTo{Bool}) + edict = dict[MOI.SingleVariable, MOI.EqualTo{Bool}] ek = MOI.ConstraintIndex{MOI.SingleVariable,MOI.EqualTo{Bool}}(1) delete!(edict, ek) @test_throws KeyError edict[ek] @@ -163,18 +163,6 @@ function test_IndexDoubleDict() return end -function test_FunctionSetDoubleDict() - dict = DoubleDicts.FunctionSetDoubleDict() - keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] - vals = [ - (MOI.SingleVariable(MOI.VariableIndex(1)), MOI.Integer()), - (MOI.SingleVariable(MOI.VariableIndex(2)), MOI.Integer()), - (MOI.SingleVariable(MOI.VariableIndex(1)), MOI.ZeroOne()), - ] - _test_basic_functionality(dict, keys, vals) - return -end - end # module TestDoubleDicts.runtests() From 6b32ec6ad2477c8c119f69d818b0c1addaf0348f Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 7 Jul 2021 16:45:41 +1200 Subject: [PATCH 03/11] Fix formatting --- docs/src/submodules/Utilities/reference.md | 1 - test/Utilities/DoubleDicts.jl | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/src/submodules/Utilities/reference.md b/docs/src/submodules/Utilities/reference.md index a0524e6de3..61f316dd26 100644 --- a/docs/src/submodules/Utilities/reference.md +++ b/docs/src/submodules/Utilities/reference.md @@ -199,7 +199,6 @@ Utilities.is_diagonal_vectorized_index Utilities.side_dimension_for_vectorized_dimension ``` - ## DoubleDicts ```@docs diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index 9e5f73f570..e6d407c389 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -140,7 +140,11 @@ end function test_DoubleDict() dict = DoubleDicts.DoubleDict{Float64}() - keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] + keys = [ + MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), + MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), + MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1), + ] vals = [1.0, 2.0, 1.0] _test_basic_functionality(dict, keys, vals) return @@ -148,7 +152,11 @@ end function test_IndexDoubleDict() dict = DoubleDicts.IndexDoubleDict() - keys = [MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1)] + keys = [ + MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(1), + MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(2), + MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(1), + ] vals = keys _test_basic_functionality(dict, keys, vals) src = DoubleDicts.IndexDoubleDict() From ceff36bc809ba93016d2710df337aa8b4fc1e0a9 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 7 Jul 2021 16:54:49 +1200 Subject: [PATCH 04/11] Fix documentation --- docs/src/submodules/Utilities/overview.md | 11 ++++++----- docs/src/submodules/Utilities/reference.md | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/src/submodules/Utilities/overview.md b/docs/src/submodules/Utilities/overview.md index 449c81ec23..373d4aa5a9 100644 --- a/docs/src/submodules/Utilities/overview.md +++ b/docs/src/submodules/Utilities/overview.md @@ -495,13 +495,14 @@ end ## DoubleDicts When writing MOI interfaces, we often need to handle situations in which we map -[`MOI.ConstraintIndex`](@ref)s to different values. For example, to a string -for [`MOI.ConstraintName`](@ref). +[`ConstraintIndex`](@ref)s to different values. For example, to a string +for [`ConstraintName`](@ref). One option is to use a dictionary like `Dict{MOI.ConstraintIndex,String}`. However, this incurs a performance cost because the key is not a concrete type. The DoubleDicts submodule helps this situation by providing two types main -types [`DoubleDict`](@ref) and [`IndexDoubleDict`](@ref). These types act like -normal dictionaries, but internally they use more efficient dictionaries -specialized to the type of the function-set pair. +types [`Utilities.DoubleDicts.DoubleDict`](@ref) and +[`Utilities.DoubleDicts.IndexDoubleDict`](@ref). These types act like normal +dictionaries, but internally they use more efficient dictionaries specialized to +the type of the function-set pair. diff --git a/docs/src/submodules/Utilities/reference.md b/docs/src/submodules/Utilities/reference.md index 61f316dd26..097f3b0ede 100644 --- a/docs/src/submodules/Utilities/reference.md +++ b/docs/src/submodules/Utilities/reference.md @@ -202,8 +202,8 @@ Utilities.side_dimension_for_vectorized_dimension ## DoubleDicts ```@docs -DoubleDict -DoubleDictInner -IndexDoubleDict -IndexDoubleDictInner +Utilities.DoubleDicts.DoubleDict +Utilities.DoubleDicts.DoubleDictInner +Utilities.DoubleDicts.IndexDoubleDict +Utilities.DoubleDicts.IndexDoubleDictInner ``` From 1d2a9b7a9cd9db0169a38830df0ff2c8552e8603 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 8 Jul 2021 12:59:27 +1200 Subject: [PATCH 05/11] Tidy iterators --- src/Utilities/DoubleDicts.jl | 228 ++++++++++++----------------------- 1 file changed, 75 insertions(+), 153 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 1519cf553d..e3520b4b04 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -9,6 +9,8 @@ abstract type AbstractDoubleDict{V} <: AbstractDict{MOI.ConstraintIndex,V} end abstract type AbstractDoubleDictInner{F,S,V} <: AbstractDict{MOI.ConstraintIndex{F,S},V} end +_inner(d::AbstractDoubleDictInner) = d.inner + """ DoubleDict{V} @@ -42,21 +44,10 @@ A type stable inner dictionary of [`DoubleDict`](@ref). """ mutable struct DoubleDictInner{F,S,V} <: AbstractDoubleDictInner{F,S,V} dict::DoubleDict{V} - inner::Union{Dict{Int64,V},Nothing} + inner::Dict{Int64,V} function DoubleDictInner{F,S}(d::DoubleDict{V}) where {F,S,V} - return new{F,S,V}(d, get(d.dict, (F, S), nothing)) - end -end - -function _inner( - d::DoubleDictInner{F,S,V}; - initialize::Bool = false, -) where {F,S,V} - if initialize && d.inner === nothing - d.inner = Dict{Int64,V}() - d.dict.dict[(F, S)] = d.inner + return new{F,S,V}(d, get!(d.dict, (F, S), Dict{Int64,V}())) end - return d.inner::Dict{Int64,V} end """ @@ -86,42 +77,18 @@ A type stable inner dictionary of [`IndexDoubleDict`](@ref). mutable struct IndexDoubleDictInner{F,S} <: AbstractDoubleDictInner{F,S,MOI.ConstraintIndex{F,S}} dict::IndexDoubleDict - inner::Union{Dict{Int64,Int64},Nothing} + inner::Dict{Int64,Int64} function IndexDoubleDictInner{F,S}(d::IndexDoubleDict) where {F,S} - return new{F,S}(d, get(d.dict, (F, S), nothing)) + return new{F,S}(d, get!(d.dict, (F, S), Dict{Int64,Int64}())) end end -function _inner( - d::IndexDoubleDictInner{F,S}; - initialize::Bool = false, -) where {F,S} - if initialize && d.inner === nothing - d.inner = Dict{Int64,Int64}() - d.dict.dict[(F, S)] = d.inner - end - return d.inner::Dict{Int64,Int64} -end - # _typed_value -@inline function _typed_value(::DoubleDict{V}, v::V, ::Type, ::Type) where {V} - return v -end - @inline function _typed_value(::DoubleDictInner{F,S,V}, v::V) where {F,S,V} return v end -@inline function _typed_value( - ::IndexDoubleDict, - v::Int64, - ::Type{F}, - ::Type{S}, -) where {F,S} - return MOI.ConstraintIndex{F,S}(v) -end - @inline function _typed_value(::IndexDoubleDictInner{F,S}, v::Int64) where {F,S} return MOI.ConstraintIndex{F,S}(v) end @@ -133,8 +100,8 @@ function MOI.Utilities._reverse_dict( dest::IndexDoubleDict, src::IndexDoubleDict, ) - for (k, v) in src.dict - dest.dict[k] = MOI.Utilities._reverse_dict(v) + for (key, value) in src.dict + dest.dict[key] = MOI.Utilities._reverse_dict(value) end return end @@ -155,8 +122,7 @@ function Base.sizehint!(::AbstractDoubleDict, ::Integer) end function Base.sizehint!(d::AbstractDoubleDictInner, n::Integer) - inner = _inner(d; initialize = true) - return sizehint!(inner, n) + return sizehint!(_inner(d), n) end # Base.length @@ -169,43 +135,34 @@ function Base.length(d::AbstractDoubleDict) return len end -function Base.length(d::AbstractDoubleDictInner) - if d.inner === nothing - return 0 - end - return length(_inner(d)) -end +Base.length(d::AbstractDoubleDictInner) = length(_inner(d)) # Base.haskey function Base.haskey( - dict::AbstractDoubleDict, + d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = get(dict.dict, (F, S), nothing) - return inner !== nothing ? haskey(inner, key.value) : false + return haskey(getindex(d, F, S), key) end function Base.haskey( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - if d.inner === nothing - return false - end return haskey(_inner(d), key.value) end # Base.get function Base.get( - dict::AbstractDoubleDict, + d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, default, ) where {F,S} - inner = get(dict.dict, (F, S), nothing) - if inner !== nothing && haskey(inner, key.value) - return _typed_value(dict, inner[key.value], F, S) + inner = getindex(d, F, S) + if haskey(inner, key) + return inner[key] end return default end @@ -213,11 +170,11 @@ end # Base.getindex function Base.getindex( - dict::AbstractDoubleDict, + d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = dict.dict[(F, S)] - return _typed_value(dict, inner[key.value], F, S) + inner = getindex(d, F, S) + return inner[key] end function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} @@ -228,43 +185,26 @@ function Base.getindex( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - if d.inner === nothing + inner = _inner(d) + if !haskey(inner, key.value) throw(KeyError(key)) end - return _typed_value(d, _inner(d)[key.value]) + return _typed_value(d, inner[key.value]) end function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} return IndexDoubleDictInner{F,S}(d) end -# _initialize_and_get - -function _initialize_and_get( - dict::AbstractDoubleDict{V}, - ::Type{F}, - ::Type{S}, -)::Dict{Int64,V} where {F,S,V} - return get!(() -> Dict{Int64,V}(), dict.dict, (F, S)) -end - -function _initialize_and_get( - dict::IndexDoubleDict, - ::Type{F}, - ::Type{S}, -)::Dict{Int64,Int64} where {F,S} - return get!(() -> Dict{Int64,Int64}(), dict.dict, (F, S)) -end - # Base.setindex! function Base.setindex!( - dict::AbstractDoubleDict{V}, + d::AbstractDoubleDict{V}, value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} - inner = _initialize_and_get(dict, F, S) - inner[key.value] = value + inner = getindex(d, F, S) + inner[key] = value return value end @@ -273,18 +213,17 @@ function Base.setindex!( value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} - inner = _inner(d; initialize = true) - inner[key.value] = value + _inner(d)[key.value] = value return value end function Base.setindex!( - dict::IndexDoubleDict, + d::IndexDoubleDict, value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = _initialize_and_get(dict, F, S) - inner[key.value] = value.value + inner = getindex(d, F, S) + inner[key] = value return value end @@ -293,8 +232,7 @@ function Base.setindex!( value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = _inner(d; initialize = true) - inner[key.value] = value.value + _inner(d)[key.value] = value.value return value end @@ -306,9 +244,6 @@ function Base.empty!(d::AbstractDoubleDict) end function Base.empty!(d::AbstractDoubleDictInner) - if d.inner === nothing - return d - end empty!(_inner(d)) return d end @@ -319,8 +254,7 @@ function Base.delete!( d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = _initialize_and_get(d, F, S) - delete!(inner, key.value) + delete!(getindex(d, F, S), key) return d end @@ -328,9 +262,7 @@ function Base.delete!( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - if !(d.inner === nothing) - delete!(_inner(d), key.value) - end + delete!(_inner(d), key.value) return d end @@ -340,9 +272,7 @@ function Base.isempty(d::AbstractDoubleDict) return isempty(d.dict) || all(isempty, values(d.dict)) end -function Base.isempty(d::AbstractDoubleDictInner) - return d.inner === nothing || isempty(_inner(d)) -end +Base.isempty(d::AbstractDoubleDictInner) = isempty(_inner(d)) # Base.values @@ -355,10 +285,7 @@ function Base.values(d::AbstractDoubleDict{V})::Vector{V} where {V} end function Base.values(d::DoubleDictInner{F,S,V})::Vector{V} where {F,S,V} - if d.inner === nothing - return V[] - end - return collect(values(_inner(d))) + return V[v for v in values(_inner(d))] end function Base.values(d::IndexDoubleDict) @@ -370,9 +297,6 @@ function Base.values(d::IndexDoubleDict) end function Base.values(d::IndexDoubleDictInner{F,S}) where {F,S} - if d.inner === nothing - return MOI.ConstraintIndex{F,S}[] - end return MOI.ConstraintIndex{F,S}.(values(_inner(d))) end @@ -387,76 +311,74 @@ function Base.keys(d::AbstractDoubleDict) end function Base.keys(d::AbstractDoubleDictInner{F,S}) where {F,S} - if d.inner === nothing - return MOI.ConstraintIndex{F,S}[] - end return MOI.ConstraintIndex{F,S}.(keys(_inner(d))) end # Base.iterate function Base.iterate(d::AbstractDoubleDict) - o_next = iterate(d.dict) - if o_next === nothing - return + outer_next = iterate(d.dict) + if outer_next === nothing + return # There are no keys. end - (o_i, o_state) = o_next - ((F, S), inner) = o_i - i_next = iterate(inner) - while i_next === nothing - o_next = iterate(d.dict, o_state) - if o_next === nothing + # The result is a (F,S)=>inner pair. + ((F, S), inner), outer_state = outer_next + inner_next = iterate(inner) + while inner_next === nothing + # It may be that the inner dictionary is empty! If so, we should go to + # the next element in the outer dictionary. + outer_next = iterate(d.dict, outer_state) + if outer_next === nothing return end - (o_i, o_state) = o_next - ((F, S), inner) = o_i - i_next = iterate(inner) + ((F, S), inner), outer_state = outer_next + # Start iterating from scratch on this new `inner` object. + inner_next = iterate(inner) end - (i_i, i_state) = i_next - pair = MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S) - return pair, (i_state, (o_i, o_state)) + (k, v), inner_state = inner_next + result = MOI.ConstraintIndex{F,S}(k) => _typed_value(getindex(d, F, S), v) + return result, (inner_state, outer_next) end function Base.iterate(d::AbstractDoubleDictInner{F,S}) where {F,S} - if d.inner === nothing - return - end - next = iterate(d.inner) + next = iterate(_inner(d)) if next === nothing return end - (i, state) = next - value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) - return value, (state, d.inner) + (k, v), inner_state = next + result = MOI.ConstraintIndex{F,S}(k) => _typed_value(d, v) + return result, (_inner(d), inner_state) end function Base.iterate(d::AbstractDoubleDict, state) - (i_state, (o_i, o_state)) = state - ((F, S), inner) = o_i - i_next = iterate(inner, i_state) - while i_next === nothing - o_next = iterate(d.dict, o_state) - if o_next === nothing - return nothing + inner_state, outer_next = state + ((F, S), inner), outer_state = outer_next + inner_next = iterate(inner, inner_state) + while inner_next === nothing + # We may have reached the end of this inner dictionary. Get the next + # element of the outer dictionary: + outer_next = iterate(d.dict, outer_state) + if outer_next === nothing + return end - (o_i, o_state) = o_next - ((F, S), inner) = o_i - i_next = iterate(inner) + ((F, S), inner), outer_state = outer_next + # Start iterating from scratch on this new `inner` object. + inner_next = iterate(inner) end - (i_i, i_state) = i_next - pair = MOI.ConstraintIndex{F,S}(i_i[1]) => _typed_value(d, i_i[2], F, S) - return pair, (i_state, (o_i, o_state)) + (k, v), inner_state = inner_next + result = MOI.ConstraintIndex{F,S}(k) => _typed_value(getindex(d, F, S), v) + return result, (inner_state, outer_next) end function Base.iterate(d::AbstractDoubleDictInner{F,S}, state) where {F,S} - (istate, inner) = state - next = iterate(inner, istate) + inner, inner_state = state + next = iterate(inner, inner_state) if next === nothing return end - (i, state) = next - value = MOI.ConstraintIndex{F,S}(i[1]) => _typed_value(d, i[2]) - return value, (state, inner) + ((k, v), next_inner_state) = next + result = MOI.ConstraintIndex{F,S}(k) => _typed_value(d, v) + return result, (inner, next_inner_state) end end From e83a0e2b1aa2e465167f9f259fd074c82cc7e95f Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 8 Jul 2021 13:10:41 +1200 Subject: [PATCH 06/11] Other fixes --- src/Utilities/DoubleDicts.jl | 74 +++++++++++++------------------ src/Utilities/cachingoptimizer.jl | 10 +++++ 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index e3520b4b04..d8d1d8e560 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -9,7 +9,16 @@ abstract type AbstractDoubleDict{V} <: AbstractDict{MOI.ConstraintIndex,V} end abstract type AbstractDoubleDictInner{F,S,V} <: AbstractDict{MOI.ConstraintIndex{F,S},V} end -_inner(d::AbstractDoubleDictInner) = d.inner +""" + typed_value(dict::AbstractDoubleDictInner{F,S,V}, value) where {F,S,V} + +Convert the `value` stored inside `dict` to the equivalent on the outer +`DoubleDict`. This is useful when the value type `V` of the inner dict is +different to the outer dict. (See, e.g., [`IndexDoubleDict`](@ref).) +""" +function typed_value(::AbstractDoubleDictInner{F,S,V}, value::V) where {F,S,V} + return value +end """ DoubleDict{V} @@ -83,32 +92,10 @@ mutable struct IndexDoubleDictInner{F,S} <: end end -# _typed_value - -@inline function _typed_value(::DoubleDictInner{F,S,V}, v::V) where {F,S,V} - return v -end - -@inline function _typed_value(::IndexDoubleDictInner{F,S}, v::Int64) where {F,S} +function typed_value(::IndexDoubleDictInner{F,S}, v::Int64) where {F,S} return MOI.ConstraintIndex{F,S}(v) end -# _reverse_dict - -# reversing IndexDoubleDict is ok because they map CI to CI -function MOI.Utilities._reverse_dict( - dest::IndexDoubleDict, - src::IndexDoubleDict, -) - for (key, value) in src.dict - dest.dict[key] = MOI.Utilities._reverse_dict(value) - end - return -end -# reversing other double dict types is not ok because the map CI fo K -# so it wont be a double dict anymore, double dict keys are always CIs. -# We keep the default fallback - # Base.sizehint! function Base.sizehint!(::AbstractDoubleDict, ::Integer) @@ -122,7 +109,7 @@ function Base.sizehint!(::AbstractDoubleDict, ::Integer) end function Base.sizehint!(d::AbstractDoubleDictInner, n::Integer) - return sizehint!(_inner(d), n) + return sizehint!(d.inner, n) end # Base.length @@ -135,7 +122,7 @@ function Base.length(d::AbstractDoubleDict) return len end -Base.length(d::AbstractDoubleDictInner) = length(_inner(d)) +Base.length(d::AbstractDoubleDictInner) = length(d.inner) # Base.haskey @@ -150,7 +137,7 @@ function Base.haskey( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - return haskey(_inner(d), key.value) + return haskey(d.inner, key.value) end # Base.get @@ -185,11 +172,10 @@ function Base.getindex( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = _inner(d) - if !haskey(inner, key.value) + if !haskey(d.inner, key.value) throw(KeyError(key)) end - return _typed_value(d, inner[key.value]) + return typed_value(d, d.inner[key.value]) end function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} @@ -213,7 +199,7 @@ function Base.setindex!( value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} - _inner(d)[key.value] = value + d.inner[key.value] = value return value end @@ -232,7 +218,7 @@ function Base.setindex!( value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - _inner(d)[key.value] = value.value + d.inner[key.value] = value.value return value end @@ -244,7 +230,7 @@ function Base.empty!(d::AbstractDoubleDict) end function Base.empty!(d::AbstractDoubleDictInner) - empty!(_inner(d)) + empty!(d.inner) return d end @@ -262,7 +248,7 @@ function Base.delete!( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - delete!(_inner(d), key.value) + delete!(d.inner, key.value) return d end @@ -272,7 +258,7 @@ function Base.isempty(d::AbstractDoubleDict) return isempty(d.dict) || all(isempty, values(d.dict)) end -Base.isempty(d::AbstractDoubleDictInner) = isempty(_inner(d)) +Base.isempty(d::AbstractDoubleDictInner) = isempty(d.inner) # Base.values @@ -285,7 +271,7 @@ function Base.values(d::AbstractDoubleDict{V})::Vector{V} where {V} end function Base.values(d::DoubleDictInner{F,S,V})::Vector{V} where {F,S,V} - return V[v for v in values(_inner(d))] + return V[v for v in values(d.inner)] end function Base.values(d::IndexDoubleDict) @@ -297,7 +283,7 @@ function Base.values(d::IndexDoubleDict) end function Base.values(d::IndexDoubleDictInner{F,S}) where {F,S} - return MOI.ConstraintIndex{F,S}.(values(_inner(d))) + return MOI.ConstraintIndex{F,S}.(values(d.inner)) end # Base.keys @@ -311,7 +297,7 @@ function Base.keys(d::AbstractDoubleDict) end function Base.keys(d::AbstractDoubleDictInner{F,S}) where {F,S} - return MOI.ConstraintIndex{F,S}.(keys(_inner(d))) + return MOI.ConstraintIndex{F,S}.(keys(d.inner)) end # Base.iterate @@ -336,18 +322,18 @@ function Base.iterate(d::AbstractDoubleDict) inner_next = iterate(inner) end (k, v), inner_state = inner_next - result = MOI.ConstraintIndex{F,S}(k) => _typed_value(getindex(d, F, S), v) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(getindex(d, F, S), v) return result, (inner_state, outer_next) end function Base.iterate(d::AbstractDoubleDictInner{F,S}) where {F,S} - next = iterate(_inner(d)) + next = iterate(d.inner) if next === nothing return end (k, v), inner_state = next - result = MOI.ConstraintIndex{F,S}(k) => _typed_value(d, v) - return result, (_inner(d), inner_state) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(d, v) + return result, (d.inner, inner_state) end function Base.iterate(d::AbstractDoubleDict, state) @@ -366,7 +352,7 @@ function Base.iterate(d::AbstractDoubleDict, state) inner_next = iterate(inner) end (k, v), inner_state = inner_next - result = MOI.ConstraintIndex{F,S}(k) => _typed_value(getindex(d, F, S), v) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(getindex(d, F, S), v) return result, (inner_state, outer_next) end @@ -377,7 +363,7 @@ function Base.iterate(d::AbstractDoubleDictInner{F,S}, state) where {F,S} return end ((k, v), next_inner_state) = next - result = MOI.ConstraintIndex{F,S}(k) => _typed_value(d, v) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(d, v) return result, (inner, next_inner_state) end diff --git a/src/Utilities/cachingoptimizer.jl b/src/Utilities/cachingoptimizer.jl index d7a26c01f3..495c5905ba 100644 --- a/src/Utilities/cachingoptimizer.jl +++ b/src/Utilities/cachingoptimizer.jl @@ -217,6 +217,16 @@ function _reverse_dict(dest::AbstractDict, src::AbstractDict) return end +function _reverse_dict( + dest::DoubleDicts.IndexDoubleDict, + src::DoubleDicts.IndexDoubleDict, +) + for (key, value) in src.dict + dest.dict[key] = MOI.Utilities._reverse_dict(value) + end + return +end + function _reverse_dict(src::D) where {D<:Dict} return D(values(src) .=> keys(src)) end From 8269413a5007457fde0ead7d87cd86ec9a10bc92 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 12 Jul 2021 17:14:18 +1200 Subject: [PATCH 07/11] Tweaks --- src/Utilities/DoubleDicts.jl | 76 +++++++++++++++++------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index d8d1d8e560..11c4ec119b 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -59,6 +59,10 @@ mutable struct DoubleDictInner{F,S,V} <: AbstractDoubleDictInner{F,S,V} end end +function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return DoubleDictInner{F,S}(d) +end + """ IndexDoubleDict @@ -92,6 +96,10 @@ mutable struct IndexDoubleDictInner{F,S} <: end end +function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return IndexDoubleDictInner{F,S}(d) +end + function typed_value(::IndexDoubleDictInner{F,S}, v::Int64) where {F,S} return MOI.ConstraintIndex{F,S}(v) end @@ -130,7 +138,7 @@ function Base.haskey( d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - return haskey(getindex(d, F, S), key) + return haskey(d[F, S], key) end function Base.haskey( @@ -147,11 +155,19 @@ function Base.get( key::MOI.ConstraintIndex{F,S}, default, ) where {F,S} - inner = getindex(d, F, S) - if haskey(inner, key) - return inner[key] + inner = d[F, S] + return get(inner, key, default) +end + +function Base.get( + d::AbstractDoubleDictInner, + key::MOI.ConstraintIndex{F,S}, + default, +) where {F,S} + if !haskey(d, key) + return default end - return default + return typed_value(d, d.inner[key.value]) end # Base.getindex @@ -160,28 +176,20 @@ function Base.getindex( d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = getindex(d, F, S) + inner = d[F, S] return inner[key] end -function Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return DoubleDictInner{F,S}(d) -end - function Base.getindex( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - if !haskey(d.inner, key.value) + if !haskey(d, key) throw(KeyError(key)) end return typed_value(d, d.inner[key.value]) end -function Base.getindex(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return IndexDoubleDictInner{F,S}(d) -end - # Base.setindex! function Base.setindex!( @@ -189,13 +197,13 @@ function Base.setindex!( value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} - inner = getindex(d, F, S) + inner = d[F, S] inner[key] = value return value end function Base.setindex!( - d::DoubleDictInner{F,S,V}, + d::AbstractDoubleDictInner{F,S,V}, value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} @@ -208,7 +216,7 @@ function Base.setindex!( value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - inner = getindex(d, F, S) + inner = d[F, S] inner[key] = value return value end @@ -240,7 +248,7 @@ function Base.delete!( d::AbstractDoubleDict, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - delete!(getindex(d, F, S), key) + delete!(d[F, S], key) return d end @@ -262,36 +270,22 @@ Base.isempty(d::AbstractDoubleDictInner) = isempty(d.inner) # Base.values -function Base.values(d::AbstractDoubleDict{V})::Vector{V} where {V} +function Base.values(d::AbstractDoubleDict{V}) where {V} out = V[] - for inner in values(d.dict) - append!(out, values(inner)) + for (F, S) in keys(d.dict) + append!(out, values(d[F, S])) end return out end -function Base.values(d::DoubleDictInner{F,S,V})::Vector{V} where {F,S,V} - return V[v for v in values(d.inner)] -end - -function Base.values(d::IndexDoubleDict) - out = MOI.ConstraintIndex[] - for ((F, S), inner) in d.dict - append!(out, MOI.ConstraintIndex{F,S}.(values(inner))) - end - return out -end - -function Base.values(d::IndexDoubleDictInner{F,S}) where {F,S} - return MOI.ConstraintIndex{F,S}.(values(d.inner)) -end +Base.values(d::AbstractDoubleDictInner) = typed_value.(Ref(d), values(d.inner)) # Base.keys function Base.keys(d::AbstractDoubleDict) out = MOI.ConstraintIndex[] - for ((F, S), inner) in d.dict - append!(out, MOI.ConstraintIndex{F,S}.(keys(inner))) + for (F, S) in keys(d.dict) + append!(out, keys(d[F, S])) end return out end @@ -322,7 +316,7 @@ function Base.iterate(d::AbstractDoubleDict) inner_next = iterate(inner) end (k, v), inner_state = inner_next - result = MOI.ConstraintIndex{F,S}(k) => typed_value(getindex(d, F, S), v) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(d[F, S], v) return result, (inner_state, outer_next) end @@ -352,7 +346,7 @@ function Base.iterate(d::AbstractDoubleDict, state) inner_next = iterate(inner) end (k, v), inner_state = inner_next - result = MOI.ConstraintIndex{F,S}(k) => typed_value(getindex(d, F, S), v) + result = MOI.ConstraintIndex{F,S}(k) => typed_value(d[F, S], v) return result, (inner_state, outer_next) end From 788dd517a4d9b138306c8eee21f88da15f04d1f5 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 14 Jul 2021 12:10:28 +1200 Subject: [PATCH 08/11] Fixes --- src/Utilities/DoubleDicts.jl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 11c4ec119b..f4b9766ab8 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -16,9 +16,7 @@ Convert the `value` stored inside `dict` to the equivalent on the outer `DoubleDict`. This is useful when the value type `V` of the inner dict is different to the outer dict. (See, e.g., [`IndexDoubleDict`](@ref).) """ -function typed_value(::AbstractDoubleDictInner{F,S,V}, value::V) where {F,S,V} - return value -end +typed_value(::AbstractDoubleDictInner, value) = value """ DoubleDict{V} @@ -164,10 +162,7 @@ function Base.get( key::MOI.ConstraintIndex{F,S}, default, ) where {F,S} - if !haskey(d, key) - return default - end - return typed_value(d, d.inner[key.value]) + return typed_value(d, get(d.inner, key.value, default)) end # Base.getindex @@ -184,10 +179,10 @@ function Base.getindex( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - if !haskey(d, key) + x = get(d.inner, key.value) do throw(KeyError(key)) end - return typed_value(d, d.inner[key.value]) + return typed_value(d, x) end # Base.setindex! From 78037f05444458a4743afdbaad0c6276071a1d88 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 14 Jul 2021 12:16:43 +1200 Subject: [PATCH 09/11] Update DoubleDicts.jl --- src/Utilities/DoubleDicts.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index f4b9766ab8..23c60cf856 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -180,7 +180,7 @@ function Base.getindex( key::MOI.ConstraintIndex{F,S}, ) where {F,S} x = get(d.inner, key.value) do - throw(KeyError(key)) + return throw(KeyError(key)) end return typed_value(d, x) end From cac4542310040e43b573c41c7176885c3bdcda05 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 15 Jul 2021 08:52:43 +1200 Subject: [PATCH 10/11] More updates --- docs/src/submodules/Utilities/overview.md | 35 +++++++++++++++++++++ src/Utilities/DoubleDicts.jl | 38 +++++++++++------------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/docs/src/submodules/Utilities/overview.md b/docs/src/submodules/Utilities/overview.md index 373d4aa5a9..2d9cf3e57d 100644 --- a/docs/src/submodules/Utilities/overview.md +++ b/docs/src/submodules/Utilities/overview.md @@ -506,3 +506,38 @@ types [`Utilities.DoubleDicts.DoubleDict`](@ref) and [`Utilities.DoubleDicts.IndexDoubleDict`](@ref). These types act like normal dictionaries, but internally they use more efficient dictionaries specialized to the type of the function-set pair. + +The most common usage of a `DoubleDict` is in the `index_map` returned by +[`copy_to`](@ref). Performance can be improved, by using a function barrier. +That is, instead of code like: +```julia +index_map = MOI.copy_to(dest, src) +for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) + for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) + dest_ci = index_map[ci] + # ... + end +end +``` +use instead: +```julia +function function_barrier( + dest, + src, + index_map, + ::Type{F}, + ::Type{S}, +) where {F,S} + type_stable_map = index_map[F, S] + for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) + dest_ci = type_stable_map[ci] + # ... + end + return +end + +index_map = MOI.copy_to(dest, src) +for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) + function_barrier(dest, src, index_map, F, S) +end +``` diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 23c60cf856..3e9296e805 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -50,10 +50,9 @@ end A type stable inner dictionary of [`DoubleDict`](@ref). """ mutable struct DoubleDictInner{F,S,V} <: AbstractDoubleDictInner{F,S,V} - dict::DoubleDict{V} - inner::Dict{Int64,V} + dict::Dict{Int64,V} function DoubleDictInner{F,S}(d::DoubleDict{V}) where {F,S,V} - return new{F,S,V}(d, get!(d.dict, (F, S), Dict{Int64,V}())) + return new{F,S,V}(get!(d.dict, (F, S), Dict{Int64,V}())) end end @@ -87,10 +86,9 @@ A type stable inner dictionary of [`IndexDoubleDict`](@ref). """ mutable struct IndexDoubleDictInner{F,S} <: AbstractDoubleDictInner{F,S,MOI.ConstraintIndex{F,S}} - dict::IndexDoubleDict - inner::Dict{Int64,Int64} + dict::Dict{Int64,Int64} function IndexDoubleDictInner{F,S}(d::IndexDoubleDict) where {F,S} - return new{F,S}(d, get!(d.dict, (F, S), Dict{Int64,Int64}())) + return new{F,S}(get!(d.dict, (F, S), Dict{Int64,Int64}())) end end @@ -115,7 +113,7 @@ function Base.sizehint!(::AbstractDoubleDict, ::Integer) end function Base.sizehint!(d::AbstractDoubleDictInner, n::Integer) - return sizehint!(d.inner, n) + return sizehint!(d.dict, n) end # Base.length @@ -128,7 +126,7 @@ function Base.length(d::AbstractDoubleDict) return len end -Base.length(d::AbstractDoubleDictInner) = length(d.inner) +Base.length(d::AbstractDoubleDictInner) = length(d.dict) # Base.haskey @@ -143,7 +141,7 @@ function Base.haskey( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - return haskey(d.inner, key.value) + return haskey(d.dict, key.value) end # Base.get @@ -162,7 +160,7 @@ function Base.get( key::MOI.ConstraintIndex{F,S}, default, ) where {F,S} - return typed_value(d, get(d.inner, key.value, default)) + return typed_value(d, get(d.dict, key.value, default)) end # Base.getindex @@ -179,7 +177,7 @@ function Base.getindex( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - x = get(d.inner, key.value) do + x = get(d.dict, key.value) do return throw(KeyError(key)) end return typed_value(d, x) @@ -202,7 +200,7 @@ function Base.setindex!( value::V, key::MOI.ConstraintIndex{F,S}, ) where {F,S,V} - d.inner[key.value] = value + d.dict[key.value] = value return value end @@ -221,7 +219,7 @@ function Base.setindex!( value::MOI.ConstraintIndex{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - d.inner[key.value] = value.value + d.dict[key.value] = value.value return value end @@ -233,7 +231,7 @@ function Base.empty!(d::AbstractDoubleDict) end function Base.empty!(d::AbstractDoubleDictInner) - empty!(d.inner) + empty!(d.dict) return d end @@ -251,7 +249,7 @@ function Base.delete!( d::AbstractDoubleDictInner{F,S}, key::MOI.ConstraintIndex{F,S}, ) where {F,S} - delete!(d.inner, key.value) + delete!(d.dict, key.value) return d end @@ -261,7 +259,7 @@ function Base.isempty(d::AbstractDoubleDict) return isempty(d.dict) || all(isempty, values(d.dict)) end -Base.isempty(d::AbstractDoubleDictInner) = isempty(d.inner) +Base.isempty(d::AbstractDoubleDictInner) = isempty(d.dict) # Base.values @@ -273,7 +271,7 @@ function Base.values(d::AbstractDoubleDict{V}) where {V} return out end -Base.values(d::AbstractDoubleDictInner) = typed_value.(Ref(d), values(d.inner)) +Base.values(d::AbstractDoubleDictInner) = typed_value.(Ref(d), values(d.dict)) # Base.keys @@ -286,7 +284,7 @@ function Base.keys(d::AbstractDoubleDict) end function Base.keys(d::AbstractDoubleDictInner{F,S}) where {F,S} - return MOI.ConstraintIndex{F,S}.(keys(d.inner)) + return MOI.ConstraintIndex{F,S}.(keys(d.dict)) end # Base.iterate @@ -316,13 +314,13 @@ function Base.iterate(d::AbstractDoubleDict) end function Base.iterate(d::AbstractDoubleDictInner{F,S}) where {F,S} - next = iterate(d.inner) + next = iterate(d.dict) if next === nothing return end (k, v), inner_state = next result = MOI.ConstraintIndex{F,S}(k) => typed_value(d, v) - return result, (d.inner, inner_state) + return result, (d.dict, inner_state) end function Base.iterate(d::AbstractDoubleDict, state) From 7b13bd58d52ac62e2ae69cbf5f6730f59795a496 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 15 Jul 2021 11:01:35 +1200 Subject: [PATCH 11/11] Update overview.md --- docs/src/submodules/Utilities/overview.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/src/submodules/Utilities/overview.md b/docs/src/submodules/Utilities/overview.md index 2d9cf3e57d..bd6e67149c 100644 --- a/docs/src/submodules/Utilities/overview.md +++ b/docs/src/submodules/Utilities/overview.md @@ -524,13 +524,10 @@ use instead: function function_barrier( dest, src, - index_map, - ::Type{F}, - ::Type{S}, + index_map::MOI.Utilities.DoubleDicts.IndexDoubleDictInner{F,S}, ) where {F,S} - type_stable_map = index_map[F, S] for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) - dest_ci = type_stable_map[ci] + dest_ci = index_map[ci] # ... end return @@ -538,6 +535,6 @@ end index_map = MOI.copy_to(dest, src) for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) - function_barrier(dest, src, index_map, F, S) + function_barrier(dest, src, index_map[F, S]) end ```