Skip to content

Commit b7e2656

Browse files
authored
Merge branch 'master' into generalize_blockindex
2 parents afb3ddd + a4a0b93 commit b7e2656

File tree

10 files changed

+362
-30
lines changed

10 files changed

+362
-30
lines changed

docs/src/lib/public.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ blockfirsts
3737
blocklasts
3838
blocklengths
3939
blocksizes
40+
eachblockaxes
4041
blocks
4142
eachblock
4243
blockcheckbounds

src/BlockArrays.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using LinearAlgebra, ArrayLayouts, FillArrays
55
export AbstractBlockArray, AbstractBlockMatrix, AbstractBlockVector, AbstractBlockVecOrMat
66
export Block, getblock, getblock!, setblock!, eachblock, blocks
77
export blockaxes, blocksize, blocklength, blockcheckbounds, BlockBoundsError, BlockIndex, BlockIndexRange, BlockIndices
8-
export blocksizes, blocklengths, blocklasts, blockfirsts, blockisequal
8+
export blocksizes, blocklengths, eachblockaxes, blocklasts, blockfirsts, blockisequal
99
export BlockRange, blockedrange, BlockedUnitRange, BlockedOneTo
1010

1111
export BlockArray, BlockMatrix, BlockVector, BlockVecOrMat, mortar
@@ -20,16 +20,16 @@ export blockappend!, blockpush!, blockpushfirst!, blockpop!, blockpopfirst!
2020
import Base: @propagate_inbounds, Array, AbstractArray, to_indices, to_index,
2121
unsafe_indices, first, last, size, length, unsafe_length,
2222
unsafe_convert,
23-
getindex, setindex!, ndims, show, view,
23+
getindex, setindex!, ndims, show, print_array, view,
2424
step,
25-
broadcast, eltype, convert, similar,
25+
broadcast, eltype, convert, similar, collect,
2626
tail, reindex,
2727
RangeIndex, Int, Integer, Number, Tuple,
2828
+, -, *, /, \, min, max, isless, in, copy, copyto!, axes, @deprecate,
29-
BroadcastStyle, checkbounds,
29+
BroadcastStyle, checkbounds, checkindex, ensure_indexable,
3030
oneunit, ones, zeros, intersect, Slice, resize!
3131

32-
using Base: ReshapedArray, dataids, oneto
32+
using Base: ReshapedArray, LogicalIndex, dataids, oneto
3333

3434
import Base: (:), IteratorSize, iterate, axes1, strides, isempty
3535
import Base.Broadcast: broadcasted, DefaultArrayStyle, AbstractArrayStyle, Broadcasted, broadcastable

src/blockaxis.jl

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ function Base.AbstractUnitRange{T}(r::BlockedUnitRange) where {T}
105105
return _BlockedUnitRange(convert(T,first(r)), convert.(T,blocklasts(r)))
106106
end
107107

108+
# See: https://github.com/JuliaLang/julia/blob/b06d26075bf7b3f4e7f1b64b120f5665d8ed76f9/base/range.jl#L991-L1004
109+
function Base.getindex(r::AbstractUnitRange, s::AbstractBlockedUnitRange{T}) where {T<:Integer}
110+
@boundscheck checkbounds(r, s)
111+
112+
f = first(r)
113+
start = oftype(f, f + first(s) - firstindex(r))
114+
lens = map(Base.Fix1(oftype, f), blocklengths(s))
115+
return blockedrange(start, lens)
116+
end
117+
108118
"""
109119
BlockedOneTo{T, <:Union{AbstractVector{T}, NTuple{<:Any,T}}} where {T}
110120
@@ -164,6 +174,16 @@ function Base.AbstractUnitRange{T}(r::BlockedOneTo) where {T}
164174
return BlockedOneTo(convert.(T,blocklasts(r)))
165175
end
166176

177+
# See: https://github.com/JuliaLang/julia/blob/b06d26075bf7b3f4e7f1b64b120f5665d8ed76f9/base/range.jl#L1006-L1010
178+
function getindex(r::Base.OneTo{T}, s::BlockedOneTo) where T
179+
@inline
180+
@boundscheck checkbounds(r, s)
181+
return BlockedOneTo(convert(AbstractVector{T}, blocklasts(s)))
182+
end
183+
function getindex(r::BlockedOneTo{T}, s::BlockedOneTo) where T
184+
return Base.oneto(r)[s]
185+
end
186+
167187
"""
168188
blockedrange(blocklengths::Union{Tuple, AbstractVector})
169189
blockedrange(first::Integer, blocklengths::Union{Tuple, AbstractVector})
@@ -406,9 +426,13 @@ end
406426
@propagate_inbounds getindex(b::AbstractBlockedUnitRange, KR::BlockSlice) = b[KR.block]
407427

