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/DoubleDicts.jl b/src/Utilities/DoubleDicts.jl new file mode 100644 index 0000000000..83a9498725 --- /dev/null +++ b/src/Utilities/DoubleDicts.jl @@ -0,0 +1,443 @@ +module DoubleDicts + +import MathOptInterface + +const MOI = MathOptInterface +const CI = MOI.ConstraintIndex +const AD = AbstractDict + +DataTypePair = Tuple{DataType, DataType} + +abstract type AbstractDoubleDict{K, V, IK, DI<:AD{IK}, DO<:AD{K, DI}} <: AD{CI, 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. + +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, 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 + +""" + 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{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}}()) +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}}} + +@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::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" * + " meaning for DoubleDict, use sizehint!(d[F,S], n::Integer) " * + "instead.")) +end + +function Base.length(d::AbstractDoubleDict) + len = 0 + for inner in values(d.dict) + len += length(inner) + end + return len +end + +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)] + return haskey(inner, key.value) + else + return false + end +end + +function Base.getindex(dict::AbstractDoubleDict, key::CI{F,S}) where {F,S} + inner = dict.dict[(F,S)] + k_value = key.value::Int64 + 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{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)] = init_inner_dict(dict, F, S) + end + return inner +end + +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[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[k_value] = v_value + return value +end + +function Base.empty!(d::AbstractDoubleDict)::Nothing + Base.empty!(d.dict) + return +end + +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) + return d +end + +function Base.isempty(d::AbstractDoubleDict) + 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.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::AbstractDoubleDict{K, V})::Vector{V} where {K, V} + out = V[] + for ((F, S), inner) in d.dict + append!(out, values(inner)) + end + return out +end + +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.iterate(d::AbstractDoubleDict) + 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]) => 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 + 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]) => 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 + +""" + 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, 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, 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 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<: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 + +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} + v +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() +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 inner_is_empty(d) + initialize_inner!(d) + end + 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::CI{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::CI{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]) +end + +function Base.setindex!(d::WithType{F, S, V}, value::V, + key::CI{F,S})::V where {F, S, V} + if inner_is_empty(d) + initialize_inner!(d) + end + v_value = value + k_value = key.value::Int64 + inner(d)[k_value] = v_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 + 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) + return d + end + return empty!(inner(d)) +end + +function Base.delete!(d::AbstractWithType{F, S}, key::CI{F,S}) where {F,S} + if inner_is_empty(d) + return d + end + k_value = key.value::Int + 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{CI{F,S}} where {F,S} + if inner_is_empty(d) + return CI{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) + return CI{F,S}[] + end + return CI{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) + end +end +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]) => typed_value(d, i[2]), (state, inner) +end + +end diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index bac6621917..37290f8ab6 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") @@ -41,7 +44,6 @@ include("mockoptimizer.jl") include("cachingoptimizer.jl") include("universalfallback.jl") -include("CleverDicts.jl") include("lazy_iterators.jl") end # module diff --git a/src/Utilities/copy.jl b/src/Utilities/copy.jl index 8f385f9314..666a47039e 100644 --- a/src/Utilities/copy.jl +++ b/src/Utilities/copy.jl @@ -71,13 +71,13 @@ end struct IndexMap <: AbstractDict{MOI.Index, MOI.Index} varmap::Union{DenseVariableDict{MOI.VariableIndex}, Dict{MOI.VariableIndex, MOI.VariableIndex}} - conmap::Dict{MOI.ConstraintIndex, MOI.ConstraintIndex} + conmap::DoubleDicts.MainIndexDoubleDict end IndexMap() = IndexMap(Dict{MOI.VariableIndex, MOI.VariableIndex}(), - Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}()) + DoubleDicts.IndexDoubleDict()) function IndexMap(n) IndexMap(dense_variable_dict(MOI.VariableIndex, n), - Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}()) + DoubleDicts.IndexDoubleDict()) end function index_map_for_variable_indices(variables) 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/DoubleDicts.jl b/test/Utilities/DoubleDicts.jl new file mode 100644 index 0000000000..3134e798d2 --- /dev/null +++ b/test/Utilities/DoubleDicts.jl @@ -0,0 +1,154 @@ +# 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} + +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]) + + 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] + + test_iterator(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 + + for (k,v) in zip(k_values, v_values) + dict[k] = v + end + + test_iterator(dict) + + 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) + idict_ = dict[MOI.SingleVariable, MOI.Integer] + @test idict.dict === idict_.dict + 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]) + @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]) + test_iterator(idict) + + delete!(idict, k_values[2]) + @test !haskey(idict, k_values[2]) + test_iterator(idict) + + @test !isempty(idict) + @test isempty(bdict) + empty!(idict) + empty!(bdict) + @test isempty(idict) + @test isempty(bdict) + + 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) + 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 "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 = [ + CI_I(1), + CI_I(2), + CI_B(1), + ] + vals = keys + basic_functionality(dict, keys, vals) +end + +@testset "FunctionSetDoubleDict" begin + dict = DoubleDicts.FunctionSetDoubleDict() + keys = [ + CI_I(1), + CI_I(2), + CI_B(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) +end \ No newline at end of file diff --git a/test/Utilities/Utilities.jl b/test/Utilities/Utilities.jl index a42ba9e132..44602f49a9 100644 --- a/test/Utilities/Utilities.jl +++ b/test/Utilities/Utilities.jl @@ -40,6 +40,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 diff --git a/test/Utilities/copy.jl b/test/Utilities/copy.jl index 142c8622f6..549668d9a8 100644 --- a/test/Utilities/copy.jl +++ b/test/Utilities/copy.jl @@ -4,6 +4,7 @@ const MOI = MathOptInterface const MOIT = MOI.Test const MOIU = MOI.Utilities +const DoubleDicts = MathOptInterface.Utilities.DoubleDicts include("../dummy.jl") @@ -20,15 +21,18 @@ 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.IndexDoubleDict()) + 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" 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,Pair{ConstraintIndex,ConstraintIndex}($cx, $cy))") + compare_without_moi(sprint(show, map), "Utilities.IndexMap($x_y,$c_x_y)") end @testset "AUTOMATIC" begin