Skip to content

Commit 1f3cd80

Browse files
authored
Merge pull request #7 from andyferris/ajf/Dictionaries.jl
Switch to Dictionaries.jl backend
2 parents 7d533b1 + 58eda6a commit 1f3cd80

File tree

7 files changed

+95
-90
lines changed

7 files changed

+95
-90
lines changed

Project.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
authors = ["Andy Ferris <[email protected]>"]
22
name = "AcceleratedArrays"
33
uuid = "44e12807-9a19-5591-91cf-c1b4fb89ce64"
4-
version = "0.2.2"
4+
version = "0.3.0"
55

66
[deps]
77
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
8+
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
89

910
[extras]
1011
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1112

1213
[targets]
1314
test = ["Test"]
15+
16+
[compat]
17+
julia = "1"
18+
SplitApplyCombine = "1"
19+
Dictionaries = "0.2"

src/AcceleratedArrays.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module AcceleratedArrays
22

3-
using SplitApplyCombine
3+
using SplitApplyCombine, Dictionaries
44

5-
using Base: @propagate_inbounds, Fix2, promote_op
5+
using Base: @propagate_inbounds, Fix2, promote_op, Callable
66

77
export accelerate, accelerate!
88
export AcceleratedArray, AcceleratedVector, AcceleratedMatrix, MaybeVector, SingleVector

src/HashIndex.jl

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Hash table acceleration index
2-
struct HashIndex{D <: AbstractDict} <: AbstractIndex
2+
struct HashIndex{D <: HashDictionary} <: AbstractIndex
33
dict::D
44
end
55

66
function HashIndex(a::AbstractArray)
7-
dict = Dict{eltype(a), Vector{eltype(keys(a))}}()
7+
dict = HashDictionary{eltype(a), Vector{eltype(keys(a))}}()
88

99
@inbounds for i in keys(a)
1010
value = a[i]
@@ -24,85 +24,85 @@ Base.summary(i::HashIndex) = "HashIndex ($(length(i.dict)) unique element$(lengt
2424
Base.in(x, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}) = haskey(a.index.dict, x)
2525

