Skip to content

Commit 5abdf5e

Browse files
getblock -> view (#139)
* ovlerload replace_in_print_matrix for PseudoBlockArray * Update test/test_blockarrays.jl Co-authored-by: Kristoffer Carlsson <[email protected]> * test Static blocks * Changes to support special views, non--allocating block axes * make simplified, fast-tracked vector broadcast code * blockbroadcast can depend on axes * fix tests * more tests pass, require Julia v1.5 * Start getblock -> view * tests pass! * Deprecations * Add deprecations * Update blocklinalg.jl * support smarter view(view(A, Block.(1:5)), Block(5)) * general view of view of AbstractBlockArray * More BlockStyle overloads * Tests pass * make view reindexing non-allocating * Update Project.toml * increase coverage, update docs * Update test_blockbroadcast.jl * increase coverage * Remove single testset to avoid timeout on Travis * increase coverage * increase broadcast test coverage * BlockIndexRange <: AbstractArray Co-authored-by: Kristoffer Carlsson <[email protected]>
1 parent 226f01d commit 5abdf5e

28 files changed

+960
-750
lines changed

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ os:
44
- linux
55
- osx
66
julia:
7-
- 1.1
87
- 1.5
98
- nightly
109
matrix:
@@ -23,7 +22,7 @@ after_success:
2322
jobs:
2423
include:
2524
- stage: "Documentation"
26-
julia: 1.4
25+
julia: 1.5
2726
os: linux
2827
script:
2928
- julia --project=docs/ --color=yes -e '

Project.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
name = "BlockArrays"
22
uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
3-
version = "0.13.0"
3+
version = "0.14.0"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
7-
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
87
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
98
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
109

1110
[compat]
12-
ArrayLayouts = "0.4.12"
13-
Compat = "2.2, 3"
14-
FillArrays = "0.10.1"
15-
julia = "1.1"
11+
ArrayLayouts = "0.5"
12+
FillArrays = "0.11"
13+
julia = "1.5"
1614

1715
[extras]
1816
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
1917
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
18+
LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02"
2019
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
2120
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
21+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2222
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2323

2424
[targets]
25-
test = ["Base64", "FillArrays", "OffsetArrays", "SparseArrays", "Test"]
25+
test = ["Base64", "FillArrays", "LazyArrays", "OffsetArrays", "SparseArrays", "StaticArrays", "Test"]

docs/src/lib/public.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ blocklasts
3838
blocklengths
3939
blocks
4040
eachblock
41-
getblock
42-
getblock!
43-
setblock!
4441
blockcheckbounds
4542
```
4643

docs/src/man/abstractblockarrayinterface.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ It is possible to override additional functions to improve speed, however.
4040
| Methods to implement | Brief description |
4141
| :---------------------- | :---------------- |
4242
| **Optional methods** |
43-
| `getblock(A, i...)` | `X[Block(i...)]`, blocked indexing |
44-
| `setblock!(A, v, i...)` | `X[Block(i...)] = v`, blocked index assignment |
45-
| `getblock!(x, A, i)` | `X[i]`, blocked index assignment with in place storage in `x` |
43+
| `BlockArrays.viewblock(A, i::Block)` | Specialised non-allocating `X[Block(i...)]`, blocked indexing |
4644

4745
For a more thorough description of the methods see the public interface documentation.
4846

docs/src/man/blockarrays.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ julia> BlockArray(undef_blocks, SparseVector{Float64, Int}, [1,2])
8787

8888
## [Setting and getting blocks and values](@id setting_and_getting)
8989

90-
A block can be set by `setblock!(block_array, v, i...)` where `v` is the array to set and `i` is the block index.
91-
An alternative syntax for this is `block_array[Block(i...)] = v` or
90+
A block can be set by `block_array[Block(i...)] = v` or
9291
`block_array[Block.(i)...]`.
9392

9493
```jldoctest block_array
@@ -99,14 +98,14 @@ julia> block_array = BlockArray{Float64}(undef_blocks, [1,2], [2,2])
9998
#undef #undef │ #undef #undef
10099
#undef #undef │ #undef #undef
101100
102-
julia> setblock!(block_array, rand(2,2), 2, 1)
101+
julia> block_array[Block(2,1)] = rand(2,2)
103102
2×2-blocked 3×4 BlockArray{Float64,2}:
104103
#undef #undef │ #undef #undef
105104
────────────────────────┼────────────────
106105
0.590845 0.566237 │ #undef #undef
107106
0.766797 0.460085 │ #undef #undef
108107
109-
julia> block_array[Block(1, 1)] = [1 2];
108+
julia> block_array[Block(1),Block(1)] = [1 2];
110109
111110
julia> block_array
112111
2×2-blocked 3×4 BlockArray{Float64,2}:
@@ -118,10 +117,15 @@ julia> block_array
118117

119118
Note that this will "take ownership" of the passed in array, that is, no copy is made.
120119

121-
A block can be retrieved with `getblock(block_array, i...)` or `block_array[Block(i...)]`:
120+
A block can be retrieved with `view(block_array, Block(i...))`,
121+
or if a copy is desired, `block_array[Block(i...)]`:
122122

123123
```jldoctest block_array
124-
julia> block_array[Block(1, 1)]
124+
julia> view(block_array, Block(1, 1))
125+
1×2 Array{Float64,2}:
126+
1.0 2.0
127+
128+
julia> block_array[Block(1, 1)] # makes a copy
125129
1×2 Array{Float64,2}:
126130
1.0 2.0
127131
@@ -130,8 +134,6 @@ julia> block_array[Block(1), Block(1)] # equivalent to above
130134
1.0 2.0
131135
```
132136

133-
Similarly to `setblock!` this does not copy the returned array.
134-
135137
For setting and getting a single scalar element, the usual `setindex!` and `getindex` are available.
136138

137139
```jl
@@ -141,19 +143,25 @@ julia> block_array[1, 2]
141143

142144
## Views of blocks
143145

144-
We can also view and modify views of blocks of `BlockArray` using the `view` syntax:
146+
To view and modify blocks of `BlockArray` use the `view` syntax.
145147
```jldoctest
146148
julia> A = BlockArray(ones(6), 1:3);
147149
148150
julia> view(A, Block(2))
149-
2-element view(::BlockArray{Float64,1,Array{Array{Float64,1},1},Tuple{BlockedUnitRange{Array{Int64,1}}}}, BlockSlice(Block(2),2:3)) with eltype Float64:
151+
2-element Array{Float64,1}:
150152
1.0
151153
1.0
152154
153155
julia> view(A, Block(2)) .= [3,4]; A[Block(2)]
154156
2-element Array{Float64,1}:
155157
3.0
156158
4.0
159+
160+
julia> view(A, Block.(1:2))
161+
3-element view(::BlockArray{Float64,1,Array{Array{Float64,1},1},Tuple{BlockedUnitRange{ArrayLayouts.RangeCumsum{Int64,UnitRange{Int64}}}}}, BlockSlice(Block{1,Int64}[Block(1), Block(2)],1:1:3)) with eltype Float64 with indices 1:1:3:
162+
1.0
163+
3.0
164+
4.0
157165
```
158166

159167

docs/src/man/pseudoblockarrays.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ it can just return the wrapped array.
1515

1616
When iteratively solving a set of equations with a gradient method the Jacobian typically has a block structure. It can be convenient
1717
to use a `PseudoBlockArray` to build up the Jacobian block by block and then pass the resulting matrix to
18-
a direct solver using `full`.
18+
a direct solver using `Matrix`.
1919

2020
## Creating PseudoBlockArrays
2121

@@ -50,15 +50,16 @@ We can also any other user defined array type that supports `similar`.
5050
## Setting and getting blocks and values
5151

5252
Setting and getting blocks uses the same API as `BlockArrays`. The difference here is that setting a block will update the block in place and getting a block
53-
will extract a copy of the block and return it. For `PseudoBlockArrays` there is a mutating block getter called `getblock!` which updates a passed in array to avoid a copy:
53+
will extract a copy of the block and return it. Note to update a passed in array without allocating
54+
one can use views:
5455

5556
```jldoctest A
5657
julia> A = zeros(2,2)
5758
2×2 Array{Float64,2}:
5859
0.0 0.0
5960
0.0 0.0
6061
61-
julia> getblock!(A, pseudo, 2, 1);
62+
julia> copyto!(A, view(pseudo, Block(2, 1)));
6263
6364
julia> A
6465
2×2 Array{Float64,2}:
@@ -83,7 +84,7 @@ We can also view and modify views of blocks of `PseudoBlockArray` using the `vie
8384
julia> A = PseudoBlockArray(ones(6), 1:3);
8485
8586
julia> view(A, Block(2))
86-
2-element view(::PseudoBlockArray{Float64,1,Array{Float64,1},Tuple{BlockedUnitRange{Array{Int64,1}}}}, BlockSlice(Block(2),2:3)) with eltype Float64:
87+
2-element view(::Array{Float64,1}, 2:3) with eltype Float64:
8788
1.0
8889
1.0
8990
@@ -93,4 +94,4 @@ julia> view(A, Block(2)) .= [3,4]; A[Block(2)]
9394
4.0
9495
```
9596
Note that, in memory, each block is in a BLAS-Level 3 compatible format, so
96-
that, in the future, algebra with blocks will be highly efficient.
97+
that algebra with blocks is highly efficient.

src/BlockArrays.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import Base: @propagate_inbounds, Array, to_indices, to_index,
2828
RangeIndex, Int, Integer, Number,
2929
+, -, *, /, \, min, max, isless, in, copy, copyto!, axes, @deprecate,
3030
BroadcastStyle, checkbounds, throw_boundserror,
31-
ones, zeros, intersect
31+
ones, zeros, intersect, Slice
3232
using Base: ReshapedArray, dataids
3333

3434

@@ -41,10 +41,6 @@ import ArrayLayouts: _fill_lmul!, MatMulVecAdd, MatMulMatAdd, MatLmulVec, MatLdi
4141
triangularlayout, triangulardata, _inv, _copyto!, axes_print_matrix_row,
4242
colsupport, rowsupport, sub_materialize
4343

44-
if !@isdefined(only)
45-
using Compat: only
46-
end
47-
4844
include("blockindices.jl")
4945
include("blockaxis.jl")
5046
include("abstractblockarray.jl")
@@ -60,4 +56,9 @@ include("show.jl")
6056
include("blockreduce.jl")
6157
include("blockdeque.jl")
6258

59+
@deprecate getblock(A::AbstractBlockArray{T,N}, I::Vararg{Integer, N}) where {T,N} view(A, Block(I))
60+
@deprecate getblock!(X, A::AbstractBlockArray{T,N}, I::Vararg{Integer, N}) where {T,N} copyto!(X, view(A, Block(I)))
61+
@deprecate setblock!(A::AbstractBlockArray{T,N}, v, I::Vararg{Integer, N}) where {T,N} (A[Block(I...)] = v)
62+
63+
6364
end # module

src/abstractblockarray.jl

Lines changed: 22 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ block2string(b, s) = string(join(map(string,b), '×'), "-blocked ", Base.dims2st
2525
_block_summary(a) = string(block2string(blocksize(a), size(a)), " ", typeof(a))
2626
Base.summary(a::AbstractBlockArray) = _block_summary(a)
2727
_show_typeof(io, a) = show(io, typeof(a))
28-
function _block_summary(io, a)
28+
function _block_summary(io, a)
2929
print(io, block2string(blocksize(a), size(a)))
3030
print(io, ' ')
3131
_show_typeof(io, a)
@@ -37,98 +37,15 @@ Base.similar(a::AbstractBlockArray{T}) where {T} = s
3737
Base.similar(a::AbstractBlockArray, ::Type{T}) where {T} = similar(a, T, axes(a))
3838
Base.similar(a::AbstractBlockArray{T}, dims::Tuple) where {T} = similar(a, T, dims)
3939

40+
# If all we know is size, just return an Array which conforms to BlockArray interface
41+
Base.similar(::Type{<:AbstractBlockArray{T,N}}, dims::Dims) where {T,N} = similar(Array{T,N}, dims)
42+
4043
Base.IndexStyle(::Type{<:AbstractBlockArray}) = IndexCartesian()
4144

4245
# need to overload axes to return BlockAxis
4346
@inline size(block_array::AbstractBlockArray) = map(length, axes(block_array))
4447
@inline axes(block_array::AbstractBlockArray) = throw(error("axes for ", typeof(block_array), " is not implemented"))
4548

46-
"""
47-
getblock(A, inds...)
48-
49-
Returns the block at blockindex `inds...`. An alternative syntax is `A[Block(inds...)].
50-
Throws a `BlockBoundsError` if this block is out of bounds.
51-
52-
```jldoctest; setup = quote using BlockArrays end
53-
julia> v = Array(reshape(1:6, (2, 3)))
54-
2×3 Array{Int64,2}:
55-
1 3 5
56-
2 4 6
57-
58-
julia> A = BlockArray(v, [1,1], [2,1])
59-
2×2-blocked 2×3 BlockArray{Int64,2}:
60-
1 3 │ 5
61-
──────┼───
62-
2 4 │ 6
63-
64-
julia> getblock(A, 2, 1)
65-
1×2 Array{Int64,2}:
66-
2 4
67-
68-
julia> A[Block(1, 2)]
69-
1×1 Array{Int64,2}:
70-
5
71-
```
72-
"""
73-
function getblock(A::AbstractBlockArray{T,N}, ::Vararg{Integer, N}) where {T,N}
74-
throw(error("getblock for ", typeof(A), " is not implemented"))
75-
end
76-
77-
78-
"""
79-
getblock!(X, A, inds...)
80-
81-
Stores the block at blockindex `inds` in `X` and returns it. Throws a `BlockBoundsError` if the
82-
attempted assigned block is out of bounds.
83-
84-
```jldoctest; setup = quote using BlockArrays end
85-
julia> A = PseudoBlockArray(ones(2, 3), [1, 1], [2, 1])
86-
2×2-blocked 2×3 PseudoBlockArray{Float64,2}:
87-
1.0 1.0 │ 1.0
88-
──────────┼─────
89-
1.0 1.0 │ 1.0
90-
91-
julia> x = zeros(1, 2);
92-
93-
julia> getblock!(x, A, 2, 1);
94-
95-
julia> x
96-
1×2 Array{Float64,2}:
97-
1.0 1.0
98-
```
99-
"""
100-
getblock!(X, A::AbstractBlockArray{T,N}, ::Vararg{Integer, N}) where {T,N} = throw(error("getblock! for ", typeof(A), " is not implemented"))
101-
102-
@inline getblock!(X, A::AbstractBlockArray{T,N}, block::Block{N}) where {T,N} = getblock!(X, A, block.n...)
103-
@inline getblock!(X, A::AbstractBlockVector, block::Block{1}) = getblock!(X, A, block.n[1])
104-
@inline getblock!(X, A::AbstractBlockArray{T, N}, block::Vararg{Block{1}, N}) where {T,N} = getblock!(X, A, (Block(block).n)...)
105-
106-
"""
107-
setblock!(A, v, inds...)
108-
109-
Stores the block `v` in the block at block index `inds` in `A`. An alternative syntax is `A[Block(inds...)] = v`.
110-
Throws a `BlockBoundsError` if this block is out of bounds.
111-
112-
```jldoctest; setup = quote using BlockArrays end
113-
julia> A = PseudoBlockArray(zeros(2, 3), [1, 1], [2, 1]);
114-
115-
julia> setblock!(A, [1 2], 1, 1);
116-
117-
julia> A[Block(2, 1)] = [3 4];
118-
119-
julia> A
120-
2×2-blocked 2×3 PseudoBlockArray{Float64,2}:
121-
1.0 2.0 │ 0.0
122-
──────────┼─────
123-
3.0 4.0 │ 0.0
124-
```
125-
"""
126-
setblock!(A::AbstractBlockArray{T,N}, v, ::Vararg{Integer, N}) where {T,N} = throw(error("setblock! for ", typeof(A), " is not implemented"))
127-
128-
@inline setblock!(A::AbstractBlockArray{T, N}, v, block::Block{N}) where {T,N} = setblock!(A, v, block.n...)
129-
@inline setblock!(A::AbstractBlockVector, v, block::Block{1}) = setblock!(A, v, block.n[1])
130-
@inline setblock!(A::AbstractBlockArray{T, N}, v, block::Vararg{Block{1}, N}) where {T,N} = setblock!(A, v, (Block(block).n)...)
131-
13249

13350
"""
13451
BlockBoundsError([A], [inds...])
@@ -193,15 +110,27 @@ end
193110
end
194111

195112
blockcheckbounds(A::AbstractArray{T, N}, i::Block{N}) where {T,N} = blockcheckbounds(A, i.n...)
113+
blockcheckbounds(A::AbstractArray{T, N}, i::Vararg{Block{1},N}) where {T,N} = blockcheckbounds(A, Int.(i)...)
114+
blockcheckbounds(A::AbstractVector{T}, i::Block{1}) where {T,N} = blockcheckbounds(A, Int(i))
196115

197116
# Convert to @generated...
198-
@propagate_inbounds Base.getindex( block_arr::AbstractBlockArray{T, N}, block::Block{N}) where {T,N} = copy(getblock(block_arr, block.n...))
199-
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockArray{T, N}, v, block::Block{N}) where {T,N} = setblock!(block_arr, v, block.n...)
200-
@propagate_inbounds Base.getindex( block_arr::AbstractBlockVector, block::Block{1}) = copy(getblock(block_arr, block.n[1]))
201-
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockVector, v, block::Block{1}) = setblock!(block_arr, v, block.n[1])
202-
@inline Base.getindex(block_arr::AbstractBlockArray{T,N}, block::Vararg{Block{1}, N}) where {T,N} = copy(getblock(block_arr, (Block(block).n)...))
203-
@inline Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Vararg{Block{1}, N}) where {T,N} = setblock!(block_arr, v, (Block(block).n)...)
117+
@propagate_inbounds Base.getindex( block_arr::AbstractBlockArray{T,N}, block::Block{N}) where {T,N} = copy(view(block_arr, block))
118+
@propagate_inbounds Base.getindex( block_arr::AbstractBlockVector{T}, block::Block{1}) where {T} = copy(view(block_arr, block))
119+
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Block{N}) where {T,N} =
120+
setindex!(block_arr, v, Block.(block.n)...)
121+
@inline @propagate_inbounds Base.getindex(block_arr::AbstractBlockArray{T,N}, block::Vararg{Block{1}, N}) where {T,N} = block_arr[Block(block)]
122+
@inline @propagate_inbounds function Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Vararg{Block{1}, N}) where {T,N}
123+
blockcheckbounds(block_arr, block...)
124+
dest = view(block_arr, block...)
125+
size(dest) == size(v) || throw(DimensionMismatch(string("tried to assign $(size(v)) array to $(size(dest)) block")))
126+
copyto!(dest, v)
127+
block_arr
128+
end
204129

130+
viewblock(block_arr, block) = Base.invoke(view, Tuple{AbstractArray, Any}, block_arr, block)
131+
@inline Base.view(block_arr::AbstractBlockArray{<:Any,N}, block::Block{N}) where N = viewblock(block_arr, block)
132+
@inline Base.view(block_arr::AbstractBlockVector, block::Block{1}) = viewblock(block_arr, block)
133+
@inline @propagate_inbounds Base.view(block_arr::AbstractBlockArray, block::Block{1}...) = view(block_arr, Block(block))
205134
"""
206135
eachblock(A::AbstractBlockArray)
207136

0 commit comments

Comments
 (0)