Skip to content

Commit

Permalink
methods are run on parent explicitly, use A for array
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaqz committed Oct 13, 2019
1 parent dc66993 commit fda792b
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 119 deletions.
78 changes: 41 additions & 37 deletions src/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,60 @@ abstract type AbstractDimensionalArray{T,N,D<:Tuple} <: AbstractArray{T,N} end

const AbDimArray = AbstractDimensionalArray

dims(a::AbDimArray) = a.dims
label(a::AbDimArray) = ""
const StandardIndices = Union{AbstractArray,Colon,Integer}

dims(A::AbDimArray) = A.dims
label(A::AbDimArray) = ""

# Array interface
Base.size(a::AbDimArray) = size(parent(a))
Base.iterate(a::AbDimArray, args...) = iterate(parent(a), args...)
Base.show(io::IO, a::AbDimArray) = begin
printstyled(io, "\n", label(a), ": "; color=:red)
show(io, typeof(a))
show(io, parent(a))
Base.size(A::AbDimArray) = size(parent(A))
Base.iterate(A::AbDimArray, args...) = iterate(parent(A), args...)
Base.show(io::IO, A::AbDimArray) = begin
printstyled(io, "\n", label(A), ": "; color=:red)
show(io, typeof(A))
show(io, parent(A))
printstyled(io, "\n\ndims:\n"; color=:magenta)
show(io, dims(a))
show(io, refdims(a))
show(io, dims(A))
show(io, refdims(A))
printstyled(io, "\n\nmetadata:\n"; color=:cyan)
end

Base.@propagate_inbounds Base.getindex(a::AbDimArray, I::Vararg{<:Integer}) =
getindex(parent(a), I...)
Base.@propagate_inbounds Base.getindex(a::AbDimArray, I::Vararg{<:Union{AbstractArray,Colon,Integer}}) =
rebuildsliced(a, getindex(parent(a), I...), I)
Base.@propagate_inbounds Base.getindex(A::AbDimArray, I::Vararg{<:Integer}) =
getindex(parent(A), I...)
Base.@propagate_inbounds Base.getindex(A::AbDimArray, I::Vararg{<:StandardIndices}) =
rebuildsliced(A, getindex(parent(A), I...), I)

Base.@propagate_inbounds Base.view(a::AbDimArray, I::Vararg{<:Union{AbstractArray,Colon,Integer}}) =
rebuildsliced(a, view(parent(a), I...), I)
Base.@propagate_inbounds Base.view(A::AbDimArray, I::Vararg{<:StandardIndices}) =
rebuildsliced(A, view(parent(A), I...), I)

Base.convert(::Type{Array{T,N}}, a::AbDimArray{T,N}) where {T,N} =
convert(Array{T,N}, parent(a))
Base.convert(::Type{Array{T,N}}, A::AbDimArray{T,N}) where {T,N} =
convert(Array{T,N}, parent(A))

Base.copy!(dst::AbDimArray, src::AbDimArray) = copy!(parent(src), parent(dst))

# Similar. TODO this need a rethink. How do we know what the new dims are?
Base.BroadcastStyle(::Type{<:AbDimArray}) = Broadcast.ArrayStyle{AbDimArray}()
# Need to cover a few type signatures to avoid ambiguity with base
Base.similar(a::AbDimArray, ::Type{T}, I::Vararg{<:Integer}) where T =
rebuildsliced(a, similar(parent(a), T, I...), I)

Base.similar(a::AbDimArray) = rebuild(a, similar(parent(a)))
Base.similar(a::AbDimArray, ::Type{T}) where T = rebuild(a, similar(parent(a), T))
Base.similar(a::AbDimArray, ::Type{T}, ::Tuple{Int64,Vararg{Int64}}) where T =
rebuild(a, similar(parent(a), T))
Base.similar(a::AbDimArray, ::Type{T}, I::Tuple{Union{Integer,OneTo},Vararg{Union{Integer,OneTo},N}}) where {T,N} =
rebuildsliced(a, similar(parent(a), T, I...), I)
Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{AbDimArray}}, ::Type{ElType}) where ElType =
rebuildsliced(find_dimensional(bc), similar(Array{ElType}, axes(bc)), axes(bc))
Base.similar(A::AbDimArray, ::Type{T}, I::Vararg{<:Integer}) where T =
rebuildsliced(A, similar(parent(A), T, I...), I)

