Skip to content

Commit

Permalink
Create constructor for creating allocated BlockArrays (#39)
Browse files Browse the repository at this point in the history
* • Support BlockArray{Float64}(1:3, 1:3), etc. syntax for creating allocated block arrays
• Deprecate BlockArray(blocks,...) in favour of _BlockArray(blocks,...) to avoid confusion with BlockArray(arr,...) function that converts arr to a BlockArray.

* add unitialized_BlockArray, and fix doc strings

* uninitialized_BlockArray(...) -> BlockArray(uninitialized, ...)

* increase coverage, updated man

* REQUIRE Compat 0.38

* 0.7 tests pass

* Always uses uninitialized, adds unitialized_blocks for BlockArray
  • Loading branch information
dlfivefifty authored and KristofferC committed Dec 2, 2017
1 parent d59bb0e commit d88a881
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 52 deletions.
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
julia 0.6
Compat 0.30.0
Compat 0.38.0
2 changes: 2 additions & 0 deletions docs/src/lib/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ blockcheckbounds

```@docs
BlockArray
uninitialized_blocks
UninitializedBlocks
```


Expand Down
33 changes: 24 additions & 9 deletions docs/src/man/blockarrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,45 @@ DocTestSetup = quote
end
```

## Creating uninitialized `BlockArrays`.

A `BlockArray` can be created with the blocks left uninitialized using the `BlockArray(block_type, block_sizes...)` function.
The `block_type` should be an array type, it could for example be `Matrix{Float64}`. The block sizes are each a `Vector{Int}` which determines the size of the blocks in that dimension. We here create a `[1,2]×[3,2]` block matrix of `Float32`s:
## Creating uninitialized `BlockArrays`

A block array can be created with initialized blocks using the `BlockArray{T}(block_sizes)`
function. The block_sizes are each an `AbstractVector{Int}` which determines the size of the blocks in that dimension. We here create a `[1,2]×[3,2]` block matrix of `Float32`s:
```julia
julia> BlockArray{Float32}(uninitialized, [1,2], [3,2])
2×2-blocked 3×5 BlockArrays.BlockArray{Float32,2,Array{Float32,2}}:
9.39116f-26 1.4013f-45 3.34245f-219.39064f-26 1.4013f-45
───────────────────────────────────────┼──────────────────────────
3.28434f-21 9.37645f-26 3.28436f-218.05301f-24 9.39077f-26
1.4013f-45 1.4013f-45 1.4013f-451.4013f-45 1.4013f-45
```
We can also any other user defined array type that supports `similar`.

## Creating `BlockArrays` with uninitialized blocks.

A `BlockArray` can be created with the blocks left uninitialized using the `BlockArray(uninitialized, block_type, block_sizes...)` function.
The `block_type` should be an array type, it could for example be `Matrix{Float64}`. The block sizes are each an `AbstractVector{Int}` which determines the size of the blocks in that dimension. We here create a `[1,2]×[3,2]` block matrix of `Float32`s:

```jldoctest
julia> BlockArray(Matrix{Float32}, [1,2], [3,2])
julia> BlockArray{Float32}(uninitialized_blocks, [1,2], [3,2])
2×2-blocked 3×5 BlockArrays.BlockArray{Float32,2,Array{Float32,2}}:
#undef #undef #undef │ #undef #undef
------------------------┼----------------
#undef #undef #undef │ #undef #undef
#undef #undef #undef │ #undef #undef
```

We can also use a `SparseVector` or any other user defined array type:
We can also use a `SparseVector` or any other user defined array type by
specifying it as the second argument:

```jl
julia> BlockArray(SparseVector{Float64, Int}, [1,2])
julia> BlockArray(uninitialized_blocks, SparseVector{Float64, Int}, [1,2])
2-blocked 3-element BlockArrays.BlockArray{Float64,1,SparseVector{Float64,Int64}}:
#undef
------
#undef
#undef
julia> BlockArray(SparseVector{Float64, Int}, [1,2])
```

Note that accessing an undefined block will throw an "access to undefined reference"-error.
Expand All @@ -41,7 +56,7 @@ An alternative syntax for this is `block_array[Block(i...)] = v` or
`block_array[Block.(i)...]`.

```jldoctest
julia> block_array = BlockArray(Matrix{Float64}, [1,2], [2,2])
julia> block_array = BlockArray{Float64}(unitialized_blocks, [1,2], [2,2])
2×2-blocked 3×4 BlockArrays.BlockArray{Float64,2,Array{Float64,2}}:
#undef #undef │ #undef #undef
----------------┼----------------
Expand Down Expand Up @@ -109,7 +124,7 @@ julia> view(A, Block(2)) .= [3,4]; A[Block(2)]

## Converting between `BlockArray` and normal arrays

An array can be repacked into a `BlockArray` with`BlockArray(array, block_sizes...)`:
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])
Expand Down
15 changes: 15 additions & 0 deletions docs/src/man/pseudoblockarrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ julia> pseudo = PseudoBlockArray(rand(3,3), [1,2], [2,1])