2626
function Base.count(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
27-
index = Base.ht_keyindex(a.index.dict, f.x)
28-
if index < 0
29-
return 0
27+
(hasindex, token) = gettoken(a.index.dict, f.x)
28+
if hasindex
29+
return length(@inbounds gettokenvalue(a.index.dict, token))
3030
else
31-
return length(a.index.dict.vals[index])
31+
return 0
3232
end
3333
end
3434

3535
function Base.findall(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
36-
index = Base.ht_keyindex(a.index.dict, f.x)
37-
if index < 0
38-
return Vector{eltype(keys(a))}()
39-
else
40-
return @inbounds a.index.dict.vals[index]
41-
end
36+
(hasindex, token) = gettoken(a.index.dict, f.x)
37+
if hasindex
38+
return @inbounds gettokenvalue(a.index.dict, token)
39+
else
40+
return Vector{eltype(keys(a))}()
41+
end
4242
end
4343

4444
# TODO: findall for arbitrary predicates by just checking each unique key? (Sometimes faster, sometimes slower?)
4545

4646
function Base.findfirst(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
47-
index = Base.ht_keyindex(a.index.dict, f.x)
48-
if index < 0
49-
return nothing
50-
else
51-
return @inbounds first(a.index.dict.vals[index])
52-
end
47+
(hasindex, token) = gettoken(a.index.dict, f.x)
48+
if hasindex
49+
return @inbounds first(gettokenvalue(a.index.dict, token))
50+
else
51+
return nothing
52+
end
5353
end
5454

5555
function Base.findlast(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
56-
index = Base.ht_keyindex(a.index.dict, f.x)
57-
if index < 0
58-
return nothing
59-
else
60-
return @inbounds last(a.index.dict.vals[index])
61-
end
56+
(hasindex, token) = gettoken(a.index.dict, f.x)
57+
if hasindex
58+
return @inbounds last(gettokenvalue(a.index.dict, token))
59+
else
60+
return nothing
61+
end
6262
end
6363

6464
function Base.filter(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
65-
index = Base.ht_keyindex(a.index.dict, f.x)
66-
if index < 0
67-
return empty(a)
68-
else
69-
return @inbounds parent(a)[a.index.dict.vals[index]]
70-
end
65+
(hasindex, token) = gettoken(a.index.dict, f.x)
66+
if hasindex
67+
return @inbounds parent(a)[(gettokenvalue(a.index.dict, token))]
68+
else
69+
return empty(a)
70+
end
7171
end
7272

7373
# TODO: filter for arbitrary predicates by just checking each unique key? (Sometimes faster, sometimes slower?)
7474

7575
function Base.unique(a::AcceleratedArray{T, <:Any, <:Any, <:HashIndex}) where {T}
76-
out = Vector{T}()
77-
@inbounds for value in keys(a.index.dict)
78-
push!(out, value)
79-
end
80-
return out
76+
out = Vector{T}()
77+
@inbounds for value in keys(a.index.dict)
78+
push!(out, value)
79+
end
80+
return out
8181
end
8282

83-
function SplitApplyCombine.group2(a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}, b::AbstractArray)
84-
return Dict((key, @inbounds b[inds]) for (key, inds) in a.index.dict)
83+
function SplitApplyCombine.group(a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}, b::AbstractArray)
84+
return map(inds -> @inbounds(b[inds]), a.index.dict)
8585
end
8686

87-
function SplitApplyCombine.groupreduce(::typeof(identity), f, op, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}; kw...)
88-
return Dict((k, mapreduce(i -> f(@inbounds a[i]), op, v; kw...)) for (k,v) in a.index.dict)
87+
function SplitApplyCombine.groupreduce(op::Callable, a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}; kw...)
88+
return map(inds -> @inbounds(reduce(op, view(a, inds); kw...)), a.index.dict)
8989
end
9090

91-
function SplitApplyCombine._groupinds(a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
92-
return a.index.dict
91+
function SplitApplyCombine.groupfind(a::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex})
92+
return a.index.dict
9393
end
9494

9595
function SplitApplyCombine._innerjoin!(out, left::AbstractArray, right::AcceleratedArray{<:Any, <:Any, <:Any, <:HashIndex}, v::AbstractArray, ::typeof(isequal))
96-
@boundscheck if (axes(l)..., axes(r)...) != axes(v)
96+
@boundscheck if (axes(left)..., axes(right)...) != axes(v)
9797
throw(DimensionMismatch("innerjoin arrays do not have matching dimensions"))
9898
end
9999

100100
dict = right.index.dict
101101

102-
@inbounds for i keys(left)
103-
dict_index = Base.ht_keyindex(dict, left(i_l))
104-
if dict_index > 0 # -1 if key not found
105-
for i_r dict.vals[dict_index]
102+
@inbounds for i_l keys(left)
103+
(hasindex, token) = gettoken(right.index.dict, @inbounds left[i_l])
104+
if hasindex
105+
for i_r gettokenvalue(dict, token)
106106
push!(out, v[Tuple(i_l)..., Tuple(i_r)...])
107107
end
108108
end
@@ -116,13 +116,13 @@ function SplitApplyCombine.leftgroupjoin(lkey, ::typeof(identity), f, ::typeof(i
116116
K = promote_op(lkey, eltype(left))
117117

118118
dict = right.index.dict
119-
out = Dict{K, Vector{T}}()
119+
out = HashDictionary{K, Vector{T}}()
120120
for a left
121121
key = lkey(a)
122122
group = get!(() -> T[], out, key)
123-
dict_index = Base.ht_keyindex(dict, key)
124-
if dict_index > 0 # -1 if key not found
125-
for b dict.vals[dict_index]
123+
(hasindex, token) = gettoken(dict, key)
124+
if hasindex
125+
for b @inbounds gettokenvalue(dict, token)
126126
push!(group, f(a, b))
127127
end
128128
end

src/UniqueHashIndex.jl

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Hash table acceleration index
2-
struct UniqueHashIndex{D <: Dict} <: AbstractUniqueIndex
2+
struct UniqueHashIndex{D <: HashDictionary} <: AbstractUniqueIndex
33
dict::D
44
end
55

66
function UniqueHashIndex(a::AbstractArray)
7-
dict = Dict{eltype(a), SingleVector{eltype(keys(a))}}()
7+
dict = HashDictionary{eltype(a), SingleVector{eltype(keys(a))}}()
88

99
@inbounds for i in keys(a)
1010
value = a[i]
11-
index = Base.ht_keyindex2!(dict, value)
12-
if index > 0 # `value` found in `dict`
13-
error("Input not unique") # TODO Use appropriate Exception
14-
else # `value` is ready to be inserted into `dict` at slot `-index`
15-
@inbounds Base._setindex!(dict, SingleVector(i), value, -index)
16-
end
11+
(hadindex, token) = gettoken!(dict, value)
12+
if hadindex
13+
error("Input not unique") # TODO Use appropriate Exception
14+
else # `value` is ready to be inserted into `dict` at slot `-index`
15+
@inbounds settokenvalue!(dict, token, SingleVector(i))
16+
end
1717
end
1818
return UniqueHashIndex(dict)
1919
end
@@ -24,55 +24,53 @@ Base.summary(::UniqueHashIndex) = "UniqueHashIndex"
2424
Base.in(x, a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex}) = haskey(a.index.dict, x)
2525

2626
function Base.count(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex})
27-
index = Base.ht_keyindex(a.index.dict, f.x)
28-
if index < 0
29-
return 0
30-
else
27+
if f.x in keys(a.index.dict)
3128
return 1
29+
else
30+
return 0
3231
end
3332
end
3433

3534
function Base.findall(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex})
36-
index = Base.ht_keyindex(a.index.dict, f.x)
37-
if index < 0
38-
return MaybeVector{eltype(keys(a))}()
39-
else
40-
return MaybeVector(@inbounds a.index.dict.vals[index][])
41-
end
35+
(hasindex, token) = gettoken(a.index.dict, f.x)
36+
if hasindex
37+
return MaybeVector(@inbounds gettokenvalue(a.index.dict, token)[])
38+
else
39+
return MaybeVector{eltype(keys(a))}()
40+
end
4241
end
4342

