Skip to content

Commit

Permalink
Replace shape with indices, and redesign similar
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jul 7, 2016
1 parent ec07812 commit c318a9f
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 79 deletions.
57 changes: 21 additions & 36 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,29 +162,6 @@ immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than i
indicesperformance(A::AbstractArray) = indicesperformance(typeof(A))
indicesperformance{T<:AbstractArray}(::Type{T}) = IndicesFast1D()

"""
shape(A)
Returns a tuple specifying the "shape" of array `A`. For arrays with
conventional indexing (indices start at 1), this is equivalent to
`size(A)`; otherwise it is equivalent to `indices(A)`.
"""
shape(a) = shape(indicesbehavior(a), a)
"""
shape(A, d)
Specifies the "shape" of the array `A` along dimension `d`. For arrays
with conventional indexing (starting at 1), this is equivalent to
`size(A, d)`; for arrays with unconventional indexing (indexing may
start at something different from 1), it is equivalent to `indices(A,
d)`.
"""
shape(a, d) = shape(indicesbehavior(a), a, d)
shape(::IndicesStartAt1, a) = size(a)
shape(::IndicesStartAt1, a, d) = size(a, d)
shape(::IndicesBehavior, a) = indices(a)
shape(::IndicesBehavior, a, d) = indices(a, d)