This "takes ownership" of the passed in array so no copy of the array is made.


## Creating initialized `BlockArrays`

A block array can be created with uninitialized entries using the `BlockArray{T}(uninitialized, block_sizes...)`
function. The block_sizes are each an `AbstractVector{Int}` which determines the size of the blocks in that dimension. We here create a `[1,2]×[3,2]` block matrix of `Float32`s:
```julia
julia> PseudoBlockArray{Float32}(uninitialized, [1,2], [3,2])
2×2-blocked 3×5 BlockArrays.BlockArray{Float32,2,Array{Float32,2}}:
9.39116f-26 1.4013f-45 3.34245f-219.39064f-26 1.4013f-45
───────────────────────────────────────┼──────────────────────────
3.28434f-21 9.37645f-26 3.28436f-218.05301f-24 9.39077f-26
1.4013f-45 1.4013f-45 1.4013f-451.4013f-45 1.4013f-45
```
We can also any other user defined array type that supports `similar`.

## Setting and getting blocks and values

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
Expand Down
9 changes: 6 additions & 3 deletions src/BlockArrays.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
__precompile__()

module BlockArrays
using Base.Cartesian
using Compat

# AbstractBlockArray interface exports
export AbstractBlockArray, AbstractBlockMatrix, AbstractBlockVector, AbstractBlockVecOrMat
Expand All @@ -10,13 +12,14 @@ export BlockRange
export BlockArray, BlockMatrix, BlockVector, BlockVecOrMat
export PseudoBlockArray, PseudoBlockMatrix, PseudoBlockVector, PseudoBlockVecOrMat

export uninitialized_blocks, UninitializedBlocks, uninitialized, Uninitialized

import Base: @propagate_inbounds, Array, to_indices, to_index, indices,
unsafe_indices, indices1, first, last, size, length, unsafe_length,
getindex, show, start, next, done, @_inline_meta, _maybetail, tail,
colon, broadcast, eltype, iteratorsize

using Base.Cartesian
using Compat



include("abstractblockarray.jl")
Expand All @@ -29,6 +32,6 @@ include("blockrange.jl")
include("views.jl")
include("convert.jl")
include("show.jl")

include("deprecate.jl")

end # module
154 changes: 126 additions & 28 deletions src/blockarray.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,55 @@
# Note: Functions surrounded by a comment blocks are there because `Vararg` is still allocating.
# When Vararg is fast enough, they can simply be removed.


#######################
# UninitializedBlocks #
#######################

"""
UninitializedBlocks
Singleton type used in block array initialization, indicating the
array-constructor-caller would like an uninitialized block array. See also
uninitialized_blocks (@ref), an alias for UninitializedBlocks().
Examples
≡≡≡≡≡≡≡≡≡≡
julia> BlockArray(uninitialized_blocks, Matrix{Float32}, [1,2], [3,2])
2×2-blocked 3×5 BlockArrays.BlockArray{Float32,2,Array{Float32,2}}:
#undef #undef #undef │ #undef #undef
------------------------┼----------------
#undef #undef #undef │ #undef #undef
#undef #undef #undef │ #undef #undef
"""
struct UninitializedBlocks end