Base.similar(A::AbDimArray) = rebuild(A, similar(parent(A)))
Base.similar(A::AbDimArray, ::Type{T}) where T = rebuild(A, similar(parent(A), T))
Base.similar(A::AbDimArray, ::Type{T}, ::Tuple{Int64,Vararg{Int64}}) where T =
rebuild(A, similar(parent(A), T))
Base.similar(A::AbDimArray, ::Type{T}, I::Tuple{Union{Integer,OneTo},Vararg{Union{Integer,OneTo},N}}) where {T,N} =
rebuildsliced(A, similar(parent(A), T, I...), I)
Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{AbDimArray}}, ::Type{ElType}) where ElType = begin
A = find_dimensional(bc)
rebuildsliced(A, similar(Array{ElType}, axes(bc)), axes(bc))
end

@inline find_dimensional(bc::Base.Broadcast.Broadcasted) = find_dimensional(bc.args)
@inline find_dimensional(ext::Base.Broadcast.Extruded) = find_dimensional(ext.x)
@inline find_dimensional(args::Tuple{}) = error("dimensional array not found")
@inline find_dimensional(args::Tuple) = find_dimensional(find_dimensional(args[1]), tail(args))
@inline find_dimensional(x) = x
@inline find_dimensional(a::AbDimArray, rest) = a
@inline find_dimensional(A::AbDimArray, rest) = A
@inline find_dimensional(::Any, rest) = find_dimensional(rest)


Expand All @@ -65,18 +69,18 @@ struct DimensionalArray{T,N,D<:Tuple,R<:Tuple,A<:AbstractArray{T,N}} <: Abstract
dims::D
refdims::R
end
DimensionalArray(a::AbstractArray, dims; refdims=()) =
DimensionalArray(a, formatdims(a, dims), refdims)
DimensionalArray(A::AbstractArray, dims; refdims=()) =
DimensionalArray(A, formatdims(A, dims), refdims)

# Getters
refdims(a::DimensionalArray) = a.refdims
refdims(A::DimensionalArray) = A.refdims

# DimensionalArray interface
@inline rebuild(a::DimensionalArray, data, dims, refdims) =
@inline rebuild(A::DimensionalArray, data, dims, refdims) =
DimensionalArray(data, dims, refdims)

# Array interface (AbstractDimensionalArray takes care of everything else)
Base.parent(a::DimensionalArray) = a.data
Base.parent(A::DimensionalArray) = A.data

Base.@propagate_inbounds Base.setindex!(a::DimensionalArray, x, I::Vararg{<:Union{AbstractArray,Colon,Real}}) =
setindex!(parent(a), x, I...)
Base.@propagate_inbounds Base.setindex!(A::DimensionalArray, x, I::Vararg{StandardIndices}) =
setindex!(parent(A), x, I...)
32 changes: 16 additions & 16 deletions src/dimension.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ shortname(d::AbDim) = shortname(typeof(d))
shortname(d::Type{<:AbDim}) = name(d)
units(dim::AbDim) = metadata(dim) == nothing ? "" : get(metadata(dim), :units, "")

bounds(a, args...) = bounds(dims(a), args...)
bounds(A, args...) = bounds(dims(A), args...)
bounds(dims::AbDimTuple, lookupdims::Tuple) = bounds(dims[[dimnum(dims, lookupdims)...]]...)
bounds(dims::AbDimTuple, dim::DimOrDimType) = bounds(dims[dimnum(dims, dim)])
bounds(dims::AbDimTuple) = (bounds(dims[1]), bounds(tail(dims))...)
Expand All @@ -70,32 +70,32 @@ end

# AbstractArray methods where dims are the dispatch argument

@inline rebuildsliced(a, data, I) = rebuild(a, data, slicedims(a, I)...)
@inline rebuildsliced(A, data, I) = rebuild(A, data, slicedims(A, I)...)

