Skip to content

Commit

Permalink
Eliminate the indicesbehavior trait
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jun 29, 2016
1 parent 41d1ded commit 1b684d5
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 103 deletions.
52 changes: 6 additions & 46 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind
linearindexing(::LinearFast, ::LinearFast) = LinearFast()
linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow()

abstract IndicesBehavior
immutable IndicesStartAt1 <: IndicesBehavior end # indices 1:size(A,d)
immutable IndicesUnitRange <: IndicesBehavior end # arb UnitRange indices
immutable IndicesList <: IndicesBehavior end # indices like (:cat, :dog, :mouse)

indicesbehavior(A::AbstractArray) = indicesbehavior(typeof(A))
indicesbehavior{T<:AbstractArray}(::Type{T}) = IndicesStartAt1()
indicesbehavior(::Number) = IndicesStartAt1()

abstract IndicesPerformance
immutable IndicesFast1D <: IndicesPerformance end # indices(A, d) is fast
immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than indices(A,d)
Expand Down Expand Up @@ -375,26 +366,6 @@ indices of `A`.
similar(f, shape::Tuple) = f(to_shape(shape))
similar(f, dims::DimOrInd...) = similar(f, dims)

promote_indices(a) = a
function promote_indices(a, b, c...)
@_inline_meta
promote_indices(promote_indices(a, b), c...)
end
# overload this to return true for your type, e.g.,
# promote_indices(a::OffsetArray, b::OffsetArray) = a
promote_indices(a::AbstractArray, b::AbstractArray) = _promote_indices(indicesbehavior(a), indicesbehavior(b), a, b)
_promote_indices(::IndicesStartAt1, ::IndicesStartAt1, a, b) = a
_promote_indices(::IndicesBehavior, ::IndicesBehavior, a, b) = throw(ArgumentError("types $(typeof(a)) and $(typeof(b)) do not have promote_indices defined"))
promote_indices(a::Number, b::AbstractArray) = b
promote_indices(a::AbstractArray, b::Number) = a

# Strip off the index-changing container---this assumes that `parent`
# performs such an operation. TODO: since few things in Base need this, it
# would be great to find a way to eliminate this function.
normalize_indices(A) = normalize_indices(indicesbehavior(A), A)
normalize_indices(::IndicesStartAt1, A) = A
normalize_indices(::IndicesBehavior, A) = parent(A)

## from general iterable to any array

function copy!(dest::AbstractArray, src)
Expand Down Expand Up @@ -1229,38 +1200,26 @@ end
# sub2ind and ind2sub
# fallbacks
function sub2ind(A::AbstractArray, I...)
@_inline_meta
sub2ind(indicesbehavior(A), A, I...)
end
function sub2ind(::IndicesStartAt1, A::AbstractArray, I...)
@_inline_meta
sub2ind(size(A), I...)
end
function sub2ind(::IndicesBehavior, A::AbstractArray, I...)
@_inline_meta
sub2ind(indices(A), I...)
end
function ind2sub(A::AbstractArray, ind)
@_inline_meta
ind2sub(indicesbehavior(A), A, ind)
end
function ind2sub(::IndicesStartAt1, A::AbstractArray, ind)
@_inline_meta
ind2sub(size(A), ind)
end
function ind2sub(::IndicesBehavior, A::AbstractArray, ind)
@_inline_meta
ind2sub(indices(A), ind)
end

# 0-dimensional arrays and indexing with []
sub2ind(::Tuple{}) = 1
sub2ind(::DimsInteger) = 1
sub2ind(::Indices) = 1
sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind((), 1, 1, I...))
# Generic cases
sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind(dims, 1, 1, I...))
sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...))
# In 1d, there's a question of whether we're doing cartesian indexing or linear indexing. Support only the former.
# In 1d, there's a question of whether we're doing cartesian indexing
# or linear indexing. Support only the former.
sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays"))
sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) # only OneTo is safe

_sub2ind(::Any, L, ind) = ind
function _sub2ind(::Tuple{}, L, ind, i::Integer, I::Integer...)
Expand All @@ -1284,6 +1243,7 @@ ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsE
ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1))
ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1))
ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays"))
ind2sub(inds::Tuple{OneTo}, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1))

_ind2sub(::Tuple{}, ::Tuple{}, ind) = (ind+1,)
function _ind2sub(out, indslast::NTuple{1}, ind)
Expand Down
18 changes: 8 additions & 10 deletions base/arraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,17 @@ function flipdim{T}(A::Array{T}, d::Integer)
end

function rotl90(A::AbstractMatrix)
B = similar_transpose(A)
ind2 = indices(A,2)
ind1, ind2 = indices(A)
B = similar(A, (ind2,ind1))
n = first(ind2)+last(ind2)
for i=indices(A,1), j=ind2
B[n-j,i] = A[i,j]
end
return B
end
function rotr90(A::AbstractMatrix)
B = similar_transpose(A)
ind1 = indices(A,1)
ind1, ind2 = indices(A)
B = similar(A, (ind2,ind1))
m = first(ind1)+last(ind1)
for i=ind1, j=indices(A,2)
B[j,m-i] = A[i,j]
Expand All @@ -246,10 +246,6 @@ end
rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k)
rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A)