408428
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:Block{1}}) = mortar([b[K] for K in KR])
409-
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndices{1}}) = mortar([b[K] for K in KR])
410-
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndex{1}}) = [b[K] for K in KR]
429+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:Block{1}}}) = mortar([b[K] for K in KR])
411430
getindex(b::AbstractBlockedUnitRange, Kkr::BlockIndices{1}) = b[block(Kkr)][Kkr.indices...]
431+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndex{1}}) = [b[K] for K in KR]
432+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:BlockIndices{1}}) = mortar([b[K] for K in KR])
433+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:BlockIndex{1}}}) = mortar([b[K] for K in KR])
434+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:BlockIndices{1}}}) = mortar([b[K] for K in KR])
435+
getindex(b::AbstractBlockedUnitRange, KR::AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}) = mortar([b[K] for K in KR])
412436

413437
_searchsortedfirst(a::AbstractVector, k) = searchsortedfirst(a, k)
414438
function _searchsortedfirst(a::Tuple, k)

src/blockedarray.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ AbstractArray{T,N}(A::BlockedArray) where {T,N} = BlockedArray(AbstractArray{T,N
193193

194194
copy(A::BlockedArray) = BlockedArray(copy(A.blocks), A.axes)
195195

196+
# Blocked version of `collect(::AbstractArray)` that preserves the
197+
# block structure.
198+
blockcollect(a::AbstractArray) = BlockedArray(collect(a), axes(a))
199+
196200
Base.dataids(A::BlockedArray) = Base.dataids(A.blocks)
197201

198202
###########################

src/blocks.jl

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ This is broken for now. See: https://github.com/JuliaArrays/BlockArrays.jl/issue
9393
a
9494
end
9595

96+
# AbstractArray version of `Iterators.product`.
97+
# https://en.wikipedia.org/wiki/Cartesian_product
98+
# https://github.com/lazyLibraries/ProductArrays.jl
99+
# https://github.com/JuliaData/SplitApplyCombine.jl#productviewf-a-b
100+
# https://github.com/JuliaArrays/MappedArrays.jl/pull/42
101+
struct ProductArray{T,N,V<:Tuple{Vararg{AbstractVector,N}}} <: AbstractArray{T,N}
102+
vectors::V
103+
end
104+
ProductArray(vectors::Vararg{AbstractVector,N}) where {N} =
105+
ProductArray{Tuple{map(eltype, vectors)...},N,typeof(vectors)}(vectors)
106+
Base.size(p::ProductArray) = map(length, p.vectors)
107+
Base.axes(p::ProductArray) = map(Base.axes1, p.vectors)
108+
@propagate_inbounds getindex(p::ProductArray{T,N}, I::Vararg{Int,N}) where {T,N} =
109+
map((v, i) -> v[i], p.vectors, I)
110+
96111
"""
97112
blocksizes(A::AbstractArray)
98113
blocksizes(A::AbstractArray, d::Integer)
@@ -110,7 +125,7 @@ julia> A = BlockArray(ones(3,3),[2,1],[1,1,1])
110125
1.0 │ 1.0 │ 1.0
111126
112127
julia> blocksizes(A)
113-
2×3 BlockArrays.BlockSizes{Tuple{Int64, Int64}, 2, BlockMatrix{Float64, Matrix{Matrix{Float64}}, Tuple{BlockedOneTo{Int64, Vector{Int64}}, BlockedOneTo{Int64, Vector{Int64}}}}}:
128+
2×3 BlockArrays.ProductArray{Tuple{Int64, Int64}, 2, Tuple{Vector{Int64}, Vector{Int64}}}:
114129
(2, 1) (2, 1) (2, 1)
115130
(1, 1) (1, 1) (1, 1)
116131
@@ -124,16 +139,80 @@ julia> blocksizes(A,2)
124139
1
125140
```
126141
"""
127-
blocksizes(A::AbstractArray) = BlockSizes(A)
142+
blocksizes(A::AbstractArray) = ProductArray(map(blocklengths, axes(A))...)
128143
@inline blocksizes(A::AbstractArray, d::Integer) = blocklengths(axes(A, d))
129144

130-
struct BlockSizes{T,N,A<:AbstractArray{<:Any,N}} <: AbstractArray{T,N}
145+
"""
146+
blocklengths(A::AbstractArray)
147+
148+
Return an iterator over the lengths of each block.
149+
See also blocksizes.
150+
151+
# Examples
152+
```jldoctest
153+
julia> A = BlockArray(ones(3,3),[2,1],[1,1,1])
154+
2×3-blocked 3×3 BlockMatrix{Float64}:
155+
1.0 │ 1.0 │ 1.0
156+
1.0 │ 1.0 │ 1.0
157+
─────┼───────┼─────
158+
1.0 │ 1.0 │ 1.0
159+
160+
julia> blocklengths(A)
161+
2×3 BlockArrays.BlockLengths{Int64, 2, BlockMatrix{Float64, Matrix{Matrix{Float64}}, Tuple{BlockedOneTo{Int64, Vector{Int64}}, BlockedOneTo{Int64, Vector{Int64}}}}}:
162+
2 2 2
163+
1 1 1
164+
165+
julia> blocklengths(A)[1,2]
166+
2
167+
```
168+
"""
169+
blocklengths(A::AbstractArray) = BlockLengths(A)
170+
blocklengths(A::AbstractVector) = map(length, blocks(A))
171+
172+
struct BlockLengths{T,N,A<:AbstractArray{<:Any,N}} <: AbstractArray{T,N}
131173
array::A
132174
end
133-
BlockSizes(a::AbstractArray{<:Any,N}) where {N} =
134-
BlockSizes{Tuple{eltype.(axes(a))...},N,typeof(a)}(a)
175+
BlockLengths(a::AbstractArray{<:Any,N}) where {N} =
176+
BlockLengths{typeof(length(a)),N,typeof(a)}(a)
135177

136-
size(bs::BlockSizes) = blocksize(bs.array)
137-
axes(bs::BlockSizes) = map(br -> Int.(br), blockaxes(bs.array))
138-
@propagate_inbounds getindex(a::BlockSizes{T,N}, i::Vararg{Int,N}) where {T,N} =
139-
size(view(a.array, Block.(i)...))
178+
size(bs::BlockLengths) = blocksize(bs.array)
179+
axes(bs::BlockLengths) = map(br -> Int.(br), blockaxes(bs.array))
180+
@propagate_inbounds getindex(a::BlockLengths{T,N}, i::Vararg{Int,N}) where {T,N} =
181+
length(view(a.array, Block.(i)...))
182+
183+
"""
184+
eachblockaxes(A::AbstractArray)
185+
eachblockaxes(A::AbstractArray, d::Integer)
186+
187+
Return an iterator over the axes of each block.
188+
See also blocksizes and blocklengths.
189+
190+
# Examples
191+
```jldoctest
192+
julia> A = BlockArray(ones(3,3),[2,1],[1,1,1])
193+
2×3-blocked 3×3 BlockMatrix{Float64}:
194+
1.0 │ 1.0 │ 1.0
195+
1.0 │ 1.0 │ 1.0
196+
─────┼───────┼─────
197+
1.0 │ 1.0 │ 1.0
198+
199+
julia> eachblockaxes(A)
200+
2×3 BlockArrays.ProductArray{Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}, 2, Tuple{Vector{Base.OneTo{Int64}}, Vector{Base.OneTo{Int64}}}}:
201+
(Base.OneTo(2), Base.OneTo(1)) … (Base.OneTo(2), Base.OneTo(1))
202+
(Base.OneTo(1), Base.OneTo(1)) (Base.OneTo(1), Base.OneTo(1))
203+
204+
julia> eachblockaxes(A)[1,2]
205+
(Base.OneTo(2), Base.OneTo(1))
206+
207+
julia> eachblockaxes(A,2)
208+
3-element Vector{Base.OneTo{Int64}}:
209+
Base.OneTo(1)
210+
Base.OneTo(1)
211+
Base.OneTo(1)
212+
```
213+
"""
214+
eachblockaxes(A::AbstractArray) =
215+
ProductArray(map(ax -> map(Base.axes1, blocks(ax)), axes(A))...)
216+
eachblockaxes(A::AbstractVector) = map(axes, blocks(Base.axes1(A)))
217+
eachblockaxes(A::AbstractArray, d::Integer) = map(Base.axes1, blocks(axes(A, d)))
218+
eachblockaxes1(A::AbstractArray) = map(Base.axes1, blocks(Base.axes1(A)))

src/views.jl

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to
2626

2727
@inline to_indices(A, inds, I::Tuple{Block{1}, Vararg{Any}}) =
2828
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
29-
@inline to_indices(A, inds, I::Tuple{BlockRange{1,R}, Vararg{Any}}) where R =
29+
@inline to_indices(A, inds, I::Tuple{BlockRange{1}, Vararg{Any}}) =
30+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
31+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:Block{1}}, Vararg{Any}}) =
32+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
33+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:BlockRange{1}}, Vararg{Any}}) =
34+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
35+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:Block{1}}}, Vararg{Any}}) =
3036
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
3137
@inline to_indices(A, inds, I::Tuple{BlockIndex{1}, Vararg{Any}}) =
3238
(inds[1][I[1]], to_indices(A, _maybetail(inds), tail(I))...)
@@ -38,12 +44,20 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to
3844
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
3945
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:BlockIndices{1}}, Vararg{Any}}) =
4046
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
47+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndex{1}}}, Vararg{Any}}) =
48+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
49+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndices{1}}}, Vararg{Any}}) =
50+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
51+
@inline to_indices(A, inds, I::Tuple{AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}, Vararg{Any}}) =
52+
(unblock(A, inds, I), to_indices(A, _maybetail(inds), tail(I))...)
4153

