Skip to content

Commit ccad1a4

Browse files
authored
[BlockSparseArrays] Sub-slices of multiple blocks (#1489)
* [BlockSparseArrays] Sub-slices of multiple blocks * [NDTensors] Bump to v0.3.24
1 parent 8840ab7 commit ccad1a4

File tree

3 files changed

+128
-9
lines changed

3 files changed

+128
-9
lines changed

src/BlockArraysExtensions/BlockArraysExtensions.jl

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ function blockrange(axis::AbstractUnitRange, r::UnitRange)
159159
end
160160

161161
function blockrange(axis::AbstractUnitRange, r::Int)
162-
error("Slicing with integer values isn't supported.")
163-
return findblock(axis, r)
162+
## return findblock(axis, r)
163+
return error("Slicing with integer values isn't supported.")
164164
end
165165

166166
function blockrange(axis::AbstractUnitRange, r::AbstractVector{<:Block{1}})
@@ -187,6 +187,24 @@ function blockrange(axis::AbstractUnitRange, r::BlockIndexRange)
187187
return Block(r):Block(r)
188188
end
189189

190+
function blockrange(axis::AbstractUnitRange, r::AbstractVector{<:BlockIndexRange{1}})
191+
return error("Slicing not implemented for range of type `$(typeof(r))`.")
192+
end
193+
194+
function blockrange(
195+
axis::AbstractUnitRange,
196+
r::BlockVector{BlockIndex{1},<:AbstractVector{<:BlockIndexRange{1}}},
197+
)
198+
return map(b -> Block(b), blocks(r))
199+
end
200+
201+
# This handles slicing with `:`/`Colon()`.
202+
function blockrange(axis::AbstractUnitRange, r::Base.Slice)
203+
# TODO: Maybe use `BlockRange`, but that doesn't output
204+
# the same thing.
205+
return only(blockaxes(axis))
206+
end
207+
190208
function blockrange(axis::AbstractUnitRange, r)
191209
return error("Slicing not implemented for range of type `$(typeof(r))`.")
192210
end
@@ -228,6 +246,22 @@ function blockindices(a::AbstractUnitRange, b::Block, r::BlockIndices)
228246
return blockindices(a, b, r.blocks)
229247
end
230248

249+
function blockindices(
250+
a::AbstractUnitRange,
251+
b::Block,
252+
r::BlockVector{BlockIndex{1},<:AbstractVector{<:BlockIndexRange{1}}},
253+
)
254+
# TODO: Change to iterate over `BlockRange(r)`
255+
# once https://github.com/JuliaArrays/BlockArrays.jl/issues/404
256+
# is fixed.
257+
for bl in blocks(r)
258+
if b == Block(bl)
259+
return only(bl.indices)
260+
end
261+
end
262+
return error("Block not found.")
263+
end
264+
231265
function cartesianindices(a::AbstractArray, b::Block)
232266
return cartesianindices(axes(a), b)
233267
end

src/abstractblocksparsearray/wrappedabstractblocksparsearray.jl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Adapt: Adapt, WrappedArray
2-
using BlockArrays: BlockArrays, BlockedUnitRange, BlockRange, blockedrange, unblock
2+
using BlockArrays:
3+
BlockArrays, BlockedUnitRange, BlockIndexRange, BlockRange, blockedrange, mortar, unblock
34
using SplitApplyCombine: groupcount
45

56
const WrappedAbstractBlockSparseArray{T,N} = WrappedArray{
@@ -46,6 +47,15 @@ function Base.to_indices(
4647
return blocksparse_to_indices(a, I)
4748
end
4849

50+
# Handle case of indexing with `[Block(1)[1:2], Block(2)[1:2]]`
51+
# by converting it to a `BlockVector` with
52+
# `mortar([Block(1)[1:2], Block(2)[1:2]])`.
53+
function Base.to_indices(
54+
a::BlockSparseArrayLike, inds, I::Tuple{AbstractVector{<:BlockIndexRange{1}},Vararg{Any}}
55+
)
56+
return to_indices(a, inds, (mortar(I[1]), Base.tail(I)...))
57+
end
58+
4959
# Fixes ambiguity error with BlockArrays.
5060
function Base.to_indices(a::BlockSparseArrayLike, I::Tuple{BlockRange{1},Vararg{Any}})
5161
return blocksparse_to_indices(a, I)
@@ -126,14 +136,25 @@ function Base.getindex(
126136
return blocksparse_getindex(a, block...)
127137
end
128138

129-
# TODO: Define `issasigned(a, ::Block{N})`.
139+
# TODO: Define `blocksparse_isassigned`.
130140
function Base.isassigned(
131141
a::BlockSparseArrayLike{<:Any,N}, index::Vararg{Block{1},N}
132142
) where {N}
133-
# TODO: Define `blocksparse_isassigned`.
134143
return isassigned(blocks(a), Int.(index)...)
135144
end
136145

146+
function Base.isassigned(a::BlockSparseArrayLike{<:Any,N}, index::Block{N}) where {N}
147+
return isassigned(a, Tuple(index)...)
148+
end
149+
150+
# TODO: Define `blocksparse_isassigned`.
151+
function Base.isassigned(
152+
a::BlockSparseArrayLike{<:Any,N}, index::Vararg{BlockIndex{1},N}
153+
) where {N}
154+
b = block.(index)
155+
return isassigned(a, b...) && isassigned(@view(a[b...]), blockindex.(index)...)
156+
end
157+
137158
function Base.setindex!(a::BlockSparseArrayLike{<:Any,N}, value, I::BlockIndex{N}) where {N}
138159
blocksparse_setindex!(a, value, I)
139160
return a

test/test_basics.jl

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
@eval module $(gensym())
22
using BlockArrays:
3-
Block, BlockRange, BlockedUnitRange, BlockVector, blockedrange, blocklength, blocksize
3+
Block,
4+
BlockRange,
5+
BlockedUnitRange,
6+
BlockVector,
7+
blockedrange,
8+
blocklength,
9+
blocklengths,
10+
blocksize,
11+
mortar
412
using LinearAlgebra: mul!
513
using NDTensors.BlockSparseArrays: BlockSparseArray, block_nstored, block_reshape
614
using NDTensors.SparseArrayInterface: nstored
@@ -331,13 +339,69 @@ include("TestBlockSparseArraysUtils.jl")
331339
@test b[4, 4] == 44
332340
end
333341

334-
# This is outputting only zero blocks.
335342
a = BlockSparseArray{elt}(undef, ([2, 3], [3, 4]))
336-
a[Block(1, 2)] = randn(elt, size(@view(a[Block(1, 2)])))
337-
a[Block(2, 1)] = randn(elt, size(@view(a[Block(2, 1)])))
343+
@views for b in [Block(1, 2), Block(2, 1)]
344+
a[b] = randn(elt, size(a[b]))
345+
end
338346
b = a[Block(2):Block(2), Block(1):Block(2)]
339347
@test block_nstored(b) == 1
340348
@test b == Array(a)[3:5, 1:end]
349+
350+
a = BlockSparseArray{elt}(undef, ([2, 3, 4], [2, 3, 4]))
351+
# TODO: Define `block_diagindices`.
352+
@views for b in [Block(1, 1), Block(2, 2), Block(3, 3)]
353+
a[b] = randn(elt, size(a[b]))
354+
end
355+
for (I1, I2) in (
356+
(mortar([Block(2)[2:3], Block(3)[1:3]]), mortar([Block(2)[2:3], Block(3)[2:3]])),
357+
([Block(2)[2:3], Block(3)[1:3]], [Block(2)[2:3], Block(3)[2:3]]),
358+
)
359+
for b in (a[I1, I2], @view(a[I1, I2]))
360+
# TODO: Rename `block_stored_length`.
361+
@test block_nstored(b) == 2
362+
@test b[Block(1, 1)] == a[Block(2, 2)[2:3, 2:3]]
363+
@test b[Block(2, 2)] == a[Block(3, 3)[1:3, 2:3]]
364+
end
365+
end
366+
367+
a = BlockSparseArray{elt}(undef, ([3, 3], [3, 3]))
368+
# TODO: Define `block_diagindices`.
369+
@views for b in [Block(1, 1), Block(2, 2)]
370+
a[b] = randn(elt, size(a[b]))
371+
end
372+
I = mortar([Block(1)[1:2], Block(2)[1:2]])
373+
b = a[:, I]
374+
@test b[Block(1, 1)] == a[Block(1, 1)][:, 1:2]
375+
@test b[Block(2, 1)] == a[Block(2, 1)][:, 1:2]
376+
@test b[Block(1, 2)] == a[Block(1, 2)][:, 1:2]
377+
@test b[Block(2, 2)] == a[Block(2, 2)][:, 1:2]
378+
@test blocklengths.(axes(b)) == ([3, 3], [2, 2])
379+
# TODO: Rename `block_stored_length`.
380+
@test blocksize(b) == (2, 2)
381+
@test block_nstored(b) == 2
382+
383+
a = BlockSparseArray{elt}(undef, ([2, 3], [3, 4]))
384+
@views for b in [Block(1, 2), Block(2, 1)]
385+
a[b] = randn(elt, size(a[b]))
386+
end
387+
@test isassigned(a, 1, 1)
388+
@test isassigned(a, 5, 7)
389+
@test !isassigned(a, 0, 1)
390+
@test !isassigned(a, 5, 8)
391+
@test isassigned(a, Block(1), Block(1))
392+
@test isassigned(a, Block(2), Block(2))
393+
@test !isassigned(a, Block(1), Block(0))
394+
@test !isassigned(a, Block(3), Block(2))
395+
@test isassigned(a, Block(1, 1))
396+
@test isassigned(a, Block(2, 2))
397+
@test !isassigned(a, Block(1, 0))
398+
@test !isassigned(a, Block(3, 2))
399+
@test isassigned(a, Block(1)[1], Block(1)[1])
400+
@test isassigned(a, Block(2)[3], Block(2)[4])
401+
@test !isassigned(a, Block(1)[0], Block(1)[1])
402+
@test !isassigned(a, Block(2)[3], Block(2)[5])
403+
@test !isassigned(a, Block(1)[1], Block(0)[1])
404+
@test !isassigned(a, Block(3)[3], Block(2)[4])
341405
end
342406
@testset "LinearAlgebra" begin
343407
a1 = BlockSparseArray{elt}([2, 3], [2, 3])

0 commit comments

Comments
 (0)