Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use DenseDict for copy_to #1122

Merged
merged 2 commits into from
Jul 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Utilities/Utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ include("functions.jl")
include("mutable_arithmetics.jl")
include("sets.jl")
include("constraints.jl")
include("dense_dict.jl")
include("copy.jl")
include("results.jl")
include("variables.jl")
Expand Down
27 changes: 22 additions & 5 deletions src/Utilities/copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,31 @@ error in case `copy_to` is called with `copy_names` equal to `true`.
"""
supports_default_copy_to(model::MOI.ModelLike, copy_names::Bool) = false

const DenseVariableDict{V} = DenseDict{MOI.VariableIndex, V, typeof(MOI.index_value), typeof(MOI.VariableIndex)}
function dense_variable_dict(::Type{V}, n) where V
return DenseDict{MOI.VariableIndex, V}(MOI.index_value, MOI.VariableIndex, n)
end

struct IndexMap <: AbstractDict{MOI.Index, MOI.Index}
varmap::Dict{MOI.VariableIndex, MOI.VariableIndex}
varmap::Union{DenseVariableDict{MOI.VariableIndex},
Dict{MOI.VariableIndex, MOI.VariableIndex}}
conmap::Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}
end
IndexMap() = IndexMap(Dict{MOI.VariableIndex, MOI.VariableIndex}(),
Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}())
function IndexMap(n)
IndexMap(dense_variable_dict(MOI.VariableIndex, n),
Dict{MOI.ConstraintIndex, MOI.ConstraintIndex}())
end

function index_map_for_variable_indices(variables)
n = length(variables)
if all(i -> variables[i] == MOI.VariableIndex(i), 1:n)
return IndexMap(n)
else
return IndexMap()
end
end

Base.getindex(idxmap::IndexMap, vi::MOI.VariableIndex) = idxmap.varmap[vi]
function Base.getindex(idxmap::IndexMap, ci::MOI.ConstraintIndex{F, S}) where {F, S}
Expand Down Expand Up @@ -298,9 +317,8 @@ the copying a model incrementally.
function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike, copy_names::Bool)
MOI.empty!(dest)

idxmap = IndexMap()

vis_src = MOI.get(src, MOI.ListOfVariableIndices())
idxmap = index_map_for_variable_indices(vis_src)
constraint_types = MOI.get(src, MOI.ListOfConstraints())
single_variable_types = Type{<:MOI.AbstractScalarSet}[]
vector_of_variables_types = Type{<:MOI.AbstractVectorSet}[]
Expand Down Expand Up @@ -659,9 +677,8 @@ the Allocate-Load API.
function allocate_load(dest::MOI.ModelLike, src::MOI.ModelLike, copy_names::Bool)
MOI.empty!(dest)

idxmap = IndexMap()

vis_src = MOI.get(src, MOI.ListOfVariableIndices())
idxmap = index_map_for_variable_indices(vis_src)
constraint_types = MOI.get(src, MOI.ListOfConstraints())
single_variable_types = [S for (F, S) in constraint_types
if F == MOI.SingleVariable]
Expand Down
41 changes: 41 additions & 0 deletions src/Utilities/dense_dict.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
struct DenseDict{K, V, F, I} <: AbstractDict{K, V}
hash::F
inverse_hash::I
set::BitSet
map::Vector{V}
blegat marked this conversation as resolved.
Show resolved Hide resolved
end

Same as `Dict{K, V}` but `hash(key)` is assumed to belong to `eachindex(map)`.
"""
struct DenseDict{K, V, F, I} <: AbstractDict{K, V}
hash::F
inverse_hash::I
set::BitSet
map::Vector{V}
function DenseDict{K, V}(hash, inverse_hash, n) where {K, V}
set = BitSet()
sizehint!(set, n)
return new{K, V, typeof(hash), typeof(inverse_hash)}(hash, inverse_hash, set, Vector{K}(undef, n))
end
end

# Implementation of the `AbstractDict` API.
# Base.empty(::DenseDict, ::Type{K}, ::Type{V}) not implemented
function Base.iterate(d::DenseDict, args...)
itr = iterate(d.set, args...)
if itr === nothing
return nothing
else
el, i = itr
return d.inverse_hash(el) => d.map[el], i
end
end
Base.length(d::DenseDict) = length(d.set)
Base.haskey(dict::DenseDict, key) = dict.hash(key) in dict.set
Base.getindex(dict::DenseDict, key) = dict.map[dict.hash(key)]
function Base.setindex!(dict::DenseDict, value, key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if one goes beyond the limit?
should it resize automatically? or even use sizehint?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it's an error, we can revisit once we have a use case for this

h = dict.hash(key)
push!(dict.set, h)
dict.map[h] = value
end
2 changes: 2 additions & 0 deletions src/indextypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ index.value == MOI.get(model, MOI.ConstraintFunction(), index).variable.value
struct ConstraintIndex{F, S}
value::Int64
end
index_value(ci::ConstraintIndex) = ci.value

"""
VariableIndex
Expand All @@ -29,6 +30,7 @@ To allow for deletion, indices need not be consecutive.
struct VariableIndex
value::Int64
end
index_value(vi::VariableIndex) = vi.value

# The default hash is slow. It's important for the performance of dictionaries
# of VariableIndices to define our own.
Expand Down
3 changes: 3 additions & 0 deletions test/Utilities/Utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ end
@testset "Caching Optimizer" begin
include("cachingoptimizer.jl")
end
@testset "DenseDict" begin
include("dense_dict.jl")
end
@testset "Copy" begin
include("copy.jl")
end
Expand Down
3 changes: 2 additions & 1 deletion test/Utilities/copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const MOI = MathOptInterface
const MOIT = MOI.Test
const MOIU = MOI.Utilities


include("../dummy.jl")

remove_moi(x::String) = replace(x, "MathOptInterface." => "")
Expand Down Expand Up @@ -170,7 +171,7 @@ mutable struct ReverseOrderConstrainedVariablesModel <: AbstractConstrainedVaria
inner ::MOIU.Model{Float64}
ReverseOrderConstrainedVariablesModel() = new(MOI.ConstraintIndex[], MOIU.Model{Float64}())
end



MOI.add_variables(model::AbstractConstrainedVariablesModel, n) = MOI.add_variables(model.inner, n)
Expand Down
34 changes: 34 additions & 0 deletions test/Utilities/dense_dict.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Test
import MathOptInterface
const MOI = MathOptInterface

mul2(x) = x * 2
div2(x) = div(x, 2)
d = MOI.Utilities.DenseDict{Int, Float64}(div2, mul2, 3)

d[4] = 0.25
@test !haskey(d, 2)
@test haskey(d, 4)
@test !haskey(d, 6)
@test length(d) == 1
@test collect(d) == [4 => 0.25]
@test d[4] == 0.25

d[2] = 1.5
@test haskey(d, 2)
@test haskey(d, 4)
@test !haskey(d, 6)
@test length(d) == 2
@test collect(d) == [2 => 1.5, 4 => 0.25]
@test d[2] == 1.5
@test d[4] == 0.25

d[6] = 0.75
@test haskey(d, 2)
@test haskey(d, 4)
@test haskey(d, 6)
@test length(d) == 3
@test collect(d) == [2 => 1.5, 4 => 0.25, 6 => 0.75]
@test d[2] == 1.5
@test d[4] == 0.25
@test d[6] == 0.75