Skip to content

Commit b8365fb

Browse files
authored
Develop block array interface (#54)
*Triangular/Symmetric/Hermitian conform to * A.block_sizes -> blocksizes(A) * Bump REQUIRE * getindex -> cumulsize/s * *Triangular/Symmetric/Hermitian conform to block array interace * clean up cumulsizes * BlockSizes -> AbstractBlockSizes * view(view(A, Block(2,3)), Block(1,1)) now works as expected
1 parent bf59a39 commit b8365fb

23 files changed

+331
-377
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ language: julia
33
os:
44
- linux
55
julia:
6-
- 0.6
76
- 0.7
7+
- 1.0
88
- nightly
99
matrix:
1010
allow_failures:

REQUIRE

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
julia 0.6
2-
Compat 0.66
1+
julia 0.7

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ makedocs(
88
format = :html,
99
sitename = "BlockArrays.jl",
1010
doctest = false,
11-
strict = VERSION.minor == 6 && sizeof(Int) == 8, # only strict mode on 0.6 and Int64
11+
strict = VERSION.major == 1 && sizeof(Int) == 8, # only strict mode on 1.0 and Int64
1212
pages = Any[
1313
"Home" => "index.md",
1414
"Manual" => [

docs/src/lib/public.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Block
3232
BlockIndex
3333
nblocks
3434
blocksize
35+
blocksizes
3536
getblock
3637
getblock!
3738
setblock!

docs/src/man/abstractblockarrayinterface.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1-
# The AbstractBlockArray interface
1+
# The `AbstractBlockSizes` interface
22

3-
In order to follow the `AbstractBlockArray` the following methods should be implemented:
3+
In order to follow the `AbstractBlockSizes` the following methods should be implemented:
44

55

66
| Methods to implement | Brief description |
77
| :---------------------- | :---------------- |
8+
| `cumulsizes(A)` | A Tuple of abstract vectors storing the cumulative block sizes |
9+
| **Optional methods** |
810
| `nblocks(A)` | Tuple of number of blocks in each dimension |
911
| `nblocks(A, i)` | Number of blocks in dimension `i` |
10-
| `blocksize(A, i...)` | Size of the block at block index `i...` |
12+
| `blocksize(A, i)` | Size of the block at block index `i` |
13+
14+
# The `AbstractBlockArray` interface
15+
16+
| Methods to implement | Brief description |
17+
| `blocksizes(A)` | Return a subtype of `AbstractBlockSizes` |
18+
| **Optional methods** | |
1119
| `getblock(A, i...)` | `X[Block(i...)]`, blocked indexing |
1220
| `setblock!(A, v, i...)` | `X[Block(i...)] = v`, blocked index assignment |
13-
| **Optional methods** | |
1421
| `getblock!(x, A, i)` | `X[i]`, blocked index assignment with in place storage in `x` |
1522

1623
For a more thorough description of the methods see the public interface documentation.
@@ -36,4 +43,3 @@ ERROR: BlockBoundsError: attempt to access 2×2-blocked 4×5 BlockArrays.BlockAr
3643
```
3744

3845
* Happy users who know how to use your new block array :)
39-

src/BlockArrays.jl

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ __precompile__()
22

33
module BlockArrays
44
using Base.Cartesian
5-
using Compat
6-
if VERSION  v"0.7.0-DEV.3465"
7-
using LinearAlgebra
8-
end
5+
using LinearAlgebra
96

107
# AbstractBlockArray interface exports
118
export AbstractBlockArray, AbstractBlockMatrix, AbstractBlockVector, AbstractBlockVecOrMat
12-
export Block, getblock, getblock!, setblock!, nblocks, blocksize, blockcheckbounds, BlockBoundsError, BlockIndex
9+
export Block, getblock, getblock!, setblock!, nblocks, blocksize, blocksizes, blockcheckbounds, BlockBoundsError, BlockIndex
1310
export BlockRange
1411

1512
export BlockArray, BlockMatrix, BlockVector, BlockVecOrMat
@@ -24,21 +21,14 @@ import Base: @propagate_inbounds, Array, to_indices, to_index,
2421
broadcast, eltype, convert, broadcast,
2522
@_inline_meta, _maybetail, tail, @_propagate_inbounds_meta, reindex,
2623
RangeIndex, Int, Integer, Number,
27-
+, -, min, max, *, isless, in
28-
29-
import Compat: copyto!, axes
30-
31-
if VERSION < v"0.7-"
32-
import Base: colon, iteratorsize, indices1, start, next, done
33-
const parentindices = parentindexes
34-
const axes1 = indices1
35-
const lmul! = Base.scale!
36-
const rmul! = Base.scale!
37-
else
38-
import Base: (:), IteratorSize, iterate, axes1
39-
import Base.Broadcast: broadcasted, DefaultArrayStyle
40-
import LinearAlgebra: lmul!, rmul!
41-
end
24+
+, -, min, max, *, isless, in, copyto!, axes, @deprecate
25+
26+
27+
28+
import Base: (:), IteratorSize, iterate, axes1
29+
import Base.Broadcast: broadcasted, DefaultArrayStyle
30+
import LinearAlgebra: lmul!, rmul!, AbstractTriangular
31+
4232

4333
include("abstractblockarray.jl")
4434

@@ -49,8 +39,9 @@ include("pseudo_blockarray.jl")
4939
include("blockrange.jl")
5040
include("views.jl")
5141
include("blockindexrange.jl")
52-
include("convert.jl")
5342
include("show.jl")
43+
include("blockarrayinterface.jl")
44+
5445
include("deprecate.jl")
5546

5647
end # module

src/abstractblockarray.jl

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,12 @@ julia> nblocks(A, 3, 2)
4545
(4, 3)
4646
```
4747
"""
48-
nblocks(block_array::AbstractBlockArray, i::Int) = nblocks(block_array)[i]
49-
50-
function nblocks(block_array::AbstractBlockArray, i::Vararg{Int, N}) where {N}
51-
if N == 0
52-
throw(error("nblocks(A) not implemented"))
53-
end
54-
b = nblocks(block_array)
55-
return ntuple(k-> b[i[k]], Val(N))
56-
end
48+
nblocks(block_array::AbstractArray, i::Int) = nblocks(block_array)[i]
5749

50+
nblocks(block_array::AbstractArray, i::Vararg{Int, N}) where {N} =
51+
nblocks(blocksizes(block_array), i...)
5852

59-
"""
60-
blocksize(A, inds...)
61-
62-
Returns a tuple containing the size of the block at block index `inds...`.
6353

64-
```jldoctest
65-
julia> A = BlockArray(rand(5, 4, 6), [1, 4], [1, 2, 1], [1, 2, 2, 1]);
66-
67-
julia> blocksize(A, 1, 3, 2)
68-
(1, 1, 2)
69-
70-
julia> blocksize(A, 2, 1, 3)
71-
(4, 1, 2)
72-
```
73-
"""
74-
function blocksize(A::AbstractBlockArray{T,N}, ::Vararg{Int, N}) where {T,N}
75-
throw(error("blocksize for ", typeof(A), " is not implemented"))
76-
end
7754

7855
"""
7956
Block(inds...)

src/blockarray.jl

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,29 @@ end
189189
end
190190
end
191191

192+
# Convert AbstractArrays that conform to block array interface
193+
convert(::Type{BlockArray{T,N,R}}, A::BlockArray{T,N,R}) where {T,N,R} = A
194+
convert(::Type{BlockArray{T,N}}, A::BlockArray{T,N}) where {T,N} = A
195+
convert(::Type{BlockArray{T}}, A::BlockArray{T}) where {T} = A
196+
convert(::Type{BlockArray}, A::BlockArray) = A
197+
198+
BlockArray{T, N}(A::AbstractArray{T2, N}) where {T,T2,N} =
199+
BlockArray(Array{T, N}(A), blocksizes(A))
200+
BlockArray{T1}(A::AbstractArray{T2, N}) where {T1,T2,N} = BlockArray{T1, N}(A)
201+
BlockArray(A::AbstractArray{T, N}) where {T,N} = BlockArray{T, N}(A)
202+
203+
convert(::Type{BlockArray{T, N}}, A::AbstractArray{T2, N}) where {T,T2,N} =
204+
BlockArray(convert(Array{T, N}, A), blocksizes(A))
205+
convert(::Type{BlockArray{T1}}, A::AbstractArray{T2, N}) where {T1,T2,N} =
206+
convert(BlockArray{T1, N}, A)
207+
convert(::Type{BlockArray}, A::AbstractArray{T, N}) where {T,N} =
208+
convert(BlockArray{T, N}, A)
209+
210+
192211
################################
193212
# AbstractBlockArray Interface #
194213
################################
195-
196-
@inline nblocks(block_array::BlockArray) = nblocks(block_array.block_sizes)
197-
@inline blocksize(block_array::BlockArray{T,N}, i::Vararg{Int, N}) where {T,N} = blocksize(block_array.block_sizes, i)
214+
@inline blocksizes(block_array::BlockArray) = block_array.block_sizes
198215

199216
@inline function getblock(block_arr::BlockArray{T,N}, block::Vararg{Int, N}) where {T,N}
200217
@boundscheck blockcheckbounds(block_arr, block...)
@@ -215,22 +232,18 @@ end
215232
###########################
216233

217234
@inline function Base.similar(block_array::BlockArray{T,N}, ::Type{T2}) where {T,N,T2}
218-
_BlockArray(similar(block_array.blocks, Array{T2, N}), copy(block_array.block_sizes))
219-
end
220-
221-
@inline function Base.size(arr::BlockArray{T,N}) where {T,N}
222-
size(arr.block_sizes)
235+
_BlockArray(similar(block_array.blocks, Array{T2, N}), copy(blocksizes(block_array)))
223236
end
224237

225238
@inline function Base.getindex(block_arr::BlockArray{T, N}, i::Vararg{Int, N}) where {T,N}
226239
@boundscheck checkbounds(block_arr, i...)
227-
@inbounds v = block_arr[global2blockindex(block_arr.block_sizes, i)]
240+
@inbounds v = block_arr[global2blockindex(blocksizes(block_arr), i)]
228241
return v
229242
end
230243

231244
@inline function Base.setindex!(block_arr::BlockArray{T, N}, v, i::Vararg{Int, N}) where {T,N}
232245
@boundscheck checkbounds(block_arr, i...)
233-
@inbounds block_arr[global2blockindex(block_arr.block_sizes, i)] = v
246+
@inbounds block_arr[global2blockindex(blocksizes(block_arr), i)] = v
234247
return block_arr
235248
end
236249

@@ -240,8 +253,8 @@ end
240253

241254
function _check_setblock!(block_arr::BlockArray{T, N}, v, block::NTuple{N, Int}) where {T,N}
242255
for i in 1:N
243-
if size(v, i) != blocksize(block_arr.block_sizes, i, block[i])
244-
throw(DimensionMismatch(string("tried to assign $(size(v)) array to ", blocksize(block_arr, block...), " block")))
256+
if size(v, i) != blocksize(block_arr, i, block[i])
257+
throw(DimensionMismatch(string("tried to assign $(size(v)) array to ", blocksize(block_arr, block), " block")))
245258
end
246259
end
247260
end
@@ -265,7 +278,7 @@ end
265278
@generated function Base.Array(block_array::BlockArray{T, N, R}) where {T,N,R}
266279
# TODO: This will fail for empty block array
267280
return quote
268-
block_sizes = block_array.block_sizes
281+
block_sizes = blocksizes(block_array)
269282
arr = similar(block_array.blocks[1], size(block_array)...)
270283
@nloops $N i i->(1:nblocks(block_sizes, i)) begin
271284
block_index = @ntuple $N i
@@ -279,7 +292,7 @@ end
279292

280293
@generated function copyto!(block_array::BlockArray{T, N, R}, arr::R) where {T,N,R <: AbstractArray}
281294
return quote
282-
block_sizes = block_array.block_sizes
295+
block_sizes = blocksizes(block_array)
283296

284297
@nloops $N i i->(1:nblocks(block_sizes, i)) begin
285298
block_index = @ntuple $N i

src/blockarrayinterface.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
blocksizes(A::AbstractTriangular) = blocksizes(parent(A))
3+
4+
5+
blocksizes(A::Symmetric) = blocksizes(parent(A))
6+
blocksizes(A::Hermitian) = blocksizes(parent(A))
7+
8+
Base.print_matrix_row(io::IO,
9+
X::Union{AbstractTriangular{<:Any,<:AbstractBlockMatrix},
10+
Symmetric{<:Any,<:AbstractBlockMatrix},
11+
Hermitian{<:Any,<:AbstractBlockMatrix}}, A::Vector,
12+
i::Integer, cols::AbstractVector, sep::AbstractString) =
13+
_blockarray_print_matrix_row(io, X, A, i, cols, sep)

src/blockindexrange.jl

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,25 @@ getindex(B::Block{N}, inds::Vararg{AbstractUnitRange{Int},N}) where N = BlockInd
2020
eltype(R::BlockIndexRange) = eltype(typeof(R))
2121
eltype(::Type{BlockIndexRange{N}}) where {N} = BlockIndex{N}
2222
eltype(::Type{BlockIndexRange{N,R}}) where {N,R} = BlockIndex{N}
23-
if VERSION < v"0.7.0-DEV.4043"
24-
iteratorsize(::Type{<:BlockIndexRange}) = Base.HasShape()
25-
else
26-
IteratorSize(::Type{<:BlockIndexRange}) = Base.HasShape{1}()
27-
end
23+
IteratorSize(::Type{<:BlockIndexRange}) = Base.HasShape{1}()
24+
2825

2926
first(iter::BlockIndexRange) = BlockIndex(iter.block.n, map(first, iter.indices))
3027
last(iter::BlockIndexRange) = BlockIndex(iter.block.n, map(last, iter.indices))
3128

32-
if VERSION < v"0.7-"
33-
@inline function start(iter::BlockIndexRange)
34-
iterfirst, iterlast = first(iter), last(iter)
35-
if any(map(>, iterfirst.α, iterlast.α))
36-
return BlockIndex(iterlast.I, iterlast.α .+ 1)
37-
end
38-
iterfirst
39-
end
40-
@inline function next(iter::BlockIndexRange, state)
41-
state, BlockIndex(state.I, inc(state.α, first(iter).α, last(iter).α))
42-
end
43-
@inline done(iter::BlockIndexRange, state) = state.α[end] > last(iter.indices[end])
44-
else
45-
@inline function iterate(iter::BlockIndexRange)
46-
iterfirst, iterlast = first(iter), last(iter)
47-
if any(map(>, iterfirst.α, iterlast.α))
48-
return nothing
49-
end
50-
iterfirst, iterfirst
51-
end
52-
@inline function iterate(iter::BlockIndexRange, state)
53-
nextstate = BlockIndex(state.I, inc(state.α, first(iter).α, last(iter).α))
54-
nextstate.α[end] > last(iter.indices[end]) && return nothing
55-
nextstate, nextstate
29+
@inline function iterate(iter::BlockIndexRange)
30+
iterfirst, iterlast = first(iter), last(iter)
31+
if any(map(>, iterfirst.α, iterlast.α))
32+
return nothing
5633
end
34+
iterfirst, iterfirst
35+
end
36+
@inline function iterate(iter::BlockIndexRange, state)
37+
nextstate = BlockIndex(state.I, inc(state.α, first(iter).α, last(iter).α))
38+
nextstate.α[end] > last(iter.indices[end]) && return nothing
39+
nextstate, nextstate
5740
end
41+
5842
size(iter::BlockIndexRange) = map(dimlength, first(iter).α, last(iter).α)
5943
length(iter::BlockIndexRange) = prod(size(iter))
6044

@@ -65,22 +49,11 @@ Block(bs::BlockIndexRange) = bs.block
6549
### Views
6650
Block(bs::BlockSlice{<:BlockIndexRange}) = Block(bs.block)
6751

52+
function _unblock(cum_sizes, I::Tuple{BlockIndexRange{1,R}, Vararg{Any}}) where {R}
53+
B = Block(first(I))
54+
range = cum_sizes[Int(B)]-1 .+ first(I).indices[1]
6855

69-
70-
if VERSION < v"0.7-"
71-
function _unblock(cum_sizes, I::Tuple{BlockIndexRange{1,R}, Vararg{Any}}) where {R}
72-
B = Block(first(I))
73-
range = cum_sizes[Int(B)]-1 + first(I).indices[1]
74-
75-
BlockSlice(I[1], range)
76-
end
77-
else # only 0.7- and above support broadcasting with a Range returning a Range
78-
function _unblock(cum_sizes, I::Tuple{BlockIndexRange{1,R}, Vararg{Any}}) where {R}
79-
B = Block(first(I))
80-
range = cum_sizes[Int(B)]-1 .+ first(I).indices[1]
81-
82-
BlockSlice(I[1], range)
83-
end
56+
BlockSlice(I[1], range)
8457
end
8558

8659

@@ -107,6 +80,8 @@ reindex(V, idxs::Tuple{BlockSlice{<:BlockRange}, Vararg{Any}},
10780
reindex(V, tail(idxs), tail(subidxs))...))
10881

10982

83+
84+
11085
# #################
11186
# # support for pointers
11287
# #################

0 commit comments

Comments
 (0)