## Bounds checking ##
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
(isa(n, Int) && isa(N, Int)) || error("Must have concrete type")
Expand Down Expand Up @@ -352,14 +329,22 @@ different element type it will create a regular `Array` instead:
See also `allocate_for`.
"""
similar{T}(a::AbstractArray{T}) = similar(a, T)
similar( a::AbstractArray, T::Type) = similar(a, T, indices(a))
similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, dims)
similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, dims)
similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, dims)
similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, Dims(dims))
similar( a::AbstractArray, T::Type) = similar(a, T, to_shape(indices(a)))
similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar( a::AbstractArray, T::Type, dims) = similar(a, T, to_shape(dims))
# similar creates an Array by default
similar( a::AbstractArray, T::Type, inds::IndicesOne) = similar(a, T, map(last, inds))
similar( a::AbstractArray, T::Type, dims::Dims) = Array(T, dims)
similar( a::AbstractArray, T::Type, dims::Dims) = Array{T}(dims)

to_shape(::Tuple{}) = ()
to_shape(dims::Dims) = dims
to_shape(dims::DimsOrInds) = map(to_shape, dims)
# each dimension
to_shape(i::Int) = i
to_shape(i::Integer) = Int(i)
to_shape(r::OneTo) = Int(last(r))
to_shape(r::UnitRange) = convert(UnitRange{Int}, r)

"""
allocate_for(storagetype, referencearray, [shape])
Expand All @@ -381,7 +366,7 @@ conventional indexing, this will likely just call
`Array{Int}(size(A))`, but if `A` has unconventional indexing then the
indices of the result will match `A`.
allocate_for(BitArray, A, (shape(A, 2),))
allocate_for(BitArray, A, (indices(A, 2),))
would create a 1-dimensional logical array whose indices match those
of the columns of `A`.
Expand All @@ -392,10 +377,10 @@ possible that several different ones will be simultaneously in use).
See also `similar`.
"""
allocate_for(f, a, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}}) = f(shape)
allocate_for(f, a) = allocate_for(f, a, shape(a))
allocate_for(f, a, shape::Union{DimOrInd,DimsOrInds}) = f(to_shape(shape))
allocate_for(f, a) = allocate_for(f, a, indices(a))
# allocate_for when passed multiple arrays. Necessary for broadcast, etc.
function allocate_for(f, as::Tuple, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}})
function allocate_for(f, as::Tuple, shape::Union{DimOrInd,DimsOrInds})
@_inline_meta
a = promote_indices(as...)
allocate_for(f, a, shape)
Expand Down Expand Up @@ -1406,7 +1391,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
return map(f,A)
end

dimsA = [shape(A)...]
dimsA = [indices(A)...]
ndimsA = ndims(A)
alldims = [1:ndimsA;]

Expand All @@ -1431,7 +1416,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
if eltype(Rsize) == Int
Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...]
else
Rsize[dims] = [indices(r1)..., ntuple(d->1:1, nextra)...]
Rsize[dims] = [indices(r1)..., ntuple(d->OneTo(1), nextra)...]
end
R = similar(r1, tuple(Rsize...,))

Expand Down
2 changes: 2 additions & 0 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ end

isassigned{N}(B::BitArray{N}, i::Int) = 1 <= i <= length(B)

linearindexing{A<:BitArray}(::Type{A}) = LinearFast()

## aux functions ##

const _msk64 = ~UInt64(0)
Expand Down
12 changes: 6 additions & 6 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Broadcast

using Base.Cartesian
using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, shape, linearindices, allocate_for, tail, dimlength
using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, allocate_for, tail, dimlength
import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, , .%, .<<, .>>, .^
export broadcast, broadcast!, bitbroadcast
export broadcast_getindex, broadcast_setindex!
Expand All @@ -13,8 +13,8 @@ export broadcast_getindex, broadcast_setindex!
## Calculate the broadcast shape of the arguments, or error if incompatible
# array inputs
broadcast_shape() = ()
broadcast_shape(A) = shape(A)
@inline broadcast_shape(A, B...) = broadcast_shape((), shape(A), map(shape, B)...)
broadcast_shape(A) = indices(A)
@inline broadcast_shape(A, B...) = broadcast_shape((), indices(A), map(indices, B)...)
# shape inputs
broadcast_shape(shape::Tuple) = shape
@inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...)
Expand All @@ -40,7 +40,7 @@ _bcsm(a::Number, b::Number) = a == b || b == 1
## Check that all arguments are broadcast compatible with shape
# comparing one input against a shape
check_broadcast_shape(shp) = nothing
check_broadcast_shape(shp, A) = check_broadcast_shape(shp, shape(A))
check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A))
check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing
check_broadcast_shape(shp, ::Tuple{}) = nothing
check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions"))
Expand Down Expand Up @@ -133,7 +133,7 @@ end
end

@inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs})
check_broadcast_shape(shape(B), As...)
check_broadcast_shape(indices(B), As...)
sz = size(B)
mapindex = map(x->newindexer(sz, x), As)
_broadcast!(f, B, mapindex, As, Val{nargs})
Expand Down Expand Up @@ -363,7 +363,7 @@ for (f, scalarf) in ((:.==, :(==)),
:((A,ind)->A), :((B,ind)->B[ind])),
(:AbstractArray, :Any, :A,
:((A,ind)->A[ind]), :((B,ind)->B)))
shape = :(shape($active))
shape = :(indices($active))
@eval begin
function ($f)(A::$sigA, B::$sigB)
P = allocate_for(BitArray, $active, $shape)
Expand Down
2 changes: 1 addition & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ start(v::SimpleVector) = 1
next(v::SimpleVector,i) = (v[i],i+1)
done(v::SimpleVector,i) = (i > v.length)
isempty(v::SimpleVector) = (v.length == 0)
indices(v::SimpleVector, d) = d == 1 ? (1:length(v)) : (1:1)
indices(v::SimpleVector, d) = d == 1 ? OneTo(length(v)) : OneTo(1)
linearindices(v::SimpleVector) = indices(v, 1)

function ==(v1::SimpleVector, v2::SimpleVector)
Expand Down
2 changes: 1 addition & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export
# Types
AbstractChannel,
AbstractMatrix,
AbstractUnitRange,
AbstractVector,
AbstractVecOrMat,
Array,
Expand Down Expand Up @@ -583,7 +584,6 @@ export
searchsortedlast,
select!,
select,
shape,
shuffle,
shuffle!,
size,
Expand Down
23 changes: 9 additions & 14 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ using Base: LinearFast, LinearSlow, AbstractCartesianIndex, fill_to_length, tail

export CartesianIndex, CartesianRange

# Traits for linear indexing
linearindexing{A<:BitArray}(::Type{A}) = LinearFast()

# CartesianIndex
immutable CartesianIndex{N} <: AbstractCartesianIndex{N}
I::NTuple{N,Int}
Expand Down Expand Up @@ -178,18 +175,18 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
# whose length is equal to the dimension we're to process next. This
# allows us to dispatch, which is important for the type-stability of
# the lines involving Colon as the final index.
index_shape(A::AbstractVector, I::Colon) = shape(A)
index_shape(A::AbstractVector, I::Colon) = indices(A)
index_shape(A::AbstractArray, I::Colon) = (length(A),)
@inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, (true,), I...)
@inline index_shape_dim(A, dim, ::Colon) = (trailingsize(A, length(dim)),)
@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (shape(A, N),)
@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (indices(A, N),)
@inline index_shape_dim(A, dim, I::Real...) = ()
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (shape(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...)
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (indices(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...)
@inline index_shape_dim(A, dim, ::Real, I...) = (index_shape_dim(A, (dim...,true), I...)...)
@inline index_shape_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (shape(i)..., index_shape_dim(A, (dim...,true), I...)...)
@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (indices(i)..., index_shape_dim(A, (dim...,true), I...)...)
@inline index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_shape_dim(A, (dim...,true), I...)...)
@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (shape(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (indices(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)

@inline decolon(A::AbstractVector, ::Colon) = (indices(A,1),)
@inline decolon(A::AbstractArray, ::Colon) = (1:length(A),)
Expand Down Expand Up @@ -291,8 +288,6 @@ end
end
end

dimlength(r::Range) = length(r)
dimlength(i::Integer) = i
@noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))"))

## setindex! ##
Expand Down Expand Up @@ -787,7 +782,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
@generated function unique{T,N}(A::AbstractArray{T,N}, dim::Int)
quote
1 <= dim <= $N || return copy(A)
hashes = allocate_for(inds->zeros(UInt, inds), A, shape(A, dim))
hashes = allocate_for(inds->zeros(UInt, inds), A, indices(A, dim))

# Compute hash for each row
k = 0
Expand All @@ -796,15 +791,15 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
end

# Collect index of first row for each hash
uniquerow = allocate_for(Array{Int}, A, shape(A, dim))
uniquerow = allocate_for(Array{Int}, A, indices(A, dim))
firstrow = Dict{Prehashed,Int}()
for k = indices(A, dim)
uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k)
end
uniquerows = collect(values(firstrow))

# Check for collisions
collided = allocate_for(falses, A, shape(A, dim))
collided = allocate_for(falses, A, indices(A, dim))
@inbounds begin
@nloops $N i A d->(if d == dim
k = i_d
Expand All @@ -819,7 +814,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
end

if any(collided)
nowcollided = allocate_for(BitArray, A, shape(A, dim))
nowcollided = allocate_for(BitArray, A, indices(A, dim))
while any(collided)
# Collect index of first row for each collided hash
empty!(firstrow)
Expand Down
2 changes: 1 addition & 1 deletion base/number.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ isinteger(x::Integer) = true
size(x::Number) = ()
size(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : 1
indices(x::Number) = ()
indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : (1:1)
indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : OneTo(1)
eltype{T<:Number}(::Type{T}) = T
ndims(x::Number) = 0
ndims{T<:Number}(::Type{T}) = 0
Expand Down
2 changes: 1 addition & 1 deletion base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ function promote_shape(a::AbstractArray, b::AbstractArray)
throw(DimensionMismatch("dimensions must match"))
end
end
return shape(a)
return indices(a)
end

function throw_setindex_mismatch(X, I)
Expand Down
2 changes: 0 additions & 2 deletions base/reshapedarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ size_strides(out::Tuple) = out
@inline size_strides(out, s, sz...) = size_strides((out..., out[end]*s), sz...)

size(A::ReshapedArray) = A.dims
size(A::ReshapedArray, d) = d <= ndims(A) ? A.dims[d] : 1
similar(A::ReshapedArray, eltype::Type) = similar(parent(A), eltype, size(A))
similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims)
linearindexing{R<:ReshapedArrayLF}(::Type{R}) = LinearFast()
parent(A::ReshapedArray) = A.parent
Expand Down
10 changes: 5 additions & 5 deletions base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Sort

using Base: Order, copymutable, linearindices, allocate_for, shape, linearindexing, viewindexing, LinearFast
using Base: Order, copymutable, linearindices, allocate_for, linearindexing, viewindexing, LinearFast

import
Base.sort,
Expand Down Expand Up @@ -447,7 +447,7 @@ function sortperm(v::AbstractVector;
by=identity,
rev::Bool=false,
order::Ordering=Forward)
p = Base.allocate_for(Vector{Int}, v, shape(v, 1))
p = Base.allocate_for(Vector{Int}, v, indices(v, 1))
for (i,ind) in zip(eachindex(p), indices(v, 1))
p[i] = ind
end
Expand Down Expand Up @@ -507,7 +507,7 @@ end
function sortrows(A::AbstractMatrix; kws...)
inds = indices(A,1)
T = slicetypeof(A, inds, :)
rows = allocate_for(Vector{T}, A, shape(A, 1))
rows = allocate_for(Vector{T}, A, indices(A, 1))
for i in inds
rows[i] = view(A, i, :)
end
Expand All @@ -518,7 +518,7 @@ end
function sortcols(A::AbstractMatrix; kws...)
inds = indices(A,2)
T = slicetypeof(A, :, inds)
cols = allocate_for(Vector{T}, A, shape(A, 2))
cols = allocate_for(Vector{T}, A, indices(A, 2))
for i in inds
cols[i] = view(A, :, i)
end
Expand All @@ -532,7 +532,7 @@ function slicetypeof{T,N}(A::AbstractArray{T,N}, i1, i2)
SubArray{T,1,typeof(A),typeof(I),fast}
end
slice_dummy(::Colon) = Colon()
slice_dummy{T}(::UnitRange{T}) = one(T)
slice_dummy{T}(::AbstractUnitRange{T}) = one(T)

## fast clever sorting for floats ##

Expand Down
19 changes: 11 additions & 8 deletions base/subarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ immutable SubArray{T,N,P,I,L} <: AbstractArray{T,N}
end
# Compute the linear indexability of the indices, and combine it with the linear indexing of the parent
function SubArray(parent::AbstractArray, indexes::Tuple, dims::Tuple)
SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, convert(Dims, dims))
SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, map(dimlength, dims))
end
function SubArray{P, I, N}(::LinearSlow, parent::P, indexes::I, dims::NTuple{N, Int})
SubArray{eltype(P), N, P, I, false}(parent, indexes, dims, 0, 0)
Expand Down Expand Up @@ -47,6 +47,9 @@ viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow()
# Of course, all other array types are slow
viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = LinearSlow()

dimlength(r::Range) = length(r)
dimlength(i::Integer) = i

# Simple utilities
size(V::SubArray) = V.dims
length(V::SubArray) = prod(V.dims)
Expand All @@ -57,7 +60,7 @@ parent(V::SubArray) = V.parent
parentindexes(V::SubArray) = V.indexes

parent(a::AbstractArray) = a
parentindexes(a::AbstractArray) = ntuple(i->1:size(a,i), ndims(a))
parentindexes(a::AbstractArray) = ntuple(i->OneTo(size(a,i)), ndims(a))

## SubArray creation
# Drops singleton dimensions (those indexed with a scalar)
Expand Down Expand Up @@ -273,19 +276,19 @@ end
# they are taken from the range/vector
# 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) ? (1:1) : error("dimension $d out of range"))
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->1:s, size(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(out::Tuple, dim, S::SubArray) = out
_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., 1:length(i1)), dim+1, S, I...))
_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}) = 1:1
indices1{T}(S::SubArray{T,0}) = OneTo(1)
indices1(S::SubArray) = (@_inline_meta; _indices1(indicesbehavior(parent(S)), S))
_indices1(::IndicesStartAt1, S::SubArray) = 1:S.dims[1]
_indices1(::IndicesStartAt1, S::SubArray) = OneTo(S.dims[1])
_indices1(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...))
_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; 1:length(i1))
_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))

Expand Down
Loading

0 comments on commit c318a9f

Please sign in to comment.