4254

4355
# splat out higher dimensional blocks
4456
# this mimics view of a CartesianIndex
4557
@inline to_indices(A, inds, I::Tuple{Block, Vararg{Any}}) =
4658
to_indices(A, inds, (Block.(I[1].n)..., tail(I)...))
59+
@inline to_indices(A, inds, I::Tuple{BlockRange, Vararg{Any}}) =
60+
to_indices(A, inds, (BlockRange.(tuple.(I[1].indices))..., tail(I)...))
4761
@inline to_indices(A, inds, I::Tuple{BlockIndex, Vararg{Any}}) =
4862
to_indices(A, inds, (BlockIndex.(I[1].I, I[1].α)..., tail(I)...))
4963
@inline to_indices(A, inds, I::Tuple{BlockIndices, Vararg{Any}}) =
@@ -57,8 +71,57 @@ to_index(::BlockRange) = throw(ArgumentError("BlockRange must be converted by to
5771
@inline to_indices(A, I::Tuple{Block, Vararg{Any}}) = to_indices(A, axes(A), I)
5872
@inline to_indices(A, I::Tuple{BlockRange, Vararg{Any}}) = to_indices(A, axes(A), I)
5973
@inline to_indices(A, I::Tuple{AbstractVector{<:Block{1}}, Vararg{Any}}) = to_indices(A, axes(A), I)
74+
@inline to_indices(A, I::Tuple{AbstractVector{<:BlockRange{1}}, Vararg{Any}}) = to_indices(A, axes(A), I)
75+
@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:Block{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I)
6076
@inline to_indices(A, I::Tuple{AbstractVector{<:BlockIndex{1}}, Vararg{Any}}) = to_indices(A, axes(A), I)
6177
@inline to_indices(A, I::Tuple{AbstractVector{<:BlockIndices{1}}, Vararg{Any}}) = to_indices(A, axes(A), I)
78+
@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndex{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I)
79+
@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:BlockIndices{1}}}, Vararg{Any}}) = to_indices(A, axes(A), I)
80+
@inline to_indices(A, I::Tuple{AbstractVector{<:AbstractVector{<:AbstractVector{<:BlockIndex{1}}}}, Vararg{Any}}) = to_indices(A, axes(A), I)
81+
82+
## BlockedLogicalIndex
83+
# Blocked version of `LogicalIndex`:
84+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L819-L831
85+
const BlockedLogicalIndex{T,R<:LogicalIndex{T},BS<:Tuple{AbstractUnitRange{<:Integer}}} = BlockedVector{T,R,BS}
86+
function BlockedLogicalIndex(I::AbstractVector{Bool})
87+
blocklengths = map(b -> count(view(I, b)), BlockRange(I))
88+
return BlockedVector(LogicalIndex(I), blocklengths)
89+
end
90+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L838-L839
91+
show(io::IO, r::BlockedLogicalIndex) = print(io, blockcollect(r))
92+
print_array(io::IO, X::BlockedLogicalIndex) = print_array(io, blockcollect(X))
93+
94+
# Blocked version of `to_index(::AbstractArray{Bool})`:
95+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/indices.jl#L309
96+
function to_index(I::AbstractBlockVector{Bool})
97+
return BlockedLogicalIndex(I)
98+
end
99+
100+
# Blocked version of `collect(::LogicalIndex)`:
101+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L837
102+
# Without this definition, `collect` will try to call `getindex` on the `LogicalIndex`
103+
# which isn't defined.
104+
collect(I::BlockedLogicalIndex) = collect(I.blocks)
105+
106+
# Iteration of BlockedLogicalIndex is just iteration over the underlying
107+
# LogicalIndex, which is implemented here:
108+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L840-L890
109+
@inline iterate(I::BlockedLogicalIndex) = iterate(I.blocks)
110+
@inline iterate(I::BlockedLogicalIndex, s) = iterate(I.blocks, s)
111+
112+
## Boundscheck for BlockLogicalindex
113+
# Like for LogicalIndex, map all calls to mask:
114+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L892-L897
115+
checkbounds(::Type{Bool}, A::AbstractArray, i::BlockedLogicalIndex) = checkbounds(Bool, A, i.blocks.mask)
116+
# `checkbounds_indices` has been handled via `I::AbstractArray` fallback
117+
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::BlockedLogicalIndex) = checkindex(Bool, inds, i.blocks.mask)
118+
checkindex(::Type{Bool}, inds::Tuple, i::BlockedLogicalIndex) = checkindex(Bool, inds, i.blocks.mask)
119+
120+
# Instantiate the BlockedLogicalIndex when constructing a SubArray, similar to
121+
# `ensure_indexable(I::Tuple{LogicalIndex,Vararg{Any}})`:
122+
# https://github.com/JuliaLang/julia/blob/3e2f90fbb8f6b0651f2601d7599c55d4e3efd496/base/multidimensional.jl#L918
123+
@inline ensure_indexable(I::Tuple{BlockedLogicalIndex,Vararg{Any}}) =
124+
(blockcollect(I[1]), ensure_indexable(tail(I))...)
62125

