From dbbccfa28c3c0af8f328a0387c4569a60799666c Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Tue, 10 Feb 2015 15:05:19 +0100 Subject: [PATCH] cat redesign --- base/abstractarray.jl | 298 ------------------------------------ base/array.jl | 34 +---- base/bitarray.jl | 154 +------------------ base/cat.jl | 345 ++++++++++++++++++++++++++++++++++++++++++ base/exports.jl | 2 + base/operators.jl | 3 + base/sysimg.jl | 1 + test/arrayops.jl | 4 +- test/perf/cat/perf.jl | 52 ++++++- 9 files changed, 413 insertions(+), 480 deletions(-) create mode 100644 base/cat.jl diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 686961e216ba0c..8486ed4cb83106 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -594,303 +594,6 @@ end get(A::AbstractArray, I::RangeVecIntList, default) = get!(similar(A, typeof(default), map(length, I)...), A, I, default) - -## Concatenation ## - -promote_eltype() = Bottom -promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) - -#TODO: ERROR CHECK -cat(catdim::Integer) = Array(Any, 0) - -vcat() = Array(Any, 0) -hcat() = Array(Any, 0) - -## cat: special cases -hcat{T}(X::T...) = T[ X[j] for i=1, j=1:length(X) ] -hcat{T<:Number}(X::T...) = T[ X[j] for i=1, j=1:length(X) ] -vcat{T}(X::T...) = T[ X[i] for i=1:length(X) ] -vcat{T<:Number}(X::T...) = T[ X[i] for i=1:length(X) ] - -function vcat(X::Number...) - T = promote_typeof(X...) - hvcat_fill(Array(T,length(X)), X) -end - -function hcat(X::Number...) - T = promote_typeof(X...) - hvcat_fill(Array(T,1,length(X)), X) -end - -function vcat{T}(V::AbstractVector{T}...) - n = 0 - for Vk in V - n += length(Vk) - end - a = similar(full(V[1]), n) - pos = 1 - for k=1:length(V) - Vk = V[k] - p1 = pos+length(Vk)-1 - a[pos:p1] = Vk - pos = p1+1 - end - a -end - -function hcat{T}(A::AbstractVecOrMat{T}...) - nargs = length(A) - nrows = size(A[1], 1) - ncols = 0 - dense = true - for j = 1:nargs - Aj = A[j] - if size(Aj, 1) != nrows - throw(ArgumentError("number of rows must match")) - end - dense &= isa(Aj,Array) - nd = ndims(Aj) - ncols += (nd==2 ? size(Aj,2) : 1) - end - B = similar(full(A[1]), nrows, ncols) - pos = 1 - if dense - for k=1:nargs - Ak = A[k] - n = length(Ak) - copy!(B, pos, Ak, 1, n) - pos += n - end - else - for k=1:nargs - Ak = A[k] - p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1 - B[:, pos:p1] = Ak - pos = p1+1 - end - end - return B -end - -function vcat{T}(A::AbstractMatrix{T}...) - nargs = length(A) - nrows = sum(a->size(a, 1), A)::Int - ncols = size(A[1], 2) - for j = 2:nargs - if size(A[j], 2) != ncols - throw(ArgumentError("number of columns must match")) - end - end - B = similar(full(A[1]), nrows, ncols) - pos = 1 - for k=1:nargs - Ak = A[k] - p1 = pos+size(Ak,1)-1 - B[pos:p1, :] = Ak - pos = p1+1 - end - return B -end - -## cat: general case - -function cat(catdims, X...) - T = promote_type(map(x->isa(x,AbstractArray) ? eltype(x) : typeof(x), X)...) - cat_t(catdims, T, X...) -end - -function cat_t(catdims, typeC::Type, X...) - catdims = collect(catdims) - nargs = length(X) - ndimsX = Int[isa(a,AbstractArray) ? ndims(a) : 0 for a in X] - ndimsC = max(maximum(ndimsX), maximum(catdims)) - catsizes = zeros(Int,(nargs,length(catdims))) - dims2cat = zeros(Int,ndimsC) - for k = 1:length(catdims) - dims2cat[catdims[k]]=k - end - - dimsC = Int[d <= ndimsX[1] ? size(X[1],d) : 1 for d=1:ndimsC] - for k = 1:length(catdims) - catsizes[1,k] = dimsC[catdims[k]] - end - for i = 2:nargs - for d = 1:ndimsC - currentdim = (d <= ndimsX[i] ? size(X[i],d) : 1) - if dims2cat[d]==0 - dimsC[d] == currentdim || throw(DimensionMismatch("mismatch in dimension $(d)")) - else - dimsC[d] += currentdim - catsizes[i,dims2cat[d]] = currentdim - end - end - end - - C = similar(isa(X[1],AbstractArray) ? full(X[1]) : [X[1]], typeC, tuple(dimsC...)) - if length(catdims)>1 - fill!(C,0) - end - - offsets = zeros(Int,length(catdims)) - for i=1:nargs - cat_one = [ dims2cat[d]==0 ? (1:dimsC[d]) : (offsets[dims2cat[d]]+(1:catsizes[i,dims2cat[d]])) for d=1:ndimsC] - C[cat_one...] = X[i] - for k = 1:length(catdims) - offsets[k] += catsizes[i,k] - end - end - return C -end - -vcat(X...) = cat(1, X...) -hcat(X...) = cat(2, X...) - -typed_vcat(T::Type, X...) = cat_t(1, T, X...) -typed_hcat(T::Type, X...) = cat_t(2, T, X...) - -cat{T}(catdims, A::AbstractArray{T}...) = cat_t(catdims, T, A...) - -cat(catdims, A::AbstractArray...) = cat_t(catdims, promote_eltype(A...), A...) - -vcat(A::AbstractArray...) = cat(1, A...) -hcat(A::AbstractArray...) = cat(2, A...) - -typed_vcat(T::Type, A::AbstractArray...) = cat_t(1, T, A...) -typed_hcat(T::Type, A::AbstractArray...) = cat_t(2, T, A...) - -# 2d horizontal and vertical concatenation - -function hvcat(nbc::Integer, as...) - # nbc = # of block columns - n = length(as) - if mod(n,nbc) != 0 - throw(ArgumentError("all rows must have the same number of block columns")) - end - nbr = div(n,nbc) - hvcat(ntuple(nbr, i->nbc), as...) -end - -function hvcat{T}(rows::(Int...), as::AbstractMatrix{T}...) - nbr = length(rows) # number of block rows - - nc = 0 - for i=1:rows[1] - nc += size(as[i],2) - end - - nr = 0 - a = 1 - for i = 1:nbr - nr += size(as[a],1) - a += rows[i] - end - - out = similar(full(as[1]), T, nr, nc) - - a = 1 - r = 1 - for i = 1:nbr - c = 1 - szi = size(as[a],1) - for j = 1:rows[i] - Aj = as[a+j-1] - szj = size(Aj,2) - if size(Aj,1) != szi - throw(ArgumentError("mismatched height in block row $(i)")) - end - if c-1+szj > nc - throw(ArgumentError("block row $(i) has mismatched number of columns")) - end - out[r:r-1+szi, c:c-1+szj] = Aj - c += szj - end - if c != nc+1 - throw(ArgumentError("block row $(i) has mismatched number of columns")) - end - r += szi - a += rows[i] - end - out -end - -hvcat(rows::(Int...)) = [] - -function hvcat{T<:Number}(rows::(Int...), xs::T...) - nr = length(rows) - nc = rows[1] - - a = Array(T, nr, nc) - if length(a) != length(xs) - throw(ArgumentError("argument count does not match specified shape")) - end - k = 1 - @inbounds for i=1:nr - if nc != rows[i] - throw(ArgumentError("row $(i) has mismatched number of columns")) - end - for j=1:nc - a[i,j] = xs[k] - k += 1 - end - end - a -end - -function hvcat_fill(a, xs) - k = 1 - nr, nc = size(a,1), size(a,2) - for i=1:nr - @inbounds for j=1:nc - a[i,j] = xs[k] - k += 1 - end - end - a -end - -function typed_hvcat(T::Type, rows::(Int...), xs::Number...) - nr = length(rows) - nc = rows[1] - for i = 2:nr - if nc != rows[i] - throw(ArgumentError("row $(i) has mismatched number of columns")) - end - end - len = length(xs) - if nr*nc != len - throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))")) - end - hvcat_fill(Array(T, nr, nc), xs) -end - -function hvcat(rows::(Int...), xs::Number...) - T = promote_typeof(xs...) - typed_hvcat(T, rows, xs...) -end - -# fallback definition of hvcat in terms of hcat and vcat -function hvcat(rows::(Int...), as...) - nbr = length(rows) # number of block rows - rs = cell(nbr) - a = 1 - for i = 1:nbr - rs[i] = hcat(as[a:a-1+rows[i]]...) - a += rows[i] - end - vcat(rs...) -end - -function typed_hvcat(T::Type, rows::(Int...), as...) - nbr = length(rows) # number of block rows - rs = cell(nbr) - a = 1 - for i = 1:nbr - rs[i] = hcat(as[a:a-1+rows[i]]...) - a += rows[i] - end - T[rs...;] -end - ## Reductions and scans ## function isequal(A::AbstractArray, B::AbstractArray) @@ -1496,4 +1199,3 @@ function randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) end randsubseq{T}(A::AbstractArray{T}, p::Real) = randsubseq!(T[], A, p) - diff --git a/base/array.jl b/base/array.jl index 13e90dc83ee179..1d07c6eb37d9e0 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1045,36 +1045,10 @@ function reverse!(v::StridedVector, s=1, n=length(v)) v end -function vcat{T}(arrays::Vector{T}...) - n = 0 - for a in arrays - n += length(a) - end - arr = Array(T, n) - ptr = pointer(arr) - offset = 0 - if isbits(T) - elsz = sizeof(T) - else - elsz = div(WORD_SIZE,8) - end - for a in arrays - nba = length(a)*elsz - ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), - ptr+offset, a, nba) - offset += nba - end - return arr -end - -function hcat{T}(V::Vector{T}...) - height = length(V[1]) - for j = 2:length(V) - if length(V[j]) != height - throw(DimensionMismatch("vectors must have same lengths")) - end - end - [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] +vcat_fill!{T}(C::Vector{T}, catrange, x::Vector) = copy!(C,first(catrange), x,1,length(x)) +hcat_fill!{T}(C::Matrix{T}, catrange, x::VecOrMat) = begin + size(C,1)==size(x,1) || throw(ArgumentError("number of rows must match")) + copy!(C,size(C,1)*(first(catrange)-1)+1,x,1,length(x)) end ## find ## diff --git a/base/bitarray.jl b/base/bitarray.jl index 11cc6a6bb420fe..b9de3ab587f107 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1713,159 +1713,17 @@ function permutedims(B::Union(BitArray,StridedArray), perm) permutedims!(P, B, perm) end - ## Concatenation ## -function hcat(B::BitVector...) - height = length(B[1]) - for j = 2:length(B) - if length(B[j]) != height - throw(DimensionMismatch("dimensions must match")) - end - end - M = BitArray(height, length(B)) - for j = 1:length(B) - copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height) - end - return M -end - -function vcat(V::BitVector...) - n = 0 - for Vk in V - n += length(Vk) - end - B = BitArray(n) - j = 1 - for Vk in V - copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk)) - j += length(Vk) - end - return B -end - -function hcat(A::Union(BitMatrix,BitVector)...) - nargs = length(A) - nrows = size(A[1], 1) - ncols = 0 - dense = true - for j = 1:nargs - Aj = A[j] - nd = ndims(Aj) - ncols += (nd==2 ? size(Aj,2) : 1) - if size(Aj, 1) != nrows - throw(DimensionMismatch("row lengths must match")) - end - end - - B = BitArray(nrows, ncols) - - pos = 1 - for k = 1:nargs - Ak = A[k] - n = length(Ak) - copy_chunks!(B.chunks, pos, Ak.chunks, 1, n) - pos += n - end - return B -end - -function vcat(A::BitMatrix...) - nargs = length(A) - nrows = sum(a->size(a, 1), A)::Int - ncols = size(A[1], 2) - for j = 2:nargs - if size(A[j], 2) != ncols - throw(DimensionMismatch("column lengths must match")) - end - end - B = BitArray(nrows, ncols) - Bc = B.chunks - nrowsA = [size(a, 1) for a in A] - Ac = [a.chunks for a in A] - pos_d = 1 - pos_s = ones(Int, nargs) - for j = 1:ncols, k = 1:nargs - copy_chunks!(Bc, pos_d, Ac[k], pos_s[k], nrowsA[k]) - pos_s[k] += nrowsA[k] - pos_d += nrowsA[k] - end - return B -end - -# general case, specialized for BitArrays and Integers -function cat(catdim::Integer, X::Union(BitArray, Integer)...) - nargs = length(X) - # using integers results in conversion to Array{Int} - # (except in the all-Bool case) - has_bitarray = false - has_integer = false - for a in X - if isa(a, BitArray) - has_bitarray = true - else - has_integer = true - end - end - # just integers and no BitArrays -> general case - has_bitarray || return invoke(cat, (Integer, Any...), catdim, X...) - dimsX = map((a->isa(a,BitArray) ? size(a) : (1,)), X) - ndimsX = map((a->isa(a,BitArray) ? ndims(a) : 1), X) - d_max = maximum(ndimsX) - - if catdim > d_max + 1 - for i = 1:nargs - if dimsX[1] != dimsX[i] - throw(DimensionMismatch("all inputs must have same dimensions when concatenating along a higher dimension")) - end - end - elseif nargs >= 2 - for d = 1:d_max - d == catdim && continue - len = d <= ndimsX[1] ? dimsX[1][d] : 1 - for i = 2:nargs - len == (d <= ndimsX[i] ? dimsX[i][d] : 1) || throw(DimensionMismatch("mismatch in dimension $d")) - end - end - end - - cat_ranges = ntuple(nargs, i->(catdim <= ndimsX[i] ? dimsX[i][catdim] : 1)) - - function compute_dims(d) - if d == catdim - catdim <= d_max && return sum(cat_ranges) - return nargs - else - d <= ndimsX[1] && return dimsX[1][d] - return 1 - end - end - - ndimsC = max(catdim, d_max) - dimsC = ntuple(ndimsC, compute_dims)::(Int...) - typeC = promote_type(map(x->isa(x,BitArray) ? eltype(x) : typeof(x), X)...) - if !has_integer || typeC == Bool - C = BitArray(dimsC) - else - C = Array(typeC, dimsC) - end +cat_containertypeof(::(Union(Bool,BitArray)...)) = BitArray +cat_container(::Type{Bool}, sz, ::Type{BitArray}) = BitArray(sz) - range = 1 - for k = 1:nargs - nextrange = range + cat_ranges[k] - cat_one = ntuple(ndimsC, i->(i != catdim ? - (1:dimsC[i]) : (range:nextrange-1) )) - # note: when C and X are BitArrays, this calls - # the special assign with ranges - C[cat_one...] = X[k] - range = nextrange - end - return C +vcat_fill!(C::BitVector, catrange, x::BitVector) = copy_chunks!(C.chunks,first(catrange), x.chunks,1,length(x)) +hcat_fill!(C::BitMatrix, catrange, x::Union(BitMatrix,BitVector)) = begin + size(C,1)==size(x,1) || throw(ArgumentError("number of rows must match")) + copy_chunks!(C.chunks,size(C,1)*(first(catrange)-1)+1,x.chunks,1,length(x)) end -# hvcat -> use fallbacks in abstractarray.jl - - # BitArray I/O write(s::IO, B::BitArray) = write(s, B.chunks) diff --git a/base/cat.jl b/base/cat.jl new file mode 100644 index 00000000000000..62b7f63680c3e1 --- /dev/null +++ b/base/cat.jl @@ -0,0 +1,345 @@ +vcat() = Array(Any, 0) +hcat() = Array(Any, 0) +cat(catdim::Integer) = Array(Any, 0) +hvcat(rows::(Int...)) = Array(Any, 0) + +# auxiliary cat functions +vcatsize(x::AbstractArray) = size(x) +vcatsize(x) = (1,) + +hcatsize(x::AbstractVector) = (length(x),1) +hcatsize(x::AbstractArray) = size(x) +hcatsize(x) = (1,1) + +catsize(catdim, x::AbstractArray) = ndims(x)>catdim ? size(x) : size(x, (1:catdim)...) +catsize(catdim, x) = tuple(ones(Int,catdim)...) + +catrange(catdim, x::AbstractArray) = 1:size(x,catdim) +catrange(catdim, x) = 1 + +vcatshape(::()) = (0,) +@inline function vcatshape(X::(AbstractVector...,)) + s1::Int = 0 + for x in X + s1 += length(x) + end + (s1,) +end +@inline function vcatshape(X::(AbstractMatrix...,)) + s1::Int = 0 + for x in X + s1 += size(x,1) + end + s2::Int = size(X[1],2) + (s1, s2) +end +vcatshape(X::(Number...,)) = (length(X),) +vcatshape{T}(X::(T...,)) = (length(X),) +@inline function vcatshape(X::(Any...,)) + sz::Dims = vcatsize(X[1]) + for k = 2:length(X) + sz = vcatshape_add(sz, vcatsize(X[k])) + end + sz +end + +stagedfunction vcatshape_add{N1,N2}(sz::NTuple{N1,Int}, sz2::NTuple{N2,Int}) + newsize = Expr(:tuple, :(sz[1]+sz2[1]), [:(sz[$i]) for i=2:N1]..., [1 for i=N1+1:N2]...) + quote + $(Expr(:meta,:inline)) + $newsize + end +end + +hcatshape(::()) = (1,0) +hcatshape(X::(Number...,)) = (1,length(X)) +hcatshape{T}(X::(T...,)) = (1,length(X)) +hcatshape(X::(AbstractVector...,)) = (length(X[1]), length(X)) +@inline function hcatshape(X::(AbstractVecOrMat...,)) + s2::Int = 0 + for x in X + s2 += size(x,2) + end + s1::Int = size(X[1],1) + (s1, s2) +end +@inline function hcatshape(X::(Any...)) + sz::Dims = hcatsize(X[1]) + for k = 2:length(X) + sz = hcatshape_add(sz, hcatsize(X[k])) + end + sz +end + +stagedfunction hcatshape_add{N1,N2}(sz::NTuple{N1,Int}, sz2::NTuple{N2, Int}) + newsize = Expr(:tuple, :(sz[1]), :(sz[2]+sz2[2]), [:(sz[$i]) for i=3:N1]..., [1 for i=N1+1:N2]...) + quote + $(Expr(:meta,:inline)) + $newsize + end +end + +catshape(catdim, ::()) = tuple(ones(Int,catdim-1)...,0) +catshape(catdim, X::(Number...,)) = tuple(ones(Int,catdim-1)...,length(X)) +@inline function catshape(catdim, X::(Any...)) + sz::Dims = catsize(catdim, X[1]) + for k = 2:length(X) + sz = catshape_add(catdim, sz, catsize(catdim, X[k])) + end + sz +end + +stagedfunction catshape_add{N1,N2}(catdim, sz::NTuple{N1,Int}, sz2::NTuple{N2,Int}) + newsize = Expr(:tuple, [:($i==catdim ? (sz[$i]+sz2[$i]) : sz[$i]) for i=1:N1]..., [1 for i=N1+1:N2]...) + quote + $(Expr(:meta,:inline)) + $newsize + end +end + +hvcatshape(rows::(Integer...),::()) = (length(rows),0) +hvcatshape(rows::(Integer...), X::(Number...,)) = (length(rows), rows[1]) +hvcatshape{T}(rows::(Integer...), X::(T...,)) = (length(rows), rows[1]) +@inline function hvcatshape(rows::(Integer...), X::(AbstractVecOrMat...,)) + nc::Int = 0 + for k = 1:rows[1] + nc += size(X[k], 2) + end + k = 1 + nr::Int = 0 + for i = 1:length(rows) + nr += size(X[k], 1) + k += rows[i] + end + return (nr, nc) +end +@inline function hvcatshape(rows::(Integer...), X::(Any...)) + k=1 + rowsz::Dims = hcatsize(X[k]) + k+=1 + for j = 2:rows[1] + rowsz = hcatshape_add(rowsz, hcatsize(X[k])) + k+=1 + end + sz::Dims = rowsz + for i = 2:length(rows) + rowsz = hcatsize(X[k]) + k+=1 + for j = 2:rows[i] + rowsz = hcatshape_add(rowsz, hcatsize(X[k])) + k+=1 + end + sz = vcatshape_add(sz, rowsz) + end + sz +end + +cat_eltypeof{T}(::(T...,)) = T +cat_eltypeof{T}(::(AbstractArray{T}...,)) = T +@inline function cat_eltypeof(X::(Any...,)) + T = cat_eltypeof((X[1],)) + for k = 2:length(X) + T = promote_type(T, cat_eltypeof((X[k],))) + end + T +end + +cat_containertypeof(::()) = Array +cat_containertypeof(::(Any...,)) = Array + +cat_container(T, sz, ::Type{Array}) = Array(T, sz) + +stagedfunction vcat_fill!{T,N}(C::AbstractArray{T,N}, catrange, x) + ranges = [:(:) for d=2:N] + return quote + $(Expr(:meta,:inline)) + C[catrange,$(ranges...)] = x + end +end + +stagedfunction hcat_fill!{T,N}(C::AbstractArray{T,N}, catrange, x) + ranges = [:(:) for d=3:N] + return quote + $(Expr(:meta,:inline)) + C[:,catrange,$(ranges...)] = x + end +end + +stagedfunction hvcat_fill!{T,N}(C::AbstractArray{T,N}, rowrange, columnrange, x) + ranges = [:(:) for d=3:N] + return quote + $(Expr(:meta,:inline)) + C[rowrange, columnrange, $(ranges...)] = x + end +end + +stagedfunction cat_fill!{T,N}(C::AbstractArray{T,N}, catdim::Integer, catrange, x) + ranges = [d==N ? :(catrange) : :(1:size(C,$d)) for d=1:N] + ex = :(C[$(ranges...)] = x) + for n = N-1:-1:1 + ranges = [d==n ? :(catrange) : :(1:size(C,$d)) for d=1:N] + ex = Expr(:if,:(catdim==$n),:(C[$(ranges...)] = x),ex) + end + return quote + $(Expr(:meta,:inline)) + $ex + end +end + +## vcat +function typed_vcat(T::Type, X...) + C = cat_container(T, vcatshape(X), cat_containertypeof(X)) + offset = 0 + for i = 1:length(X) + x = X[i] + r = offset+catrange(1, x) + vcat_fill!(C, r, x) + offset = last(r) + end + C +end + +function vcat(X...) + C = cat_container(cat_eltypeof(X), vcatshape(X), cat_containertypeof(X)) + offset = 0 + for i = 1:length(X) + x = X[i] + r = offset+catrange(1, x) + vcat_fill!(C, r, x) + offset = last(r) + end + C +end + +## hcat +function typed_hcat(T::Type, X...) + C=cat_container(T, hcatshape(X), cat_containertypeof(X)) + offset = 0 + for i = 1:length(X) + x = X[i] + r = offset+catrange(2, x) + hcat_fill!(C, r, x) + offset = last(r) + end + C +end + +function hcat(X...) + C=cat_container(cat_eltypeof(X), hcatshape(X), cat_containertypeof(X)) + offset = 0 + for i = 1:length(X) + x = X[i] + r = offset+catrange(2, x) + hcat_fill!(C, r, x) + offset = last(r) + end + C +end + +# cat: general case +function cat(catdim::Integer, X...) + C=cat_container(cat_eltypeof(X), catshape(catdim, X), cat_containertypeof(X)) + offset = 0 + for i = 1:length(X) + x = X[i] + r = offset+catrange(catdim, x) + cat_fill!(C, catdim, r, x) + offset = last(r) + end + C +end + +## hvcat: 2d horizontal and vertical concatenation +function typed_hvcat(T::Type, rows::(Int...), X...) + C = cat_container(T, hvcatshape(rows,X), cat_containertypeof(X)) + rowoffset = 0 + k = 1 + for nbc in rows + rowrange = rowoffset + catrange(1, X[k]) + columnoffset = 0 + for j = 1:nbc + x = X[k] + columnrange = columnoffset + catrange(2, x) + hvcat_fill!(C, rowrange, columnrange, x) + columnoffset = last(columnrange) + k+=1 + end + columnoffset == size(C,2) || throw(ArgumentError("block row $(i) has mismatched number of columns")) + rowoffset = last(rowrange) + end + k == length(X)+1 || throw(ArgumentError("mismatched number of blocks")) + rowoffset == size(C,1) || throw(ArgumentError("mismatched number of rows")) + C +end + +function hvcat(rows::(Int...), X...) + C = cat_container(cat_eltypeof(X), hvcatshape(rows,X), cat_containertypeof(X)) + rowoffset = 0 + k = 1 + for nbc in rows + rowrange = rowoffset + catrange(1, X[k]) + columnoffset = 0 + for j = 1:nbc + x = X[k] + columnrange = columnoffset + catrange(2, x) + hvcat_fill!(C, rowrange, columnrange, x) + columnoffset = last(columnrange) + k+=1 + end + columnoffset == size(C,2) || throw(ArgumentError("block row $(i) has mismatched number of columns")) + rowoffset = last(rowrange) + end + k == length(X)+1 || throw(ArgumentError("mismatched number of blocks")) + rowoffset == size(C,1) || throw(ArgumentError("mismatched number of rows")) + C +end + +function hvcat(nbc::Integer, A...) + # nbc = # of block columns + n = length(A) + if mod(n,nbc) != 0 + throw(ArgumentError("all rows must have the same number of block columns")) + end + nbr = div(n,nbc) + hvcat(ntuple(i->nbc, nbr), A...) +end + +## dcat: cat along diagonals +function dcat(catdims, X...) + dims = Int[d for d in catdims] + M = length(dims) + N = maximum(dims) + + catsizes = zeros(Int, N) + for k = 1:length(X) + x = X[k] + for j = 1:M + catdim = dims[j] + catsizes[catdim] += length(catrange(catdim, x)) + end + isa(x,AbstractArray) && (N = max(N,ndims(x))) + end + for d = 1:N + catsizes[d] == 0 && (catsizes[d]=length(catrange(d, X[1]))) + end + C=cat_container(cat_eltypeof(X), tuple(catsizes...), cat_containertypeof(X)) + M > 1 && fill!(C,0) + + offsets = zeros(Int, M) + catranges = Array(Union(Int,UnitRange{Int}), N) + for d = 1:N + catranges[d] = 1:catsizes[d] + end + for k = 1:length(X) + x=X[k] + for j=1:M + catdim = dims[j] + catranges[catdim] = offsets[j] + catrange(catdim, x) + offsets[j] = last(catranges[catdim]) + end + C[catranges...] = x + end + C +end + +blkdiag(X...) = dcat([1,2], X...) diff --git a/base/exports.jl b/base/exports.jl index c051eb2f5b480a..9f7c037a0d4f4d 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -504,6 +504,7 @@ export # arrays bitbroadcast, + blkdiag, broadcast!, broadcast!_function, broadcast, @@ -525,6 +526,7 @@ export cumsum, cumsum!, cumsum_kbn, + dcat, eachindex, extrema, fill!, diff --git a/base/operators.jl b/base/operators.jl index d532d3052ab8d9..064b0aa84739cb 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -175,6 +175,9 @@ eltype(::Type{Any}) = Any eltype(t::DataType) = eltype(super(t)) eltype(x) = eltype(typeof(x)) +promote_eltype() = Bottom +promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) + # copying immutable things copy(x::Union(Symbol,Number,AbstractString,Function,Tuple,LambdaStaticData, TopNode,QuoteNode,DataType,UnionType)) = x diff --git a/base/sysimg.jl b/base/sysimg.jl index f6f422eb00bbfb..772e0d66240584 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -58,6 +58,7 @@ include("rational.jl") include("abstractarray.jl") include("subarray.jl") include("array.jl") +include("cat.jl") include("subarray2.jl") include("functors.jl") include("bitarray.jl") diff --git a/test/arrayops.jl b/test/arrayops.jl index f3dcd757b6dc62..240d92633bba39 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -82,9 +82,9 @@ tmp = zeros(Int,map(maximum,rng)...) tmp[rng...] = A[rng...] @test tmp == cat(3,zeros(Int,2,3),[0 0 0; 0 47 52],zeros(Int,2,3),[0 0 0; 0 127 132]) -@test cat([1,2],1,2,3.,4.,5.) == diagm([1,2,3.,4.,5.]) +@test dcat([1,2],1,2,3.,4.,5.) == diagm([1,2,3.,4.,5.]) blk = [1 2;3 4] -tmp = cat([1,3],blk,blk) +tmp = dcat([1,3],blk,blk) @test tmp[1:2,1:2,1] == blk @test tmp[1:2,1:2,2] == zero(blk) @test tmp[3:4,1:2,1] == zero(blk) diff --git a/test/perf/cat/perf.jl b/test/perf/cat/perf.jl index a5b1468fa3f8c4..6b6270aca25bcb 100644 --- a/test/perf/cat/perf.jl +++ b/test/perf/cat/perf.jl @@ -81,6 +81,48 @@ function catnd_perf2(n, iter) end end +function vcat_vect_perf(n, iter) + a = rand(n) + b = rand(n) + for i = 1:iter + c = [a; b; b; a] + end +end + +function vcat_vect_perf2(n, iter) + a = rand(n) + b = rand(n) + for i=1:iter + c = Array(Float64, 4n) + c[ 1: n] = a + c[ n+1:2n] = b + c[2n+1:3n] = b + c[3n+1:4n] = a + end +end + +function hvcat_number_perf(n,iter) + for i = 1:iter + c = [1 2 3 4 5; 6 7 8 9 10; 11 12 13 14 15; 16 17 18 19 20; 21 22 23 24 25] + end +end + +function hcat_bitarray_perf(n,iter) + a = trues(n,n) + b = falses(n,n) + for i = 1:iter + c = [a b b a] + end +end + +function typed_vcat_mixed_perf(n,iter) + for i = 1:iter + c = Float64[1:n; 1.; 1.; 1:n] + end +end + + + problemsizes = [(5, 20000, "small"), (500, 2, "large")] testdata = [(cat2d_perf, "hvcat", "horizontal/vertical matrix concatenation", problemsizes), (cat2d_perf2, "hvcat_setind", "horizontal/vertical matrix concatenation using setindex", problemsizes), @@ -89,6 +131,12 @@ testdata = [(cat2d_perf, "hvcat", "horizontal/vertical matrix concatenat (vcat_perf, "vcat", "vertical matrix concatenation", problemsizes), (vcat_perf2, "vcat_setind", "vertical matrix concatenation using setindex", problemsizes), (catnd_perf, "catnd", "N-dimensional matrix concatenation", problemsizes), - (catnd_perf2, "catnd_setind", "N-dimensional matrix concatenation using setindex", problemsizes)] -include("../perfgeneric.jl") + (catnd_perf2, "catnd_setind", "N-dimensional matrix concatenation using setindex", problemsizes), + (vcat_vect_perf, "vcat_vect", "vertical vector concatenation", [(16, 20000, "small"), (250000, 2, "large")]), + (vcat_vect_perf2, "vcat_vect_setind", "vertical vector concatenation using setindex", [(16, 20000, "small"), (250000, 2, "large")]), + (hvcat_number_perf, "hvcat_number", "horizontal/vertical number concatenation", [(1,20000, "standard")]), + (hcat_bitarray_perf, "hcat_bitarray", "horizontal bitarray concatenation", problemsizes), + (typed_vcat_mixed_perf, "typed_vcat_mixed", "typed vertical mixed concatenation", [(16, 20000, "small"), (250000, 2, "large")])] + +include("../perfgeneric.jl")