Skip to content

Commit 16005d2

Browse files
authored
BlockSkylineMatrix–Diagonal arithmetic, fix BlockDiagonal (#58)
* Fixed BlockDiagonal * Added BlockSkylineMatrix–{,Bi,Tri,SymTri}Diagonal arithmetic (fixes #57)
1 parent ff8d25a commit 16005d2

File tree

5 files changed

+224
-14
lines changed

5 files changed

+224
-14
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "BlockBandedMatrices"
22
uuid = "ffab5731-97b5-5995-9138-79e8c1846df0"
3-
version = "0.8.8"
3+
version = "0.8.9"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"

src/BlockSkylineMatrix.jl

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct BlockSkylineSizes{BS<:NTuple{2,AbstractUnitRange{Int}}, LL<:AbstractVecto
5252
u::UU
5353
end
5454

55-
const BlockBandedSizes = BlockSkylineSizes{NTuple{2,BlockedUnitRange{Vector{Int}}}, Fill{Int,1,Tuple{OneTo{Int}}}, Fill{Int,1,Tuple{OneTo{Int}}},
55+
const BlockBandedSizes = BlockSkylineSizes{NTuple{2,BlockedUnitRange{Vector{Int}}}, Fill{Int,1,Tuple{OneTo{Int}}}, Fill{Int,1,Tuple{OneTo{Int}}},
5656
BandedMatrix{Int,Matrix{Int},OneTo{Int}}, Vector{Int}}
5757

5858

@@ -247,7 +247,7 @@ BlockBandedMatrix(A::AbstractMatrix) = convert(BlockBandedMatrix, A)
247247
similar(A::BlockSkylineMatrix, T::Type=eltype(A), bs::BlockSkylineSizes=A.block_sizes) =
248248
BlockSkylineMatrix{T}(undef, bs)
249249

250-
axes(A::BlockSkylineMatrix) = A.block_sizes.axes
250+
axes(A::BlockSkylineMatrix) = A.block_sizes.axes
251251

252252
################################
253253
# BlockSkylineMatrix Interface #
@@ -390,7 +390,7 @@ const BlockBandedBlock{T} = SubArray{T,2,<:BlockSkylineMatrix,<:Tuple{<:BlockSli
390390

391391

392392
# gives the columns of parent(V).data that encode the block
393-
_parent_blocks(V::BlockBandedBlock)::Tuple{Int,Int} =
393+
_parent_blocks(V::BlockBandedBlock)::Tuple{Int,Int} =
394394
first(first(parentindices(V)).block.n),first(last(parentindices(V)).block.n)
395395

396396
######################################
@@ -437,3 +437,147 @@ end
437437
throw(BandError(A, J-K))
438438
end
439439
end
440+
441+
"""
442+
copy_accommodating_diagonals(A::BlockSkylineMatrix, diagonals::UnitRange{<:Integer})
443+
444+
Return copy of `A`, ensuring blocks are present covering the
445+
`diagonals` as well.
446+
"""
447+
function copy_accommodating_diagonals(A::BlockSkylineMatrix, diagonals::UnitRange{<:Integer}, ::Type{T}=eltype(A)) where T
448+
checksquareblocks(A)
449+
bs = A.block_sizes
450+
l,u = bs.l,bs.u
451+
ax = first(bs.axes)
452+
453+
if 0 diagonals
454+
l = max.(l, 0)
455+
u = max.(u, 0)
456+
end
457+
458+
for d = extrema(diagonals)
459+
d == 0 && continue # Already taken care of above
460+
v = d > 0 ? u : l
461+
# The j:th element of rows is the row index which the diagonal
462+
# d covers in the j:th column.
463+
rows = clamp.(ax .- d, 1, last(ax))
464+
for (j,i) in enumerate(rows)
465+
# First we find which block the j:th element of the main
466+
# diagonal would occupy.
467+
md_block = findfirst((j), ax.lasts)
468+
469+
# Next we find which block covers row i
470+
d_block = findfirst((i), ax.lasts)
471+
472+
# Finally, we increase the block-bandwidth as necessary
473+
v[md_block] = max(v[md_block], abs(d_block-md_block))
474+
end
475+
end
476+
477+
BlockSkylineMatrix{T}(A, BlockSkylineSizes((ax,ax), l, u))
478+
end
479+
480+
for op in (:-, :+)
481+
@eval begin
482+
function $op(A::BlockSkylineMatrix, I::UniformScaling)
483+
B = copy_accommodating_diagonals(A, 0:0, Base._return_type(+, Tuple{eltype(A), eltype(I)}))
484+
@inbounds for i in axes(A, 1)
485+
B[i,i] = $op(B[i,i], I.λ)
486+
end
487+
B
488+
end
489+
function $op(I::UniformScaling, A::BlockSkylineMatrix)
490+
B = copy_accommodating_diagonals($op(A), 0:0, Base._return_type(+, Tuple{eltype(A), eltype(I)}))
491+
@inbounds for i in axes(A, 1)
492+
B[i,i] += I.λ
493+
end
494+
B
495+
end
496+
497+
function $op(A::BlockSkylineMatrix, D::Diagonal)
498+
B = copy_accommodating_diagonals(A, 0:0, Base._return_type(+, Tuple{eltype(A), eltype(D)}))
499+
@inbounds for i in axes(A, 1)
500+
B[i,i] = $op(B[i,i], D.diag[i])
501+
end
502+
B
503+
end
504+
function $op(D::Diagonal, A::BlockSkylineMatrix)
505+
B = copy_accommodating_diagonals($op(A), 0:0, Base._return_type(+, Tuple{eltype(A), eltype(D)}))
506+
@inbounds for i in axes(A, 1)
507+
B[i,i] += D.diag[i]
508+
end
509+
B
510+
end
511+
512+
function $op(A::BlockSkylineMatrix, Bd::Bidiagonal)
513+
B = copy_accommodating_diagonals(A, Bd.uplo == 'U' ? (0:1) : (-1:0),
514+
Base._return_type(+, Tuple{eltype(A), eltype(Bd)}))
515+
@inbounds for i in axes(A, 1)
516+
B[i,i] = $op(B[i,i], Bd.dv[i])
517+
end
518+
@inbounds for i in 1:size(A, 1)-1
519+
Bd.uplo == 'U' && (B[i,i+1] = $op(B[i,i+1], Bd.ev[i]))
520+
Bd.uplo == 'L' && (B[i+1,i] = $op(B[i+1,i], Bd.ev[i]))
521+
end
522+
B
523+
end
524+
function $op(Bd::Bidiagonal, A::BlockSkylineMatrix)
525+
B = copy_accommodating_diagonals($op(A), Bd.uplo == 'U' ? (0:1) : (-1:0),
526+
Base._return_type(+, Tuple{eltype(A), eltype(Bd)}))
527+
@inbounds for i in axes(A, 1)
528+
B[i,i] += Bd.dv[i]
529+
end
530+
@inbounds for i in 1:size(A, 1)-1
531+
Bd.uplo == 'U' && (B[i,i+1] += Bd.ev[i])
532+
Bd.uplo == 'L' && (B[i+1,i] += Bd.ev[i])
533+
end
534+
B
535+
end
536+
537+
function $op(A::BlockSkylineMatrix, T::Tridiagonal)
538+
B = copy_accommodating_diagonals(A, -1:1, Base._return_type(+, Tuple{eltype(A), eltype(T)}))
539+
@inbounds for i in axes(A, 1)
540+
B[i,i] = $op(B[i,i], T.d[i])
541+
end
542+
@inbounds for i in 1:size(A, 1)-1
543+
B[i,i+1] = $op(B[i,i+1], T.du[i])
544+
B[i+1,i] = $op(B[i+1,i], T.dl[i])
545+
end
546+
B
547+
end
548+
function $op(T::Tridiagonal, A::BlockSkylineMatrix)
549+
B = copy_accommodating_diagonals($op(A), -1:1, Base._return_type(+, Tuple{eltype(A), eltype(T)}))
550+
@inbounds for i in axes(A, 1)
551+
B[i,i] += T.d[i]
552+
end
553+
@inbounds for i in 1:size(A, 1)-1
554+
B[i,i+1] += T.du[i]
555+
B[i+1,i] += T.dl[i]
556+
end
557+
B
558+
end
559+
560+
function $op(A::BlockSkylineMatrix, T::SymTridiagonal)
561+
B = copy_accommodating_diagonals(A, -1:1, Base._return_type(+, Tuple{eltype(A), eltype(T)}))
562+
@inbounds for i in axes(A, 1)
563+
B[i,i] = $op(B[i,i], T.dv[i])
564+
end
565+
@inbounds for i in 1:size(A, 1)-1
566+
B[i,i+1] = $op(B[i,i+1], T.ev[i])
567+
B[i+1,i] = $op(B[i+1,i], T.ev[i])
568+
end
569+
B
570+
end
571+
function $op(T::SymTridiagonal, A::BlockSkylineMatrix)
572+
B = copy_accommodating_diagonals($op(A), -1:1, Base._return_type(+, Tuple{eltype(A), eltype(T)}))
573+
@inbounds for i in axes(A, 1)
574+
B[i,i] += T.dv[i]
575+
end
576+
@inbounds for i in 1:size(A, 1)-1
577+
B[i,i+1] += T.ev[i]
578+
B[i+1,i] += T.ev[i]
579+
end
580+
B
581+
end
582+
end
583+
end

src/interfaceimpl.jl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ const BlockDiagonal{T,VT<:Matrix{T}} = BlockMatrix{T,<:Diagonal{VT}}
2525

2626
BlockDiagonal(A) = mortar(Diagonal(A))
2727

28-
function sizes_from_blocks(A::Diagonal, _)
28+
function sizes_from_blocks(A::Diagonal, _)
2929
# for k = 1:length(A.du)
3030
# size(A.du[k],1) == sz[1][k] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
3131
# size(A.du[k],2) == sz[2][k+1] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
3232
# size(A.dl[k],1) == sz[1][k+1] || throw(ArgumentError("block sizes of lower diagonal inconsisent with diagonal"))
3333
# size(A.dl[k],2) == sz[2][k] || throw(ArgumentError("block sizes of lower diagonal inconsisent with diagonal"))
3434
# end
3535
(size.(A.diag, 1), size.(A.diag,2))
36-
end
36+
end
3737

3838

3939
# Block Bi/Tridiagonal
@@ -43,7 +43,7 @@ const BlockBidiagonal{T,VT<:Matrix{T}} = BlockMatrix{T,<:Bidiagonal{VT}}
4343
BlockTridiagonal(A,B,C) = mortar(Tridiagonal(A,B,C))
4444
BlockBidiagonal(A, B, uplo) = mortar(Bidiagonal(A,B,uplo))
4545

46-
function sizes_from_blocks(A::Tridiagonal, _)
46+
function sizes_from_blocks(A::Tridiagonal, _)
4747
# for k = 1:length(A.du)
4848
# size(A.du[k],1) == sz[1][k] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
4949
# size(A.du[k],2) == sz[2][k+1] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
@@ -53,7 +53,7 @@ function sizes_from_blocks(A::Tridiagonal, _)
5353
(size.(A.d, 1), size.(A.d,2))
5454
end
5555

56-
function sizes_from_blocks(A::Bidiagonal, _)
56+
function sizes_from_blocks(A::Bidiagonal, _)
5757
# for k = 1:length(A.du)
5858
# size(A.du[k],1) == sz[1][k] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
5959
# size(A.du[k],2) == sz[2][k+1] || throw(ArgumentError("block sizes of upper diagonal inconsisent with diagonal"))
@@ -83,21 +83,30 @@ checksquareblocks(A) = blockisequal(axes(A)...) || throw(DimensionMismatch("bloc
8383

8484
for op in (:-, :+)
8585
@eval begin
86-
function $op(A::BlockTridiagonal, λ::UniformScaling)
86+
function $op(A::BlockDiagonal, λ::UniformScaling)
87+
checksquareblocks(A)
88+
mortar(Diagonal(broadcast($op, A.blocks.diag, Ref(λ))))
89+
end
90+
function $op::UniformScaling, A::BlockDiagonal)
91+
checksquareblocks(A)
92+
mortar(Diagonal(broadcast($op, Ref(λ), A.blocks.diag)))
93+
end
94+
95+
function $op(A::BlockTridiagonal, λ::UniformScaling)
8796
checksquareblocks(A)
8897
mortar(Tridiagonal(A.blocks.dl, broadcast($op, A.blocks.d, Ref(λ)), A.blocks.du))
8998
end
90-
function $op::UniformScaling, A::BlockTridiagonal)
99+
function $op::UniformScaling, A::BlockTridiagonal)
91100
checksquareblocks(A)
92101
mortar(Tridiagonal(broadcast($op,A.blocks.dl), broadcast($op, Ref(λ), A.blocks.d), broadcast($op,A.blocks.du)))
93102
end
94-
function $op(A::BlockBidiagonal, λ::UniformScaling)
103+
function $op(A::BlockBidiagonal, λ::UniformScaling)
95104
checksquareblocks(A)
96105
mortar(Bidiagonal(broadcast($op, A.blocks.dv, Ref(λ)), A.blocks.ev, A.blocks.uplo))
97106
end
98-
function $op::UniformScaling, A::BlockBidiagonal)
107+
function $op::UniformScaling, A::BlockBidiagonal)
99108
checksquareblocks(A)
100109
mortar(Bidiagonal(broadcast($op, Ref(λ), A.blocks.dv), broadcast($op,A.blocks.ev), A.blocks.uplo))
101110
end
102111
end
103-
end
112+
end

test/test_blockskyline.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,50 @@ Random.seed!(0)
104104
@test Matrix(AC) == Matrix(A) + Matrix(C)
105105
@test blockisequal(axes(AC),axes(C))
106106
end
107+
108+
@testset "BlockSkylineMatrix arithmetic" begin
109+
t = Int
110+
rows = cols = 1:4
111+
N = sum(rows)
112+
D = Diagonal(ones(t, N))
113+
Bu = Bidiagonal(ones(t, N), 2ones(t, N-1), 'U')
114+
Bl = Bidiagonal(ones(t, N), 2ones(t, N-1), 'L')
115+
T = Tridiagonal(ones(t, N-1), 2ones(t, N), 3ones(t, N-1))
116+
ST = SymTridiagonal(ones(t, N), 2ones(t, N-1))
117+
118+
function check_blockskylinematrix_arithmetic(A, op, B, (l,u))
119+
C = op(A, B)
120+
Cdense = op((A isa BlockSkylineMatrix ? Matrix(A) : A),
121+
(B isa BlockSkylineMatrix ? Matrix(B) : B))
122+
@test C == Cdense
123+
124+
bs = (A isa BlockSkylineMatrix ? A : B).block_sizes
125+
expl = max.(l, bs.l)
126+
expu = max.(u, bs.u)
127+
128+
Cbs = C.block_sizes
129+
@test all(Cbs.l .== expl)
130+
@test all(Cbs.u .== expu)
131+
end
132+
133+
for (l,u) = [([0,0,0,0],[0,0,0,0]),
134+
([-1,-1,-1,-1],[1,2,2,2]),
135+
([-1,-2,-2,-2],[1,1,2,2]),
136+
([2,2,1,1],[-1,-1,-1,-1]),
137+
([2,2,1,1],[-2,-2,-2,-1])]
138+
M = BlockSkylineMatrix{t}(undef, rows, cols, (l,u))
139+
M.data .= 1
140+
for (A,expected_bws) = [(3I, ([0,0,0,0],[0,0,0,0])),
141+
(D, ([0,0,0,0],[0,0,0,0])),
142+
(Bu, ([0,0,0,0],[0,1,1,1])),
143+
(Bl, ([1,1,1,0],[0,0,0,0])),
144+
(T, ([1,1,1,0],[0,1,1,1])),
145+
(ST, ([1,1,1,0],[0,1,1,1]))]
146+
for op = (+, -)
147+
check_blockskylinematrix_arithmetic(M, op, A, expected_bws)
148+
check_blockskylinematrix_arithmetic(A, op, M, expected_bws)
149+
end
150+
end
151+
end
152+
end
107153
end

test/test_misc.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ import BandedMatrices: bandwidths, AbstractBandedMatrix, BandedStyle, bandeddata
2121
@test BandedMatrix(V) == V
2222
end
2323

24+
@testset "Block Diagonal" begin
25+
A = BlockBandedMatrices.BlockDiagonal(fill([1 2],3))
26+
@test blockbandwidths(A) == (0,0)
27+
@test isblockbanded(A)
28+
@test A[Block(1,1)] == [1 2]
29+
@test @inferred(getblock(A,1,2)) == @inferred(A[Block(1,2)]) == [0 0]
30+
@test_throws DimensionMismatch A+I
31+
A = BlockBandedMatrices.BlockDiagonal(fill([1 2; 1 2],3))
32+
@test A+I == I+A == mortar(Diagonal(fill([2 2; 1 3],3))) == Matrix(A) + I
33+
end
34+
2435
@testset "Block Bidiagonal" begin
2536
Bu = BlockBidiagonal(fill([1 2],4), fill([3 4],3), :U)
2637
Bl = BlockBidiagonal(fill([1 2],4), fill([3 4],3), :L)
@@ -52,4 +63,4 @@ end
5263
@test A+I == I+A == mortar(Tridiagonal(fill([1 2; 1 2],3), fill([4 4; 3 5],4), fill([4 5; 4 5],3))) == Matrix(A) + I
5364
@test A-I == mortar(Tridiagonal(fill([1 2; 1 2],3), fill([2 4; 3 3],4), fill([4 5; 4 5],3))) == Matrix(A) - I
5465
@test I-A == mortar(Tridiagonal(fill(-[1 2; 1 2],3), fill([-2 -4; -3 -3],4), fill(-[4 5; 4 5],3))) == I - Matrix(A)
55-
end
66+
end

0 commit comments

Comments
 (0)