Base.@propagate_inbounds Base.getindex(a::AbstractArray, dims::Vararg{<:AbDim{<:Number}}) =
getindex(a, dims2indices(a, dims)...)
Base.@propagate_inbounds Base.getindex(a::AbstractArray, dims::Vararg{<:AbstractDimension}) =
getindex(a, dims2indices(a, dims)...)
Base.@propagate_inbounds Base.getindex(A::AbstractArray, dims::Vararg{<:AbDim{<:Number}}) =
getindex(A, dims2indices(A, dims)...)
Base.@propagate_inbounds Base.getindex(A::AbstractArray, dims::Vararg{<:AbstractDimension}) =
getindex(A, dims2indices(A, dims)...)

Base.@propagate_inbounds Base.setindex!(a::AbstractArray, x, dims::Vararg{<:AbstractDimension}) =
setindex!(a, x, dims2indices(a, dims)...)
Base.@propagate_inbounds Base.setindex!(A::AbstractArray, x, dims::Vararg{<:AbstractDimension}) =
setindex!(A, x, dims2indices(A, dims)...)

Base.@propagate_inbounds Base.view(a::AbstractArray, dims::Vararg{<:AbstractDimension}) =
view(a, dims2indices(a, dims)...)
Base.@propagate_inbounds Base.view(A::AbstractArray, dims::Vararg{<:AbstractDimension}) =
view(A, dims2indices(A, dims)...)

@inline Base.axes(a::AbstractArray, dims::DimOrDimType) = axes(a, dimnum(a, dims))
@inline Base.size(a::AbstractArray, dims::DimOrDimType) = size(a, dimnum(a, dims))
@inline Base.axes(A::AbstractArray, dims::DimOrDimType) = axes(A, dimnum(A, dims))
@inline Base.size(A::AbstractArray, dims::DimOrDimType) = size(A, dimnum(A, dims))


#= SplitApplyCombine methods?
Should allow groupby using dims lookup to make this worth the dependency
Like group by time/lattitude/height band etc.
SplitApplyCombine.splitdims(a::AbstractArray, dims::AllDimensions) =
SplitApplyCombine.splitdims(a, dimnum(a, dims))
SplitApplyCombine.splitdims(A::AbstractArray, dims::AllDimensions) =
SplitApplyCombine.splitdims(A, dimnum(A, dims))
SplitApplyCombine.splitdimsview(a::AbstractArray, dims::AllDimensions) =
SplitApplyCombine.splitdimsview(a, dimnum(a, dims))
SplitApplyCombine.splitdimsview(A::AbstractArray, dims::AllDimensions) =
SplitApplyCombine.splitdimsview(A, dimnum(A, dims))
=#


Expand Down
84 changes: 45 additions & 39 deletions src/methods.jl
Original file line number Diff line number Diff line change
@@ -1,57 +1,62 @@

# Methods where dims are an argument. This is an experimental approach
# that might need to change.
# targeting underscore _methods so we can use dispatch on the dims arg

# TODO hanbdle rebuild in the array dispatch, the dimensions are probably wrong in some cases.
for (mod, fname) in ((:Base, :sum), (:Base, :prod), (:Base, :maximum), (:Base, :minimum), (:Statistics, :mean))
_fname = Symbol('_', fname)
@eval begin
@inline ($mod.$_fname)(a::AbstractArray{T,N}, dims::AllDimensions) where {T,N} =
($mod.$_fname)(a, dimnum(a, dims))
@inline ($mod.$_fname)(f, a::AbstractArray{T,N}, dims::AllDimensions) where {T,N} =
($mod.$_fname)(f, a, dimnum(a, dims))
@inline ($mod.$_fname)(A::AbstractArray, dims::AllDimensions) =
rebuild(A, ($mod.$_fname)(parent(A), dimnum(A, dims)), reducedims(A, dims))
@inline ($mod.$_fname)(f, A::AbstractArray, dims::AllDimensions) =
rebuild(A, ($mod.$_fname)(f, parent(A), dimnum(A, dims)), reducedims(A, dims))
@inline ($mod.$_fname)(A::AbDimArray, dims::Union{Int,Base.Dims}) =
rebuild(A, ($mod.$_fname)(parent(A), dims), reducedims(A, dims))
@inline ($mod.$_fname)(f, A::AbDimArray, dims::Union{Int,Base.Dims}) =
rebuild(A, ($mod.$_fname)(f, parent(A), dims), reducedims(A, dims))
end
end