similar_transpose(A::AbstractMatrix) = similar_transpose(indicesbehavior(A), A)
similar_transpose(::IndicesStartAt1, A::AbstractMatrix) = similar(A, (size(A,2), size(A,1)))
similar_transpose(::IndicesBehavior, A::AbstractMatrix) = similar(A, (indices(A,2), indices(A,1)))

## Transpose ##
transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A)
ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A)
Expand Down Expand Up @@ -315,11 +311,13 @@ function ccopy!(B, A)
end

function transpose(A::AbstractMatrix)
B = similar_transpose(A)
ind1, ind2 = indices(A)
B = similar(A, (ind2, ind1))
transpose!(B, A)
end
function ctranspose(A::AbstractMatrix)
B = similar_transpose(A)
ind1, ind2 = indices(A)
B = similar(A, (ind2, ind1))
ctranspose!(B, A)
end
ctranspose{T<:Real}(A::AbstractVecOrMat{T}) = transpose(A)
Expand Down
8 changes: 1 addition & 7 deletions base/permuteddimsarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

module PermutedDimsArrays

using Base: IndicesStartAt1, IndicesBehavior, indicesbehavior

export permutedims

# Some day we will want storage-order-aware iteration, so put perm in the parameters
Expand Down Expand Up @@ -47,14 +45,10 @@ _genperm(out, I) = out
@inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,))

function Base.permutedims{T,N}(A::AbstractArray{T,N}, perm)
dest = similar_permute(A, perm)
dest = similar(A, genperm(indices(A), perm))
permutedims!(dest, A, perm)
end

similar_permute(A::AbstractArray, perm) = similar_permute(indicesbehavior(A), A, perm)
similar_permute{T,N}(::IndicesStartAt1, A::AbstractArray{T,N}, perm) = similar(A, genperm(size(A), perm))
similar_permute{T,N}(::IndicesBehavior, A::AbstractArray{T,N}, perm) = similar(A, genperm(indices(A), perm))

function Base.permutedims!(dest, src::AbstractArray, perm)
Base.checkdims_perm(dest, src, perm)
P = PermutedDimsArray(dest, invperm(perm))
Expand Down
26 changes: 11 additions & 15 deletions base/reshapedarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,20 @@ start(R::ReshapedArrayIterator) = start(R.iter)
end
length(R::ReshapedArrayIterator) = length(R.iter)

reshape(parent::AbstractArray, ref::AbstractArray) = reshape(indicesbehavior(ref), parent, ref)
reshape(::IndicesStartAt1, parent::AbstractArray, ref::AbstractArray) = reshape(parent, size(ref))
reshape(::IndicesBehavior, parent::AbstractArray, ref::AbstractArray) = reshape(parent, indices(ref))

reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims)
reshape(parent::AbstractArray, len::Integer) = reshape(parent, (Int(len),))
reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims)
reshape(parent::AbstractArray, shp::Tuple) = _reshape(parent, to_shape(shp))
reshape(parent::AbstractArray, dims::DimOrInd...) = reshape(parent, dims)

reshape{T,N}(parent::AbstractArray{T,N}, ndims::Type{Val{N}}) = parent
function reshape{T,AN,N}(parent::AbstractArray{T,AN}, ndims::Type{Val{N}})
reshape(parent, rdims((), size(parent), Val{N}))
end
# Move elements from sz to out until out reaches the desired dimensionality N,
# either filling with 1 or collapsing the product of trailing dims into the last element
@pure rdims{N}(out::NTuple{N}, sz::Tuple{}, ::Type{Val{N}}) = out
@pure rdims{N}(out::NTuple{N}, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., last(out) * prod(sz))
@pure rdims{N}(out::Tuple, sz::Tuple{}, ::Type{Val{N}}) = rdims((out..., 1), (), Val{N})
@pure rdims{N}(out::Tuple, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(sz)), tail(sz), Val{N})
reshape(parent, rdims((), indices(parent), Val{N}))
end
# Move elements from inds to out until out reaches the desired
# dimensionality N, either filling with OneTo(1) or collapsing the
# product of trailing dims into the last element
@pure rdims{N}(out::NTuple{N}, inds::Tuple{}, ::Type{Val{N}}) = out
@pure rdims{N}(out::NTuple{N}, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., length(last(out)) * prod(map(length, inds)))
@pure rdims{N}(out::Tuple, inds::Tuple{}, ::Type{Val{N}}) = rdims((out..., OneTo(1)), (), Val{N})
@pure rdims{N}(out::Tuple, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(inds)), tail(inds), Val{N})

