Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate blocks into axis, support offset blocks #95

Merged
merged 44 commits into from
Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b20c06a
Create BlockAxis
dlfivefifty Nov 28, 2019
d0439bd
use searchsortedfirst for simple block lookup
dlfivefifty Nov 28, 2019
db092e2
Update blockaxis.jl
dlfivefifty Nov 29, 2019
85eb2bc
BlockAxis working
dlfivefifty Nov 29, 2019
df030c8
start deleting BlockSizes
dlfivefifty Nov 29, 2019
a803309
BlockIndex with BlockAxis
dlfivefifty Nov 29, 2019
be9c08b
basic BlockArray works
dlfivefifty Nov 29, 2019
ec059a0
update PseudoBlockArray
dlfivefifty Nov 30, 2019
26f9ed3
tests starting to pass
dlfivefifty Nov 30, 2019
4b66b87
work on views
dlfivefifty Nov 30, 2019
37c0a91
improvements to view
dlfivefifty Dec 2, 2019
a6f90e1
blockaxes returns a BlockRange
dlfivefifty Dec 2, 2019
926b9a9
tests pass!(?)
dlfivefifty Dec 2, 2019
3491587
broadcast now partially working
dlfivefifty Dec 3, 2019
a099b7c
Fix broadcast blocksize
dlfivefifty Dec 3, 2019
db889d3
test_blockarrays now passes
dlfivefifty Dec 3, 2019
e6d0f80
fix blockaxis BlockRange indexing
dlfivefifty Dec 3, 2019
730340f
minor fixes
dlfivefifty Dec 3, 2019
596d5cb
Redesign as CumsumBlockRange
dlfivefifty Dec 3, 2019
33a2cdc
Block-views now working
dlfivefifty Dec 3, 2019
5b5172b
Most tests pass
dlfivefifty Dec 3, 2019
06b5461
Add reshape
dlfivefifty Dec 4, 2019
9df15e1
tests pass!
dlfivefifty Dec 5, 2019
d697361
Add show for CumsumBlockRange
dlfivefifty Dec 6, 2019
afb4154
Fixes from BlockBandedMatrices
dlfivefifty Dec 8, 2019
84358f5
Improve similar
dlfivefifty Dec 8, 2019
1b829a9
CumsumBlockRange constructor renamed blockedrange to avoid ambiguity …
dlfivefifty Dec 9, 2019
46d8975
use sortedunion with special overrides
dlfivefifty Dec 11, 2019
962c7b2
Facilitate changing BroadcastStyle
dlfivefifty Dec 15, 2019
3497420
fix tests
dlfivefifty Dec 16, 2019
e9062fb
Drop Julia v1.0
dlfivefifty Dec 16, 2019
1c1c224
Update .travis.yml
dlfivefifty Dec 16, 2019
3664098
update docs
dlfivefifty Dec 16, 2019
22aff27
add show
dlfivefifty Dec 17, 2019
e18e78e
update docs
dlfivefifty Dec 17, 2019
d34af9c
Update blockaxis.jl
dlfivefifty Dec 17, 2019
bb9652e
Update internals.md
dlfivefifty Dec 17, 2019
d5fbf83
Update src/blockbroadcast.jl
dlfivefifty Dec 20, 2019
8eb560c
_block_cumsum -> blocklasts
dlfivefifty Dec 21, 2019
614d7c3
Merge branch 'dl/blockaxis' of https://github.com/JuliaArrays/BlockAr…
dlfivefifty Dec 21, 2019
e8b12d3
Update test_blockarrayinterface.jl
dlfivefifty Dec 22, 2019
fbb8ebb
add docs, test blockfirst and convert
dlfivefifty Dec 22, 2019
072f738
add blockisequal
dlfivefifty Dec 22, 2019
4521347
increase coverage
dlfivefifty Dec 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ os:
- linux
- osx
julia:
- 1.0
- 1.1
- 1.2
- 1.3
Expand All @@ -25,7 +24,7 @@ after_success:
jobs:
include:
- stage: "Documentation"
julia: 1.0
julia: 1.3
os: linux
script:
- julia --project=docs/ --color=yes -e '
Expand Down
8 changes: 5 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
name = "BlockArrays"
uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
version = "0.10.2"
version = "0.11"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[compat]
julia = "1"
julia = "1.1"

[extras]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Base64", "Test", "SparseArrays"]
test = ["Base64", "FillArrays", "OffsetArrays", "SparseArrays", "Test"]
3 changes: 1 addition & 2 deletions docs/src/lib/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ Pages = ["internals.md"]
## Internals

