Skip to content

Commit f3d3d59

Browse files
committed
Added BlockSkylineMatrix–{,Bi,Tri,SymTri}Diagonal arithmetic (fixes #57)
1 parent 29cdfb6 commit f3d3d59

File tree

6 files changed

+197
-21
lines changed

6 files changed

+197
-21
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/BlockBandedMatrices.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import BandedMatrices: isbanded, bandwidths, bandwidth, banded_getindex, colrang
4545
_banded_colval, _banded_rowval, _banded_nzval # for sparse
4646

4747
export BandedBlockBandedMatrix, BlockBandedMatrix, BlockSkylineMatrix, blockbandwidth, blockbandwidths,
48-
subblockbandwidth, subblockbandwidths, Ones, Zeros, Fill, Block, BlockDiagonal, BlockTridiagonal, BlockBidiagonal, isblockbanded
48+
subblockbandwidth, subblockbandwidths, Ones, Zeros, Fill, Block, BlockTridiagonal, BlockBidiagonal, isblockbanded
4949

5050

5151
const Block1 = Block{1,Int}

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: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,3 @@ for op in (:-, :+)
110110
end
111111
end
112112
end
113-
114-
function replace_in_print_matrix(A::BlockDiagonal, i::Integer, j::Integer, s::AbstractString)
115-
bi = findblockindex.(axes(A), (i,j))
116-
I,J = block.(bi)
117-
i,j = blockindex.(bi)
118-
Int(J-I) == 0 ? s : Base.replace_with_centered_mark(s)
119-
end
120-
121-
function replace_in_print_matrix(A::BlockTridiagonal, i::Integer, j::Integer, s::AbstractString)
122-
bi = findblockindex.(axes(A), (i,j))
123-
I,J = block.(bi)
124-
i,j = blockindex.(bi)
125-
-1 Int(J-I)  1 ? s : Base.replace_with_centered_mark(s)
126-
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import BandedMatrices: bandwidths, AbstractBandedMatrix, BandedStyle, bandeddata
2222
end
2323

2424
@testset "Block Diagonal" begin
25-
A = BlockDiagonal(fill([1 2],3))
25+
A = BlockBandedMatrices.BlockDiagonal(fill([1 2],3))
2626
@test blockbandwidths(A) == (0,0)
2727
@test isblockbanded(A)
2828
@test A[Block(1,1)] == [1 2]
2929
@test @inferred(getblock(A,1,2)) == @inferred(A[Block(1,2)]) == [0 0]
3030
@test_throws DimensionMismatch A+I
31-
A = BlockDiagonal(fill([1 2; 1 2],3))
31+
A = BlockBandedMatrices.BlockDiagonal(fill([1 2; 1 2],3))
3232
@test A+I == I+A == mortar(Diagonal(fill([2 2; 1 3],3))) == Matrix(A) + I
3333
end
3434

0 commit comments

Comments
 (0)