function _reshape(parent::AbstractArray, dims::Dims)
prod(dims) == length(parent) || throw(DimensionMismatch("parent has $(length(parent)) elements, which is incompatible with size $dims"))
Expand Down
2 changes: 1 addition & 1 deletion base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ function sort(A::AbstractArray, dim::Integer;
else
Av = A[:]
sort_chunks!(Av, size(A,1), alg, order)
reshape(Av, A)
reshape(Av, indices(A))
end
end

Expand Down
17 changes: 2 additions & 15 deletions base/subarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -279,33 +279,20 @@ end
# Since bounds-checking is performance-critical and uses
# indices, it's worth optimizing these implementations thoroughly
indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? OneTo(1) : error("dimension $d out of range"))
indices(S::SubArray) = (@_inline_meta; _indices(indicesbehavior(parent(S)), S))
_indices(::IndicesStartAt1, S::SubArray) = (@_inline_meta; map(s->OneTo(s), size(S)))
_indices(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...))
indices(S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...))
_indices(out::Tuple, dim, S::SubArray) = out
_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., OneTo(length(i1))), dim+1, S, I...))
_indices(out::Tuple, dim, S::SubArray, ::Real, I...) = (@_inline_meta; _indices(out, dim+1, S, I...))
_indices(out::Tuple, dim, S::SubArray, ::Colon, I...) = (@_inline_meta; _indices((out..., indices(parent(S), dim)), dim+1, S, I...))
indices1{T}(S::SubArray{T,0}) = OneTo(1)
indices1(S::SubArray) = (@_inline_meta; _indices1(indicesbehavior(parent(S)), S))
_indices1(::IndicesStartAt1, S::SubArray) = OneTo(S.dims[1])
_indices1(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...))
indices1(S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...))
_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; OneTo(length(i1)))
_indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...))
_indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim))

# Moreover, incides(S) is fast but indices(S, d) is slower
indicesperformance{T<:SubArray}(::Type{T}) = IndicesSlow1D()

indicesbehavior(S::SubArray) = _indicesbehavior(indicesbehavior(parent(S)), S)
_indicesbehavior(::IndicesStartAt1, S::SubArray) = IndicesStartAt1()
_indicesbehavior(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indicesbehavior(keepcolon((), S.indexes...), S))
keepcolon(out) = out
keepcolon(out, ::Colon, I...) = (@_inline_meta; (Colon(),))
keepcolon(out, i1, I...) = (@_inline_meta; keepcolon(out, I...))
_indicesbehavior(::Tuple{}, S::SubArray) = IndicesStartAt1()
_indicesbehavior(::Tuple{Colon}, S::SubArray) = indicesbehavior(parent(S))

## Compatability
# deprecate?
function parentdims(s::SubArray)
Expand Down
17 changes: 8 additions & 9 deletions doc/manual/interfaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,8 @@ Methods to implement
:func:`similar(A, dims::NTuple{Int}) <similar>` ``similar(A, eltype(A), dims)`` Return a mutable array with the same element type and size `dims`
:func:`similar(A, ::Type{S}, dims::NTuple{Int}) <similar>` ``Array{S}(dims)`` Return a mutable array with the specified element type and size
**Non-traditional indices** **Default definition** **Brief description**
:func:`Base.indicesbehavior(::Type) <indicesbehavior>` ``Base.IndicesStartAt1()`` Trait with values ``IndicesStartAt1()``, ``IndicesUnitRange()``, ``IndicesList()``
:func:`indices(A, d) <indices>` ``1:size(A, d)`` Return the range of valid indices along dimension ``d``
:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) <similar>` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below for discussion of ``Ind``)
:func:`indices(A, d) <indices>` ``OneTo(size(A, d))`` Return the ``AbstractUnitRange`` of valid indices along dimension ``d``
:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) <similar>` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below)
===================================================================== ============================================ =======================================================================================

If a type is defined as a subtype of ``AbstractArray``, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See the :ref:`arrays manual page <man-arrays>` and :ref:`standard library section <stdlib-arrays>` for more supported methods.
Expand Down Expand Up @@ -290,9 +289,9 @@ In addition to all the iterable and indexable methods from above, these types ca

If you are defining an array type that allows non-traditional indexing
(indices that start at something other than 1), you should specialize
``indices`` and ``indicesbehavior``. You should also specialize
``similar`` so that the ``dims`` argument (ordinarily a ``Dims``
size-tuple) can be a mixture of ``Integer`` and ``UnitRange`` objects;
the ``Integer`` entries imply that the indexing starts from 1, whereas
the dimensions encoded with ``UnitRange`` may have arbitrary starting
index.
``indices``. You should also specialize ``similar`` so that the
``dims`` argument (ordinarily a ``Dims`` size-tuple) can accept
``AbstractUnitRange`` objects, perhaps range-types ``Ind`` of your own
design. For example, if indexing always starts with 0 for your
arrays, you likely want to define a ``ZeroTo`` range type. Otherwise,
you can use standard ``UnitRange``.

0 comments on commit 1b684d5

Please sign in to comment.