Skip to content

Commit

Permalink
Merge pull request #10 from mbauman/ohpointfive
Browse files Browse the repository at this point in the history
Support for 0.5
  • Loading branch information
mbauman committed Feb 29, 2016
2 parents 3b1640e + fa264e9 commit 4b2ceef
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 98 deletions.
1 change: 1 addition & 0 deletions src/RaggedArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ include("indexing.jl")
include("array.jl")
include("show.jl")
include("raggedrangematrix.jl")
include("views.jl")

export RaggedArray, AbstractRaggedArray, RaggedRangeMatrix, RaggedDimension, raggedlengths, rectsize

Expand Down
15 changes: 8 additions & 7 deletions src/abstract.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ RaggedDimension(i::Real) = convert(Int, i)
import Base: ==
=={T}(a::RaggedDimension{T}, b::RaggedDimension{T}) = a.szs == b.szs
=={T<:Union{Tuple,AbstractArray}}(a::RaggedDimension{T}, b::T) = a.szs == b
=={T<:Union{Tuple,AbstractArray}}(b::RaggedDimension{T}, a::T) = b.szs == a
=={T<:Union{Tuple,AbstractArray}}(a::T, b::RaggedDimension{T}) = b.szs == a
==(a::RaggedDimension, b::RaggedDimension) = (for (x,y) in zip(a, b); x == y || return false; end; return true)
==(a::Union{Tuple,AbstractArray}, b::RaggedDimension) = (for (x,y) in zip(a, b); x == y || return false; end; return true)
==(a::RaggedDimension, b::Union{Tuple,AbstractArray}) = (for (x,y) in zip(a, b); x == y || return false; end; return true)
# TODO: Hash tuples and all abstract arrays the same: by their contents
Base.hash(a::RaggedDimension, h::UInt) = hash(a.szs, hash(UInt(0xe4daeb7692ca0a52), h))
function Base.hash(a::RaggedDimension, h::UInt)
h = hash(UInt(0xe4daeb7692ca0a52), h)
for sz in a.szs
h = hash(sz, h)
end
h
end


Base.start(d::RaggedDimension) = start(d.szs)
Expand All @@ -63,10 +68,6 @@ Base.isless(a::RaggedDimension, b::Int) = isless(maximum(a), b)
return :(size(R, D)::Int)
end

#TODO: I really don't like this... but it's needed for SubArrays
import Base: *
*(i::Int, d::RaggedDimension) = i*maximum(d)

