diff --git a/docs/src/lib/internals.md b/docs/src/lib/internals.md index 780cafaa..6b0a2264 100644 --- a/docs/src/lib/internals.md +++ b/docs/src/lib/internals.md @@ -23,7 +23,9 @@ BlockedOneTo BlockedUnitRange BlockRange BlockIndexRange +BlockIndices BlockSlice +NoncontiguousBlockSlice unblock SubBlockIterator blockcheckbounds_indices diff --git a/src/BlockArrays.jl b/src/BlockArrays.jl index 63497ce5..d824ab04 100644 --- a/src/BlockArrays.jl +++ b/src/BlockArrays.jl @@ -4,7 +4,7 @@ using LinearAlgebra, ArrayLayouts, FillArrays # AbstractBlockArray interface exports export AbstractBlockArray, AbstractBlockMatrix, AbstractBlockVector, AbstractBlockVecOrMat export Block, getblock, getblock!, setblock!, eachblock, blocks -export blockaxes, blocksize, blocklength, blockcheckbounds, BlockBoundsError, BlockIndex, BlockIndexRange +export blockaxes, blocksize, blocklength, blockcheckbounds, BlockBoundsError, BlockIndex, BlockIndexRange, BlockIndices export blocksizes, blocklengths, blocklasts, blockfirsts, blockisequal, blockequals, blockisapprox export eachblockaxes export BlockRange, blockedrange, BlockedUnitRange, BlockedOneTo diff --git a/src/blockaxis.jl b/src/blockaxis.jl index 022c661e..a8fbf930 100644 --- a/src/blockaxis.jl +++ b/src/blockaxis.jl @@ -7,9 +7,9 @@ @propagate_inbounds getindex(b::AbstractArray, K::BlockIndex{1}, J::BlockIndex{1}...) = b[BlockIndex(tuple(K, J...))] -@propagate_inbounds getindex(b::AbstractArray{T,N}, K::BlockIndexRange{N}) where {T,N} = b[block(K)][K.indices...] -@propagate_inbounds getindex(b::LayoutArray{T,N}, K::BlockIndexRange{N}) where {T,N} = b[block(K)][K.indices...] -@propagate_inbounds getindex(b::LayoutArray{T,1}, K::BlockIndexRange{1}) where {T} = b[block(K)][K.indices...] +@propagate_inbounds getindex(b::AbstractArray{T,N}, K::BlockIndices{N}) where {T,N} = b[block(K)][K.indices...] +@propagate_inbounds getindex(b::LayoutArray{T,N}, K::BlockIndices{N}) where {T,N} = b[block(K)][K.indices...] +@propagate_inbounds getindex(b::LayoutArray{T,1}, K::BlockIndices{1}) where {T} = b[block(K)][K.indices...] function findblockindex(b::AbstractVector, k::Integer) @boundscheck k in b || throw(BoundsError()) @@ -539,11 +539,11 @@ end getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:Block{1}}) = mortar([b[K] for K in KR]) getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:Block{1}}}) = mortar([b[K] for K in KR]) -getindex(b::AbstractBlockedUnitRange, Kkr::BlockIndexRange{1}) = b[block(Kkr)][Kkr.indices...] +getindex(b::AbstractBlockedUnitRange, Kkr::BlockIndices{1}) = b[block(Kkr)][Kkr.indices...] getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndex{1}}) = [b[K] for K in KR] -getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndexRange{1}}) = mortar([b[K] for K in KR]) +getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndices{1}}) = mortar([b[K] for K in KR]) getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:BlockIndex{1}}}) = mortar([b[K] for K in KR]) -getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:BlockIndexRange{1}}}) = mortar([b[K] for K in KR]) +getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:BlockIndices{1}}}) = mortar([b[K] for K in KR]) getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}) = mortar([b[K] for K in KR]) _searchsortedfirst(a::AbstractVector, k) = searchsortedfirst(a, k) @@ -567,7 +567,7 @@ Base.dataids(b::AbstractBlockedUnitRange) = Base.dataids(blocklasts(b)) Base.checkindex(::Type{Bool}, b::BlockRange, K::Integer) = checkindex(Bool, Integer.(b), K) Base.checkindex(::Type{Bool}, b::AbstractUnitRange{<:Integer}, K::Block{1}) = checkindex(Bool, blockaxes(b,1), Integer(K)) -function Base.checkindex(::Type{Bool}, axis::AbstractBlockedUnitRange, ind::BlockIndexRange{1}) +function Base.checkindex(::Type{Bool}, axis::AbstractBlockedUnitRange, ind::BlockIndices{1}) checkindex(Bool, axis, first(ind)) && checkindex(Bool, axis, last(ind)) end function Base.checkindex(::Type{Bool}, axis::AbstractBlockedUnitRange, ind::BlockIndex{1}) diff --git a/src/blockindices.jl b/src/blockindices.jl index d2ce8540..d1c6372e 100644 --- a/src/blockindices.jl +++ b/src/blockindices.jl @@ -163,7 +163,7 @@ julia> a[Block(2,2)[2,3]] 20 ``` """ -struct BlockIndex{N,TI<:Tuple{Vararg{Integer,N}},Tα<:Tuple{Vararg{Integer,N}}} +struct BlockIndex{N,TI<:Tuple{Vararg{Any,N}},Tα<:Tuple{Vararg{Any,N}}} I::TI α::Tα end @@ -171,15 +171,15 @@ end @inline BlockIndex(a::NTuple{N,Block{1}}, b::Tuple) where N = BlockIndex(Int.(a), b) @inline BlockIndex(::Tuple{}, b::Tuple{}) = BlockIndex{0,Tuple{},Tuple{}}((), ()) -@inline BlockIndex(a::Integer, b::Integer) = BlockIndex((a,), (b,)) -@inline BlockIndex(a::Tuple, b::Integer) = BlockIndex(a, (b,)) -@inline BlockIndex(a::Integer, b::Tuple) = BlockIndex((a,), b) +@inline BlockIndex(a, b) = BlockIndex((a,), (b,)) +@inline BlockIndex(a::Tuple, b) = BlockIndex(a, (b,)) +@inline BlockIndex(a, b::Tuple) = BlockIndex((a,), b) @inline BlockIndex() = BlockIndex((), ()) @inline BlockIndex(a::Block, b::Tuple) = BlockIndex(a.n, b) -@inline BlockIndex(a::Block, b::Integer) = BlockIndex(a, (b,)) +@inline BlockIndex(a::Block, b) = BlockIndex(a, (b,)) -@inline function BlockIndex(I::Tuple{Vararg{Integer,N}}, α::Tuple{Vararg{Integer,M}}) where {M,N} +@inline function BlockIndex(I::Tuple{Vararg{Any,N}}, α::Tuple{Vararg{Any,M}}) where {M,N} M <= N || throw(ArgumentError("number of indices must not exceed the number of blocks")) α2 = ntuple(k -> k <= M ? α[k] : 1, N) BlockIndex(I, α2) @@ -207,19 +207,71 @@ end checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{<:BlockIndex{N}}) where N = all(i -> checkbounds(Bool, A, i), I) -struct BlockIndexRange{N,R<:Tuple{Vararg{AbstractUnitRange{<:Integer},N}},I<:Tuple{Vararg{Integer,N}},BI<:Integer} <: AbstractArray{BlockIndex{N,NTuple{N,BI},I},N} +struct BlockIndices{N,R<:Tuple{Vararg{AbstractVector,N}},I<:Tuple{Vararg{Any,N}},BI} <: AbstractArray{BlockIndex{N,NTuple{N,BI},I},N} block::Block{N,BI} indices::R - function BlockIndexRange(block::Block{N,BI}, inds::R) where {N,BI<:Integer,R<:Tuple{Vararg{AbstractUnitRange{<:Integer},N}}} + function BlockIndices(block::Block{N,BI}, inds::R) where {N,BI<:Integer,R<:Tuple{Vararg{AbstractVector,N}}} I = Tuple{eltype.(inds)...} return new{N,R,I,BI}(block,inds) end end +""" + BlockIndices(block, startind:stopind) + +Represents a cartesian product of indices inside a block. + +It can be constructed and used to index into `BlockArrays` in the following manner: + +```jldoctest +julia> BlockIndices(Block(1,2), ([1,3],[2,4])) +Block(1, 2)[[1, 3], [2, 4]] + +julia> Block(1)[[1,3]] == BlockIndices(Block(1), [1,3]) +true + +julia> Block(1,2)[[1,3],[2,4]] == BlockIndices(Block(1,2), ([1,3],[2,4])) +true + +julia> BlockIndices((Block(1)[[1,3]], Block(2)[[2,4]])) +Block(1, 2)[[1, 3], [2, 4]] + +julia> arr = Array(reshape(1:25, (5,5))); + +julia> a = BlockedArray(arr, [3,2], [1,4]) +2×2-blocked 5×5 BlockedMatrix{Int64}: + 1 │ 6 11 16 21 + 2 │ 7 12 17 22 + 3 │ 8 13 18 23 + ───┼──────────────── + 4 │ 9 14 19 24 + 5 │ 10 15 20 25 + +julia> a[Block(1,2)[[1,3],[2,4]]] +2×2 Matrix{Int64}: + 11 21 + 13 23 + +julia> a[Block(2,2)[[2],[2,4]]] +1×2 Matrix{Int64}: + 15 25 +``` +""" +BlockIndices + +BlockIndices(block::Block{N}, inds::Vararg{AbstractVector,N}) where {N} = + BlockIndices(block,inds) +function BlockIndices(inds::Tuple{BlockIndices{1},Vararg{BlockIndices{1}}}) + BlockIndices(Block(block.(inds)), map(ind -> ind.indices[1], inds)) +end + +const BlockIndexRange{N,R<:Tuple{Vararg{AbstractUnitRange{<:Integer},N}},I<:Tuple{Vararg{Any,N}},BI} = BlockIndices{N,R,I,BI} + """ BlockIndexRange(block, startind:stopind) -Represents a cartesian range inside a block. +Represents a cartesian range inside a block. Type alias for `BlockIndices` with +the indices constrained to ranges. It can be constructed and used to index into `BlockArrays` in the following manner: @@ -260,60 +312,60 @@ julia> a[Block(2,2)[1:2,3:4]] """ BlockIndexRange +BlockIndexRange(block::Block{N}, inds::Tuple{Vararg{AbstractUnitRange{<:Integer},N}}) where {N} = + BlockIndices(block, inds) BlockIndexRange(block::Block{N}, inds::Vararg{AbstractUnitRange{<:Integer},N}) where {N} = - BlockIndexRange(block,inds) - + BlockIndices(block, inds) function BlockIndexRange(inds::Tuple{BlockIndexRange{1},Vararg{BlockIndexRange{1}}}) BlockIndexRange(Block(block.(inds)), map(ind -> ind.indices[1], inds)) end -block(R::BlockIndexRange) = R.block +block(R::BlockIndices) = R.block -copy(R::BlockIndexRange) = BlockIndexRange(R.block, map(copy, R.indices)) +copy(R::BlockIndices) = BlockIndices(R.block, map(copy, R.indices)) getindex(::Block{0}) = BlockIndex() -getindex(B::Block{N}, inds::Vararg{Integer,N}) where N = BlockIndex(B,inds) -getindex(B::Block{N}, inds::Vararg{AbstractUnitRange{<:Integer},N}) where N = BlockIndexRange(B,inds) +getindex(B::Block{N}, inds::Vararg{Any,N}) where N = BlockIndex(B,inds) +getindex(B::Block{N}, inds::Vararg{AbstractVector,N}) where N = BlockIndices(B,inds) getindex(B::Block{1}, inds::Colon) = B getindex(B::Block{1}, inds::Base.Slice) = B +getindex(B::BlockIndices{0}) = B.block[] +@propagate_inbounds getindex(B::BlockIndices{N}, kr::Vararg{AbstractVector,N}) where N = BlockIndices(B.block, map(getindex, B.indices, kr)) +@propagate_inbounds getindex(B::BlockIndices{N}, inds::Vararg{Int,N}) where N = B.block[Base.reindex(B.indices, inds)...] -getindex(B::BlockIndexRange{0}) = B.block[] -@propagate_inbounds getindex(B::BlockIndexRange{N}, kr::Vararg{AbstractUnitRange{<:Integer},N}) where {N} = BlockIndexRange(B.block, map(getindex, B.indices, kr)) -@propagate_inbounds getindex(B::BlockIndexRange{N}, inds::Vararg{Int,N}) where N = B.block[Base.reindex(B.indices, inds)...] - -eltype(R::BlockIndexRange) = eltype(typeof(R)) -eltype(::Type{BlockIndexRange{N}}) where {N} = BlockIndex{N} -eltype(::Type{BlockIndexRange{N,R,I,BI}}) where {N,R,I,BI} = BlockIndex{N,NTuple{N,BI},I} -IteratorSize(::Type{<:BlockIndexRange}) = Base.HasShape{1}() +eltype(R::BlockIndices) = eltype(typeof(R)) +eltype(::Type{BlockIndices{N}}) where {N} = BlockIndex{N} +eltype(::Type{BlockIndices{N,R,I,BI}}) where {N,R,I,BI} = BlockIndex{N,NTuple{N,BI},I} +IteratorSize(::Type{<:BlockIndices}) = Base.HasShape{1}() -first(iter::BlockIndexRange) = BlockIndex(iter.block.n, map(first, iter.indices)) -last(iter::BlockIndexRange) = BlockIndex(iter.block.n, map(last, iter.indices)) +first(iter::BlockIndices) = BlockIndex(iter.block.n, map(first, iter.indices)) +last(iter::BlockIndices) = BlockIndex(iter.block.n, map(last, iter.indices)) -@inline function iterate(iter::BlockIndexRange) +@inline function iterate(iter::BlockIndices) iterfirst, iterlast = first(iter), last(iter) if any(map(>, iterfirst.α, iterlast.α)) return nothing end iterfirst, iterfirst end -@inline function iterate(iter::BlockIndexRange, state) +@inline function iterate(iter::BlockIndices, state) nextstate = BlockIndex(state.I, inc(state.α, first(iter).α, last(iter).α)) nextstate.α[end] > last(iter.indices[end]) && return nothing nextstate, nextstate end -size(iter::BlockIndexRange) = map(dimlength, first(iter).α, last(iter).α) -length(iter::BlockIndexRange) = prod(size(iter)) +size(iter::BlockIndices) = map(dimlength, first(iter).α, last(iter).α) +length(iter::BlockIndices) = prod(size(iter)) -Block(bs::BlockIndexRange) = bs.block +Block(bs::BlockIndices) = bs.block ## # checkindex ## -function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::BlockIndexRange{N}) where N +function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::BlockIndices{N}) where N bl = block(I) checkbounds(Bool, A, bl) || return false # TODO: Replace with `eachblockaxes(A)[bl]` once that is defined. @@ -334,10 +386,11 @@ end """ BlockSlice(block, indices) -Represent an AbstractUnitRange{<:Integer} of indices that attaches a block. +Represents an AbstractUnitRange{<:Integer} of indices attached to a block, +a subblock, or a range of blocks. Upon calling `to_indices()`, Blocks are converted to BlockSlice objects to represent -the indices over which the Block spans. +the indices over which the block, subblock, or range of blocks spans. This mimics the relationship between `Colon` and `Base.Slice`. """ @@ -347,6 +400,7 @@ struct BlockSlice{BB,T<:Integer,INDS<:AbstractUnitRange{T}} <: AbstractUnitRange end Block(bs::BlockSlice{<:Block}) = bs.block +Block(bs::BlockSlice{<:BlockIndices}) = Block(bs.block) for f in (:axes, :unsafe_indices, :axes1, :first, :last, :size, :length, @@ -366,36 +420,46 @@ _indices(B) = B # Avoid creating a SubArray wrapper in certain non-allocating cases @propagate_inbounds view(C::CartesianIndices{N}, bs::Vararg{BlockSlice,N}) where {N} = view(C, map(x->x.indices, bs)...) -Block(bs::BlockSlice{<:BlockIndexRange}) = Block(bs.block) - """ - BlockedSlice(blocks, indices) + NoncontiguousBlockSlice(blocks, indices) -Represents blocked indices attached to a collection of corresponding blocks. +Represents an AbstractVector of indices attached to a (potentially non-contiguous) subblock, +set of blocks, or set of subblocks. This is the generalization of `BlockSlice` to +non-contiguous slices. -Upon calling `to_indices()`, a collection of blocks are converted to BlockedSlice objects to represent +Upon calling `to_indices()`, a collection of blocks are converted to NoncontiguousBlockSlice objects to represent the indices over which the blocks span. This mimics the relationship between `Colon` and `Base.Slice`, `Block` and `BlockSlice`, etc. """ -struct BlockedSlice{BB,T<:Integer,INDS<:AbstractVector{T}} <: AbstractVector{T} - blocks::BB +struct NoncontiguousBlockSlice{BB,T,INDS<:AbstractVector{T}} <: AbstractVector{T} + block::BB indices::INDS end -for f in (:axes, :size) - @eval $f(S::BlockedSlice) = $f(S.indices) +Block(bs::NoncontiguousBlockSlice{<:Block}) = bs.block +Block(bs::NoncontiguousBlockSlice{<:BlockIndices}) = Block(bs.block) + +for f in (:axes, :unsafe_indices, :axes1, :first, :last, :size, :length, + :unsafe_length, :start) + @eval $f(S::NoncontiguousBlockSlice) = $f(S.indices) end -@propagate_inbounds getindex(S::BlockedSlice, i::Integer) = getindex(S.indices, i) -@propagate_inbounds getindex(S::BlockedSlice, k::Block{1}) = BlockSlice(S.blocks[Int(k)], getindex(S.indices, k)) +_indices(B::NoncontiguousBlockSlice) = B.indices + +@propagate_inbounds getindex(S::NoncontiguousBlockSlice, i::Integer) = getindex(S.indices, i) +@propagate_inbounds getindex(S::NoncontiguousBlockSlice{<:Block{1}}, k::AbstractVector{<:Integer}) = + NoncontiguousBlockSlice(S.block[_indices(k)], S.indices[_indices(k)]) +@propagate_inbounds getindex(S::NoncontiguousBlockSlice{<:BlockIndices{1,<:Tuple{AbstractVector}}}, k::AbstractVector{<:Integer}) = + NoncontiguousBlockSlice(S.block[_indices(k)], S.indices[_indices(k)]) +@propagate_inbounds getindex(S::NoncontiguousBlockSlice{<:AbstractVector{<:Block{1}}}, k::Block{1}) = + BlockSlice(S.block[Int(k)], getindex(S.indices, k)) struct BlockRange{N,R<:NTuple{N,AbstractUnitRange{<:Integer}}} <: AbstractArray{Block{N,Int},N} indices::R BlockRange{N,R}(inds::R) where {N,R} = new{N,R}(inds) end - # The following is adapted from Julia v0.7 base/multidimensional.jl # definition of CartesianRange diff --git a/src/show.jl b/src/show.jl index 1a66ef88..2544e94d 100644 --- a/src/show.jl +++ b/src/show.jl @@ -186,13 +186,13 @@ function Base.show(io::IO, B::BlockIndex) print(io, "]") end -function Base.show(io::IO, B::BlockIndexRange) +function Base.show(io::IO, B::BlockIndices) show(io, Block(B)) print(io, "[") print_tuple_elements(io, B.indices) print(io, "]") end -Base.show(io::IO, ::MIME"text/plain", B::BlockIndexRange) = show(io, B) +Base.show(io::IO, ::MIME"text/plain", B::BlockIndices) = show(io, B) Base.show(io::IO, mimetype::MIME"text/plain", a::AbstractBlockedUnitRange) = Base.invoke(show, Tuple{typeof(io),MIME"text/plain",AbstractArray},io, mimetype, a) diff --git a/src/views.jl b/src/views.jl index 5b0dbb80..5524e090 100644 --- a/src/views.jl +++ b/src/views.jl @@ -11,7 +11,7 @@ function unblock(A, inds, I) end _blockslice(B, a::AbstractUnitRange) = BlockSlice(B, a) -_blockslice(B, a) = BlockedSlice(B, a) +_blockslice(B, a) = NoncontiguousBlockSlice(B, a) # Allow `ones(2)[Block(1)[1:1], Block(1)[1:1]]` which is # similar to `ones(2)[1:1, 1:1]`. @@ -20,7 +20,7 @@ unblock(A, ::Tuple{}, I) = BlockSlice(first(I),Base.OneTo(length(I[1]))) to_index(::Block) = throw(ArgumentError("Block must be converted by to_indices(...)")) to_index(::BlockIndex) = throw(ArgumentError("BlockIndex must be converted by to_indices(...)")) -to_index(::BlockIndexRange) = throw(ArgumentError("BlockIndexRange must be converted by to_indices(...)")) +to_index(::BlockIndices) = throw(ArgumentError("BlockIndices must be converted by to_indices(...)")) to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to_indices(...)")) @@ -36,15 +36,17 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) @inline to_indices(A, inds, I::Tuple{BlockIndex{1}, Vararg{Any}}) = (inds[1][I[1]], to_indices(A, _maybetail(inds), tail(I))...) -@inline to_indices(A, inds, I::Tuple{BlockIndexRange{1}, Vararg{Any}}) = +@inline to_indices(A, inds, I::Tuple{BlockIndices{1,R}, Vararg{Any}}) where R = + (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) +@inline to_indices(A, inds, I::Tuple{AbstractVector{Block{1,R}}, Vararg{Any}}) where R = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) @inline to_indices(A, inds, I::Tuple{AbstractVector{<:BlockIndex{1}}, Vararg{Any}}) = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) -@inline to_indices(A, inds, I::Tuple{AbstractVector{<:BlockIndexRange{1}}, Vararg{Any}}) = +@inline to_indices(A, inds, I::Tuple{AbstractVector{<:BlockIndices{1}}, Vararg{Any}}) = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) @inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndex{1}}}, Vararg{Any}}) = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) -@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndexRange{1}}}, Vararg{Any}}) = +@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndices{1}}}, Vararg{Any}}) = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) @inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}, Vararg{Any}}) = (unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...) @@ -58,11 +60,11 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to to_indices(A, inds, (BlockRange.(tuple.(I[1].indices))..., tail(I)...)) @inline to_indices(A, inds, I::Tuple{BlockIndex, Vararg{Any}}) = to_indices(A, inds, (BlockIndex.(I[1].I, I[1].α)..., tail(I)...)) -@inline to_indices(A, inds, I::Tuple{BlockIndexRange, Vararg{Any}}) = - to_indices(A, inds, (BlockIndexRange.(Block.(I[1].block.n), tuple.(I[1].indices))..., tail(I)...)) +@inline to_indices(A, inds, I::Tuple{BlockIndices, Vararg{Any}}) = + to_indices(A, inds, (BlockIndices.(Block.(I[1].block.n), tuple.(I[1].indices))..., tail(I)...)) # In 0.7, we need to override to_indices to avoid calling linearindices -@inline to_indices(A, I::Tuple{BlockIndexRange, Vararg{Any}}) = to_indices(A, axes(A), I) +@inline to_indices(A, I::Tuple{BlockIndices, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{BlockIndex, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{Block, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{BlockRange, Vararg{Any}}) = to_indices(A, axes(A), I) @@ -70,9 +72,9 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to @inline to_indices(A, I::Tuple{AbstractVector{<:BlockRange{1}}, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:Block{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{AbstractVector{<:BlockIndex{1}}, Vararg{Any}}) = to_indices(A, axes(A), I) -@inline to_indices(A, I::Tuple{AbstractVector{<:BlockIndexRange{1}}, Vararg{Any}}) = to_indices(A, axes(A), I) +@inline to_indices(A, I::Tuple{AbstractVector{<:BlockIndices{1}}, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndex{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I) -@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndexRange{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I) +@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndices{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I) @inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}, Vararg{Any}}) = to_indices(A, axes(A), I) ## BlockedLogicalIndex @@ -119,8 +121,8 @@ checkindex(::Type{Bool}, inds::AbstractUnitRange, i::BlockedLogicalIndex) = chec (blockcollect(I[1]), ensure_indexable(tail(I))...) @propagate_inbounds reindex(idxs::Tuple{BlockSlice{<:BlockRange}, Vararg{Any}}, - subidxs::Tuple{BlockSlice{<:BlockIndexRange}, Vararg{Any}}) = - (BlockSlice(BlockIndexRange(Block(idxs[1].block.indices[1][Int(subidxs[1].block.block)]), + subidxs::Tuple{BlockSlice{<:BlockIndices}, Vararg{Any}}) = + (BlockSlice(BlockIndices(Block(idxs[1].block.indices[1][Int(subidxs[1].block.block)]), subidxs[1].block.indices), idxs[1].indices[subidxs[1].indices]), reindex(tail(idxs), tail(subidxs))...) @@ -143,28 +145,28 @@ _splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...) # path in `AbstractBlockStyle` broadcasting. @propagate_inbounds function Base.unsafe_view( A::BlockArray{<:Any, N}, - I::Vararg{BlockSlice{<:BlockIndexRange{1}}, N}) where {N} + I::Vararg{BlockSlice{<:BlockIndices{1}}, N}) where {N} B = view(A, map(block, I)...) return view(B, _splatmap(x -> x.block.indices, I)...) end @propagate_inbounds function Base.unsafe_view( A::BlockedArray{<:Any, N}, - I::Vararg{BlockSlice{<:BlockIndexRange{1}}, N}) where {N} + I::Vararg{BlockSlice{<:BlockIndices{1}}, N}) where {N} return view(A.blocks, map(x -> x.indices, I)...) end @propagate_inbounds function Base.unsafe_view( A::ReshapedArray{<:Any, N, <:AbstractBlockArray{<:Any, M}}, - I::Vararg{BlockSlice{<:BlockIndexRange{1}}, N}) where {N, M} + I::Vararg{BlockSlice{<:BlockIndices{1}}, N}) where {N, M} # Note: assuming that I[M+1:end] are verified to be singletons return reshape(view(A.parent, I[1:M]...), Val(N)) end @propagate_inbounds function Base.unsafe_view( A::Array, - I1::BlockSlice{<:BlockIndexRange{1}}, - Is::Vararg{BlockSlice{<:BlockIndexRange{1}}}, + I1::BlockSlice{<:BlockIndices{1}}, + Is::Vararg{BlockSlice{<:BlockIndices{1}}}, ) I = (I1, Is...) @assert ndims(A) == length(I) @@ -172,7 +174,7 @@ end end # make sure we reindex correctrly -@inline function Base._maybe_reindex(V, I::Tuple{BlockSlice{<:BlockIndexRange{1}}, Vararg{Any}}, ::Tuple{}) +@inline function Base._maybe_reindex(V, I::Tuple{BlockSlice{<:BlockIndices{1}}, Vararg{Any}}, ::Tuple{}) @inbounds idxs = to_indices(V.parent, reindex(V.indices, I)) view(V.parent, idxs...) end @@ -209,11 +211,11 @@ block(A::Block) = A @inline view(block_arr::AbstractBlockArray{<:Any,N}, blocks::Vararg{BlockSlice1, N}) where N = view(block_arr, map(block,blocks)...) -const BlockSlices = Union{Base.Slice,BlockSlice{<:BlockRange{1}},BlockedSlice} +const BlockSlices = Union{Base.Slice,BlockSlice{<:BlockRange{1}},NoncontiguousBlockSlice{<:AbstractVector{<:Block{1}}}} # view(V::SubArray{<:Any,N,NTuple{N,BlockSlices}}, _block_reindex(b::BlockSlice, i::Block{1}) = b.block[Int(i)] -_block_reindex(b::BlockedSlice, i::Block{1}) = b.blocks[Int(i)] +_block_reindex(b::NoncontiguousBlockSlice, i::Block{1}) = b.block[Int(i)] _block_reindex(b::Slice, i::Block{1}) = i @inline view(V::SubArray{<:Any,N,<:AbstractBlockArray,<:NTuple{N,BlockSlices}}, block::Block{N}) where N = diff --git a/test/test_blockarrays.jl b/test/test_blockarrays.jl index a2a73c5d..0c7d216c 100644 --- a/test/test_blockarrays.jl +++ b/test/test_blockarrays.jl @@ -998,6 +998,22 @@ end @test A[Block(1)[1], Block(1)[1:1]] == BlockArray(A)[Block(1)[1], Block(1)[1:1]] == A[1,1:1] end + @testset "Non-contiguous BlockIndexRange" begin + a = BlockedArray(randn(5), [2,3]) + @test a[Block(2)[[1,3]]] == a[[3,5]] + A = BlockedArray(randn(5,5), [2,3], [2,3]) + @test A[Block(2,2)[[1,3],[2,3]]] == A[[3,5],[4,5]] + @test A[Block(2,2)[[1,3],1:2]] == A[[3,5],3:4] + end + + @testset "Nested block indexing" begin + a = BlockedArray(randn(4), [2,2]) + b = BlockedArray(randn(4), [2,2]) + A = mortar([a,b]) + @test A[Block(2)[Block(1)]] == A[Block(2)][Block(1)] == b[Block(1)] + @test A[Block(2)[Block(1)[2]]] == A[Block(2)][Block(1)[2]] == b[Block(1)[2]] + end + @testset "BlockIndexRange blocks" begin a = mortar([Block(1)[1:2], Block(3)[2:3]]) @test a[Block(1)] === Block(1)[1:2] diff --git a/test/test_blockindices.jl b/test/test_blockindices.jl index d39e0c50..5393c1af 100644 --- a/test/test_blockindices.jl +++ b/test/test_blockindices.jl @@ -2,7 +2,7 @@ module TestBlockIndices using BlockArrays, FillArrays, Test, StaticArrays, ArrayLayouts using OffsetArrays -import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, BlockedSlice +import BlockArrays: BlockIndex, BlockIndexRange, BlockIndices, BlockSlice, NoncontiguousBlockSlice @testset "Blocks" begin @test Int(Block(2)) === Integer(Block(2)) === Number(Block(2)) === 2 @@ -86,11 +86,20 @@ import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, BlockedSlice @testset "BlockIndex" begin @test Block()[] == BlockIndex() @test Block(1)[1] == BlockIndex((1,),(1,)) + @test Block(1)[Block(1)] == BlockIndex((1,),(Block(1),)) @test Block(1)[1:2] == BlockIndexRange(Block(1),(1:2,)) + @test Block(1)[[1,3]] == BlockIndices(Block(1),([1,3],)) @test Block(1,1)[1,1] == BlockIndex((1,1),(1,1)) == BlockIndex((1,1),(1,)) @test Block(1,1)[1:2,1:2] == BlockIndexRange(Block(1,1),(1:2,1:2)) + @test Block(1,1)[[1,3],1:2] == BlockIndices(Block(1,1),([1,3],1:2)) @test BlockIndexRange((Block(1)[1:2],Block(1)[1:2])) == BlockIndexRange(Block(1,1),(1:2,1:2)) + @test BlockIndices((Block(1)[[1,3]],Block(1)[[2,4]])) == BlockIndices(Block(1,1),([1,3],[2,4])) @test Block(1)[1:3][1:2] == BlockIndexRange(Block(1),1:2) + @test Block(1)[[1,3,5]][[1,3]] == BlockIndices(Block(1),[1,5]) + @test Block(1)[[1,3,5]][2:3] == BlockIndices(Block(1),[3,5]) + @test Block(1)[2:4][[1,3]] == BlockIndices(Block(1),[2,4]) + @test Block(1,1)[1:3,1:3][1:2,1:2] == BlockIndexRange(Block(1,1),1:2,1:2) + @test Block(1,1)[1:3,1:3][1:2,[1,3]] == BlockIndices(Block(1,1),1:2,[1,3]) @test Block(1,1)[2:4,2:4][2:3,2:3] == BlockIndexRange(Block(1,1),(3:4,3:4)) @test BlockIndexRange(Block(),())[] == BlockIndex() @test BlockIndex((2,2,2),(2,)) == BlockIndex((2,2,2),(2,1,)) == BlockIndex((2,2,2),(2,1,1)) @@ -98,7 +107,10 @@ import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, BlockedSlice @test BlockIndex(UInt(2),(2,)) === BlockIndex((UInt(2),),(2,)) @test BlockIndex(Block(2),2) === BlockIndex(Block(2),(2,)) @test BlockIndex(Block(2),UInt(2)) === BlockIndex(Block(2),(UInt(2),)) + @test BlockIndex(Block(2),Block(2)) === BlockIndex(Block(2),(Block(2),)) @test copy(Block(1)[1:2]) === BlockIndexRange(Block(1),1:2) + @test copy(Block(1)[[1,3]]) == BlockIndices(Block(1),[1,3]) + @test copy(Block(1)[[1,3]]) ≢ BlockIndices(Block(1),[1,3]) end @testset "BlockRange" begin @@ -615,6 +627,7 @@ end b = blockedrange([1,2,3]) @test b[Block(3)[2]] == b[Block(3)][2] == 5 @test b[Block(3)[2:3]] == b[Block(3)][2:3] == 5:6 + @test b[Block(3)[[3,2]]] == b[Block(3)][[3,2]] == [6,5] end @testset "BlockRange indexing" begin @@ -863,6 +876,10 @@ end @test b[1:2] ≡ b[1:2][1:2] ≡ BlockSlice(Block(5)[1:2],1:2) @test Block(b) ≡ Block(5) + bi = BlockSlice(Block(2)[2:4],3:5) + @test Block(bi) ≡ Block(2) + @test bi[2:3] ≡ BlockSlice(Block(2)[3:4],4:5) + @testset "OneTo converts" begin for b in (BlockSlice(Block(1), 1:1), BlockSlice(Block.(1:1), 1:1), BlockSlice(Block(1)[1:1], 1:1)) @test convert(typeof(b), Base.OneTo(1)) ≡ b @@ -880,14 +897,35 @@ end end end -@testset "BlockedSlice" begin - b = BlockedSlice([Block(2), Block(1)], mortar([3:5, 1:2])) +@testset "NoncontiguousBlockSlice" begin + b = NoncontiguousBlockSlice([Block(2),Block(1)], mortar([3:5,1:2])) @test length(b) == 5 for i in eachindex(b.indices) @test b[i] === b.indices[i] end @test b[Block(1)] === BlockSlice(Block(2), 3:5) @test b[Block(2)] === BlockSlice(Block(1), 1:2) + @test BlockArrays._indices(b) == mortar([3:5,1:2]) + + b = NoncontiguousBlockSlice(Block(3), 2:4) + @test b[2:3] == NoncontiguousBlockSlice(Block(3)[3:4], 3:4) + @test b[[1,3]] == NoncontiguousBlockSlice(Block(3)[[1,3]], [2,4]) + @test Block(b) === Block(3) + @test BlockArrays._indices(b) === 2:4 + + b = NoncontiguousBlockSlice(Block(3)[[2,4,6]], [3,5,7]) + @test b isa NoncontiguousBlockSlice{<:BlockIndices{1}} + @test Block(b) === Block(3) + @test BlockArrays._indices(b) == [3,5,7] + @test b[2:3] == NoncontiguousBlockSlice(Block(3)[[4,6]], [5,7]) + @test b[2:3] isa NoncontiguousBlockSlice{<:BlockIndices{1}} + @test Block(b) === Block(3) + @test Block(b[2:3]) === Block(3) + @test BlockArrays._indices(b[2:3]) == [5,7] + @test b[[1,3]] == NoncontiguousBlockSlice(Block(3)[[2,6]], [3,7]) + @test b[[1,3]] isa NoncontiguousBlockSlice{<:BlockIndices{1}} + @test Block(b[[1,3]]) === Block(3) + @test BlockArrays._indices(b[[1,3]]) == [3,7] end #= diff --git a/test/test_blockrange.jl b/test/test_blockrange.jl index a5a1b179..ffb2ba2b 100644 --- a/test/test_blockrange.jl +++ b/test/test_blockrange.jl @@ -32,7 +32,7 @@ using BlockArrays, Test V = view(A, [Block(3), Block(2)]) @test V == [4, 5, 6, 2, 3] I = parentindices(V)[1] - @test I isa BlockArrays.BlockedSlice{<:Vector{<:Block{1}}} + @test I isa BlockArrays.NoncontiguousBlockSlice{<:Vector{<:Block{1}}} @test V[Block(1)] == 4:6 @test V[Block(2)] == 2:3 @test view(V, Block(1)) === view(A, Block(3)) diff --git a/test/test_blockviews.jl b/test/test_blockviews.jl index 4dc6c710..c014326b 100644 --- a/test/test_blockviews.jl +++ b/test/test_blockviews.jl @@ -2,7 +2,7 @@ module TestBlockViews using BlockArrays, ArrayLayouts, Test using FillArrays -import BlockArrays: BlockedLogicalIndex +import BlockArrays: BlockedLogicalIndex, NoncontiguousBlockSlice import Base: LogicalIndex # useds to force SubArray return @@ -219,6 +219,22 @@ bview(a, b) = Base.invoke(view, Tuple{AbstractArray,Any}, a, b) @test A[Block.(2:3)] == A[2:end] end + @testset "getindex and view with Block-vector" begin + A = BlockArray(reshape(collect(1:(6*12)),6,12), 1:3, 3:5) + V = view(A, [Block(3),Block(2)], [Block(3),Block(2)]) + @test V[Block(1,1)] == A[Block(3,3)] + @test V[Block(2,1)] == A[Block(2,3)] + @test V[Block(1,2)] == A[Block(3,2)] + @test V[Block(2,2)] == A[Block(2,2)] + I = parentindices(V) + @test I[1] isa NoncontiguousBlockSlice{<:Vector{<:Block{1}}} + @test I[2] isa NoncontiguousBlockSlice{<:Vector{<:Block{1}}} + @test view(V, Block(1,1)) === view(A, Block(3,3)) + @test view(V, Block(2,1)) === view(A, Block(2,3)) + @test view(V, Block(1,2)) === view(A, Block(3,2)) + @test view(V, Block(2,2)) === view(A, Block(2,2)) + end + @testset "non-allocation blocksize" begin A = BlockArray(randn(5050), 1:100) @test blocksize(A) == (100,) @@ -316,7 +332,7 @@ bview(a, b) = Base.invoke(view, Tuple{AbstractArray,Any}, a, b) @test a[Block(1)[1:3]] ≡ view(a,Block(1)[1:3]) ≡ view(v,Block(1)[1:3]) ≡ 7:9 end - @testset "blockrange-of-blockreange" begin + @testset "blockrange-of-blockrange" begin a = mortar([7:9,5:6]) v = view(a,Block.(1:2)) @test view(v, Block(1)) ≡ 7:9