```@docs
blockindex2global
global2blockindex
BlockedUnitRange
BlockRange
BlockIndexRange
BlockSlice
Expand Down
7 changes: 5 additions & 2 deletions docs/src/lib/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ AbstractBlockArray
BlockBoundsError
Block
BlockIndex
nblocks
blockaxes
blockisequal
blocksize
blocksizes
blockfirsts
blocklasts
blocklengths
getblock
getblock!
setblock!
Expand Down
44 changes: 34 additions & 10 deletions docs/src/man/abstractblockarrayinterface.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
# The `AbstractBlockSizes` interface
# The block axis interface

In order to follow the `AbstractBlockSizes` the following methods should be implemented:
A block array's block structure is dictated by its axes, which
are typically a `BlockedUnitRange` but may also be a `UnitRange`,
which is assumed to be a single block, or other type that implements
the block axis interface.


| Methods to implement | Brief description |
| :---------------------- | :---------------- |
| `cumulsizes(A)` | A Tuple of abstract vectors storing the cumulative block sizes |
| **Optional methods** |
| `nblocks(A)` | Tuple of number of blocks in each dimension |
| `nblocks(A, i)` | Number of blocks in dimension `i` |
| `blocksize(A, i)` | Size of the block at block index `i` |
| `blockaxes(A)` | A one-tuple returning a range of blocks specifying the block structure |
| `getindex(A, K::Block{1})` | return a unit range of indices in the specified block |
| `blocklasts(A)` | Returns the last index of each block |
| `findblock(A, k)` | return the block that contains the `k`th entry of `A`


# The `AbstractBlockArray` interface

An arrays block structure is inferred from an axes, and therefore every array
is in some sense already a block array:
```julia
julia> A = randn(5,5)
5×5 Array{Float64,2}:
0.452801 -0.416508 1.17406 1.52575 3.1574
0.413142 -1.34722 -1.28597 0.637721 0.30655
0.34907 -0.887615 0.284972 -0.0212884 -0.225832
0.466102 -1.10425 1.49226 0.968436 -2.13637
-0.0971956 -1.7664 -0.592629 -1.48947 1.53418

julia> A[Block(1,1)]
5×5 Array{Float64,2}:
0.452801 -0.416508 1.17406 1.52575 3.1574
0.413142 -1.34722 -1.28597 0.637721 0.30655
0.34907 -0.887615 0.284972 -0.0212884 -0.225832
0.466102 -1.10425 1.49226 0.968436 -2.13637
-0.0971956 -1.7664 -0.592629 -1.48947 1.53418
```
It is possible to override additional functions to improve speed, however.

| Methods to implement | Brief description |
| :---------------------- | :---------------- |
| `blocksizes(A)` | Return a subtype of `AbstractBlockSizes` |
| **Optional methods** |
| **Optional methods** |
| `getblock(A, i...)` | `X[Block(i...)]`, blocked indexing |
| `setblock!(A, v, i...)` | `X[Block(i...)] = v`, blocked index assignment |
| `getblock!(x, A, i)` | `X[i]`, blocked index assignment with in place storage in `x` |

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

With the methods above implemented the following are automatically provided:
With the methods above implemented the following are automatically provided for arrays
that are subtypes of `AbstractBlockArray`:

* A pretty printing `show` function that uses unicode lines to split up the blocks:
```
Expand Down
8 changes: 4 additions & 4 deletions docs/src/man/blockarrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ julia> BlockArray(rand(4, 4), [2,2], [1,1,2])
0.844314 │ 0.794279 │ 0.0421491 0.683791

julia> block_array_sparse = BlockArray(sprand(4, 5, 0.7), [1,3], [2,3])
2×2-blocked 4×5 BlockArray{Float64,2,Array{SparseMatrixCSC{Float64,Int64},2},BlockArrays.BlockSizes{2,Array{Int64,1}}}:
2×2-blocked 4×5 BlockArray{Float64,2,Array{SparseMatrixCSC{Float64,Int64},2},Tuple{BlockedUnitRange{Array{Int64,1}},BlockedUnitRange{Array{Int64,1}}}}:
0.0341601 0.374187 │ 0.0118196 0.299058 0.0
---------------------┼-------------------------------
0.0945445 0.931115 │ 0.0460428 0.0 0.0
Expand Down Expand Up @@ -67,7 +67,7 @@ The `block_type` should be an array type. It specifies the internal block type,

