From 6162f4c46671c12b075a8da9930d9b4600816030 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Jul 2020 03:45:45 -0300 Subject: [PATCH 01/11] [ci skip] double dict first sketch --- src/Utilities/DoubleDict.jl | 314 ++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 src/Utilities/DoubleDict.jl diff --git a/src/Utilities/DoubleDict.jl b/src/Utilities/DoubleDict.jl new file mode 100644 index 0000000000..afef7b48ce --- /dev/null +++ b/src/Utilities/DoubleDict.jl @@ -0,0 +1,314 @@ +module DoubleDicts + +import MathOptInterface + +const MOI = MathOptInterface +const CI{F,S} = MOI.ConstraintIndex{F,S} + +""" + DoubleDict + +Special dictionary to map ConstraintIndex to ConstraintIndex with minimal +performance loss from the type instability due different constraint types. +""" +mutable struct DoubleDict{D, D2} + dict::D2{Tuple{DataType, DataType}, D{Int, Int}} + DoubleDict() = new{}(Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) +end + +function Base.sizehint!(::DoubleDict, ::Integer) + error("sizehint!(::DoubleDict, ::Integer) has no proper meaning for "* + "DoubleDict, use sizehint!(d::DoubleDict, ::Type{F}, ::Type{S}, n::Integer)"* + " instead.") +end +# function Base.sizehint!(d::DoubleDict, ::Type{F}, ::Type{S}, n) where {F,S} +# inner = lazy_get(d, F, S)::Dict{Int,Int} +# sizehint!(inner, n) +# end + +function Base.length(d::DoubleDict) + len = 0 + for inner in d.dict + len += length(inner) + end + return len +end +# function Base.length(d::DoubleDict, ::Type{F}, ::Type{S}, n) where {F,S} +# inner = get(d.dict, (F,S), nothing)::Union{Nothing, Dict{Int,Int}} +# if inner === nothing +# return 0 +# else +# return length(inner) +# end +# end + +function Base.haskey(dict::DoubleDict, key::CI{F,S}) where {F,S} + inner = get(dict.dict, (F,S), nothing) + if inner !== nothing + inner = dict.dict[(F,S)] + return haskey(inner, key.value) + else + return false + end +end + +function Base.getindex(dict::DoubleDict, key::CI{F,S}) where {F,S} + inner = dict.dict[(F,S)] + k_value = key.value::Int + return CI{F,S}(inner[k_value]) +end + +""" + lazy_get(dict::DoubleDict, ::Type{F}, ::Type{S}) + +description +""" +function lazy_get(dict::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} + inner = get(dict.dict, (F,S), nothing) + if inner === nothing + return dict.dict[(F,S)] = Dict{Int,Int}() + end + return inner +end + +function Base.setindex!(dict::DoubleDict, value::CI{F,S}, key::CI{F,S}) where {F,S} + v_value = value.value::Int + k_value = key.value::Int + inner = lazy_get(dict, F, S)::Dict{Int,Int} + inner[k_value] = v_value + return dict +end + +function Base.empty!(d::DoubleDict)::Nothing + Base.empty!(d.dict) + return +end +# function Base.empty!(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} +# inner = get(d.dict, (F,S), nothing) +# if inner === nothing +# return +# else +# empty!(inner) +# end +# return +# end + +function Base.delete!(d::DoubleDict, key::CI{F,S}) where {F,S} + k_value = key.value::Int + inner = lazy_get(d, F, S)::Dict{Int,Int} + delete!(inner, k_value) + return d +end + +function Base.isempty(d::DoubleDict) + if isempty(d.dict) + return true + end + for val in values(d.dict) + if !isempty(val) + return false + end + end + return true +end +# function Base.isempty(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} +# inner = get(d.dict, (F,S), nothing) +# if inner === nothing +# return true +# else +# return isempty(inner) +# end +# end + +function Base.values(d::DoubleDict) + out = CI[] + for ((F, S), inner) in d.dict + append!(out, CI{F,S}.(values(inner))) + end + return out +end +# function Base.values(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} +# inner = get(d.dict, (F,S), nothing) +# if inner === nothing +# return CI{F,S}[] +# else +# return CI{F,S}.(values(inner)) +# end +# end + +function Base.keys(d::DoubleDict) + out = CI[] + for ((F,S), inner) in d.dict + append!(out, CI{F,S}.(keys(inner))) + end + return out +end +# function Base.keys(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} +# inner = get(d.dict, (F,S), nothing) +# if inner === nothing +# return CI{F,S}[] +# else +# return CI{F,S}.(keys(inner)) +# end +# end + +function Base.iterate(d::DoubleDict) + o_next = iterate(d.dict) + if o_next === nothing + return nothing + 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 + return nothing + end + (o_i, o_state) = o_next + ((F,S), inner) = o_i + i_next = iterate(inner) + end + (i_i, i_state) = i_next + return CI{F,S}(i_i[1]) => CI{F,S}(i_i[2]), (i_state, (o_i, o_state)) +end +function Base.iterate(d::DoubleDict, 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 + end + (o_i, o_state) = o_next + ((F,S), inner) = o_i + i_next = iterate(inner) + end + (i_i, i_state) = i_next + return CI{F,S}(i_i[1]) => CI{F,S}(i_i[2]), (i_state, (o_i, o_state)) +end + +""" + WithType{D<:DoubleDict, F<:MOI.AbstractFunction, S<:MOI.AbstractSet} + +Used to specialize methods and iterators for a given contraint type CI{F,S}. +""" +struct WithType{F, S, D, D2} + dict::D + inner::Union{D2, Nothing} + function WithType{F, S}(d::D{D2}) where {D<:DoubleDict} + # inner = get(d.dict, (F, S), D2()) + inner = get(d.dict, (F, S), nothing) + new{F, S, D, D2}(d, inner) + end +end + +function initialize_inner!(d::WithType{F, S, D, D2}) where {F, S, D, D2} + d.inner = D2() +end + +function Base.sizehint!(d::WithType{F, S}, n::Integer) where {F, S} + if d.inner === nothing + initialize_inner!(d) + end + sizehint!(d.inner, n) +end + +function Base.length(d::WithType{F, S}) where {F, S} + if d.inner === nothing + return 0 + end + return length(d.inner) +end + +function Base.haskey(d::WithType{F, S}, key::CI{F,S}) where {F,S} + if d.inner === nothing + return false + end + return haskey(d.inner, key.value) +end + +function Base.getindex(d::WithType{F, S}, + key::CI{F,S}) where {F, S} + if d.inner === nothing + error("No key") + end + inner = d.inner + k_value = key.value::Int + return CI{F,S}(inner[k_value]) +end + +function Base.setindex!(d::WithType{F, S}, value::CI{F,S}, + key::CI{F,S}) where {F, S} + if d.inner === nothing + initialize_inner!(d) + end + v_value = value.value::Int + k_value = key.value::Int + d.inner[k_value] = v_value + return d +end + +function Base.empty!(d::WithType{F, S}) where {F,S} + if d.inner === nothing + return d + end + return empty!(d.inner) +end + +function Base.delete!(d::WithType{F, S}, key::CI{F,S}) where {F,S} + if d.inner === nothing + return d + end + k_value = key.value::Int + delete!(d.inner, k_value) + return d +end + +function Base.isempty(d::WithType{F, S}) where {F,S} + if d.inner === nothing + return true + end + return isempty(d.inner) +end + +function Base.values(d::WithType{F, S}) where {F,S} + if d.inner === nothing + return CI{F,S}[] + end + return CI{F,S}.(values(d.inner)) +end + +function Base.keys(d::WithType{F, S}) where {F,S} + if d.inner === nothing + return CI{F,S}[] + end + return CI{F,S}.(keys(d.inner)) +end + +function Base.iterate(d::WithType{F, S}) where {F, S} + # inner = get(d.dict, (F,S), nothing) + 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]) => CI{F,S}(i[2]), (state, inner) + end +end +function Base.iterate(::WithType{F, S}, state) where {F, S} + (istate, inner) = state + next = iterate(inner, istate) + if next === nothing + return nothing + end + (i, state) = next + return CI{F,S}(i[1]) => CI{F,S}(i[2]), (state, inner) +end + +end From c838b5cc7d19ecba500375ab7150d77cb6d09375 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Jul 2020 14:18:10 -0300 Subject: [PATCH 02/11] wrapping up double dicts --- src/Utilities/CleverDicts.jl | 7 +- .../{DoubleDict.jl => DoubleDicts.jl} | 12 +-- src/Utilities/Utilities.jl | 1 + test/Utilities/DoubleDicts.jl | 81 +++++++++++++++++++ test/Utilities/Utilities.jl | 3 + 5 files changed, 97 insertions(+), 7 deletions(-) rename src/Utilities/{DoubleDict.jl => DoubleDicts.jl} (94%) create mode 100644 test/Utilities/DoubleDicts.jl diff --git a/src/Utilities/CleverDicts.jl b/src/Utilities/CleverDicts.jl index ce6b21adb1..dc0db7e4a2 100644 --- a/src/Utilities/CleverDicts.jl +++ b/src/Utilities/CleverDicts.jl @@ -46,7 +46,7 @@ index_to_key(::Type{MyKey}, i::Int) = MyKey(i) key_to_index(key::MyKey) = key.x ``` """ -mutable struct CleverDict{K, V} +mutable struct CleverDict{K, V} <: AbstractDict{K, V} last_index::Int vector::Union{Nothing, Vector{V}} dict::Union{Nothing, OrderedCollections.OrderedDict{K, V}} @@ -153,6 +153,7 @@ end function Base.delete!(c::CleverDict{K, V}, key::K)::Nothing where {K, V} if c.dict === nothing c.dict = OrderedCollections.OrderedDict{K, Union{Nothing, V}}() + sizehint!(c.dict, length(c.vector)) for (i, info) in enumerate(c.vector) c.dict[index_to_key(K, i)] = info end @@ -228,4 +229,8 @@ function Base.keys(c::CleverDict{K, V}) where {K, V} return c.dict === nothing ? index_to_key.(K, 1:length(c)) : keys(c.dict) end +function Base.sizehint!(c::CleverDict{K, V}, n) where {K, V} + return c.dict === nothing ? sizehint!(c.vector, n) : sizehint!(c.dict, n) +end + end diff --git a/src/Utilities/DoubleDict.jl b/src/Utilities/DoubleDicts.jl similarity index 94% rename from src/Utilities/DoubleDict.jl rename to src/Utilities/DoubleDicts.jl index afef7b48ce..93c6bf41e2 100644 --- a/src/Utilities/DoubleDict.jl +++ b/src/Utilities/DoubleDicts.jl @@ -11,9 +11,9 @@ const CI{F,S} = MOI.ConstraintIndex{F,S} Special dictionary to map ConstraintIndex to ConstraintIndex with minimal performance loss from the type instability due different constraint types. """ -mutable struct DoubleDict{D, D2} - dict::D2{Tuple{DataType, DataType}, D{Int, Int}} - DoubleDict() = new{}(Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) +struct DoubleDict{D<:AbstractDict{Int, Int}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} + dict::D2 + DoubleDict() = new{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}(Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) end function Base.sizehint!(::DoubleDict, ::Integer) @@ -28,7 +28,7 @@ end function Base.length(d::DoubleDict) len = 0 - for inner in d.dict + for inner in values(d.dict) len += length(inner) end return len @@ -194,10 +194,10 @@ end Used to specialize methods and iterators for a given contraint type CI{F,S}. """ -struct WithType{F, S, D, D2} +struct WithType{F, S, D, D2} <: AbstractDict{CI{F,S}, CI{F,S}} dict::D inner::Union{D2, Nothing} - function WithType{F, S}(d::D{D2}) where {D<:DoubleDict} + function WithType{F, S}(d::D) where {D2, D<:DoubleDict{D2}, F, S} # inner = get(d.dict, (F, S), D2()) inner = get(d.dict, (F, S), nothing) new{F, S, D, D2}(d, inner) diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index de39a929f7..d396c2af1c 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -41,6 +41,7 @@ include("cachingoptimizer.jl") include("universalfallback.jl") include("CleverDicts.jl") +include("DoubleDicts.jl") include("lazy_iterators.jl") end # module diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl new file mode 100644 index 0000000000..cbcd08bfe0 --- /dev/null +++ b/test/Utilities/DoubleDicts.jl @@ -0,0 +1,81 @@ +# include("C:/Users/joaquimgarcia/.julia/dev/MathOptInterface/test/Utilities/DoubleDicts.jl") +# include("C:/Users/joaquimgarcia/.julia/dev/MathOptInterface/src/Utilities/DoubleDicts.jl") + +using MathOptInterface, Test +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} + +dict = DoubleDicts.DoubleDict() + +@test isempty(dict) + +@test length(dict) == 0 + +empty!(dict) +@test isempty(dict) + +@test !haskey(dict, CI_I(1)) + +dict[CI_I(1)] = CI_I(1) + +@test haskey(dict, CI_I(1)) + +@test values(dict) == [CI_I(1)] +@test keys(dict) == [CI_I(1)] +@test dict[CI_I(1)] == CI_I(1) + +for (k,v) in dict + @test k == v +end + +delete!(dict, CI_I(1)) + +@test !haskey(dict, CI_I(1)) +@test isempty(dict) + +dict[CI_I(1)] = CI_I(1) +@test length(dict) == 1 + +empty!(dict) +@test isempty(dict) +@test length(dict) == 0 + +dict[CI_I(1)] = CI_I(1) + +idict = DoubleDicts.WithType{MOI.SingleVariable, MOI.Integer}(dict) +bdict = DoubleDicts.WithType{MOI.SingleVariable, MOI.ZeroOne}(dict) + +sizehint!(idict, 2) + +@test length(idict) == 1 +@test length(bdict) == 0 + +@test haskey(idict, CI_I(1)) +@test !haskey(idict, CI_I(2)) +@test !haskey(bdict, CI_B(1)) + +@test values(idict) == [CI_I(1)] +@test keys(idict) == [CI_I(1)] +@test idict[CI_I(1)] == CI_I(1) + +idict[CI_I(2)] = CI_I(2) + +@test haskey(idict, CI_I(2)) +delete!(idict, CI_I(2)) +@test !haskey(idict, CI_I(2)) + +for (k,v) in idict + @test k == v +end + +@test !isempty(idict) +@test isempty(bdict) +empty!(idict) +empty!(bdict) +@test isempty(idict) +@test isempty(bdict) \ No newline at end of file diff --git a/test/Utilities/Utilities.jl b/test/Utilities/Utilities.jl index fc4db83c80..df0037a83a 100644 --- a/test/Utilities/Utilities.jl +++ b/test/Utilities/Utilities.jl @@ -37,6 +37,9 @@ end @testset "CleverDicts" begin include("CleverDicts.jl") end +@testset "DoubleDicts" begin + include("DoubleDicts.jl") +end @testset "Lazy iterators" begin include("lazy_iterators.jl") end From eb36bd3138f1d6bdd900d4911b99b64a726c7a73 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Jul 2020 15:41:16 -0300 Subject: [PATCH 03/11] replace index map --- src/Utilities/DoubleDicts.jl | 5 ++++- src/Utilities/Utilities.jl | 5 +++-- src/Utilities/copy.jl | 6 +++--- test/Utilities/CleverDicts.jl | 2 ++ test/Utilities/copy.jl | 7 +++++-- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 93c6bf41e2..3ede7d10f7 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -13,9 +13,12 @@ performance loss from the type instability due different constraint types. """ struct DoubleDict{D<:AbstractDict{Int, Int}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} dict::D2 - DoubleDict() = new{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}(Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) + DoubleDict() = new{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}( + Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) end +MainDoubleDict = DoubleDict{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}} + function Base.sizehint!(::DoubleDict, ::Integer) error("sizehint!(::DoubleDict, ::Integer) has no proper meaning for "* "DoubleDict, use sizehint!(d::DoubleDict, ::Type{F}, ::Type{S}, n::Integer)"* diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index d396c2af1c..d49f6cf750 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -26,6 +26,9 @@ function print_with_acronym(io::IO, s::AbstractString) print(io, s) end +include("CleverDicts.jl") +include("DoubleDicts.jl") + include("functions.jl") include("mutable_arithmetics.jl") include("sets.jl") @@ -40,8 +43,6 @@ include("mockoptimizer.jl") include("cachingoptimizer.jl") include("universalfallback.jl") -include("CleverDicts.jl") -include("DoubleDicts.jl") include("lazy_iterators.jl") end # module diff --git a/src/Utilities/copy.jl b/src/Utilities/copy.jl index b03cc9fbf4..0fe2cfa863 100644 --- a/src/Utilities/copy.jl +++ b/src/Utilities/copy.jl @@ -65,10 +65,10 @@ supports_default_copy_to(model::MOI.ModelLike, copy_names::Bool) = false struct IndexMap <: AbstractDict{MOI.Index, MOI.Index} varmap::Dict{MOI.VariableIndex, MOI.VariableIndex} - conmap::Dict{MOI.ConstraintIndex, MOI.ConstraintIndex} + conmap::DoubleDicts.MainDoubleDict end -IndexMap() = IndexMap(Dict{MOI.VariableIndex, MOI.VariableIndex}(), - Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}()) +IndexMap() = IndexMap(Dict{MOI.VariableIndex, MOI.VariableIndex}(), + DoubleDicts.DoubleDict()) Base.getindex(idxmap::IndexMap, vi::MOI.VariableIndex) = idxmap.varmap[vi] function Base.getindex(idxmap::IndexMap, ci::MOI.ConstraintIndex{F, S}) where {F, S} diff --git a/test/Utilities/CleverDicts.jl b/test/Utilities/CleverDicts.jl index d68b333b90..a18e7e16af 100644 --- a/test/Utilities/CleverDicts.jl +++ b/test/Utilities/CleverDicts.jl @@ -20,10 +20,12 @@ CleverDicts.index_to_key(::Type{MyKey}, index::Int) = MyKey{index}() d = CleverDicts.CleverDict{MathOptInterface.VariableIndex, String}() key = CleverDicts.add_item(d, "first") @test key == MathOptInterface.VariableIndex(1) + sizehint!(d, 1) @test d[key] == "first" @test haskey(d, key) == true @test_throws KeyError d[MathOptInterface.VariableIndex(2)] delete!(d, key) + sizehint!(d, 2) @test_throws KeyError d[key] @test_throws KeyError d[key] = "key" @test haskey(d, key) == false diff --git a/test/Utilities/copy.jl b/test/Utilities/copy.jl index 56f8bd7ff2..27138bd341 100644 --- a/test/Utilities/copy.jl +++ b/test/Utilities/copy.jl @@ -4,6 +4,8 @@ const MOI = MathOptInterface const MOIT = MOI.Test const MOIU = MOI.Utilities +const DoubleDicts = MathOptInterface.Utilities.DoubleDicts + include("../dummy.jl") remove_moi(x::String) = replace(x, "MathOptInterface." => "") @@ -19,7 +21,8 @@ end y = MOI.VariableIndex(2) cx = MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integer}(1) cy = MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integer}(2) - map = MOIU.IndexMap(Dict(x => y), Dict(cx => cy)) + map = MOIU.IndexMap(Dict(x => y), DoubleDicts.DoubleDict()) + map.conmap[cx] = cy @test length(map) == 2 # `x=>y` in Julia <= 1.1 and `x => y` in Julia >= 1.2 if VERSION < v"1.2" @@ -27,7 +30,7 @@ end else x_y = string(x => y) end - compare_without_moi(sprint(show, map), "Utilities.IndexMap($x_y,Pair{ConstraintIndex,ConstraintIndex}($cx, $cy))") + compare_without_moi(sprint(show, map), "Utilities.IndexMap($x_y,$cx => $cy)") end @testset "AUTOMATIC" begin From 15c2f034c035b953c2fb106a3f3b30e0b80f88a3 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Jul 2020 19:52:06 -0300 Subject: [PATCH 04/11] make double dict mode generic to handle any values type --- src/Utilities/DoubleDicts.jl | 212 +++++++++++++++++++--------------- src/Utilities/copy.jl | 4 +- test/Utilities/DoubleDicts.jl | 160 ++++++++++++++----------- test/Utilities/copy.jl | 2 +- 4 files changed, 218 insertions(+), 160 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 3ede7d10f7..85280afe84 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -5,47 +5,49 @@ import MathOptInterface const MOI = MathOptInterface const CI{F,S} = MOI.ConstraintIndex{F,S} +abstract type AbstractDoubleDict{V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} end + """ DoubleDict Special dictionary to map ConstraintIndex to ConstraintIndex with minimal performance loss from the type instability due different constraint types. """ -struct DoubleDict{D<:AbstractDict{Int, Int}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} +struct DoubleDict{V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} dict::D2 - DoubleDict() = new{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}( + DoubleDict{V}() where V = new{V, Dict{Int, V}, Dict{Tuple{DataType, DataType}, Dict{Int, V}}}( + Dict{Tuple{DataType, DataType}, Dict{Int, V}}()) +end + +struct IndexDoubleDict{V<:Int, D<:AbstractDict{Int, Int}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} + dict::D2 + IndexDoubleDict() = new{Int, Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}( Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) end -MainDoubleDict = DoubleDict{Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}} +MainIndexDoubleDict = IndexDoubleDict{Int, Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}} + +@inline function typed_value(::DoubleDict{V}, v::V, ::Type{F}, ::Type{S})::V where {V, F, S} + v +end +@inline function typed_value(::IndexDoubleDict, v::Int, ::Type{F}, ::Type{S})::CI{F,S} where {F, S} + CI{F,S}(v) +end function Base.sizehint!(::DoubleDict, ::Integer) - error("sizehint!(::DoubleDict, ::Integer) has no proper meaning for "* - "DoubleDict, use sizehint!(d::DoubleDict, ::Type{F}, ::Type{S}, n::Integer)"* - " instead.") + error("sizehint!(d::DoubleDict, ::Integer) has no proper meaning for "* + "DoubleDict, use sizehint!(WithType{F,S}(d), n::Integer) instead.") end -# function Base.sizehint!(d::DoubleDict, ::Type{F}, ::Type{S}, n) where {F,S} -# inner = lazy_get(d, F, S)::Dict{Int,Int} -# sizehint!(inner, n) -# end -function Base.length(d::DoubleDict) +function Base.length(d::AbstractDoubleDict) len = 0 for inner in values(d.dict) len += length(inner) end return len end -# function Base.length(d::DoubleDict, ::Type{F}, ::Type{S}, n) where {F,S} -# inner = get(d.dict, (F,S), nothing)::Union{Nothing, Dict{Int,Int}} -# if inner === nothing -# return 0 -# else -# return length(inner) -# end -# end -function Base.haskey(dict::DoubleDict, key::CI{F,S}) where {F,S} +function Base.haskey(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} inner = get(dict.dict, (F,S), nothing) if inner !== nothing inner = dict.dict[(F,S)] @@ -55,10 +57,10 @@ function Base.haskey(dict::DoubleDict, key::CI{F,S}) where {F,S} end end -function Base.getindex(dict::DoubleDict, key::CI{F,S}) where {F,S} +function Base.getindex(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} inner = dict.dict[(F,S)] k_value = key.value::Int - return CI{F,S}(inner[k_value]) + return typed_value(dict, inner[k_value], F, S) end """ @@ -66,15 +68,22 @@ end description """ -function lazy_get(dict::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} +function lazy_get(dict::AbstractDoubleDict{V}, ::Type{F}, ::Type{S}) where {V,F,S} inner = get(dict.dict, (F,S), nothing) if inner === nothing - return dict.dict[(F,S)] = Dict{Int,Int}() + return dict.dict[(F,S)] = Dict{Int,V}() end return inner end -function Base.setindex!(dict::DoubleDict, value::CI{F,S}, key::CI{F,S}) where {F,S} +function Base.setindex!(dict::AbstractDoubleDict{V}, value::V, key::CI{F,S}) where {V,F,S} + v_value = value + k_value = key.value::Int + inner = lazy_get(dict, F, S)::Dict{Int,V} + inner[k_value] = v_value + return dict +end +function Base.setindex!(dict::IndexDoubleDict, value::CI{F,S}, key::CI{F,S}) where {F,S} v_value = value.value::Int k_value = key.value::Int inner = lazy_get(dict, F, S)::Dict{Int,Int} @@ -82,28 +91,19 @@ function Base.setindex!(dict::DoubleDict, value::CI{F,S}, key::CI{F,S}) where {F return dict end -function Base.empty!(d::DoubleDict)::Nothing +function Base.empty!(d::AbstractDoubleDict)::Nothing Base.empty!(d.dict) return end -# function Base.empty!(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} -# inner = get(d.dict, (F,S), nothing) -# if inner === nothing -# return -# else -# empty!(inner) -# end -# return -# end - -function Base.delete!(d::DoubleDict, key::CI{F,S}) where {F,S} + +function Base.delete!(d::AbstractDoubleDict{V}, key::CI{F,S}) where {V,F,S} k_value = key.value::Int - inner = lazy_get(d, F, S)::Dict{Int,Int} + inner = lazy_get(d, F, S) delete!(inner, k_value) return d end -function Base.isempty(d::DoubleDict) +function Base.isempty(d::AbstractDoubleDict) if isempty(d.dict) return true end @@ -114,48 +114,31 @@ function Base.isempty(d::DoubleDict) end return true end -# function Base.isempty(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} -# inner = get(d.dict, (F,S), nothing) -# if inner === nothing -# return true -# else -# return isempty(inner) -# end -# end -function Base.values(d::DoubleDict) +function Base.values(d::IndexDoubleDict)::Vector{CI} out = CI[] for ((F, S), inner) in d.dict append!(out, CI{F,S}.(values(inner))) end return out end -# function Base.values(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} -# inner = get(d.dict, (F,S), nothing) -# if inner === nothing -# return CI{F,S}[] -# else -# return CI{F,S}.(values(inner)) -# end -# end +function Base.values(d::AbstractDoubleDict{V})::Vector{V} where V + out = V[] + for ((F, S), inner) in d.dict + append!(out, V.(values(inner))) + end + return out +end -function Base.keys(d::DoubleDict) +function Base.keys(d::AbstractDoubleDict) out = CI[] for ((F,S), inner) in d.dict append!(out, CI{F,S}.(keys(inner))) end return out end -# function Base.keys(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} -# inner = get(d.dict, (F,S), nothing) -# if inner === nothing -# return CI{F,S}[] -# else -# return CI{F,S}.(keys(inner)) -# end -# end -function Base.iterate(d::DoubleDict) +function Base.iterate(d::AbstractDoubleDict) o_next = iterate(d.dict) if o_next === nothing return nothing @@ -173,9 +156,9 @@ function Base.iterate(d::DoubleDict) i_next = iterate(inner) end (i_i, i_state) = i_next - return CI{F,S}(i_i[1]) => CI{F,S}(i_i[2]), (i_state, (o_i, o_state)) + return CI{F,S}(i_i[1]) => typed_value(d, i_i[2], F, S), (i_state, (o_i, o_state)) end -function Base.iterate(d::DoubleDict, state) +function Base.iterate(d::AbstractDoubleDict, state) (i_state, (o_i, o_state)) = state ((F,S), inner) = o_i i_next = iterate(inner, i_state) @@ -189,60 +172,99 @@ function Base.iterate(d::DoubleDict, state) i_next = iterate(inner) end (i_i, i_state) = i_next - return CI{F,S}(i_i[1]) => CI{F,S}(i_i[2]), (i_state, (o_i, o_state)) + return CI{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, D, D2} <: AbstractDict{CI{F,S}, V} end """ WithType{D<:DoubleDict, F<:MOI.AbstractFunction, S<:MOI.AbstractSet} Used to specialize methods and iterators for a given contraint type CI{F,S}. """ -struct WithType{F, S, D, D2} <: AbstractDict{CI{F,S}, CI{F,S}} - dict::D - inner::Union{D2, Nothing} - function WithType{F, S}(d::D) where {D2, D<:DoubleDict{D2}, F, S} +struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} + dict::D2 + inner::Union{D, Nothing} + function WithType{F, S}(d::D3) where + {V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, + D3<:DoubleDict{V, D, D2}, F, S} # inner = get(d.dict, (F, S), D2()) inner = get(d.dict, (F, S), nothing) - new{F, S, D, D2}(d, inner) + new{F, S, V, D, D3}(d, inner) end end +struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, D, D2} + dict::D2 + inner::Union{D, Nothing} + function IndexWithType{F, S}(d::D3) where + {V<:Int, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, + D3<:IndexDoubleDict{V, D, D2}, F, S} + # inner = get(d.dict, (F, S), D2()) + inner = get(d.dict, (F, S), nothing) + new{F, S, CI{F,S}, D, D3}(d, inner) + end +end + +function with_type(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return WithType{F, S}(d) +end +function with_type(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} + return IndexWithType{F, S}(d) +end -function initialize_inner!(d::WithType{F, S, D, D2}) where {F, S, D, D2} - d.inner = D2() +@inline function typed_value(::WithType{F, S, V}, v::V)::V where {V, F, S} + v +end +@inline function typed_value(::IndexWithType{F, S}, v::Int)::CI{F,S} where {F, S} + CI{F,S}(v) end -function Base.sizehint!(d::WithType{F, S}, n::Integer) where {F, S} +function initialize_inner!(d::AbstractWithType{F, S, V, D}) where {F, S, V, D} + d.inner = D() +end + +function Base.sizehint!(d::AbstractWithType{F, S}, n::Integer) where {F, S} if d.inner === nothing initialize_inner!(d) end sizehint!(d.inner, n) end -function Base.length(d::WithType{F, S}) where {F, S} +function Base.length(d::AbstractWithType{F, S}) where {F, S} if d.inner === nothing return 0 end return length(d.inner) end -function Base.haskey(d::WithType{F, S}, key::CI{F,S}) where {F,S} +function Base.haskey(d::AbstractWithType{F, S}, key::CI{F,S}) where {F,S} if d.inner === nothing return false end return haskey(d.inner, key.value) end -function Base.getindex(d::WithType{F, S}, +function Base.getindex(d::AbstractWithType{F, S}, key::CI{F,S}) where {F, S} if d.inner === nothing error("No key") end inner = d.inner k_value = key.value::Int - return CI{F,S}(inner[k_value]) + return typed_value(d, inner[k_value]) end -function Base.setindex!(d::WithType{F, S}, value::CI{F,S}, +function Base.setindex!(d::WithType{F, S, V}, value::V, + key::CI{F,S}) where {F, S, V} + if d.inner === nothing + initialize_inner!(d) + end + v_value = value#.value::Int + k_value = key.value::Int + d.inner[k_value] = v_value + return d +end +function Base.setindex!(d::IndexWithType{F, S}, value::CI{F,S}, key::CI{F,S}) where {F, S} if d.inner === nothing initialize_inner!(d) @@ -253,14 +275,14 @@ function Base.setindex!(d::WithType{F, S}, value::CI{F,S}, return d end -function Base.empty!(d::WithType{F, S}) where {F,S} +function Base.empty!(d::AbstractWithType{F, S}) where {F,S} if d.inner === nothing return d end return empty!(d.inner) end -function Base.delete!(d::WithType{F, S}, key::CI{F,S}) where {F,S} +function Base.delete!(d::AbstractWithType{F, S}, key::CI{F,S}) where {F,S} if d.inner === nothing return d end @@ -269,28 +291,34 @@ function Base.delete!(d::WithType{F, S}, key::CI{F,S}) where {F,S} return d end -function Base.isempty(d::WithType{F, S}) where {F,S} +function Base.isempty(d::AbstractWithType{F, S}) where {F,S} if d.inner === nothing return true end return isempty(d.inner) end -function Base.values(d::WithType{F, S}) where {F,S} +function Base.values(d::WithType{F, S, V})::Vector{V} where {F, S, V} + if d.inner === nothing + return V[] + end + return V.(values(d.inner)) +end +function Base.values(d::IndexWithType{F, S})::Vector{CI{F,S}} where {F,S} if d.inner === nothing return CI{F,S}[] end return CI{F,S}.(values(d.inner)) end -function Base.keys(d::WithType{F, S}) where {F,S} +function Base.keys(d::AbstractWithType{F, S}) where {F,S} if d.inner === nothing return CI{F,S}[] end - return CI{F,S}.(keys(d.inner)) + return CI{F,S}.(keys(d.inner)) end -function Base.iterate(d::WithType{F, S}) where {F, S} +function Base.iterate(d::AbstractWithType{F, S}) where {F, S} # inner = get(d.dict, (F,S), nothing) inner = d.inner if inner === nothing @@ -301,17 +329,17 @@ function Base.iterate(d::WithType{F, S}) where {F, S} return nothing end (i, state) = next - return CI{F,S}(i[1]) => CI{F,S}(i[2]), (state, inner) + return CI{F,S}(i[1]) => typed_value(d, i[2]), (state, inner) end end -function Base.iterate(::WithType{F, S}, state) where {F, S} +function Base.iterate(d::AbstractWithType{F, S}, state) where {F, S} (istate, inner) = state next = iterate(inner, istate) if next === nothing return nothing end (i, state) = next - return CI{F,S}(i[1]) => CI{F,S}(i[2]), (state, inner) + return CI{F,S}(i[1]) => typed_value(d, i[2]), (state, inner) end end diff --git a/src/Utilities/copy.jl b/src/Utilities/copy.jl index 0fe2cfa863..83458182d2 100644 --- a/src/Utilities/copy.jl +++ b/src/Utilities/copy.jl @@ -65,10 +65,10 @@ supports_default_copy_to(model::MOI.ModelLike, copy_names::Bool) = false struct IndexMap <: AbstractDict{MOI.Index, MOI.Index} varmap::Dict{MOI.VariableIndex, MOI.VariableIndex} - conmap::DoubleDicts.MainDoubleDict + conmap::DoubleDicts.MainIndexDoubleDict end IndexMap() = IndexMap(Dict{MOI.VariableIndex, MOI.VariableIndex}(), - DoubleDicts.DoubleDict()) + DoubleDicts.IndexDoubleDict()) Base.getindex(idxmap::IndexMap, vi::MOI.VariableIndex) = idxmap.varmap[vi] function Base.getindex(idxmap::IndexMap, ci::MOI.ConstraintIndex{F, S}) where {F, S} diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index cbcd08bfe0..c06a02bade 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -10,72 +10,102 @@ const DoubleDicts = MathOptInterface.Utilities.DoubleDicts const CI_I = CI{MOI.SingleVariable, MOI.Integer} const CI_B = CI{MOI.SingleVariable, MOI.ZeroOne} -dict = DoubleDicts.DoubleDict() - -@test isempty(dict) - -@test length(dict) == 0 - -empty!(dict) -@test isempty(dict) - -@test !haskey(dict, CI_I(1)) - -dict[CI_I(1)] = CI_I(1) - -@test haskey(dict, CI_I(1)) - -@test values(dict) == [CI_I(1)] -@test keys(dict) == [CI_I(1)] -@test dict[CI_I(1)] == CI_I(1) - -for (k,v) in dict - @test k == v +function basic_functionality(dict, k_values, v_values) + @test isempty(dict) + @test length(dict) == 0 + + empty!(dict) + @test isempty(dict) + @test !haskey(dict, k_values[1]) + + dict[k_values[1]] = v_values[1] + @test haskey(dict, k_values[1]) + @test values(dict) == [v_values[1]] + @test keys(dict) == [k_values[1]] + @test dict[k_values[1]] == v_values[1] + + kk = [] + vv = [] + for (k,v) in dict + push!(kk, k) + push!(vv, v) + end + @test length(kk) == length(keys(dict)) + @test isempty(setdiff(kk, keys(dict))) + @test length(vv) == length(values(dict)) + @test isempty(setdiff(vv, values(dict))) + + delete!(dict, k_values[1]) + @test !haskey(dict, k_values[1]) + @test isempty(dict) + + dict[k_values[1]] = v_values[1] + @test length(dict) == 1 + empty!(dict) + @test isempty(dict) + @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) + sizehint!(idict, 2) + @test length(idict) == 1 + @test length(bdict) == 0 + + @test haskey(idict, k_values[1]) + @test !haskey(idict, k_values[2]) + @test !haskey(bdict, k_values[3]) + + @test values(idict) == [v_values[1]] + @test keys(idict) == [k_values[1]] + @test idict[k_values[1]] == v_values[1] + + idict[k_values[2]] = v_values[2] + @test haskey(idict, k_values[2]) + delete!(idict, k_values[2]) + @test !haskey(idict, k_values[2]) + + kk = [] + vv = [] + for (k,v) in dict + push!(kk, k) + push!(vv, v) + end + @test length(kk) == length(keys(dict)) + @test isempty(setdiff(kk, keys(dict))) + @test length(vv) == length(values(dict)) + @test isempty(setdiff(vv, values(dict))) + + @test !isempty(idict) + @test isempty(bdict) + empty!(idict) + empty!(bdict) + @test isempty(idict) + @test isempty(bdict) end -delete!(dict, CI_I(1)) - -@test !haskey(dict, CI_I(1)) -@test isempty(dict) - -dict[CI_I(1)] = CI_I(1) -@test length(dict) == 1 - -empty!(dict) -@test isempty(dict) -@test length(dict) == 0 - -dict[CI_I(1)] = CI_I(1) - -idict = DoubleDicts.WithType{MOI.SingleVariable, MOI.Integer}(dict) -bdict = DoubleDicts.WithType{MOI.SingleVariable, MOI.ZeroOne}(dict) - -sizehint!(idict, 2) - -@test length(idict) == 1 -@test length(bdict) == 0 - -@test haskey(idict, CI_I(1)) -@test !haskey(idict, CI_I(2)) -@test !haskey(bdict, CI_B(1)) - -@test values(idict) == [CI_I(1)] -@test keys(idict) == [CI_I(1)] -@test idict[CI_I(1)] == CI_I(1) - -idict[CI_I(2)] = CI_I(2) - -@test haskey(idict, CI_I(2)) -delete!(idict, CI_I(2)) -@test !haskey(idict, CI_I(2)) - -for (k,v) in idict - @test k == v +@testset "IndexDoubleDict" begin + dict = DoubleDicts.IndexDoubleDict() + keys = [ + CI_I(1), + CI_I(2), + CI_B(1), + ] + vals = keys + basic_functionality(dict, keys, vals) end -@test !isempty(idict) -@test isempty(bdict) -empty!(idict) -empty!(bdict) -@test isempty(idict) -@test isempty(bdict) \ No newline at end of file +@testset "DoubleDict" begin + dict = DoubleDicts.DoubleDict{Float64}() + keys = [ + CI_I(1), + CI_I(2), + CI_B(1), + ] + vals = [ + 1.0, + 2.0, + 1.0, + ] + basic_functionality(dict, keys, vals) +end \ No newline at end of file diff --git a/test/Utilities/copy.jl b/test/Utilities/copy.jl index 27138bd341..4c7d6f4306 100644 --- a/test/Utilities/copy.jl +++ b/test/Utilities/copy.jl @@ -21,7 +21,7 @@ end y = MOI.VariableIndex(2) cx = MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integer}(1) cy = MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integer}(2) - map = MOIU.IndexMap(Dict(x => y), DoubleDicts.DoubleDict()) + map = MOIU.IndexMap(Dict(x => y), DoubleDicts.IndexDoubleDict()) map.conmap[cx] = cy @test length(map) == 2 # `x=>y` in Julia <= 1.1 and `x => y` in Julia >= 1.2 From 9ec997197d18f578bc5c2b644899d530bc3b8cfc Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Jul 2020 22:11:40 -0300 Subject: [PATCH 05/11] fix string --- test/Utilities/copy.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Utilities/copy.jl b/test/Utilities/copy.jl index a8130106f2..549668d9a8 100644 --- a/test/Utilities/copy.jl +++ b/test/Utilities/copy.jl @@ -27,10 +27,12 @@ end # `x=>y` in Julia <= 1.1 and `x => y` in Julia >= 1.2 if VERSION < v"1.2" x_y = string(x) * "=>" * string(y) + c_x_y = string(cx) * "=>" * string(cy) else x_y = string(x => y) + c_x_y = string(cx => cy) end - compare_without_moi(sprint(show, map), "Utilities.IndexMap($x_y,$cx => $cy)") + compare_without_moi(sprint(show, map), "Utilities.IndexMap($x_y,$c_x_y)") end @testset "AUTOMATIC" begin From cfacfb087ed3a76d1db0dc1649e1d588db7b707e Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 13 Jul 2020 00:19:12 -0300 Subject: [PATCH 06/11] add mutability and getindex --- src/Utilities/DoubleDicts.jl | 11 +++++++++-- test/Utilities/DoubleDicts.jl | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 85280afe84..9522f84d94 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -182,7 +182,7 @@ abstract type AbstractWithType{F, S, V, D, D2} <: AbstractDict{CI{F,S}, V} end Used to specialize methods and iterators for a given contraint type CI{F,S}. """ -struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} +mutable struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} dict::D2 inner::Union{D, Nothing} function WithType{F, S}(d::D3) where @@ -193,7 +193,7 @@ struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} new{F, S, V, D, D3}(d, inner) end end -struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, D, D2} +mutable struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, D, D2} dict::D2 inner::Union{D, Nothing} function IndexWithType{F, S}(d::D3) where @@ -212,6 +212,13 @@ function with_type(d::IndexDoubleDict, ::Type{F}, ::Type{S}) where {F,S} return IndexWithType{F, S}(d) 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 + @inline function typed_value(::WithType{F, S, V}, v::V)::V where {V, F, S} v end diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index c06a02bade..9c3074aa0b 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -48,6 +48,8 @@ function basic_functionality(dict, k_values, v_values) 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] + @test idict.dict === idict_.dict sizehint!(idict, 2) @test length(idict) == 1 @test length(bdict) == 0 @@ -82,6 +84,9 @@ function basic_functionality(dict, k_values, v_values) empty!(bdict) @test isempty(idict) @test isempty(bdict) + + bdict[k_values[3]] = v_values[3] + length(bdict) == 1 end @testset "IndexDoubleDict" begin From 453b5c01d127173e72a5188e8eafa4d518235410 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 13 Jul 2020 21:10:17 -0300 Subject: [PATCH 07/11] docs tests and fixes --- src/Utilities/DoubleDicts.jl | 169 ++++++++++++++++++++-------------- test/Utilities/DoubleDicts.jl | 43 +++++++-- 2 files changed, 133 insertions(+), 79 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 9522f84d94..2b7dac2429 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -5,24 +5,50 @@ import MathOptInterface const MOI = MathOptInterface const CI{F,S} = MOI.ConstraintIndex{F,S} -abstract type AbstractDoubleDict{V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} end +abstract type AbstractDoubleDict{V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} end """ - DoubleDict + DoubleDict{V} -Special dictionary to map ConstraintIndex to ConstraintIndex with minimal -performance loss from the type instability due different constraint types. +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. + +When optimal performance or type stability is required its 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: + + ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct DoubleDict{V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} +struct DoubleDict{V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} dict::D2 - DoubleDict{V}() where V = new{V, Dict{Int, V}, Dict{Tuple{DataType, DataType}, Dict{Int, V}}}( - Dict{Tuple{DataType, DataType}, Dict{Int, V}}()) + DoubleDict{V}() where V = new{V, Dict{Int64, V}, Dict{Tuple{DataType, DataType}, Dict{Int64, V}}}( + Dict{Tuple{DataType, DataType}, Dict{Int64, V}}()) end -struct IndexDoubleDict{V<:Int, D<:AbstractDict{Int, Int}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} +""" + IndexDoubleDict + +Specialized version of `DoubleDict` in which keys and values are of type +`ConstraintIndex` (`CI`). + +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. + +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]`` +""" +struct IndexDoubleDict{V<:Int64, D<:AbstractDict{Int64, Int64}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} dict::D2 - IndexDoubleDict() = new{Int, Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}}( - Dict{Tuple{DataType, DataType}, Dict{Int, Int}}()) + IndexDoubleDict() = new{Int64, Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}}( + Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}()) end MainIndexDoubleDict = IndexDoubleDict{Int, Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}} @@ -30,13 +56,14 @@ MainIndexDoubleDict = IndexDoubleDict{Int, Dict{Int, Int}, Dict{Tuple{DataType, @inline function typed_value(::DoubleDict{V}, v::V, ::Type{F}, ::Type{S})::V where {V, F, S} v end -@inline function typed_value(::IndexDoubleDict, v::Int, ::Type{F}, ::Type{S})::CI{F,S} where {F, S} +@inline function typed_value(::IndexDoubleDict, v::Int64, ::Type{F}, ::Type{S})::CI{F,S} where {F, S} CI{F,S}(v) end -function Base.sizehint!(::DoubleDict, ::Integer) - error("sizehint!(d::DoubleDict, ::Integer) has no proper meaning for "* - "DoubleDict, use sizehint!(WithType{F,S}(d), n::Integer) instead.") +function Base.sizehint!(::AbstractDoubleDict, ::Integer) + throw(ErrorException("sizehint!(d::DoubleDict, ::Integer) has no proper" * + " meaning for DoubleDict, use sizehint!(WithType{F,S}(d), n::Integer) " * + "instead.")) end function Base.length(d::AbstractDoubleDict) @@ -59,7 +86,7 @@ end function Base.getindex(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} inner = dict.dict[(F,S)] - k_value = key.value::Int + k_value = key.value::Int64 return typed_value(dict, inner[k_value], F, S) end @@ -71,24 +98,24 @@ description function lazy_get(dict::AbstractDoubleDict{V}, ::Type{F}, ::Type{S}) where {V,F,S} inner = get(dict.dict, (F,S), nothing) if inner === nothing - return dict.dict[(F,S)] = Dict{Int,V}() + return dict.dict[(F,S)] = Dict{Int64,V}() end return inner end function Base.setindex!(dict::AbstractDoubleDict{V}, value::V, key::CI{F,S}) where {V,F,S} v_value = value - k_value = key.value::Int - inner = lazy_get(dict, F, S)::Dict{Int,V} + k_value = key.value::Int64 + inner = lazy_get(dict, F, S)::Dict{Int64,V} inner[k_value] = v_value - return dict + return value end function Base.setindex!(dict::IndexDoubleDict, value::CI{F,S}, key::CI{F,S}) where {F,S} - v_value = value.value::Int - k_value = key.value::Int - inner = lazy_get(dict, F, S)::Dict{Int,Int} + v_value = value.value::Int64 + k_value = key.value::Int64 + inner = lazy_get(dict, F, S)::Dict{Int64,Int64} inner[k_value] = v_value - return dict + return value end function Base.empty!(d::AbstractDoubleDict)::Nothing @@ -97,7 +124,7 @@ function Base.empty!(d::AbstractDoubleDict)::Nothing end function Base.delete!(d::AbstractDoubleDict{V}, key::CI{F,S}) where {V,F,S} - k_value = key.value::Int + k_value = key.value::Int64 inner = lazy_get(d, F, S) delete!(inner, k_value) return d @@ -186,9 +213,8 @@ mutable struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} dict::D2 inner::Union{D, Nothing} function WithType{F, S}(d::D3) where - {V, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, + {V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, D3<:DoubleDict{V, D, D2}, F, S} - # inner = get(d.dict, (F, S), D2()) inner = get(d.dict, (F, S), nothing) new{F, S, V, D, D3}(d, inner) end @@ -197,19 +223,18 @@ mutable struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, dict::D2 inner::Union{D, Nothing} function IndexWithType{F, S}(d::D3) where - {V<:Int, D<:AbstractDict{Int, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, + {V<:Int64, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, D3<:IndexDoubleDict{V, D, D2}, F, S} - # inner = get(d.dict, (F, S), D2()) - inner = get(d.dict, (F, S), nothing) + inner = get(d.dict, (F, S), nothing)::Union{D, Nothing} new{F, S, CI{F,S}, D, D3}(d, inner) end end -function with_type(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} - return WithType{F, S}(d) +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}) where {F,S} - return IndexWithType{F, S}(d) +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 Base.getindex(d::DoubleDict, ::Type{F}, ::Type{S}) where {F,S} @@ -230,103 +255,107 @@ function initialize_inner!(d::AbstractWithType{F, S, V, D}) where {F, S, V, D} d.inner = D() end +inner_is_empty(d::AbstractWithType)::Bool = d.inner === nothing +function inner(d::AbstractWithType{F, S, V, D})::D where {F, S, V, D} + d.inner::D +end + function Base.sizehint!(d::AbstractWithType{F, S}, n::Integer) where {F, S} - if d.inner === nothing + if inner_is_empty(d) initialize_inner!(d) end - sizehint!(d.inner, n) + sizehint!(inner(d), n) end function Base.length(d::AbstractWithType{F, S}) where {F, S} - if d.inner === nothing + if inner_is_empty(d) return 0 end - return length(d.inner) + return length(inner(d)) end function Base.haskey(d::AbstractWithType{F, S}, key::CI{F,S}) where {F,S} - if d.inner === nothing + if inner_is_empty(d) return false end - return haskey(d.inner, key.value) + return haskey(inner(d), key.value) end function Base.getindex(d::AbstractWithType{F, S}, key::CI{F,S}) where {F, S} - if d.inner === nothing - error("No key") + if inner_is_empty(d) + throw(KeyError(key)) end - inner = d.inner - k_value = key.value::Int - return typed_value(d, inner[k_value]) + k_value = key.value::Int64 + return typed_value(d, inner(d)[k_value]) end function Base.setindex!(d::WithType{F, S, V}, value::V, - key::CI{F,S}) where {F, S, V} - if d.inner === nothing + key::CI{F,S})::V where {F, S, V} + if inner_is_empty(d) initialize_inner!(d) end - v_value = value#.value::Int - k_value = key.value::Int - d.inner[k_value] = v_value - return d + v_value = value + k_value = key.value::Int64 + inner(d)[k_value] = v_value + return value end -function Base.setindex!(d::IndexWithType{F, S}, value::CI{F,S}, - key::CI{F,S}) where {F, S} - if d.inner === nothing +function Base.setindex!(d::IndexWithType{F, S, V, D, D2}, value::CI{F,S}, + key::CI{F,S})::CI{F,S} where {F, S, V, D, D2} + if inner_is_empty(d) initialize_inner!(d) end - v_value = value.value::Int - k_value = key.value::Int - d.inner[k_value] = v_value - return d + _inner = inner(d)::Dict{Int64, Int64} + v_value = value.value::Int64 + k_value = key.value::Int64 + _inner[k_value] = v_value + return value end function Base.empty!(d::AbstractWithType{F, S}) where {F,S} - if d.inner === nothing + if inner_is_empty(d) return d end - return empty!(d.inner) + return empty!(inner(d)) end function Base.delete!(d::AbstractWithType{F, S}, key::CI{F,S}) where {F,S} - if d.inner === nothing + if inner_is_empty(d) return d end k_value = key.value::Int - delete!(d.inner, k_value) + delete!(inner(d), k_value) return d end function Base.isempty(d::AbstractWithType{F, S}) where {F,S} - if d.inner === nothing + if inner_is_empty(d) return true end - return isempty(d.inner) + return isempty(inner(d)) end function Base.values(d::WithType{F, S, V})::Vector{V} where {F, S, V} - if d.inner === nothing + if inner_is_empty(d) return V[] end - return V.(values(d.inner)) + return V.(values(inner(d))) end function Base.values(d::IndexWithType{F, S})::Vector{CI{F,S}} where {F,S} - if d.inner === nothing + if inner_is_empty(d) return CI{F,S}[] end - return CI{F,S}.(values(d.inner)) + return CI{F,S}.(values(inner(d))) end function Base.keys(d::AbstractWithType{F, S}) where {F,S} - if d.inner === nothing + if inner_is_empty(d) return CI{F,S}[] end - return CI{F,S}.(keys(d.inner)) + return CI{F,S}.(keys(inner(d))) end function Base.iterate(d::AbstractWithType{F, S}) where {F, S} - # inner = get(d.dict, (F,S), nothing) inner = d.inner if inner === nothing return nothing diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index 9c3074aa0b..b1d874d27c 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -11,6 +11,8 @@ const CI_I = CI{MOI.SingleVariable, MOI.Integer} const CI_B = CI{MOI.SingleVariable, MOI.ZeroOne} function basic_functionality(dict, k_values, v_values) + @test_throws ErrorException sizehint!(dict, 1) + @test isempty(dict) @test length(dict) == 0 @@ -24,8 +26,7 @@ function basic_functionality(dict, k_values, v_values) @test keys(dict) == [k_values[1]] @test dict[k_values[1]] == v_values[1] - kk = [] - vv = [] + kk, vv = [], [] for (k,v) in dict push!(kk, k) push!(vv, v) @@ -41,6 +42,21 @@ function basic_functionality(dict, k_values, v_values) dict[k_values[1]] = v_values[1] @test length(dict) == 1 + + for (k,v) in zip(k_values, v_values) + dict[k] = v + end + + kk, vv = [], [] + for (k,v) in dict + push!(kk, k) + push!(vv, v) + end + @test length(kk) == length(keys(dict)) + @test isempty(setdiff(kk, keys(dict))) + @test length(vv) == length(values(dict)) + @test isempty(setdiff(vv, values(dict))) + empty!(dict) @test isempty(dict) @test length(dict) == 0 @@ -53,6 +69,8 @@ function basic_functionality(dict, k_values, v_values) sizehint!(idict, 2) @test length(idict) == 1 @test length(bdict) == 0 + @test values(bdict) == typeof(v_values[3])[] + @test keys(bdict) == typeof(k_values[3])[] @test haskey(idict, k_values[1]) @test !haskey(idict, k_values[2]) @@ -67,16 +85,15 @@ function basic_functionality(dict, k_values, v_values) delete!(idict, k_values[2]) @test !haskey(idict, k_values[2]) - kk = [] - vv = [] - for (k,v) in dict + kk, vv = [], [] + for (k,v) in idict push!(kk, k) push!(vv, v) end - @test length(kk) == length(keys(dict)) - @test isempty(setdiff(kk, keys(dict))) - @test length(vv) == length(values(dict)) - @test isempty(setdiff(vv, values(dict))) + @test length(kk) == length(keys(idict)) + @test isempty(setdiff(kk, keys(idict))) + @test length(vv) == length(values(idict)) + @test isempty(setdiff(vv, values(idict))) @test !isempty(idict) @test isempty(bdict) @@ -87,6 +104,14 @@ function 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}) + ek = CI{MOI.SingleVariable, MOI.EqualTo{Bool}}(1) + @test_throws KeyError edict[ek] + sizehint!(edict, 0) + @test length(edict) == 0 + @test_throws KeyError edict[ek] + delete!(edict, ek) end @testset "IndexDoubleDict" begin From e50dd28f5422c74786c6ecb99e4ce8adde7d6c8f Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 13 Jul 2020 22:07:39 -0300 Subject: [PATCH 08/11] more tests --- src/Utilities/DoubleDicts.jl | 2 +- test/Utilities/DoubleDicts.jl | 54 +++++++++++++++++------------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 2b7dac2429..e2348b41b6 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -62,7 +62,7 @@ end function Base.sizehint!(::AbstractDoubleDict, ::Integer) throw(ErrorException("sizehint!(d::DoubleDict, ::Integer) has no proper" * - " meaning for DoubleDict, use sizehint!(WithType{F,S}(d), n::Integer) " * + " meaning for DoubleDict, use sizehint!(d[F,S], n::Integer) " * "instead.")) end diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index b1d874d27c..fe64e25ffa 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -10,12 +10,31 @@ const DoubleDicts = MathOptInterface.Utilities.DoubleDicts const CI_I = CI{MOI.SingleVariable, MOI.Integer} const CI_B = CI{MOI.SingleVariable, MOI.ZeroOne} +function test_iterator(dict) + kk, vv = [], [] + for (k,v) in dict + push!(kk, k) + push!(vv, v) + end + @test length(kk) == length(keys(dict)) + @test isempty(setdiff(kk, keys(dict))) + @test length(vv) == length(values(dict)) + @test isempty(setdiff(vv, values(dict))) +end + function basic_functionality(dict, k_values, v_values) @test_throws ErrorException sizehint!(dict, 1) @test isempty(dict) @test length(dict) == 0 + dict[k_values[1]] = v_values[1] + delete!(dict, k_values[1]) + test_iterator(dict) + + dict[k_values[3]] = v_values[3] + test_iterator(dict) + empty!(dict) @test isempty(dict) @test !haskey(dict, k_values[1]) @@ -26,15 +45,7 @@ function basic_functionality(dict, k_values, v_values) @test keys(dict) == [k_values[1]] @test dict[k_values[1]] == v_values[1] - kk, vv = [], [] - for (k,v) in dict - push!(kk, k) - push!(vv, v) - end - @test length(kk) == length(keys(dict)) - @test isempty(setdiff(kk, keys(dict))) - @test length(vv) == length(values(dict)) - @test isempty(setdiff(vv, values(dict))) + test_iterator(dict) delete!(dict, k_values[1]) @test !haskey(dict, k_values[1]) @@ -47,15 +58,7 @@ function basic_functionality(dict, k_values, v_values) dict[k] = v end - kk, vv = [], [] - for (k,v) in dict - push!(kk, k) - push!(vv, v) - end - @test length(kk) == length(keys(dict)) - @test isempty(setdiff(kk, keys(dict))) - @test length(vv) == length(values(dict)) - @test isempty(setdiff(vv, values(dict))) + test_iterator(dict) empty!(dict) @test isempty(dict) @@ -82,18 +85,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) + delete!(idict, k_values[2]) @test !haskey(idict, k_values[2]) - - kk, vv = [], [] - for (k,v) in idict - push!(kk, k) - push!(vv, v) - end - @test length(kk) == length(keys(idict)) - @test isempty(setdiff(kk, keys(idict))) - @test length(vv) == length(values(idict)) - @test isempty(setdiff(vv, values(idict))) + test_iterator(idict) @test !isempty(idict) @test isempty(bdict) @@ -107,11 +103,13 @@ function basic_functionality(dict, k_values, v_values) edict = DoubleDicts.with_type(dict, MOI.SingleVariable, MOI.EqualTo{Bool}) ek = CI{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) + test_iterator(edict) end @testset "IndexDoubleDict" begin From 6761b189623070a9b8dda7587b6e585324f2ce0a Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Tue, 14 Jul 2020 17:04:48 -0300 Subject: [PATCH 09/11] simplify type definitions --- src/Utilities/DoubleDicts.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index e2348b41b6..8266a669a9 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -21,7 +21,7 @@ fully type stable dictionary with values of type `V` and keys of type ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct DoubleDict{V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} +struct DoubleDict{V, D, D2} <: AbstractDoubleDict{V, D, D2} dict::D2 DoubleDict{V}() where V = new{V, Dict{Int64, V}, Dict{Tuple{DataType, DataType}, Dict{Int64, V}}}( Dict{Tuple{DataType, DataType}, Dict{Int64, V}}()) @@ -45,13 +45,13 @@ fully type stable dictionary with values of type ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct IndexDoubleDict{V<:Int64, D<:AbstractDict{Int64, Int64}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} <: AbstractDoubleDict{V, D, D2} +struct IndexDoubleDict{D, D2} <: AbstractDoubleDict{Int64, D, D2} dict::D2 - IndexDoubleDict() = new{Int64, Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}}( + IndexDoubleDict() = new{Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}}( Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}()) end -MainIndexDoubleDict = IndexDoubleDict{Int, Dict{Int, Int}, Dict{Tuple{DataType, DataType}, Dict{Int, Int}}} +MainIndexDoubleDict = IndexDoubleDict{Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}} @inline function typed_value(::DoubleDict{V}, v::V, ::Type{F}, ::Type{S})::V where {V, F, S} v @@ -223,8 +223,8 @@ mutable struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, dict::D2 inner::Union{D, Nothing} function IndexWithType{F, S}(d::D3) where - {V<:Int64, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, - D3<:IndexDoubleDict{V, D, D2}, F, S} + {D<:AbstractDict{Int64, Int64}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, + D3<:IndexDoubleDict{D, D2}, F, S} inner = get(d.dict, (F, S), nothing)::Union{D, Nothing} new{F, S, CI{F,S}, D, D3}(d, inner) end From 3b9103b78517a103c7062fc1dcf6a7b84d922c8c Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sat, 18 Jul 2020 20:26:12 -0300 Subject: [PATCH 10/11] Add FunctionSetDoubleDict and make double dict a subtype of abstract dict --- src/Utilities/DoubleDicts.jl | 137 +++++++++++++++++++++++++--------- test/Utilities/DoubleDicts.jl | 25 +++++-- 2 files changed, 120 insertions(+), 42 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 8266a669a9..658cfc7ff8 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -3,9 +3,12 @@ module DoubleDicts import MathOptInterface const MOI = MathOptInterface -const CI{F,S} = MOI.ConstraintIndex{F,S} +const CI = MOI.ConstraintIndex +const AD = AbstractDict -abstract type AbstractDoubleDict{V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}} end +DataTypePair = Tuple{DataType, DataType} + +abstract type AbstractDoubleDict{K, V, IK, DI<:AD{IK}, DO<:AD{K, DI}} <: AD{K, V} end """ DoubleDict{V} @@ -14,6 +17,7 @@ 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. +Therefore this type is actually a subtype of `AbstractDict{Tuple{DataType, DataType}, V}`. When optimal performance or type stability is required its possible to obtain a fully type stable dictionary with values of type `V` and keys of type @@ -21,10 +25,10 @@ fully type stable dictionary with values of type `V` and keys of type ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct DoubleDict{V, D, D2} <: AbstractDoubleDict{V, D, D2} - dict::D2 - DoubleDict{V}() where V = new{V, Dict{Int64, V}, Dict{Tuple{DataType, DataType}, Dict{Int64, V}}}( - Dict{Tuple{DataType, DataType}, Dict{Int64, V}}()) +struct DoubleDict{V, DI, DO} <: AbstractDoubleDict{DataTypePair, V, Int64, DI, DO} + dict::DO + DoubleDict{V}() where V = new{V, Dict{Int64, V}, Dict{DataTypePair, Dict{Int64, V}}}( + Dict{DataTypePair, Dict{Int64, V}}()) end """ @@ -45,10 +49,16 @@ fully type stable dictionary with values of type ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct IndexDoubleDict{D, D2} <: AbstractDoubleDict{Int64, D, D2} - dict::D2 - IndexDoubleDict() = new{Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}}( - Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}()) +struct IndexDoubleDict{DI, DO} <: AbstractDoubleDict{DataTypePair, Int64, Int64, DI, DO} + dict::DO + IndexDoubleDict() = new{Dict{Int64, Int64}, Dict{DataTypePair, Dict{Int64, Int64}}}( + Dict{DataTypePair, Dict{Int64, Int64}}()) +end + +struct FunctionSetDoubleDict{DI, DO} <: AbstractDoubleDict{DataTypePair, Tuple, Int64, DI, DO} + dict::DO + FunctionSetDoubleDict() = new{Dict{Int64}, Dict{DataTypePair, Dict{Int64}}}( + Dict{DataTypePair, Dict{Int64}}()) end MainIndexDoubleDict = IndexDoubleDict{Dict{Int64, Int64}, Dict{Tuple{DataType, DataType}, Dict{Int64, Int64}}} @@ -59,6 +69,9 @@ end @inline function typed_value(::IndexDoubleDict, v::Int64, ::Type{F}, ::Type{S})::CI{F,S} where {F, S} CI{F,S}(v) end +@inline function typed_value(::FunctionSetDoubleDict, v::Tuple{F,S}, ::Type{F}, ::Type{S})::Tuple{F,S} where {F, S} + v +end function Base.sizehint!(::AbstractDoubleDict, ::Integer) throw(ErrorException("sizehint!(d::DoubleDict, ::Integer) has no proper" * @@ -90,30 +103,47 @@ function Base.getindex(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} return typed_value(dict, inner[k_value], F, S) end +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(::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} + return Dict{Int64, Int64}() +end + """ lazy_get(dict::DoubleDict, ::Type{F}, ::Type{S}) description """ -function lazy_get(dict::AbstractDoubleDict{V}, ::Type{F}, ::Type{S}) where {V,F,S} +function lazy_get(dict::AbstractDoubleDict{K, V}, ::Type{F}, ::Type{S}) where {K,V,F,S} inner = get(dict.dict, (F,S), nothing) if inner === nothing - return dict.dict[(F,S)] = Dict{Int64,V}() + return dict.dict[(F,S)] = init_inner_dict(dict, F, S) end return inner end -function Base.setindex!(dict::AbstractDoubleDict{V}, value::V, key::CI{F,S}) where {V,F,S} +function Base.setindex!(dict::AbstractDoubleDict{K, V}, value::V, key::CI{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 + return value +end +function Base.setindex!(dict::DoubleDict{V}, value::V, key::CI{F,S}) where {V,F,S} v_value = value k_value = key.value::Int64 - inner = lazy_get(dict, F, S)::Dict{Int64,V} + inner = lazy_get(dict, F, S)::Dict{Int64, V} inner[k_value] = v_value return value end function Base.setindex!(dict::IndexDoubleDict, value::CI{F,S}, key::CI{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 = lazy_get(dict, F, S)::Dict{Int64, Int64} inner[k_value] = v_value return value end @@ -123,7 +153,7 @@ function Base.empty!(d::AbstractDoubleDict)::Nothing return end -function Base.delete!(d::AbstractDoubleDict{V}, key::CI{F,S}) where {V,F,S} +function Base.delete!(d::AbstractDoubleDict, key::CI{F,S}) where {F,S} k_value = key.value::Int64 inner = lazy_get(d, F, S) delete!(inner, k_value) @@ -149,10 +179,10 @@ function Base.values(d::IndexDoubleDict)::Vector{CI} end return out end -function Base.values(d::AbstractDoubleDict{V})::Vector{V} where V +function Base.values(d::AbstractDoubleDict{K, V})::Vector{V} where {K, V} out = V[] for ((F, S), inner) in d.dict - append!(out, V.(values(inner))) + append!(out, values(inner)) end return out end @@ -202,31 +232,38 @@ function Base.iterate(d::AbstractDoubleDict, state) return CI{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, D, D2} <: AbstractDict{CI{F,S}, V} end """ WithType{D<:DoubleDict, F<:MOI.AbstractFunction, S<:MOI.AbstractSet} Used to specialize methods and iterators for a given contraint type CI{F,S}. """ -mutable struct WithType{F, S, V, D, D2} <: AbstractWithType{F, S, V, D, D2} - dict::D2 - inner::Union{D, Nothing} - function WithType{F, S}(d::D3) where - {V, D<:AbstractDict{Int64, V}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, - D3<:DoubleDict{V, D, D2}, F, S} +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<:AD{Int64, V}, DO<:AD{DataTypePair, DI}, DD<:DoubleDict{V, DI, DO}, F, S} inner = get(d.dict, (F, S), nothing) - new{F, S, V, D, D3}(d, inner) + 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} + dict::DD + inner::Union{DI, Nothing} + function IndexWithType{F, S}(d::DD) where + {DI<:AD{Int64, Int64}, DO<:AD{DataTypePair, DI}, DD<:IndexDoubleDict{DI, DO}, F, S} + inner = get(d.dict, (F, S), nothing)::Union{DI, Nothing} + new{F, S, CI{F,S}, DI, DD}(d, inner) end end -mutable struct IndexWithType{F, S, V, D, D2} <: AbstractWithType{F, S, CI{F,S}, D, D2} - dict::D2 - inner::Union{D, Nothing} - function IndexWithType{F, S}(d::D3) where - {D<:AbstractDict{Int64, Int64}, D2<:AbstractDict{Tuple{DataType, DataType}, D}, - D3<:IndexDoubleDict{D, D2}, F, S} - inner = get(d.dict, (F, S), nothing)::Union{D, Nothing} - new{F, S, CI{F,S}, D, D3}(d, inner) +mutable struct FunctionSetWithType{F, S, V, DI, DD} <: AbstractWithType{F, S, CI{F,S}, DI, DD} + dict::DD + inner::Union{DI, Nothing} + function FunctionSetWithType{F, S}(d::DD) where + {F, S, DI<:AD{Int64}, DO<:AD{DataTypePair, DI}, DD<:FunctionSetDoubleDict{DI, DO}} + inner = get(d.dict, (F, S), nothing)::Union{DI{Tuple{F,S}}, Nothing} + new{F, S, Tuple{F,S}, DI{Tuple{F,S}}, DD}(d, inner) end end @@ -236,6 +273,9 @@ 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) @@ -243,6 +283,9 @@ 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} v @@ -250,6 +293,9 @@ end @inline function typed_value(::IndexWithType{F, S}, v::Int)::CI{F,S} where {F, S} CI{F,S}(v) end +@inline function typed_value(::FunctionSetWithType{F, S, V}, v::Tuple{F,S})::Tuple{F,S} where {V, F, S} + v +end function initialize_inner!(d::AbstractWithType{F, S, V, D}) where {F, S, V, D} d.inner = D() @@ -300,8 +346,8 @@ function Base.setindex!(d::WithType{F, S, V}, value::V, inner(d)[k_value] = v_value return value end -function Base.setindex!(d::IndexWithType{F, S, V, D, D2}, value::CI{F,S}, - key::CI{F,S})::CI{F,S} where {F, S, V, D, D2} +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 @@ -311,6 +357,17 @@ function Base.setindex!(d::IndexWithType{F, S, V, D, D2}, value::CI{F,S}, _inner[k_value] = v_value return value end +function Base.setindex!(d::FunctionSetWithType{F, S, V}, value::Tuple{F,S}, + key::CI{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}} + v_value = value + k_value = key.value::Int64 + _inner[k_value] = v_value + return value +end function Base.empty!(d::AbstractWithType{F, S}) where {F,S} if inner_is_empty(d) @@ -339,7 +396,7 @@ function Base.values(d::WithType{F, S, V})::Vector{V} where {F, S, V} if inner_is_empty(d) return V[] end - return V.(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) @@ -347,6 +404,12 @@ function Base.values(d::IndexWithType{F, S})::Vector{CI{F,S}} where {F,S} end return CI{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) diff --git a/test/Utilities/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl index fe64e25ffa..3134e798d2 100644 --- a/test/Utilities/DoubleDicts.jl +++ b/test/Utilities/DoubleDicts.jl @@ -112,6 +112,21 @@ function basic_functionality(dict, k_values, v_values) test_iterator(edict) end +@testset "DoubleDict" begin + dict = DoubleDicts.DoubleDict{Float64}() + keys = [ + CI_I(1), + CI_I(2), + CI_B(1), + ] + vals = [ + 1.0, + 2.0, + 1.0, + ] + basic_functionality(dict, keys, vals) +end + @testset "IndexDoubleDict" begin dict = DoubleDicts.IndexDoubleDict() keys = [ @@ -123,17 +138,17 @@ end basic_functionality(dict, keys, vals) end -@testset "DoubleDict" begin - dict = DoubleDicts.DoubleDict{Float64}() +@testset "FunctionSetDoubleDict" begin + dict = DoubleDicts.FunctionSetDoubleDict() keys = [ CI_I(1), CI_I(2), CI_B(1), ] vals = [ - 1.0, - 2.0, - 1.0, + (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) end \ No newline at end of file From e1c065ed6a9ea6b6d344fad6f96909fc1f14c5a6 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 19 Jul 2020 13:17:12 -0300 Subject: [PATCH 11/11] fix abstract types --- src/Utilities/DoubleDicts.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Utilities/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl index 658cfc7ff8..83a9498725 100644 --- a/src/Utilities/DoubleDicts.jl +++ b/src/Utilities/DoubleDicts.jl @@ -8,7 +8,7 @@ const AD = AbstractDict DataTypePair = Tuple{DataType, DataType} -abstract type AbstractDoubleDict{K, V, IK, DI<:AD{IK}, DO<:AD{K, DI}} <: AD{K, V} end +abstract type AbstractDoubleDict{K, V, IK, DI<:AD{IK}, DO<:AD{K, DI}} <: AD{CI, V} end """ DoubleDict{V} @@ -17,7 +17,6 @@ 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. -Therefore this type is actually a subtype of `AbstractDict{Tuple{DataType, DataType}, V}`. When optimal performance or type stability is required its possible to obtain a fully type stable dictionary with values of type `V` and keys of type @@ -49,7 +48,7 @@ fully type stable dictionary with values of type ``inner = dict[MOI.SingleVariable, MOI.Integers]`` """ -struct IndexDoubleDict{DI, DO} <: AbstractDoubleDict{DataTypePair, Int64, Int64, DI, DO} +struct IndexDoubleDict{DI, DO} <: AbstractDoubleDict{DataTypePair, CI, Int64, DI, DO} dict::DO IndexDoubleDict() = new{Dict{Int64, Int64}, Dict{DataTypePair, Dict{Int64, Int64}}}( Dict{DataTypePair, Dict{Int64, Int64}}()) @@ -257,7 +256,7 @@ mutable struct IndexWithType{F, S, V, DI, DD} <: AbstractWithType{F, S, CI{F,S}, new{F, S, CI{F,S}, DI, DD}(d, inner) end end -mutable struct FunctionSetWithType{F, S, V, DI, DD} <: AbstractWithType{F, S, CI{F,S}, DI, DD} +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