for fname in (:std, :var)
_fname = Symbol('_', fname)
@eval begin
@inline (Statistics.$_fname)(a::AbstractArray, corrected::Bool, mean, dims::AllDimensions) =
(Statistics.$_fname)(a, corrected, mean, dimnum(a, dims))
@inline (Statistics.$_fname)(A::AbstractArray, corrected::Bool, mean, dims::AllDimensions) =
rebuild(A, (Statistics.$_fname)(A, corrected, mean, dimnum(A, dims)), reducedims(A, dims))
@inline (Statistics.$_fname)(A::AbDimArray, corrected::Bool, mean, dims::Union{Int,Base.Dims}) =
rebuild(A, (Statistics.$_fname)(parent(A), corrected, mean, dims), reducedims(A, dims))
end
end

Statistics._median(a::AbstractArray, dims::AllDimensions) =
Statistics._median(a, dimnum(a, dims))
Statistics._median(A::AbstractArray, dims::AllDimensions) =
rebuild(A, Statistics._median(parent(A), dimnum(A, dims)), reducedims(A, dims))
Statistics._median(A::AbDimArray, dims::Union{Int,Base.Dims}) =
rebuild(A, Statistics._median(parent(A), dims), reducedims(A, dims))

Base._mapreduce_dim(f, op, nt::NamedTuple{(),<:Tuple}, A::AbstractArray, dims::AllDimensions) =
Base._mapreduce_dim(f, op, nt, A, dimnum(A, dims))
rebuild(A, Base._mapreduce_dim(f, op, nt, parent(A), dimnum(A, dims)), reducedims(A, dims))
Base._mapreduce_dim(f, op, nt::NamedTuple{(),<:Tuple}, A::AbDimArray, dims::Union{Int,Base.Dims}) =
rebuild(A, Base._mapreduce_dim(f, op, nt, parent(A), dimnum(A, dims)), reducedims(A, dims))
# Unfortunately Base/accumulate.jl kwargs methods all force dims to be Integer.
# accumulate wont work unless that is relaxed, or we copy half of the file here.
Base._accumulate!(op, B, A, dims::AllDimensions, init::Union{Nothing, Some}) =
Base._accumulate!(op, B, A, dimnum(A, dims), init)

Base._dropdims(a::AbstractArray, dim::Union{AbDim,Type{<:AbDim}}) =
rebuildsliced(a, Base._dropdims(a, dimnum(a, dim)), dims2indices(a, basetype(dim)(1)))
Base._dropdims(a::AbstractArray, dims::AbDimTuple) =
rebuildsliced(a, Base._dropdims(a, dimnum(a, dims)),
dims2indices(a, Tuple((basetype(d)(1) for d in dims))))

Base._dropdims(A::AbstractArray, dim::Union{AbDim,Type{<:AbDim}}) =
rebuildsliced(A, Base._dropdims(A, dimnum(A, dim)), dims2indices(A, basetype(dim)(1)))
Base._dropdims(A::AbstractArray, dims::AbDimTuple) =
rebuildsliced(A, Base._dropdims(A, dimnum(A, dims)),
dims2indices(A, Tuple((basetype(d)(1) for d in dims))))


# TODO cov, cor mapslices, eachslice, reverse, sort and sort! need _methods without kwargs in base so
# we can dispatch on dims. Instead we dispatch on array type for now, which means
# these aren't usefull unless you inherit from AbDimArray.

Base.mapslices(f, a::AbDimArray; dims=1, kwargs...) = begin
dimnums = dimnum(a, dims)
data = mapslices(f, parent(a); dims=dimnums, kwargs...)
rebuildsliced(a, data, dims2indices(a, reducedims(DimensionalData.dims(a, dimnums))))
Base.mapslices(f, A::AbDimArray; dims=1, kwargs...) = begin
dimnums = dimnum(A, dims)
data = mapslices(f, parent(A); dims=dimnums, kwargs...)
rebuild(A, data, reducedims(A, DimensionalData.dims(A, dimnums)))
end