"""
raggedlengths(R, indexes...)
Expand Down
72 changes: 29 additions & 43 deletions src/indexing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,17 @@
to_index(I::AbstractArray{Bool}) = find(I)
to_index(I::AbstractArray) = I
to_index(i) = i
import Base: index_shape, index_shape_dim, index_lengths, index_lengths_dim
# shape of array to create for getindex() with indexes I, dropping trailing scalars
index_shape(A::AbstractRaggedArray, I::AbstractArray) = error("linear indexing not supported")
index_shape(A::AbstractRaggedArray, I::AbstractArray{Bool}) = error("linear indexing not supported")
index_shape(A::AbstractRaggedArray, I::Colon) = error("linear indexing not supported")
@generated function index_shape{_,__,RD}(A::AbstractRaggedArray{_,__,RD}, I...)
N = length(I)
sz = Expr(:tuple)
outer_idxs = [:(I[$d]) for d=RD+1:N]
for d=1:N
if !any(i->i<:Union{AbstractArray,Colon}, I[d:end])
break
elseif I[d] <: Colon
if d == RD
# If there's a Colon over the ragged dimension, return the
# selected ragged size(s). If the outer indices aren't all
# scalars, this will return an array and force the creation
# of a new AbstractRaggedArray for the indexed output.
push!(sz.args, :(RaggedDimension(raggedlengths(A, $(outer_idxs...)))))
else
push!(sz.args, d < N ? :(size(A, $d)) : :(trailingsize(A, Val{$d})))
end
elseif I[d] <: AbstractArray{Bool}
push!(sz.args, :(sum(I[$d])))
elseif I[d] <: AbstractVector
push!(sz.args, :(length(I[$d])))
else
push!(sz.args, 1)
end
end
quote
$(Expr(:meta, :inline))
$sz
end
end
index_shape_dim{T,N,RD}(A::AbstractRaggedArray{T,N,RD}, dim, ::Colon) =
dim == RD ? throw(ArgumentError("linear indexing through a ragged dimension is unsupported")) : trailingsize(A, dim)
index_shape_dim{T,N,RD}(A::AbstractRaggedArray{T,N,RD}, dim, ::Colon, i, I...) =
(dim == RD ? RaggedDimension(raggedlengths(A, i, I...)) : size(A, dim), index_shape_dim(A, dim+1, i, I...)...)

index_lengths_dim{T,N,RD}(A::AbstractRaggedArray{T,N,RD}, dim, ::Colon) =
dim == RD ? throw(ArgumentError("linear indexing through a ragged dimension is unsupported")) : trailingsize(A, dim)
index_lengths_dim{T,N,RD}(A::AbstractRaggedArray{T,N,RD}, dim, ::Colon, i, I...) =
(dim == RD ? RaggedDimension(raggedlengths(A, i, I...)) : size(A, dim), index_lengths_dim(A, dim+1, i, I...)...)

abstract RaggedIndexing <: Base.LinearIndexing
immutable RaggedFast <: RaggedIndexing; end
Expand Down Expand Up @@ -76,6 +53,7 @@ indexref(::Colon, i::Int) = i
@inline indexref(A::AbstractArray, i::Int) = unsafe_getindex(A, i)
convert_ints() = ()
@inline convert_ints(x, xs...) = (convert(Int, x), convert_ints(xs...)...)
@noinline throw_checksize_error(A, shp) = throw(ArgumentError("output array is the wrong size; expected $shp, got $(size(A))"))

# Just use one big generated method to do all the fallback dispatch in one place.
@generated function Base.getindex{T,AN,RD}(A::AbstractRaggedArray{T,AN,RD}, I...)
Expand All @@ -97,8 +75,9 @@ convert_ints() = ()
quote
checkbounds(A, $(Isplat...))
@nexprs $N d->(I_d = to_index(I[d]))
dest = similar(A, @ncall $N index_shape A I)
@ncall $N checksize dest I # TODO: stricter checksize for ragged arrays?
shp = @ncall $N index_shape A I
dest = similar(A, shp)
size(dest) == shp || throw_checksize_error(A, shp)
@ncall $N getindex! dest A I
end
end
Expand All @@ -114,7 +93,8 @@ end
quote
D = eachindex(dest)
Ds = start(D)
@nloops $N i dest d->(j_d = indexref(I[d], i_d)) begin
idxlens = index_lengths(src, I...)
@nloops $N i d->(1:idxlens[d]) d->(j_d = indexref(I[d], i_d)) begin
d, Ds = next(D, Ds)
v = @ncall $N ragged_unsafe_getindex src j
Base.unsafe_setindex!(dest, v, d)
Expand All @@ -124,20 +104,25 @@ end
end

# Adapted from Base.Cartesian's @nloops: ragged iteration!
macro ragged_nloops(N, itersym, rangeexpr, args...)
_ragged_nloops(N, itersym, rangeexpr, args...)
macro ragged_nloops(N, RD, itersym, rangeexpr, args...)
_ragged_nloops(N, RD, itersym, rangeexpr, args...)
end
import Base.Cartesian: inlineanonymous
function _ragged_nloops(N::Int, RD::Int, itersym::Symbol, arraysym::Symbol, args::Expr...)
function _ragged_nloops(N::Int, RD::Int, itersym::Symbol, rangeexpr, args::Expr...)
if !(1 <= length(args) <= 3)
throw(ArgumentError("number of arguments must be 1 ≤ length(args) ≤ 3, got $nargs"))
end
body = args[end]
ex = Expr(:escape, body)
for dim = 1:N
itervar = inlineanonymous(itersym, dim)
rng = dim == RD ? :(1:raggedlengths($arraysym, $([symbol(itersym, :_, d) for d=RD+1:N]...))) :
:(1:size($arraysym, $dim))
if isa(rangeexpr, Symbol)
rng = dim == RD ? :(1:raggedlengths($arraysym, $([symbol(itersym, :_, d) for d=RD+1:N]...))) :
:(1:size($arraysym, $dim))
else
rng = dim == RD ? :(1:$(inlineanonymous(rangeexpr, dim))[$([symbol(itersym, :_, d) for d=RD+1:N]...)]) :
:(1:$(inlineanonymous(rangeexpr, dim)))
end
preexpr = length(args) > 1 ? inlineanonymous(args[1], dim) : (:(nothing))
postexpr = length(args) > 2 ? inlineanonymous(args[2], dim) : (:(nothing))
ex = quote
Expand All @@ -152,12 +137,13 @@ function _ragged_nloops(N::Int, RD::Int, itersym::Symbol, arraysym::Symbol, args
end

# Assigning output to a ragged array
@generated function getindex!{_,__,RD}(dest::AbstractRaggedArray{_,__,RD}, src::AbstractRaggedArray, I...)
@generated function getindex!{_,__,RD}(dest::AbstractRaggedArray, src::AbstractRaggedArray{_,__,RD}, I...)
N = length(I)
quote
D = eachindex(dest)
Ds = start(D)
@ragged_nloops $N $RD i dest d->(j_d = indexref(I[d], i_d)) begin
idxlens = index_lengths(src, I...)
@ragged_nloops $N $RD i d->(idxlens[d]) d->(j_d = indexref(I[d], i_d)) begin
d, Ds = next(D, Ds)
v = @ncall $N ragged_unsafe_getindex src j
setindex!(dest, v, d) # TODO: this can become unsafe once checksize is stricter
Expand Down
43 changes: 0 additions & 43 deletions src/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,3 @@ function Base.summary{T,N,RD}(a::AbstractRaggedArray{T,N,RD})
dims = join(map(d->(d == RD ? "??" : string(size(a, d))), 1:ndims(a)), 'x')
string(dims, " ", typeof(a))
end

import Base: print_matrix, sub_unsafe
# This is a crazy whack-a-mole hack, but it works... at least for now...
function Base.show_nd(io::IO, a::AbstractRaggedArray, limit, print_matrix, label_slices)
if isempty(a)
return
end
tail = size(a)[3:end]
nd = ndims(a)-2
for I in CartesianRange(tail)
idxs = I.I
if limit
for i = 1:nd
ii = idxs[i]
if size(a,i+2) > 10
if ii == 4 && all(x->x==1,idxs[1:i-1])
for j=i+1:nd
szj = size(a,j+2)
if szj>10 && 3 < idxs[j] <= szj-3
@goto skip
end
end
#println(io, idxs)
print(io, "...\n\n")
@goto skip
end
if 3 < ii <= size(a,i+2)-3
@goto skip
end
end
end
end
if label_slices
print(io, "[:, :, ")
for i = 1:(nd-1); print(io, "$(idxs[i]), "); end
println(io, idxs[end], "] =")
end
slice = sub_unsafe(a, (1:size(a,1), 1:size(a,2), idxs...))
print_matrix(io, slice)
print(io, idxs == tail ? "" : "\n\n")
@label skip
end
end
15 changes: 15 additions & 0 deletions src/views.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Eventually, we may want to support true Ragged views, but this
# hack allows us to get 95% of the functionality without much work.
#
# Simply avoid checking bounds upon construction. TODO: check the rectangular extents
import Base: sub, slice
if VERSION >= v"0.5.0-dev+2663"
slice(A::AbstractRaggedArray, I::Union{AbstractVector, Real, Colon}...) = SubArray(A, I, map(maximum, index_shape(A, I...)))
sub(A::AbstractRaggedArray, I::Union{AbstractVector, Real, Colon}...) = SubArray(A, I, map(maximum, index_shape(A, Base.keep_leading_scalars(I)...)))
else
sub(A::AbstractRaggedArray, I::Union{AbstractVector, Int, Colon}...) = Base.sub_unsafe(A, I)
slice(A::AbstractRaggedArray, I::Union{AbstractVector, Int, Colon}...) = Base.slice_unsafe(A, I)
#TODO: I really don't like this... but it's needed for SubArrays
import Base: *
*(i::Int, d::RaggedDimension) = i*maximum(d)
end
19 changes: 14 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ end
@test R[:, 3] == [6,7,8,9]
@test R[:, 4] == [10,]

@test R[1, :] == [1 4 6 10]
if VERSION >= v"0.5.0-dev+1145" # Drop scalar dimensions
@test R[1, :] == [1,4,6,10]
else
@test R[1, :] == [1 4 6 10]
end
@test_throws BoundsError R[2, :]
@test_throws BoundsError R[3, :]
@test_throws BoundsError R[4, :]
Expand Down Expand Up @@ -178,7 +182,8 @@ N = NestedRagged(Vector{Int}[[1,2], [3,4,5], [6,7,8,9], [10]])
R = RaggedArray(Int, (2,3,4,1),4)
j = 0
for i in eachindex(R)
R[i] = (j+=1)
j+=1
R[i] = j
end
@test N == R == RaggedArray(Vector{Int}[[1,2], [3,4,5], [6,7,8,9], [10]])
@test collect(N) == collect(R) == collect(1:10)
Expand All @@ -194,10 +199,10 @@ N = NestedRagged(reshape(Matrix{Int}[[1 2], [3 4 5], [6 7 8 9], [10 11]], 2,2))
@test RaggedArray(reshape(Matrix{Int}[[1 2], [3 4 5], [6 7 8 9], [10 11]], 2,2)) == N[:,:,:,:]
@test N[:,:,:,:] == N

S = N[1,:,1:2]
S = N[1:1,:,1:2]
@test NestedRagged(Matrix{Int}[[1 2],[3 4 5]]) == S
@test S[1,:,1] == [1 2]
@test S[1,:,2] == [3 4 5]
@test S[1:1,:,1] == [1 2]
@test S[1:1,:,2] == [3 4 5]

@test RaggedArrays.ragged_sub2ind(N, 1, 2, 2) == 4 == N[1, 2, 2]

Expand Down Expand Up @@ -250,3 +255,7 @@ end
@test collect(CartesianRange((RaggedDimension([0,1,0]),3))) == [CartesianIndex((1,2))]
@test collect(CartesianRange((RaggedDimension([1,0,0]),3))) == [CartesianIndex((1,1))]
@test collect(CartesianRange((RaggedDimension([0,0,0]),3))) == []

# Ensure RaggedDimensions hash and equal the same, regardless of their underlying storage
@test RaggedDimension((1,2,3)) == RaggedDimension([1 2 3]) == RaggedDimension([1, 2, 3])
@test hash(RaggedDimension((1,2,3))) == hash(RaggedDimension([1 2 3])) == hash(RaggedDimension([1, 2, 3]))

0 comments on commit 4b2ceef

Please sign in to comment.