Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Drop dimensions indexed by scalars #13612

Merged
merged 2 commits into from
Nov 9, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -47,6 +47,9 @@ Library improvements

* Linear algebra:

* All dimensions indexed by scalars are now dropped, whereas previously only
trailing scalar dimensions would be omitted from the result.

* New `normalize` and `normalize!` convenience functions for normalizing
vectors ([#13681]).

5 changes: 4 additions & 1 deletion base/linalg/diagonal.jl
Original file line number Diff line number Diff line change
@@ -121,7 +121,10 @@ function A_ldiv_B!{T}(D::Diagonal{T}, V::AbstractMatrix{T})
if d == zero(T)
throw(SingularException(i))
end
V[i,:] *= inv(d)
d⁻¹ = inv(d)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cute

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐨

for j=1:size(V,2)
@inbounds V[i,j] *= d⁻¹
end
end
V
end
37 changes: 20 additions & 17 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
@@ -166,15 +166,15 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
@inline index_lengths_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_lengths_dim(A, dim+1, I...)...)
@inline index_lengths_dim(A, dim, i::AbstractArray, I...) = (length(i), index_lengths_dim(A, dim+1, I...)...)

# shape of array to create for getindex() with indexes I, dropping trailing scalars
# shape of array to create for getindex() with indexes I, dropping scalars
index_shape(A::AbstractArray, I::AbstractArray) = size(I) # Linear index reshape
index_shape(A::AbstractArray, I::AbstractArray{Bool}) = (sum(I),) # Logical index
index_shape(A::AbstractArray, I::Colon) = (length(A),)
@inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, 1, I...)
index_shape_dim(A, dim, I::Real...) = ()
index_shape_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (size(A, dim), index_shape_dim(A, dim+1, i, I...)...)
@inline index_shape_dim(A, dim, ::Real, I...) = (1, index_shape_dim(A, dim+1, I...)...)
@inline index_shape_dim(A, dim, ::Real, I...) = (index_shape_dim(A, dim+1, I...)...)
@inline index_shape_dim(A, dim, i::AbstractVector{Bool}, I...) = (sum(i), index_shape_dim(A, dim+1, I...)...)
@inline index_shape_dim(A, dim, i::AbstractVector, I...) = (length(i), index_shape_dim(A, dim+1, I...)...)

@@ -238,7 +238,8 @@ end
$(Expr(:meta, :inline))
D = eachindex(dest)
Ds = start(D)
@nloops $N i dest d->(j_d = unsafe_getindex(I[d], i_d)) begin
idxlens = index_lengths(src, I...) # TODO: unsplat?
@nloops $N i d->(1:idxlens[d]) d->(j_d = unsafe_getindex(I[d], i_d)) begin
d, Ds = next(D, Ds)
v = @ncall $N unsafe_getindex src j
unsafe_setindex!(dest, v, d)
@@ -248,18 +249,18 @@ end
end

# checksize ensures the output array A is the correct size for the given indices
checksize(A::AbstractArray, I::AbstractArray) = size(A) == size(I) || throw(DimensionMismatch("index 1 has size $(size(I)), but size(A) = $(size(A))"))
checksize(A::AbstractArray, I::AbstractArray{Bool}) = length(A) == sum(I) || throw(DimensionMismatch("index 1 selects $(sum(I)) elements, but length(A) = $(length(A))"))
@generated function checksize(A::AbstractArray, I...)
N = length(I)
quote
@nexprs $N d->(_checksize(A, d, I[d]) || throw(DimensionMismatch("index $d selects $(length(I[d])) elements, but size(A, $d) = $(size(A,d))")))
end
end
_checksize(A::AbstractArray, dim, I) = size(A, dim) == length(I)
_checksize(A::AbstractArray, dim, I::AbstractVector{Bool}) = size(A, dim) == sum(I)
_checksize(A::AbstractArray, dim, ::Colon) = true
_checksize(A::AbstractArray, dim, ::Real) = size(A, dim) == 1
@noinline throw_checksize_error(arr, dim, idx) = throw(DimensionMismatch("index $d selects $(length(I[d])) elements, but size(A, $d) = $(size(A,d))"))