# This is copied from base as we can't efficiently wrap this function
# through the kwarg with a rebuild in the generator. Doing it this way
# wierdly makes it 2x faster to use a dim than an integer.
# wierdly makes it faster to use a dim than an integer.
if VERSION > v"1.1-"
Base.eachslice(A::AbDimArray; dims=1, kwargs...) = begin
if dims isa Tuple && length(dims) == 1
Expand All @@ -65,23 +70,24 @@ if VERSION > v"1.1-"
end

for fname in (:cor, :cov)
@eval Statistics.$fname(a::AbDimArray{T,2}; dims=1, kwargs...) where T = begin
newdata = Statistics.$fname(parent(a); dims=dimnum(a, dims), kwargs...)
I = dims2indices(a, dims, 1)
newdims, newrefdims = slicedims(a, I)
rebuild(a, newdata, (newdims[1], newdims[1]), newrefdims)
@eval Statistics.$fname(A::AbDimArray{T,2}; dims=1, kwargs...) where T = begin
newdata = Statistics.$fname(parent(A); dims=dimnum(A, dims), kwargs...)
I = dims2indices(A, dims, 1)
newdims, newrefdims = slicedims(A, I)
rebuild(A, newdata, (newdims[1], newdims[1]), newrefdims)
end
end

@inline Base.reverse(a::AbDimArray{T,N}; dims=1) where {T,N} = begin
dnum = dimnum(a, dims)
@inline Base.reverse(A::AbDimArray{T,N}; dims=1) where {T,N} = begin
dnum = dimnum(A, dims)
# Reverse the dimension. TODO: make this type stable
newdims = revdims(DimensionalData.dims(a), dnum)
newdims = revdims(DimensionalData.dims(A), dnum)
# Reverse the data
newdata = reverse(parent(a); dims=dnum)
rebuild(a, newdata, newdims, refdims(a))
newdata = reverse(parent(A); dims=dnum)
rebuild(A, newdatA, newdims, refdims(A))
end

# TODO change order after reverse
@inline revdims(dimstorev::Tuple, dnum) = begin
dim = dimstorev[end]
if length(dimstorev) == dnum
Expand All @@ -93,10 +99,10 @@ end

for fname in [:permutedims, :transpose, :adjoint]
@eval begin
@inline Base.$fname(a::AbDimArray{T,2}) where T =
rebuild(a, $fname(parent(a)), reverse(dims(a)), refdims(a))
@inline Base.$fname(A::AbDimArray{T,2}) where T =
rebuild(A, $fname(parent(A)), reverse(dims(A)), refdims(A))
end
end

Base.permutedims(a::AbDimArray{T,N}, perm) where {T,N} =
rebuild(a, permutedims(parent(a), dimnum(a, perm)), permutedims(dims(a), perm))
Base.permutedims(A::AbDimArray{T,N}, perm) where {T,N} =
rebuild(A, permutedims(parent(A), dimnum(A, perm)), permutedims(dims(A), perm))
29 changes: 18 additions & 11 deletions src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Missing dimensions are replaced with `nothing`
@inline Base.permutedims(tosort::Union{Vector{<:Integer},Tuple{<:Integer,Vararg}}, perm::AbDimTuple) =
tosort