```julia
julia> BlockArray(undef_blocks, SparseVector{Float64, Int}, [1,2])
2-blocked 3-element BlockArray{Float64,1,Array{SparseVector{Float64,Int64},1},BlockArrays.BlockSizes{1,Tuple{Array{Int64,1}}}}:
2-blocked 3-element BlockArray{Float64,1,Array{SparseVector{Float64,Int64},1},Tuple{BlockedUnitRange{Array{Int64,1}}}}:
#undef
------
#undef
Expand Down Expand Up @@ -146,7 +146,7 @@ We can also view and modify views of blocks of `BlockArray` using the `view` syn
julia> A = BlockArray(ones(6), 1:3);

julia> view(A, Block(2))
2-element view(::BlockArray{Float64,1,Array{Array{Float64,1},1},BlockArrays.BlockSizes{1,Tuple{Array{Int64,1}}}}, BlockSlice(Block{1,Int64}((2,)),2:3)) with eltype Float64:
2-element view(::BlockArray{Float64,1,Array{Array{Float64,1},1},Tuple{BlockedUnitRange{Array{Int64,1}}}}, BlockSlice(Block(2),2:3)) with eltype Float64:
1.0
1.0

Expand All @@ -164,7 +164,7 @@ An array can be repacked into a `BlockArray` with `BlockArray(array, block_sizes

```jl
julia> block_array_sparse = BlockArray(sprand(4, 5, 0.7), [1,3], [2,3])
2×2-blocked 4×5 BlockArray{Float64,2,Array{SparseMatrixCSC{Float64,Int64},2},BlockArrays.BlockSizes{2,Tuple{Array{Int64,1}}}}:
2×2-blocked 4×5 BlockArray{Float64,2,Array{SparseMatrixCSC{Float64,Int64},2},Tuple{BlockedUnitRange{Array{Int64,1}},BlockedUnitRange{Array{Int64,1}}}}:
0.0341601 0.374187 │ 0.0118196 0.299058 0.0
---------------------┼-------------------------------
0.0945445 0.931115 │ 0.0460428 0.0 0.0
Expand Down
2 changes: 1 addition & 1 deletion docs/src/man/pseudoblockarrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ We can also view and modify views of blocks of `PseudoBlockArray` using the `vie
julia> A = PseudoBlockArray(ones(6), 1:3);

julia> view(A, Block(2))
2-element view(::PseudoBlockArray{Float64,1,Array{Float64,1},BlockArrays.BlockSizes{1,Tuple{Array{Int64,1}}}}, BlockSlice(Block{1,Int64}((2,)),2:3)) with eltype Float64:
2-element view(::PseudoBlockArray{Float64,1,Array{Float64,1},Tuple{BlockedUnitRange{Array{Int64,1}}}}, BlockSlice(Block(2),2:3)) with eltype Float64:
1.0
1.0

Expand Down
19 changes: 8 additions & 11 deletions src/BlockArrays.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
__precompile__()

module BlockArrays
using Base.Cartesian
using LinearAlgebra

# AbstractBlockArray interface exports
export AbstractBlockArray, AbstractBlockMatrix, AbstractBlockVector, AbstractBlockVecOrMat
export Block, getblock, getblock!, setblock!, nblocks, blocksize, blocksizes, blockcheckbounds, BlockBoundsError, BlockIndex
export BlockRange
export Block, getblock, getblock!, setblock!
export blockaxes, blocksize, blocklength, blockcheckbounds, BlockBoundsError, BlockIndex
export blocklengths, blocklasts, blockfirsts, blockisequal
export BlockRange, blockedrange, BlockedUnitRange

export BlockArray, BlockMatrix, BlockVector, BlockVecOrMat, mortar
export PseudoBlockArray, PseudoBlockMatrix, PseudoBlockVector, PseudoBlockVecOrMat

export undef_blocks, undef
export undef_blocks, undef, findblock, findblockindex

import Base: @propagate_inbounds, Array, to_indices, to_index,
unsafe_indices, first, last, size, length, unsafe_length,
unsafe_convert,
getindex, show,
step,
broadcast, eltype, convert, similar,
@_inline_meta, _maybetail, tail, @_propagate_inbounds_meta, reindex,
RangeIndex, Int, Integer, Number,
Expand All @@ -26,22 +27,18 @@ import Base: @propagate_inbounds, Array, to_indices, to_index,
using Base: ReshapedArray, dataids



import Base: (:), IteratorSize, iterate, axes1
import Base.Broadcast: broadcasted, DefaultArrayStyle, AbstractArrayStyle, Broadcasted
import LinearAlgebra: lmul!, rmul!, AbstractTriangular, HermOrSym, AdjOrTrans,
StructuredMatrixStyle


include("blockindices.jl")
include("blockaxis.jl")
include("abstractblockarray.jl")

include("blocksizes.jl")
include("blockindices.jl")
include("blockarray.jl")
include("pseudo_blockarray.jl")
include("blockrange.jl")
include("views.jl")
include("blockindexrange.jl")
include("show.jl")
include("blockarrayinterface.jl")
include("blockbroadcast.jl")
Expand Down
123 changes: 20 additions & 103 deletions src/abstractblockarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,109 +22,26 @@ const AbstractBlockVector{T} = AbstractBlockArray{T, 1}
const AbstractBlockVecOrMat{T} = Union{AbstractBlockMatrix{T}, AbstractBlockVector{T}}

block2string(b, s) = string(join(map(string,b), '×'), "-blocked ", Base.dims2string(s))
Base.summary(a::AbstractBlockArray) = string(block2string(nblocks(a), size(a)), " ", typeof(a))
_block_summary(a) = string(block2string(blocksize(a), size(a)), " ", typeof(a))
Base.summary(a::AbstractBlockArray) = _block_summary(a)
_show_typeof(io, a) = show(io, typeof(a))
function Base.summary(io::IO, a::AbstractBlockArray)
print(io, block2string(nblocks(a), size(a)))
function _block_summary(io, a)
print(io, block2string(blocksize(a), size(a)))
print(io, ' ')
_show_typeof(io, a)
end
Base.similar(block_array::AbstractBlockArray{T}) where {T} = similar(block_array, T)
Base.IndexStyle(::Type{<:AbstractBlockArray}) = IndexCartesian()

"""
nblocks(A, [dim...])

Returns a tuple containing the number of blocks in a block array. Optionally you can specify
the dimension(s) you want the number of blocks for.

```jldoctest; setup = quote using BlockArrays end
julia> A = BlockArray(rand(5,4,6), [1,4], [1,2,1], [1,2,2,1]);

julia> nblocks(A)
(2, 3, 4)

julia> nblocks(A, 2)
3

julia> nblocks(A, 3, 2)
(4, 3)
```
"""
nblocks(block_array::AbstractArray, i::Integer) = nblocks(block_array)[i]

nblocks(block_array::AbstractArray, i::Vararg{Integer, N}) where {N} =
nblocks(blocksizes(block_array), i...)


"""
Block(inds...)

A `Block` is simply a wrapper around a set of indices or enums so that it can be used to dispatch on. By
indexing a `AbstractBlockArray` with a `Block` the a block at that block index will be returned instead of
a single element.

```jldoctest; setup = quote using BlockArrays end
julia> A = BlockArray(ones(2,3), [1, 1], [2, 1])
2×2-blocked 2×3 BlockArray{Float64,2}:
1.0 1.0 │ 1.0
──────────┼─────
1.0 1.0 │ 1.0

julia> A[Block(1, 1)]
1×2 Array{Float64,2}:
1.0 1.0
```
"""
struct Block{N, T}
n::NTuple{N, T}
Block{N, T}(n::NTuple{N, T}) where {N, T} = new{N, T}(n)
end
Base.summary(io::IO, a::AbstractBlockArray) = _block_summary(io, a)

# avoid to_shape which complicates axes
Base.similar(a::AbstractBlockArray{T}) where {T} = similar(a, T)
Base.similar(a::AbstractBlockArray, ::Type{T}) where {T} = similar(a, T, axes(a))
Base.similar(a::AbstractBlockArray{T}, dims::Tuple) where {T} = similar(a, T, dims)

Block{N, T}(n::Vararg{T, N}) where {N,T} = Block{N, T}(n)
Block{N}(n::Vararg{T, N}) where {N,T} = Block{N, T}(n)
Block() = Block{0,Int}()
Block(n::Vararg{T, N}) where {N,T} = Block{N, T}(n)
Block{1}(n::Tuple{T}) where {T} = Block{1, T}(n)
Block{N}(n::NTuple{N, T}) where {N,T} = Block{N, T}(n)
Block(n::NTuple{N, T}) where {N,T} = Block{N, T}(n)

@inline function Block(blocks::NTuple{N, Block{1, T}}) where {N,T}
Block{N, T}(ntuple(i -> blocks[i].n[1], Val(N)))
end


# The following code is taken from CartesianIndex
@inline (+)(index::Block{N}) where {N} = Block{N}(map(+, index.n))
@inline (-)(index::Block{N}) where {N} = Block{N}(map(-, index.n))

@inline (+)(index1::Block{N}, index2::Block{N}) where {N} =
Block{N}(map(+, index1.n, index2.n))
@inline (-)(index1::Block{N}, index2::Block{N}) where {N} =
Block{N}(map(-, index1.n, index2.n))
@inline min(index1::Block{N}, index2::Block{N}) where {N} =
Block{N}(map(min, index1.n, index2.n))
@inline max(index1::Block{N}, index2::Block{N}) where {N} =
Block{N}(map(max, index1.n, index2.n))

@inline (+)(i::Integer, index::Block) = index+i
@inline (+)(index::Block{N}, i::Integer) where {N} = Block{N}(map(x->x+i, index.n))
@inline (-)(index::Block{N}, i::Integer) where {N} = Block{N}(map(x->x-i, index.n))
@inline (-)(i::Integer, index::Block{N}) where {N} = Block{N}(map(x->i-x, index.n))
@inline (*)(a::Integer, index::Block{N}) where {N} = Block{N}(map(x->a*x, index.n))
@inline (*)(index::Block, a::Integer) = *(a,index)

# comparison
@inline isless(I1::Block{N}, I2::Block{N}) where {N} = Base.IteratorsMD._isless(0, I1.n, I2.n)

# conversions
convert(::Type{T}, index::Block{1}) where {T<:Number} = convert(T, index.n[1])
convert(::Type{T}, index::Block) where {T<:Tuple} = convert(T, index.n)
Base.IndexStyle(::Type{<:AbstractBlockArray}) = IndexCartesian()

Int(index::Block{1}) = Int(index.n[1])
Integer(index::Block{1}) = index.n[1]
Number(index::Block{1}) = index.n[1]
# need to overload axes to return BlockAxis
@inline size(block_array::AbstractBlockArray) = map(length, axes(block_array))
@inline axes(block_array::AbstractBlockArray) = throw(error("axes for ", typeof(block_array), " is not implemented"))

"""
getblock(A, inds...)
Expand Down Expand Up @@ -222,10 +139,12 @@ struct BlockBoundsError <: Exception
a::Any
i::Any
BlockBoundsError() = new()
BlockBoundsError(a::AbstractBlockArray) = new(a)
BlockBoundsError(a::AbstractBlockArray, @nospecialize(i)) = new(a,i)
BlockBoundsError(a::AbstractArray) = new(a)
BlockBoundsError(a::AbstractArray, @nospecialize(i)) = new(a,i)
end

BlockBoundsError(a::AbstractArray, I::Block) = BlockBoundsError(a, I.n)

function Base.showerror(io::IO, ex::BlockBoundsError)
print(io, "BlockBoundsError")
if isdefined(ex, :a)
Expand All @@ -250,7 +169,7 @@ specialize this method if they need to provide custom block bounds checking beha
julia> A = BlockArray(rand(2,3), [1,1], [2,1]);

julia> blockcheckbounds(A, 3, 2)
ERROR: BlockBoundsError: attempt to access 2×2-blocked 2×3 BlockArray{Float64,2,Array{Array{Float64,2},2},BlockArrays.BlockSizes{2,Tuple{Array{Int64,1},Array{Int64,1}}}} at block index [3,2]
ERROR: BlockBoundsError: attempt to access 2×2-blocked 2×3 BlockArray{Float64,2,Array{Array{Float64,2},2},Tuple{BlockedUnitRange{Array{Int64,1}},BlockedUnitRange{Array{Int64,1}}}} at block index [3,2]
[...]
```
"""
Expand All @@ -263,14 +182,12 @@ ERROR: BlockBoundsError: attempt to access 2×2-blocked 2×3 BlockArray{Float64,
end

@inline function blockcheckbounds(::Type{Bool}, A::AbstractArray{T, N}, i::Vararg{Integer, N}) where {T,N}
n = nblocks(A)
n = blockaxes(A)
k = 0
for idx in 1:N # using enumerate here will allocate
k += 1
@inbounds _i = i[idx]
if _i <= 0 || _i > n[k]
return false
end
Block(_i) in n[k] || return false
end
return true
end
Expand Down
Loading