checksize(A::AbstractArray, I::AbstractArray) = size(A) == size(I) || throw_checksize_error(A, 1, I)
checksize(A::AbstractArray, I::AbstractArray{Bool}) = length(A) == sum(I) || throw_checksize_error(A, 1, I)

checksize(A::AbstractArray, I...) = _checksize(A, 1, I...)
_checksize(A::AbstractArray, dim) = true
# Skip scalars
_checksize(A::AbstractArray, dim, ::Real, J...) = _checksize(A, dim, J...)
_checksize(A::AbstractArray, dim, I, J...) = (size(A, dim) == length(I) || throw_checksize_error(A, dim, I); _checksize(A, dim+1, J...))
_checksize(A::AbstractArray, dim, I::AbstractVector{Bool}, J...) = (size(A, dim) == sum(I) || throw_checksize_error(A, dim, I); _checksize(A, dim+1, J...))
_checksize(A::AbstractArray, dim, ::Colon, J...) = _checksize(A, dim+1, J...)

@inline unsafe_setindex!(v::BitArray, x::Bool, ind::Int) = (Base.unsafe_bitsetindex!(v.chunks, x, ind); v)
@inline unsafe_setindex!(v::BitArray, x, ind::Real) = (Base.unsafe_bitsetindex!(v.chunks, convert(Bool, x), to_index(ind)); v)
@@ -585,7 +586,8 @@ end