@inline Base.permutedims(tosort::AbDimTuple, order::UnionAllTupleOrVector ) =
@inline Base.permutedims(tosort::AbDimTuple, order::UnionAllTupleOrVector) =
permutedims(tosort, Tuple(map(u -> u(), order)))
@inline Base.permutedims(tosort::UnionAllTupleOrVector, order::AbDimTuple) =
permutedims(Tuple(map(u -> u(), tosort)), order)
Expand Down Expand Up @@ -41,8 +41,8 @@ end
"""
Convert a tuple of AbstractDimension to indices, ranges or Colon.
"""
@inline dims2indices(a, lookup, emptyval=Colon()) =
dims2indices(dims(a), lookup, emptyval)
@inline dims2indices(A, lookup, emptyval=Colon()) =
dims2indices(dims(A), lookup, emptyval)
@inline dims2indices(dims::Tuple, lookup, emptyval=Colon()) =
dims2indices(dims, (lookup,), emptyval)
@inline dims2indices(dims::Tuple, lookup::Tuple, emptyval=Colon()) =
Expand Down Expand Up @@ -70,10 +70,10 @@ the new struct but are useful to give context to plots.
Called at the array level the returned tuple will also include the
previous reference dims attached to the array.
"""
@inline slicedims(a, dims::AbDimTuple) = slicedims(a, dims2indices(a, dims))
@inline slicedims(A, dims::AbDimTuple) = slicedims(A, dims2indices(A, dims))
@inline slicedims(dims2slice::AbDimTuple, dims::AbDimTuple) =
slicedims(dims2slice, dims2indices(dims2slice, dims))
@inline slicedims(a, I::Tuple) = slicedims(dims(a), refdims(a), I)
@inline slicedims(A, I::Tuple) = slicedims(dims(A), refdims(A), I)
@inline slicedims(dims::Tuple, refdims::Tuple, I::Tuple) = begin
newdims, newrefdims = slicedims(dims, I)
newdims, (refdims..., newrefdims...)
Expand All @@ -99,7 +99,7 @@ end
"""
Get the number of an AbstractDimension as ordered in the array
"""
@inline dimnum(a, lookup) = dimnum(typeof(dims(a)), lookup)
@inline dimnum(A, lookup) = dimnum(typeof(dims(A)), lookup)
@inline dimnum(dimtypes::Type, lookup::AbstractArray) = dimnum(dimtypes, (lookup...,))
@inline dimnum(dimtypes::Type, lookup::Number) = lookup
@inline dimnum(dimtypes::Type, lookup::Tuple) =
Expand All @@ -124,10 +124,10 @@ which is easier to handle.
Errors are thrown if dims don't match the array dims or size.
"""
@inline formatdims(a::AbstractArray{T,N}, dims::Tuple) where {T,N} = begin
@inline formatdims(A::AbstractArray{T,N}, dims::Tuple) where {T,N} = begin
dimlen = length(dims)
dimlen == N || throw(ArgumentError("dims ($dimlen) don't match array dimensions $(N)"))
formatdims(size(a), dims)
formatdims(size(A), dims)
end
@inline formatdims(size::Tuple, dims::Tuple) =
(formatdims(size[1], dims[1]), formatdims(tail(size), tail(dims))...,)
Expand Down Expand Up @@ -155,14 +155,21 @@ of 1, but the number of dimensions has not changed.
Used in mean, reduce, etc.
"""
@inline reducedims(dim) = reducedims((dim,))
@inline reducedims(dims::Tuple) = map(d -> basetype(d)(1), dims)
reducedims(A, dimstoreduce) = reducedims(A, (dimstoreduce,))
reducedims(A, dimstoreduce::Tuple) = reducedims(dims(A), dimstoreduce)
reducedims(dims::AbDimTuple, dimstoreduce::Tuple) =
map(reducedims, dims, permutedims(dimstoreduce, dims))
reducedims(dims::AbDimTuple, dimstoreduce::Tuple{Vararg{Int}}) =
map(reducedims, dims, permutedims(map(i -> dims[i], dimstoreduce), dims))

reducedims(dim::AbDim, dimtoreduce::AbDim) = basetype(dim)(first(val(dim)))
reducedims(dim::AbDim, dimtoreduce::Nothing) = dim


"""
Get the dimension(s) matching the type(s) of the lookup dimension.
"""
@inline dims(a, lookup) = dims(dims(a), lookup)
@inline dims(A, lookup) = dims(dims(A), lookup)
@inline dims(ds::AbDimTuple, lookup::Integer) = ds[lookup]
@inline dims(ds::AbDimTuple, lookup::Tuple) =
(dims(ds, lookup[1]), dims(ds, tail(lookup))...)
Expand Down

0 comments on commit fda792b

Please sign in to comment.