4443
function Base.filter(f::Fix2{typeof(isequal)}, a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex})
45-
index = Base.ht_keyindex(a.index.dict, f.x)
46-
if index < 0
47-
return MaybeVector{eltype(a)}()
48-
else
49-
return MaybeVector{eltype(a)}(f.x)
50-
end
44+
if f.x in keys(a.index.dict)
45+
return MaybeVector{eltype(a)}(f.x)
46+
else
47+
return MaybeVector{eltype(a)}()
48+
end
5149
end
5250

53-
function SplitApplyCombine.group2(a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex}, b::AbstractArray)
54-
return Dict((key, SingleVector(@inbounds b[inds[]])) for (key, inds) in a.index.dict)
51+
function SplitApplyCombine.group(a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex}, b::AbstractArray)
52+
return map(inds -> SingleVector(@inbounds b[inds[]]), a.index.dict)
5553
end
5654

5755
function SplitApplyCombine.groupreduce(::typeof(identity), f, op, a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex}; kw...)
58-
return Dict((k, mapreduce(i -> f(@inbounds a[i]), op, v; kw...)) for (k, v) in a.index.dict)
56+
return map(inds -> @inbounds(reduce(op, a[inds[]]; kw...)), a.index.dict)
5957
end
6058

61-
function SplitApplyCombine._groupinds(a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex})
62-
return a.index.dict
59+
function SplitApplyCombine.groupfind(a::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex})
60+
return a.index.dict
6361
end
6462

6563
function SplitApplyCombine._innerjoin!(out, left::AbstractArray, right::AcceleratedArray{<:Any, <:Any, <:Any, <:UniqueHashIndex}, v::AbstractArray, ::typeof(isequal))
66-
@boundscheck if (axes(l)..., axes(r)...) != axes(v)
64+
@boundscheck if (axes(left)..., axes(right)...) != axes(v)
6765
throw(DimensionMismatch("innerjoin arrays do not have matching dimensions"))
6866
end
6967

7068
dict = right.index.dict
7169

72-
@inbounds for i keys(left)
73-
dict_indUniqueex = Base.ht_keyindex(dict, left(i_l))
74-
if dict_index > 0 # -1 if key not found
75-
i_r = dict.vals[dict_index][]
70+
@inbounds for i_l keys(left)
71+
(hasindex, token) = gettoken(dict, left[i_l])
72+
if hasindex
73+
i_r = gettokenvalue(dict, token)[]
7674
push!(out, v[Tuple(i_l)..., Tuple(i_r)...])
7775
end
7876
end

test/HashIndex.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
@test issetequal(unique(b), [1,2,3])
1919

2020
@test group(identity, b) == group(identity, a)
21-
@test groupinds(identity, b) == groupinds(identity, a)
21+
@test groupfind(identity, b) == groupfind(identity, a)
2222
@test groupreduce(identity, +, b) == groupreduce(identity, +, a)
2323

2424
@test issetequal(innerjoin(identity, identity, tuple, isequal, b, [0, 1, 2]),

test/UniqueHashIndex.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@test unique(b) === b
2020

2121
@test group(iseven, b) == group(iseven, a)
22-
@test groupinds(iseven, b) == groupinds(iseven, a)
22+
@test groupfind(iseven, b) == groupfind(iseven, a)
2323
@test groupreduce(iseven, +, b) == groupreduce(iseven, +, a)
2424

2525
@test issetequal(innerjoin(identity, identity, tuple, isequal, b, [0, 1, 2]),

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using Test
22
using AcceleratedArrays
33
using SplitApplyCombine
4+
using Dictionaries
45

5-
@test isempty(detect_ambiguities(Base, AcceleratedArrays))
6+
@test isempty(setdiff(detect_ambiguities(Base, AcceleratedArrays, Dictionaries), detect_ambiguities(Base, Dictionaries) ))
67

78
@testset "AcceleratedArrays" begin
89
include("Interval.jl")

0 commit comments

Comments
 (0)