storeind = 1
Xc, Bc = X.chunks, B.chunks
@nloops($N, i, d->1:size(X, d+1),
idxlens = index_lengths(B, I0, I...) # TODO: unsplat?
@nloops($N, i, d->(1:idxlens[d+1]),
d->nothing, # PRE
d->(ind += stride_lst_d - gap_lst_d), # POST
begin # BODY
@@ -608,7 +610,8 @@ end
$(symbol(:offset_, N)) = 1
ind = 0
Xc, Bc = X.chunks, B.chunks
@nloops $N i X d->(offset_{d-1} = offset_d + (unsafe_getindex(I[d], i_d)-1)*stride_d) begin
idxlens = index_lengths(B, I...) # TODO: unsplat?
@nloops $N i d->(1:idxlens[d]) d->(offset_{d-1} = offset_d + (unsafe_getindex(I[d], i_d)-1)*stride_d) begin
ind += 1
unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind)
end
2 changes: 0 additions & 2 deletions base/sparse/sparsematrix.jl
Original file line number Diff line number Diff line change
@@ -1352,8 +1352,6 @@ function getindex{T}(A::SparseMatrixCSC{T}, i0::Integer, i1::Integer)
((r1 > r2) || (A.rowval[r1] != i0)) ? zero(T) : A.nzval[r1]
end

getindex{T<:Integer}(A::SparseMatrixCSC, i::Integer, J::AbstractVector{T}) = getindex(A,[i],J)

# Colon translation
getindex(A::SparseMatrixCSC, ::Colon, ::Colon) = copy(A)
getindex(A::SparseMatrixCSC, i, ::Colon) = getindex(A, i, 1:size(A, 2))
33 changes: 33 additions & 0 deletions base/sparse/sparsevector.jl
Original file line number Diff line number Diff line change
@@ -345,6 +345,7 @@ sprandbool(r::AbstractRNG, n::Integer, p::AbstractFloat) = sprand(r, n, p, trueb

## Indexing into Matrices can return SparseVectors

# Column slices
function getindex(x::SparseMatrixCSC, ::Colon, j::Integer)
checkbounds(x, :, j)
r1 = convert(Int, x.colptr[j])
@@ -369,6 +370,38 @@ end
SparseVector(M.m, M.rowval, M.nzval)
end

# Row slices
getindex(A::SparseMatrixCSC, i::Integer, ::Colon) = A[i, 1:end]
getindex{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, i::Integer, J::AbstractVector{Bool}) = A[i, find(J)]
function Base.getindex{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, i::Integer, J::AbstractVector)
checkbounds(A, i, J)
nJ = length(J)
colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval

nzinds = Array(Ti, 0)
nzvals = Array(Tv, 0)

# adapted from SparseMatrixCSC's sorted_bsearch_A
ptrI = 1
@inbounds for j = 1:nJ
col = J[j]
rowI = i
ptrA = colptrA[col]
stopA = colptrA[col+1]-1
if ptrA <= stopA
if rowvalA[ptrA] <= rowI
ptrA = searchsortedfirst(rowvalA, rowI, ptrA, stopA, Base.Order.Forward)
if ptrA <= stopA && rowvalA[ptrA] == rowI
push!(nzinds, ptrI)
push!(nzvals, nzvalA[ptrA])
end
end
ptrI += 1
end
end
return SparseVector(nJ, nzinds, nzvals)
end


# Logical and linear indexing into SparseMatrices
getindex{Tv}(A::SparseMatrixCSC{Tv}, I::AbstractVector{Bool}) = _logical_index(A, I) # Ambiguities
4 changes: 2 additions & 2 deletions base/subarray.jl
Original file line number Diff line number Diff line change
@@ -604,8 +604,8 @@ end

# Indexing with non-scalars. For now, this returns a copy, but changing that
# is just a matter of deleting the explicit call to copy.
getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(sub(V, I...))
unsafe_getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(sub_unsafe(V, I))
getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(slice(V, I...))
unsafe_getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(slice_unsafe(V, I))

# Nonscalar setindex! falls back to the AbstractArray versions

4 changes: 2 additions & 2 deletions doc/manual/arrays.rst
Original file line number Diff line number Diff line change
@@ -233,8 +233,8 @@ where each ``I_k`` may be:
The result ``X`` generally has dimensions
``(length(I_1), length(I_2), ..., length(I_n))``, with location
``(i_1, i_2, ..., i_n)`` of ``X`` containing the value
``A[I_1[i_1], I_2[i_2], ..., I_n[i_n]]``. Trailing dimensions
indexed with scalars are dropped. For example, the dimensions of ``A[I, 1]`` will be
``A[I_1[i_1], I_2[i_2], ..., I_n[i_n]]``. All dimensions indexed with scalars are
dropped. For example, the result of ``A[2, I, 3]`` will be a vector with size
``(length(I),)``. Boolean vectors are first transformed with ``find``; the size of
a dimension indexed by a boolean vector will be the number of true values in the vector.
As a special part of this syntax, the ``end`` keyword may be used to represent the last
12 changes: 6 additions & 6 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ a = rand(1, 1, 8, 8, 1)
sz = (5,8,7)
A = reshape(1:prod(sz),sz...)
@test A[2:6] == [2:6;]
@test A[1:3,2,2:4] == cat(3,46:48,86:88,126:128)
@test A[1:3,2,2:4] == cat(2,46:48,86:88,126:128)
@test A[:,7:-3:1,5] == [191 176 161; 192 177 162; 193 178 163; 194 179 164; 195 180 165]
@test A[:,3:9] == reshape(11:45,5,7)
rng = (2,2:3,2:2:5)
@@ -111,7 +111,7 @@ tmp = cat([1,3],blk,blk)

x = rand(2,2)
b = x[1,:]
@test isequal(size(b), (1, 2))
@test isequal(size(b), (2,))
b = x[:,1]
@test isequal(size(b), (2,))

@@ -129,7 +129,7 @@ B[[3,1],[2,4]] = [21 22; 23 24]
B[4,[2,3]] = 7
@test B == [0 23 1 24 0; 11 12 13 14 15; 0 21 3 22 0; 0 7 7 0 0]

@test isequal(reshape(1:27, 3, 3, 3)[1,:], [1 4 7 10 13 16 19 22 25])
@test isequal(reshape(1:27, 3, 3, 3)[1,:], [1, 4, 7, 10, 13, 16, 19, 22, 25])

a = [3, 5, -7, 6]
b = [4, 6, 2, -7, 1]
@@ -575,12 +575,12 @@ let

@test isequal(c[:,1], cv)
@test isequal(c[:,3], cv2)
@test isequal(c[4,:], [2.0 2.0 2.0 2.0]*1000)
@test isequal(c[4,:], [2.0, 2.0, 2.0, 2.0]*1000)

c = cumsum_kbn(A, 2)

@test isequal(c[1,:], cv2')
@test isequal(c[3,:], cv')
@test isequal(c[1,:], cv2)
@test isequal(c[3,:], cv)
@test isequal(c[:,4], [2.0,2.0,2.0,2.0]*1000)

end
6 changes: 3 additions & 3 deletions test/bitarray.jl
Original file line number Diff line number Diff line change
@@ -171,10 +171,10 @@ function gen_getindex_data()
m1, m2 = rand_m1m2()
produce((m1, m2, Bool))
m1, m2 = rand_m1m2()
produce((m1, 1:m2, BitMatrix))
produce((m1, :, BitMatrix))
produce((m1, 1:m2, BitVector))
produce((m1, :, BitVector))
m1, m2 = rand_m1m2()
produce((m1, randperm(m2), BitMatrix))
produce((m1, randperm(m2), BitVector))
m1, m2 = rand_m1m2()
produce((1:m1, m2, BitVector))
produce((:, m2, BitVector))
2 changes: 1 addition & 1 deletion test/parallel_exec.jl
Original file line number Diff line number Diff line change
@@ -242,7 +242,7 @@ map!(x->1, d)
@test fill!(d, 2.) == fill(2, 10, 10)
@test d[:] == fill(2, 100)
@test d[:,1] == fill(2, 10)
@test d[1,:] == fill(2, 1, 10)
@test d[1,:] == fill(2, 10)

# Boundary cases where length(S) <= length(pids)
@test 2.0 == remotecall_fetch(D->D[2], id_other, Base.shmem_fill(2.0, 2; pids=[id_me, id_other]))
2 changes: 1 addition & 1 deletion test/readdlm.jl
Original file line number Diff line number Diff line change
@@ -205,7 +205,7 @@ let i18n_data = ["Origin (English)", "Name (English)", "Origin (Native)", "Name
writedlm(i18n_buff, i18n_arr, ',')
@test i18n_arr == readcsv(i18n_buff)

hdr = i18n_arr[1, :]
hdr = i18n_arr[1:1, :]
data = i18n_arr[2:end, :]
writedlm(i18n_buff, i18n_arr, ',')
@test (data, hdr) == readcsv(i18n_buff, header=true)
6 changes: 3 additions & 3 deletions test/sparsedir/sparse.jl
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ de33 = eye(3)
# also side-checks sparse ref
for i = 1 : 10
a = sprand(5, 4, 0.5)
@test all([a[1:2,1:2] a[1:2,3:4]; a[3:5,1] [a[3:4,2:4]; a[5,2:4]]] == a)
@test all([a[1:2,1:2] a[1:2,3:4]; a[3:5,1] [a[3:4,2:4]; a[5:5,2:4]]] == a)
end

# sparse ref
@@ -460,13 +460,13 @@ let a = spzeros(Int, 10, 10)
@test countnz(a) == 0
a[1,:] = 1
@test countnz(a) == 10
@test a[1,:] == sparse(ones(Int,1,10))
@test a[1,:] == sparse(ones(Int,10))
a[:,2] = 2
@test countnz(a) == 19
@test a[:,2] == 2*sparse(ones(Int,10))

a[1,:] = 1:10
@test a[1,:] == sparse([1:10;]')
@test a[1,:] == sparse([1:10;])
a[:,2] = 1:10
@test a[:,2] == sparse([1:10;])
end
2 changes: 1 addition & 1 deletion test/subarray.jl
Original file line number Diff line number Diff line change
@@ -388,7 +388,7 @@ sA[:] = -3
sA = sub(A, 1:3, 3, 2:5)
@test Base.parentdims(sA) == [1:3;]
@test size(sA) == (3,1,4)
@test sA == A[1:3,3,2:5]
@test sA == A[1:3,3:3,2:5]
@test sA[:] == A[1:3,3,2:5][:]
sA = sub(A, 1:2:3, 1:3:5, 1:2:8)
@test Base.parentdims(sA) == [1:3;]