63126
@propagate_inbounds reindex(idxs::Tuple{BlockSlice{<:BlockRange}, Vararg{Any}},
64127
subidxs::Tuple{BlockSlice{<:BlockIndices}, Vararg{Any}}) =

test/test_blockarrays.jl

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module TestBlockArrays
22

33
using SparseArrays, BlockArrays, FillArrays, LinearAlgebra, Test, OffsetArrays, Images
4-
import BlockArrays: _BlockArray
4+
import BlockArrays: _BlockArray, blockcollect
55

66
const Fill = FillArrays.Fill
77

@@ -255,6 +255,32 @@ end
255255
@test zero(b) isa typeof(b)
256256
end
257257

258+
@testset "blockcollect" begin
259+
a = randn(6, 6)
260+
@test blockcollect(a) == a
261+
@test blockcollect(a) a
262+
@test blockcollect(a).blocks a
263+
# TODO: Maybe special case this to call `collect` and return a `Matrix`?
264+
@test blockcollect(a) isa BlockedMatrix{Float64,Matrix{Float64}}
265+
@test blockisequal(axes(blockcollect(a)), axes(a))
266+
@test blocksize(blockcollect(a)) == (1, 1)
267+
268+
b = BlockedArray(randn(6, 6), [3, 3], [3, 3])
269+
@test blockcollect(b) == b
270+
@test blockcollect(b) b
271+
@test blockcollect(b).blocks b
272+
@test blockcollect(b) isa BlockedMatrix{Float64,Matrix{Float64}}
273+
@test blockisequal(axes(blockcollect(b)), axes(b))
274+
@test blocksize(blockcollect(b)) == (2, 2)
275+
276+
c = BlockArray(randn(6, 6), [3, 3], [3, 3])
277+
@test blockcollect(c) == c
278+
@test blockcollect(c) c
279+
@test blockcollect(c) isa BlockedMatrix{Float64,Matrix{Float64}}
280+
@test blockisequal(axes(blockcollect(c)), axes(c))
281+
@test blocksize(blockcollect(c)) == (2, 2)
282+
end
283+
258284
@test_throws DimensionMismatch BlockArray([1,2,3],[1,1])
259285

