Skip to content

Commit d482cd9

Browse files
Nested block indexing (JuliaArrays#482)
This enables nested block indexing by generalizing `BlockIndex` to allow indices that aren't integers, for example: ```julia julia> using BlockArrays julia> a = mortar([[1, 2], [3, 4]]) 2-blocked 4-element BlockVector{Int64}: 1 2 ─ 3 4 julia> b = mortar([[5, 6], [7, 8]]) 2-blocked 4-element BlockVector{Int64}: 5 6 ─ 7 8 julia> A = mortar([a, b]) 2-blocked 8-element BlockVector{Int64, Vector{BlockVector{Int64, Vector{Vector{Int64}}, Tuple{BlockedOneTo{Int64, Vector{Int64}}}}}, Tuple{BlockedOneTo{Int64, Vector{Int64}}}}: 1 2 3 4 ─ 5 6 7 8 julia> A[Block(2)[Block(1)]] 2-element Vector{Int64}: 5 6 ``` As an alternative to just generalizing `BlockIndex`, maybe we could define a new type such as `GenericBlockIndex` and then make `BlockIndex` a type alias for that with the indices restricted to integers. I don't have a strong opinion about that but this design was simpler and I don't see much advantage one way or another. Related to JuliaArrays#49 and JuliaArrays#446, though note that in this PR the nested block structure isn't reflected in the axes, which would be required for more systematic support of nested block structures. This PR is split off from JuliaArrays#462. --------- Co-authored-by: Sheehan Olver <[email protected]>
1 parent 7ebbea7 commit d482cd9

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

.github/workflows/downstream.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ jobs:
3434
julia-version: ['1']
3535
os: [ubuntu-latest]
3636
package:
37-
- {repo: BlockBandedMatrices.jl, group: JuliaLinearAlgebra}
3837
- {repo: ApproxFunBase.jl, group: JuliaApproximation}
3938
- {repo: ContinuumArrays.jl, group: JuliaApproximation}
39+
- {repo: MultivariateOrthogonalPolynomials.jl, group: JuliaApproximation}
4040
- {repo: LazyArrays.jl, group: JuliaArrays}
41+
- {repo: BlockBandedMatrices.jl, group: JuliaLinearAlgebra}
4142
- {repo: LazyBandedMatrices.jl, group: JuliaLinearAlgebra}
4243
- {repo: InfiniteLinearAlgebra.jl, group: JuliaLinearAlgebra}
4344

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "BlockArrays"
22
uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
3-
version = "1.8"
3+
version = "1.9"
44

55

66
[deps]

src/abstractblockarray.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ end
218218
@inline view(block_arr::AbstractBlockVector, block::Block{1}) = viewblock(block_arr, block)
219219
@propagate_inbounds view(block_arr::AbstractBlockArray, block::Block{1}...) = view(block_arr, Block(block))
220220

221+
@inline view(block_arr::AbstractBlockArray{<:Any,N}, b::BlockIndex{N}) where N = view(view(block_arr, block(b)), blockindices(b)...)
222+
@inline view(block_arr::AbstractBlockVector, b::BlockIndex{1}) = view(view(block_arr, block(b)), blockindices(b)...)
223+
@propagate_inbounds view(block_arr::AbstractBlockArray{<:Any,N}, b::Vararg{BlockIndex{1},N}) where N = view(block_arr, BlockIndex(b))
224+
221225
"""
222226
eachblock(A::AbstractBlockArray)
223227

src/blockindices.jl

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,31 +163,32 @@ julia> a[Block(2,2)[2,3]]
163163
20
164164
```
165165
"""
166-
struct BlockIndex{N,TI<:Tuple{Vararg{Integer,N}},Tα<:Tuple{Vararg{Integer,N}}}
166+
struct BlockIndex{N,TI<:Tuple{Vararg{Integer,N}},Tα<:Tuple{Vararg{Any,N}}}
167167
I::TI
168168
α::Tα
169169
end
170170

171171
@inline BlockIndex(a::NTuple{N,Block{1}}, b::Tuple) where N = BlockIndex(Int.(a), b)
172172
@inline BlockIndex(::Tuple{}, b::Tuple{}) = BlockIndex{0,Tuple{},Tuple{}}((), ())
173173

174-
@inline BlockIndex(a::Integer, b::Integer) = BlockIndex((a,), (b,))
175-
@inline BlockIndex(a::Tuple, b::Integer) = BlockIndex(a, (b,))
174+
@inline BlockIndex(a::Integer, b) = BlockIndex((a,), (b,))
175+
@inline BlockIndex(a::Tuple, b) = BlockIndex(a, (b,))
176176
@inline BlockIndex(a::Integer, b::Tuple) = BlockIndex((a,), b)
177177
@inline BlockIndex() = BlockIndex((), ())
178178

179179
@inline BlockIndex(a::Block, b::Tuple) = BlockIndex(a.n, b)
180-
@inline BlockIndex(a::Block, b::Integer) = BlockIndex(a, (b,))
180+
@inline BlockIndex(a::Block, b) = BlockIndex(a, (b,))
181181

182-
@inline function BlockIndex(I::Tuple{Vararg{Integer,N}}, α::Tuple{Vararg{Integer,M}}) where {M,N}
182+
@inline function BlockIndex(I::Tuple{Vararg{Integer,N}}, α::Tuple{Vararg{Any,M}}) where {M,N}
183183
M <= N || throw(ArgumentError("number of indices must not exceed the number of blocks"))
184184
α2 = ntuple(k -> k <= M ? α[k] : 1, N)
185185
BlockIndex(I, α2)
186186
end
187187

188188
block(b::BlockIndex) = Block(b.I...)
189-
blockindex(b::BlockIndex{1}) = b.α[1]
190-
blockindex(b::BlockIndex) = CartesianIndex(b.α)
189+
blockindices(b::BlockIndex) = b.α
190+
blockindex(b::BlockIndex{1}) = blockindices(b)[1]
191+
blockindex(b::BlockIndex) = merge_indices(blockindices(b))
191192

192193
BlockIndex(indcs::Tuple{Vararg{BlockIndex{1},N}}) where N = BlockIndex(block.(indcs), blockindex.(indcs))
193194

@@ -318,15 +319,34 @@ function BlockIndexRange(inds::Tuple{BlockIndexRange{1},Vararg{BlockIndexRange{1
318319
BlockIndexRange(Block(block.(inds)), map(ind -> ind.indices[1], inds))
319320
end
320321

322+
blockindices(b::BlockIndices) = b.indices
321323
block(R::BlockIndices) = R.block
322324

323325
copy(R::BlockIndices) = BlockIndices(R.block, map(copy, R.indices))
324326

327+
split_index(i::CartesianIndex) = Tuple(i)
328+
split_index(i::Block) = Tuple(i)
329+
split_index(i::BlockIndex) = map(BlockIndex, Tuple(block(i)), blockindices(i))
330+
split_index(i::BlockIndexRange) = map(BlockIndexRange, Tuple(block(i)), blockindices(i))
331+
332+
merge_indices(i::Tuple{Vararg{Integer}}) = CartesianIndex(i)
333+
merge_indices(i::Tuple{Vararg{Block{1}}}) = Block(i)
334+
merge_indices(i::Tuple{Vararg{BlockIndex{1}}}) = BlockIndex(i)
335+
merge_indices(i::Tuple{Vararg{BlockIndexRange{1}}}) = BlockIndexRange(i)
336+
325337
getindex(::Block{0}) = BlockIndex()
326-
getindex(B::Block{N}, inds::Vararg{Integer,N}) where N = BlockIndex(B,inds)
338+
getindex(B::Block{N}, inds::Vararg{Any,N}) where N = BlockIndex(B,inds)
327339
getindex(B::Block{N}, inds::Vararg{AbstractVector,N}) where N = BlockIndices(B,inds)
340+
getindex(B::Block{1}, inds) = BlockIndex(B,inds)
341+
getindex(B::Block{N}, inds::Vararg{AbstractUnitRange{<:Integer},N}) where N = BlockIndices(B,inds)
328342
getindex(B::Block{1}, inds::Colon) = B
329343
getindex(B::Block{1}, inds::Base.Slice) = B
344+
getindex(B::Block{N}, inds::Block{N}) where N = B[split_index(inds)...]
345+
getindex(B::Block{1}, inds::Block{1}) = BlockIndex(B,inds)
346+
getindex(B::Block{N}, inds::BlockIndex{N}) where N = B[split_index(inds)...]
347+
getindex(B::Block{1}, inds::BlockIndex{1}) = BlockIndex(B,inds)
348+
getindex(B::Block{N}, inds::BlockIndexRange{N}) where N = B[split_index(inds)...]
349+
getindex(B::Block{1}, inds::BlockIndexRange{1}) = BlockIndex(B,inds)
330350

331351
getindex(B::BlockIndices{0}) = B.block[]
332352
@propagate_inbounds getindex(B::BlockIndices{N}, kr::Vararg{AbstractVector,N}) where {N} = BlockIndices(B.block, map(getindex, B.indices, kr))

test/test_blockarrays.jl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,17 +996,36 @@ end
996996
@testset "Blockindex" begin
997997
a = BlockedArray(randn(3), [1,2])
998998
@test a[Block(1)[1]] == a[1]
999-
@test view(a, Block(1)[1]) view(a, 1)
999+
@test view(a, Block(1)[1]) view(parent(a), 1)
1000+
@test view(a, Block(1)[1]) == view(a, 1)
10001001
@test a[Block(1)[1:1]] == a[1:1]
1002+
10011003
A = BlockedArray(randn(3,3), [1,2], [1,2])
10021004
@test A[Block(1)[1], Block(1)[1]] == A[Block(1,1)[1,1]] == A[1,1]
10031005
# Regression test for #442
1004-
@test view(A, Block(1)[1], Block(1)[1]) view(A, Block(1,1)[1,1]) view(A, 1, 1)
1006+
@test view(A, Block(1)[1], Block(1)[1]) view(A, Block(1,1)[1,1]) view(parent(A), 1, 1)
10051007
@test A[Block(1)[1:1], Block(1)[1:1]] == A[Block(1,1)[1:1,1:1]] == A[1:1,1:1]
10061008
@test A[Block(1)[1:1], Block(1)[1]] == BlockArray(A)[Block(1)[1:1], Block(1)[1]] == A[1:1,1]
10071009
@test A[Block(1)[1], Block(1)[1:1]] == BlockArray(A)[Block(1)[1], Block(1)[1:1]] == A[1,1:1]
10081010
end
10091011

1012+
@testset "Nested block indexing" begin
1013+
va = BlockedArray(randn(4), [2,2])
1014+
vb = BlockedArray(randn(4), [2,2])
1015+
V = mortar([va,vb])
1016+
@test V[Block(2)[Block(1)]] == view(V, Block(2)[Block(1)]) == V[Block(2)][Block(1)] == vb[Block(1)]
1017+
@test V[Block(2)[Block(1)[2]]] == view(V, Block(2)[Block(1)[2]])[] == V[Block(2)][Block(1)[2]] == vb[Block(1)[2]]
1018+
@test V[Block(2)[Block(1)[1:2]]] == view(V, Block(2)[Block(1)[1:2]]) == V[Block(2)][Block(1)[1:2]] == vb[Block(1)[1:2]]
1019+
1020+
ma = BlockedArray(randn(4,4), [2,2], [2,2])
1021+
mb = BlockedArray(randn(4,4), [2,2], [2,2])
1022+
mc = BlockedArray(randn(4,4), [2,2], [2,2])
1023+
md = BlockedArray(randn(4,4), [2,2], [2,2])
1024+
M = mortar([[ma] [mc]; [mb] [md]])
1025+
@test M[Block(2,2)[Block(1,1)]] == view(M, Block(2,2)[Block(1,1)]) == M[Block(2,2)][Block(1,1)] == md[Block(1,1)]
1026+
@test M[Block(2,2)[Block(1,1)[2,2]]] == view(M, Block(2,2)[Block(1,1)[2,2]])[] == M[Block(2,2)][Block(1,1)[2,2]] == md[Block(1,1)[2,2]]
1027+
@test M[Block(2,2)[Block(1,1)[1:2,2:2]]] == view(M, Block(2,2)[Block(1,1)[1:2,2:2]]) == M[Block(2,2)][Block(1,1)[1:2,2:2]] == md[Block(1,1)[1:2,2:2]]
1028+
end
10101029
@testset "BlockIndices" begin
10111030
a = BlockedArray(randn(5), [2,3])
10121031
@test a[Block(2)[[1,3]]] == a[[3,5]]

test/test_blockindices.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module TestBlockIndices
33
using BlockArrays, FillArrays, Test, StaticArrays, ArrayLayouts
44
using OffsetArrays
55
import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, NoncontiguousBlockSlice
6+
import BlockArrays: split_index, merge_indices
67

78
@testset "Blocks" begin
89
@test Int(Block(2)) === Integer(Block(2)) === Number(Block(2)) === 2
@@ -86,6 +87,7 @@ import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, NoncontiguousBlockS
8687
@testset "BlockIndex" begin
8788
@test Block()[] == BlockIndex()
8889
@test Block(1)[1] == BlockIndex((1,),(1,))
90+
@test Block(1)[Block(1)] == BlockIndex((1,),(Block(1),))
8991
@test Block(1)[1:2] == BlockIndexRange(Block(1),(1:2,))
9092
@test Block(1)[[1,3]] == BlockIndices(Block(1),([1,3],))
9193
@test Block(1,1)[1,1] == BlockIndex((1,1),(1,1)) == BlockIndex((1,1),(1,))
@@ -107,6 +109,7 @@ import BlockArrays: BlockIndex, BlockIndexRange, BlockSlice, NoncontiguousBlockS
107109
@test BlockIndex(UInt(2),(2,)) === BlockIndex((UInt(2),),(2,))
108110
@test BlockIndex(Block(2),2) === BlockIndex(Block(2),(2,))
109111
@test BlockIndex(Block(2),UInt(2)) === BlockIndex(Block(2),(UInt(2),))
112+
@test BlockIndex(Block(2),Block(2)) === BlockIndex(Block(2),(Block(2),))
110113
@test copy(Block(1)[1:2]) === BlockIndexRange(Block(1),1:2)
111114
@test copy(Block(1)[[1,3]]) == BlockIndices(Block(1),[1,3])
112115
@test copy(Block(1)[[1,3]]) BlockIndices(Block(1),[1,3])
@@ -1021,6 +1024,19 @@ end
10211024
@test a[[Block(1),Block(3)]] == a[Block.(1:2:3)] == [1,3]
10221025
end
10231026

1027+
@testset "split_index" begin
1028+
@test split_index(CartesianIndex(1, 2)) (1, 2)
1029+
@test split_index(Block(1, 2)) (Block(1), Block(2))
1030+
@test split_index(Block(1, 2)[3, 4]) (Block(1)[3], Block(2)[4])
1031+
@test split_index(Block(1, 2)[3:4, 4:5]) (Block(1)[3:4], Block(2)[4:5])
1032+
end
1033+
1034+
@testset "merge_indices" begin
1035+
@test merge_indices((1, 2)) CartesianIndex(1, 2)
1036+
@test merge_indices((Block(1), Block(2))) Block(1, 2)
1037+
@test merge_indices((Block(1)[3], Block(2)[4])) Block(1, 2)[3, 4]
1038+
@test merge_indices((Block(1)[3:4], Block(2)[4:5])) Block(1, 2)[3:4, 4:5]
1039+
end
10241040
@testset "to_index" begin
10251041
@test_throws ArgumentError Base.to_index(Block(3))
10261042
@test_throws ArgumentError Base.to_index(Block(3)[2])

0 commit comments

Comments
 (0)