"""
uninitialized_blocks
Alias for UninitializedBlocks(), which constructs an instance of the singleton
type UninitializedBlocks (@ref), used in block array initialization to indicate the
array-constructor-caller would like an uninitialized block array.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> BlockArray(uninitialized_blocks, Matrix{Float32}, [1,2], [3,2])
2×2-blocked 3×5 BlockArrays.BlockArray{Float32,2,Array{Float32,2}}:
#undef #undef #undef │ #undef #undef
------------------------┼----------------
#undef #undef #undef │ #undef #undef
#undef #undef #undef │ #undef #undef
"""
const uninitialized_blocks = UninitializedBlocks()

##############
# BlockArray #
##############

function _BlockArray end

"""
BlockArray{T, N, R <: AbstractArray{T, N}} <: AbstractBlockArray{T, N}
Expand All @@ -16,15 +61,15 @@ In the type definition, `R` defines the array type that each block has, for exam
struct BlockArray{T, N, R <: AbstractArray{T, N}} <: AbstractBlockArray{T, N}
blocks::Array{R, N}
block_sizes::BlockSizes{N}
end

# Auxilary outer constructors
function BlockArray(blocks::Array{R, N}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}}
return BlockArray{T, N, R}(blocks, block_sizes)
global function _BlockArray(blocks::Array{R, N}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}}
new{T, N, R}(blocks, block_sizes)
end
end

function BlockArray(blocks::Array{R, N}, block_sizes::Vararg{Vector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
return BlockArray{T, N, R}(blocks, BlockSizes(block_sizes...))
# Auxilary outer constructors
function _BlockArray(blocks::Array{R, N}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
return _BlockArray(blocks, BlockSizes(block_sizes...))
end

const BlockMatrix{T, R <: AbstractMatrix{T}} = BlockArray{T, 2, R}
Expand All @@ -35,11 +80,25 @@ const BlockVecOrMat{T, R} = Union{BlockMatrix{T, R}, BlockVector{T, R}}
# Constructors #
################

@inline function _BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
_BlockArray(R, BlockSizes(block_sizes...))
end

function _BlockArray(::Type{R}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}}
n_blocks = nblocks(block_sizes)
blocks = Array{R, N}(uninitialized, n_blocks)
_BlockArray(blocks, block_sizes)
end

@inline function uninitialized_blocks_BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
_BlockArray(R, block_sizes...)
end

"""
Constructs a `BlockArray` with uninitialized blocks from a block type `R` with sizes defind by `block_sizes`.
```jldoctest
julia> BlockArray(Matrix{Float64}, [1,3], [2,2])
julia> BlockArray(uninitialized_blocks, Matrix{Float64}, [1,3], [2,2])
2×2-blocked 4×4 BlockArrays.BlockArray{Float64,2,Array{Float64,2}}:
#undef │ #undef #undef #undef │
--------┼--------------------------┼
Expand All @@ -49,17 +108,66 @@ julia> BlockArray(Matrix{Float64}, [1,3], [2,2])
#undef │ #undef #undef #undef │
```
"""
@inline function BlockArray(::Type{R}, block_sizes::Vararg{Vector{Int}, N}) where {T,N,R <: AbstractArray{T, N}}
BlockArray(R, BlockSizes(block_sizes...))
@inline function BlockArray(::UninitializedBlocks, ::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
uninitialized_blocks_BlockArray(R, block_sizes...)
end

function BlockArray(::Type{R}, block_sizes::BlockSizes{N}) where {T,N,R <: AbstractArray{T, N}}
n_blocks = nblocks(block_sizes)
blocks = Array{R, N}(n_blocks)
BlockArray{T,N,R}(blocks, block_sizes)
@inline function BlockArray{T}(::UninitializedBlocks, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N}
BlockArray(uninitialized_blocks, Array{T,N}, block_sizes...)
end

@inline function BlockArray{T,N}(::UninitializedBlocks, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N}
BlockArray(uninitialized_blocks, Array{T,N}, block_sizes...)
end

@inline function BlockArray{T,N,R}(::UninitializedBlocks, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
BlockArray(uninitialized_blocks, R, block_sizes...)
end

function BlockArray(arr::AbstractArray{T, N}, block_sizes::Vararg{Vector{Int}, N}) where {T,N}


@generated function initialized_blocks_BlockArray(::Type{R}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}}
return quote
block_arr = _BlockArray(R, block_sizes)
@nloops $N i i->(1:nblocks(block_sizes, i)) begin
block_index = @ntuple $N i
setblock!(block_arr, similar(R, blocksize(block_sizes, block_index)), block_index...)
end

return block_arr
end
end


function initialized_blocks_BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
initialized_blocks_BlockArray(R, BlockSizes(block_sizes...))
end

@inline function BlockArray{T}(::Uninitialized, block_sizes::BlockSizes{N}) where {T, N}
initialized_blocks_BlockArray(Array{T, N}, block_sizes)
end

@inline function BlockArray{T, N}(::Uninitialized, block_sizes::BlockSizes{N}) where {T, N}
initialized_blocks_BlockArray(Array{T, N}, block_sizes)
end

@inline function BlockArray{T, N, R}(::Uninitialized, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}}
initialized_blocks_BlockArray(R, block_sizes)
end

@inline function BlockArray{T}(::Uninitialized, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N}
initialized_blocks_BlockArray(Array{T, N}, block_sizes...)
end

@inline function BlockArray{T, N}(::Uninitialized, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N}
initialized_blocks_BlockArray(Array{T, N}, block_sizes...)
end

@inline function BlockArray{T, N, R}(::Uninitialized, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}}
initialized_blocks_BlockArray(R, block_sizes...)
end

function BlockArray(arr::AbstractArray{T, N}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T,N}
for i in 1:N
if sum(block_sizes[i]) != size(arr, i)
throw(DimensionMismatch("block size for dimension $i: $(block_sizes[i]) does not sum to the array size: $(size(arr, i))"))
Expand All @@ -70,7 +178,7 @@ end

@generated function BlockArray(arr::AbstractArray{T, N}, block_sizes::BlockSizes{N}) where {T,N}
return quote
block_arr = BlockArray(typeof(arr), block_sizes)
block_arr = _BlockArray(typeof(arr), block_sizes)
@nloops $N i i->(1:nblocks(block_sizes, i)) begin
block_index = @ntuple $N i
indices = globalrange(block_sizes, block_index)
Expand All @@ -81,13 +189,6 @@ end
end
end


BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T,N,R <: AbstractArray{T, N}} =
BlockArray(R, Vector{Int}.(block_sizes)...)

BlockArray(arr::AbstractArray{T, N}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T,N} =
BlockArray(arr, Vector{Int}.(block_sizes)...)

################################
# AbstractBlockArray Interface #
################################
Expand All @@ -114,14 +215,11 @@ end
###########################

@inline function Base.similar(block_array::BlockArray{T,N}, ::Type{T2}) where {T,N,T2}
BlockArray(similar(block_array.blocks, Array{T2, N}), copy(block_array.block_sizes))
_BlockArray(similar(block_array.blocks, Array{T2, N}), copy(block_array.block_sizes))
end

@generated function Base.size(arr::BlockArray{T,N}) where {T,N}
exp = Expr(:tuple, [:(arr.block_sizes[$i][end] - 1) for i in 1:N]...)
return quote
@inbounds return $exp
end
@inline function Base.size(arr::BlockArray{T,N}) where {T,N}
size(arr.block_sizes)
end

@inline function Base.getindex(block_arr::BlockArray{T, N}, i::Vararg{Int, N}) where {T,N}
Expand Down
8 changes: 8 additions & 0 deletions src/blocksizes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ end
return exp
end

# Gives the total sizes
@generated function Base.size(block_sizes::BlockSizes{N}) where {N}
exp = Expr(:tuple, [:(block_sizes[$i][end] - 1) for i in 1:N]...)
return quote
@inbounds return $exp
end
end

function Base.show(io::IO, block_sizes::BlockSizes{N}) where {N}
if N == 0
print(io, "[]")
Expand Down
4 changes: 4 additions & 0 deletions src/deprecate.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@deprecate BlockArray(blocks::Array{R, N}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}} BlockArrays._BlockArray(blocks, block_sizes)
@deprecate BlockArray(blocks::Array{R, N}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}} BlockArrays._BlockArray(blocks, block_sizes...)
@deprecate BlockArray(::Type{R}, block_sizes::BlockSizes{N}) where {T, N, R <: AbstractArray{T, N}} BlockArrays._BlockArray(R, block_sizes)
@deprecate BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T, N, R <: AbstractArray{T, N}} BlockArrays._BlockArray(R, block_sizes...)
Loading

0 comments on commit d88a881

Please sign in to comment.