260286
@testset "mortar" begin
@@ -1085,6 +1111,21 @@ end
10851111
@test a[[Block(1)[1:2], Block(2)[1:2]], [Block(1)[1:2], Block(2)[1:2]]] == [a[Block(1,1)[1:2,1:2]] a[Block(1,2)[1:2,1:2]]; a[Block(2,1)[1:2,1:2]] a[Block(2,2)[1:2,1:2]]]
10861112
@test a[[Block(1)[1], Block(2)[2]], [Block(1)[1:2], Block(2)[1:2]]] == [a[Block(1)[1],Block(1)[1:2]]' a[Block(1)[1], Block(2)[1:2]]'; a[Block(2)[2],Block(1)[1:2]]' a[Block(2)[2], Block(2)[1:2]]']
10871113
end
1114+
@testset "Blocked block-vector indexing (#359)" begin
1115+
for a in (BlockArray(randn(14, 14), 2:5, 2:5), BlockedArray(randn(14, 14), 2:5, 2:5))
1116+
for I in (
1117+
[Block.(1:2), Block.(3:4)],
1118+
[[Block(1), Block(3)], [Block(2), Block(4)]],
1119+
[[Block(1)[1:2], Block(3)[1:2]], [Block(2)[1:2], Block(4)[1:2]]],
1120+
[[[Block(1)[1], Block(1)[2]], [Block(3)[1], Block(3)[2]]], [[Block(2)[1], Block(2)[2]], [Block(4)[1], Block(4)[2]]]],
1121+
)
1122+
b = a[I, I]
1123+
for (i, j) in Iterators.product(1:length(I), 1:length(I))
1124+
@test a[I[i], I[j]] == b[Block(i), Block(j)]
1125+
end
1126+
end
1127+
end
1128+
end
10881129
end
10891130

10901131
end # module

0 commit comments

Comments
 (0)