Skip to content

Commit

Permalink
Merge c66cf2e into 7c9fce8
Browse files Browse the repository at this point in the history
  • Loading branch information
mbauman committed Sep 15, 2016
2 parents 7c9fce8 + c66cf2e commit 4a9c190
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 122 deletions.
37 changes: 9 additions & 28 deletions src/core.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
# Core types and definitions

if VERSION < v"0.5.0-dev"
macro pure(ex)
esc(ex)
end
else
using Base: @pure
end
using Base: @pure

@doc """
Type-stable axis-specific indexing and identification with a
Expand Down Expand Up @@ -258,28 +252,15 @@ end
# A simple display method to include axis information. It might be nice to
# eventually display the axis labels alongside the data array, but that is
# much more difficult.
if VERSION < v"0.5.0-dev"
function Base.writemime{T,N}(io::IO, m::MIME"text/plain", A::AxisArray{T,N})
println(io, "$N-dimensional AxisArray{$T,$N,...} with axes:")
for (name, val) in zip(axisnames(A), axisvalues(A))
print(io, " :$name, ")
Base.showlimited(io, val)
println(io)
end
print(io, "And data, a ")
writemime(io, m, A.data)
end
else
function Base.show{T,N}(io::IO, m::MIME"text/plain", A::AxisArray{T,N})
println(io, "$N-dimensional AxisArray{$T,$N,...} with axes:")
for (name, val) in zip(axisnames(A), axisvalues(A))
print(io, " :$name, ")
show(IOContext(io, :limit=>true), val)
println(io)
end
print(io, "And data, a ")
show(io, m, A.data)
function Base.show{T,N}(io::IO, m::MIME"text/plain", A::AxisArray{T,N})
println(io, "$N-dimensional AxisArray{$T,$N,...} with axes:")
for (name, val) in zip(axisnames(A), axisvalues(A))
print(io, " :$name, ")
show(IOContext(io, :limit=>true), val)
println(io)
end
print(io, "And data, a ")
show(io, m, A.data)
end

# Custom methods specific to AxisArrays
Expand Down
101 changes: 21 additions & 80 deletions src/indexing.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### Indexing returns either a scalar or a smartly-subindexed AxisArray ###

# Limit indexing to types supported by SubArrays, at least initially
typealias Idx Union{Colon,Int,AbstractVector{Int}}
typealias Idx Union{Colon,Int,AbstractArray{Int}}

# Defer linearindexing to the wrapped array
import Base: linearindexing, unsafe_getindex, unsafe_setindex!
Expand All @@ -23,85 +23,25 @@ Base.setindex!(A::AxisArray, v, idx::Base.IteratorsMD.CartesianIndex) = (A.data[
# TODO: do we want to be dogmatic about using views? For the data? For the axes?
# TODO: perhaps it would be better to return an entirely lazy SubAxisArray view
@generated function Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, idxs::Idx...)
newdims = length(idxs)
# If the last index is a linear indexing range that may span multiple
# dimensions in the original AxisArray, we can no longer track those axes.
droplastaxis = N > newdims && !(idxs[end] <: Real) ? 1 : 0
# Drop trailing scalar dimensions
while newdims > 0 && idxs[newdims] <: Real
newdims -= 1
end
droplastaxis = N > length(idxs) && !(idxs[end] <: Real)
names = axisnames(A)
axes = Expr(:tuple)
Isplat = Expr[]
reshape = false
newshape = Expr[]
for i = 1:newdims-droplastaxis
for i = 1:length(idxs)
if i == length(idxs) && droplastaxis
push!(Isplat, :(idxs[end]))
break
end
prepaxis!(axes.args, Isplat, idxs[i], names, i)
end
for i = newdims-droplastaxis+1:length(idxs)
push!(Isplat, :(idxs[$i]))
end
quote
data = view(A.data, $(Isplat...))
AxisArray(data, $axes) # TODO: avoid checking the axes here
end
end

# When we index with non-vector arrays, we *add* dimensions. This isn't
# supported by SubArray currently, so we instead return a copy.
# TODO: we probably shouldn't hack Base like this, but it's so convenient...
if VERSION < v"0.5.0-dev"
@inline Base.index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), Base.index_shape_dim(A, dim+1, I...)...)
@inline Base.index_shape_dim(A, dim, i::AbstractArray, I...) = (size(i)..., Base.index_shape_dim(A, dim+1, I...)...)
end
@generated function Base.getindex(A::AxisArray, I::Union{Idx, AbstractArray{Int}}...)
N = length(I)
Isplat = [:(I[$d]) for d=1:N]
# Determine the new axes:
# Like above, drop linear indexing over multiple axes
droplastaxis = ndims(A) > N && !(I[end] <: Real) ? 1 : 0
# Drop trailing scalar dimensions
lastnonscalar = N
while lastnonscalar > 0 && I[lastnonscalar] <: Real
lastnonscalar -= 1
end
names = axisnames(A)
newaxes = Expr[]
for d=1:lastnonscalar-droplastaxis
if I[d] <: AxisArray
idxnames = axisnames(I[d])
for i=1:ndims(I[d])
push!(newaxes, :($(Axis{Symbol(names[d], "_", idxnames[i])})(I[$d].axes[$i].val)))
end
elseif I[d] <: Idx
push!(newaxes, :($(Axis{names[d]})(A.axes[$d].val[J[$d]])))
elseif I[d] <: AbstractArray
for i=1:ndims(I[d])
push!(newaxes, :($(Axis{Symbol(names[d], "_", i)})(1:size(I[$d], $i))))
end
end
end
quote
# First copy the data using scalar indexing - an adaptation of Base
checkbounds(A, I...)
J = Base.to_indexes($(Isplat...))
sz = Base.index_shape(A, J...)
idx_lens = Base.index_lengths(A, J...)
src = A.data
dest = similar(A.data, sz)
D = eachindex(dest)
Ds = start(D)
Base.Cartesian.@nloops $N i d->(1:idx_lens[d]) d->(@inbounds j_d = J[d][i_d]) begin
d, Ds = next(D, Ds)
v = Base.Cartesian.@ncall $N unsafe_getindex src j
unsafe_setindex!(dest, v, d)
end
# And now create the AxisArray:
AxisArray(dest, $(newaxes...))
end
end

# Setindex is so much simpler. Just assign it to the data:
Base.setindex!(A::AxisArray, v, idxs::Idx...) = (A.data[idxs...] = v)

Expand Down Expand Up @@ -219,21 +159,22 @@ function prepaxis!{I<:Union{AbstractVector,Colon}}(axesargs, Isplat, ::Type{I},
end
function prepaxis!{I<:AxisArray}(axesargs, Isplat, ::Type{I}, names, i)
idxnames = axisnames(I)
push!(axesargs, :($(Axis{Symbol(names[i], "_", idxnames[1])})(idxs[$i].axes[1].val)))
for j=1:ndims(I)
push!(axesargs, :($(Axis{Symbol(names[i], "_", idxnames[j])})(idxs[$i].axes[$j].val)))
end
push!(Isplat, :(idxs[$i]))
axesargs, Isplat
end
# For anything scalar-like
if VERSION < v"0.5.0-dev"
function prepaxis!{I}(axesargs, Isplat, ::Type{I}, names, i)
idx = :(idxs[$i]:idxs[$i])
push!(axesargs, :($(Axis{names[i]})(A.axes[$i].val[$idx])))
push!(Isplat, idx)
axesargs, Isplat
end
else
function prepaxis!{I}(axesargs, Isplat, ::Type{I}, names, i)
push!(Isplat, :(idxs[$i]))
axesargs, Isplat
function prepaxis!{I<:AbstractArray}(axesargs, Isplat, ::Type{I}, names, i)
idxnames = axisnames(I)
for j=1:ndims(I)
push!(axesargs, :($(Axis{Symbol(names[i], "_", j)})(1:size(I[$d], $i))))
end
push!(Isplat, :(idxs[$i]))
axesargs, Isplat
end
# For anything scalar-like
function prepaxis!{I}(axesargs, Isplat, ::Type{I}, names, i)
push!(Isplat, :(idxs[$i]))
axesargs, Isplat
end
14 changes: 6 additions & 8 deletions src/search.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,12 @@ function Base.searchsorted(a::Range, I::ClosedInterval)
searchsortedfirst(a, I.left):searchsortedlast(a, I.right)
end

if VERSION > v"0.5.0-dev+4557"
# When running with "--check-bounds=yes" (like on Travis), the bounds-check isn't elided
@inline function Base.unsafe_getindex{T}(r::FloatRange{T}, i::Integer)
convert(T, (r.start + (i-1)*r.step)/r.divisor)
end
@inline function Base.unsafe_getindex(r::FloatRange, s::OrdinalRange)
FloatRange(r.start + (first(s)-1)*r.step, step(s)*r.step, length(s), r.divisor)
end
# When running with "--check-bounds=yes" (like on Travis), the bounds-check isn't elided
@inline function Base.unsafe_getindex{T}(r::FloatRange{T}, i::Integer)
convert(T, (r.start + (i-1)*r.step)/r.divisor)
end
@inline function Base.unsafe_getindex(r::FloatRange, s::OrdinalRange)
FloatRange(r.start + (first(s)-1)*r.step, step(s)*r.step, length(s), r.divisor)
end

function unsafe_searchsortedlast{T<:Number}(a::Range{T}, x::Number)
Expand Down
12 changes: 6 additions & 6 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,34 @@ H = similar(A, Float64, 1,1,1)
A = AxisArray(1:3)
@test A.data == 1:3
@test axisnames(A) == (:row,)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (1:3,)
A = AxisArray(reshape(1:16, 2,2,2,2))
@test A.data == reshape(1:16, 2,2,2,2)
@test axisnames(A) == (:row,:col,:page,:dim_4)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (1:2, 1:2, 1:2, 1:2)
# Just axis names
A = AxisArray(1:3, :a)
@test A.data == 1:3
@test axisnames(A) == (:a,)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (1:3,)
A = AxisArray([1 3; 2 4], :a)
@test A.data == [1 3; 2 4]
@test axisnames(A) == (:a, :col)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (1:2, 1:2)
# Just axis values
A = AxisArray(1:3, .1:.1:.3)
@test A.data == 1:3
@test axisnames(A) == (:row,)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (.1:.1:.3,)
A = AxisArray(reshape(1:16, 2,2,2,2), .5:.5:1)
@test A.data == reshape(1:16, 2,2,2,2)
@test axisnames(A) == (:row,:col,:page,:dim_4)
VERSION >= v"0.5.0-dev" && @inferred(axisnames(A))
@inferred(axisnames(A))
@test axisvalues(A) == (.5:.5:1, 1:2, 1:2, 1:2)

# Test axisdim
Expand Down

0 comments on commit 4a9c190

Please sign in to comment.