Skip to content

Commit 34792c3

Browse files
author
Pawel Latawiec
committed
BlockDiagonal experiment
1 parent 113b436 commit 34792c3

File tree

3 files changed

+133
-46
lines changed

3 files changed

+133
-46
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ uuid = "42fd0dbc-a981-5370-80f2-aaf504508153"
33
version = "0.9.2"
44

55
[deps]
6+
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"
67
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
78
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
89
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
@@ -12,3 +13,4 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1213
[compat]
1314
RecipesBase = "0.6, 0.7, 0.8, 1.0"
1415
julia = "1.3"
16+
BlockDiagonals = "0.1"

src/lal.jl

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Printf
2+
import BlockDiagonals: BlockDiagonal
3+
import BlockDiagonals
24
import Base: iterate
3-
import LinearAlgebra: UpperTriangular
5+
import LinearAlgebra: UpperTriangular, UpperHessenberg
46

57
"""
68
LookAheadLanczosDecompOptions
@@ -71,21 +73,23 @@ mutable struct LookAheadLanczosDecomp{OpT, OptT, VecT, MatT, ElT, ElRT}
7173
γ::Vector{ElRT}
7274

7375
# Eq. 2.13
74-
D::Matrix{ElT}
76+
D::BlockDiagonal{ElT, Matrix{ElT}}
7577
# Eq. 3.14
76-
E::Matrix{ElT}
78+
E::BlockDiagonal{ElT, Matrix{ElT}}
7779
# Defined after Eq. 5.1
78-
F::Matrix{ElT}
80+
F::BlockDiagonal{ElT, Matrix{ElT}}
7981
F̃lastcol::Vector{ElT}
8082
# Eq. 5.1
8183
G::Vector{ElT}
8284
# Eq. 3.11
8385
H::Vector{ElT}
8486

8587
# Eq. 3.9
88+
# need to keep previous columns of U for G checks
8689
U::UpperTriangular{ElT, Matrix{ElT}}
87-
L::Matrix{ElT}
88-
90+
# need to keep previous columns of L for H checks
91+
L::UpperHessenberg{ElT, Matrix{ElT}}
92+
8993
# Indices tracking location in block and sequence
9094
n::Int
9195
k::Int
@@ -173,16 +177,16 @@ function LookAheadLanczosDecomp(
173177
γ = Vector{real(elT)}(undef, 1)
174178
γ[1] = 1.0
175179

176-
D = Matrix{elT}(undef, 0, 0)
177-
E = Matrix{elT}(undef, 0, 0)
180+
D = BlockDiagonal{elT, Matrix{elT}}(Vector{Matrix{elT}}())
181+
E = BlockDiagonal{elT, Matrix{elT}}(Vector{Matrix{elT}}())
178182
G = Vector{elT}()
179183
H = Vector{elT}()
180184

181-
F = Matrix{elT}(undef, 0, 0)
185+
F = BlockDiagonal{elT, Matrix{elT}}(Vector{Matrix{elT}}())
182186
F̃lastcol = Vector{elT}()
183187

184188
U = UpperTriangular(Matrix{elT}(undef, 0, 0))
185-
L = Matrix{elT}(undef, 0, 0)
189+
L = UpperHessenberg(Matrix{elT}(undef, 0, 0))
186190

187191
# Alg 5.2.0
188192
n = 1
@@ -241,6 +245,34 @@ _VW_block_size(ld) = ld.n+1 - ld.nl[ld.l]
241245
_VW_prev_block_size(ld) = ld.nl[ld.l] - ld.nl[max(1, ld.l-1)]
242246
_is_block_small(ld, n) = n < ld.opts.max_block_size
243247

248+
"""
249+
_grow_last_block!(A, Bcol, Brow, Bcorner)
250+
251+
Grows the last block in-place in `A` by appending the column `Bcol`, the row `Brow`, and the corner element `Bcorner`. `Bcol` and `Brow` are automatically truncated to match the size of the grown block
252+
"""
253+
function _grow_last_block!(A::BlockDiagonal{T, TM}, Bcol, Brow, Bcorner) where {T, TM}
254+
n = BlockDiagonals.nblocks(A)
255+
b = BlockDiagonals.blocks(A)
256+
s = size(last(b), 1)
257+
b[n] = TM([
258+
b[n] Bcol[end-s+1:end]
259+
Brow[:, end-s+1:end] Bcorner
260+
])
261+
return A
262+
end
263+
264+
"""
265+
_start_new_block!(A, B)
266+
267+
Appends a new block to the end of `A` with `B`
268+
"""
269+
function _start_new_block!(A::BlockDiagonal{T, TM}, B) where {T, TM}
270+
push!(BlockDiagonals.blocks(A), TM(fill(only(B), 1, 1)))
271+
return A
272+
end
273+
274+
Base.size(B::BlockDiagonals.BlockDiagonal) = sum(firstsize, BlockDiagonals.blocks(B), init=0), sum(lastsize, BlockDiagonals.blocks(B), init=0)
275+
244276
start(::LookAheadLanczosDecomp) = 1
245277
done(ld::LookAheadLanczosDecomp, iteration::Int) = iteration ld.opts.max_iter
246278
function iterate(ld::LookAheadLanczosDecomp, n::Int=start(ld))
@@ -288,7 +320,7 @@ function _update_PQ_sequence!(ld)
288320
if !innerp
289321
# Alg. 5.2.8
290322
_update_pq_regular!(ld)
291-
_mv_pq!(ld)
323+
_matvec_pq!(ld)
292324
# Alg. 5.2.9
293325
_update_Gn!(ld)
294326
innerp = inner_ok && _check_G(ld)
@@ -306,19 +338,19 @@ function _update_PQ_sequence!(ld)
306338
# Alg. 5.2.11
307339
isverbose(ld) && @info "Inner P-Q construction, second G check"
308340
_update_pq_inner!(ld)
309-
_mv_pq!(ld, true)
341+
_matvec_pq!(ld, true)
310342
end
311343
else
312344
# Alg. 5.2.11
313345
isverbose(ld) && @info "Inner P-Q construction, first G check"
314346
_update_pq_inner!(ld)
315-
_mv_pq!(ld)
347+
_matvec_pq!(ld)
316348
end
317349
else
318350
# Alg. 5.2.11
319351
isverbose(ld) && @info "Inner P-Q construction, singular E check"
320352
_update_pq_inner!(ld)
321-
_mv_pq!(ld)
353+
_matvec_pq!(ld)
322354
end
323355
ld.innerp = innerp
324356
return ld
@@ -397,20 +429,16 @@ function _update_D!(ld)
397429
# Alg. 5.2.1
398430
# Eq. 5.2:
399431
# F[n-1] = Wt[n-1]V[n]L[n-1] = D[n-1]L[1:n-1, 1:n-1] + l[n, n-1]D[1:n-1, n][0 ... 0 1]
400-
# => D[1:end-1, end] = (F[:, end] - (D_prev L[1:end-1, 1:end]))[:, end] / ρ
432+
# => D[1:end-1, end] = (F[:, end] - (D_prev L[1:end-1, end])) / ρ
401433
# Eq. 3.15, (D Γ)ᵀ = (D Γ)
402434
# D[n, n] = wtv
403435

404-
# TODO: closed block
405-
if isone(ld.n)
406-
ld.D = fill(ld.wtv, 1, 1)
436+
if isone(ld.n) || _VW_block_size(ld) == 1
437+
_start_new_block!(ld.D, ld.wtv)
407438
else
408-
D_lastcol = (ld.F[:, end] - (ld.D * ld.L[1:end-1, :])[:, end]) / ld.ρ
409-
D_lastrow = D_lastcol * ld.γ[end] ./ ld.γ[1:end-1]
410-
ld.D = [
411-
ld.D D_lastcol
412-
transpose(D_lastrow) ld.wtv
413-
]
439+
D_lastcol = (ld.F[:, end] - (ld.D * ld.L[1:end-1, end])) / ld.ρ
440+
D_lastrow = transpose(D_lastcol * ld.γ[end] ./ ld.γ[1:end-1])
441+
_grow_last_block!(ld.D, D_lastcol, D_lastrow, ld.wtv)
414442
end
415443
return ld
416444
end
@@ -433,14 +461,17 @@ function _update_Flastrow!(ld)
433461
# Eq. 5.2 (w/ indices advanced):
434462
# F_{n} = D_{n}L[1:n, 1:n] + l[n+1, n]D_{n}[1:n, n+1][0 ... 0 1]
435463
# TODO: block
436-
if !isone(ld.n) # We only need to do this if we are constructing a block
464+
if isone(ld.n)
465+
_start_new_block!(ld.F, 0.0)
466+
else
437467
Flastrow = reshape(ld.D[end:end, :] * ld.L, :)
438468
ld.F̃lastcol = Flastrow .* ld.γ[1:end-1] ./ ld.γ[end]
439469
# we are not able to fill in the last column yet, so we fill with zero
440-
ld.F = [
441-
ld.F fill(0.0, size(ld.F, 1))
442-
transpose(Flastrow) 0.0
443-
]
470+
if _VW_block_size(ld) == 1
471+
_grow_last_block!(ld.F, fill(0.0, size(ld.F, 1)), transpose(Flastrow), 0.0)
472+
else
473+
_grow_last_block!(ld.F, fill(0.0, size(ld.F, 1)), transpose(Flastrow), 0.0)
474+
end
444475
end
445476
end
446477

@@ -453,6 +484,7 @@ function _update_U!(ld, innerp)
453484
idx_offset = 0
454485
# TODO
455486
# we only store the entries from mk[kstar] to n-1
487+
456488
ld.U = UpperTriangular(
457489
[
458490
ld.U fill(0.0, n-1, 1)
@@ -547,7 +579,7 @@ function _update_pq_inner!(ld)
547579
return ld
548580
end
549581

550-
function _mv_pq!(ld, retry=false)
582+
function _matvec_pq!(ld, retry=false)
551583
# Common part of Alg. 5.2.8, Alg. 5.2.11
552584
# if retry, then this means we have already added data to the vectors, but our
553585
# inner block check failed, so we overwrite what he have. This is the case if
@@ -580,16 +612,13 @@ function _update_E!(ld)
580612
# 5.2.14
581613
n = ld.n
582614

583-
if isone(ld.n)
584-
ld.E = fill(ld.qtAp, 1, 1)
615+
if isone(ld.n) || _PQ_block_size(ld) == 1
616+
_start_new_block!(ld.E, ld.qtAp)
585617
else
586618
ΓUtinvΓ = ld.γ .* transpose(ld.U) ./ transpose(ld.γ)
587-
Elastrow = (ΓUtinvΓ \ ld.F[1:n, 1:n-1])[n, :]
619+
Elastrow = (ΓUtinvΓ[end, end] \ ld.F[n:n, 1:n-1] - ΓUtinvΓ[end:end, 1:end-1]*ld.E)
588620
Elastcol = (Elastrow .* ld.γ[1:n-1] ./ ld.γ[n])
589-
ld.E = [
590-
ld.E Elastcol
591-
transpose(Elastrow) ld.qtAp
592-
]
621+
_grow_last_block!(ld.E, Elastcol, Elastrow, ld.qtAp)
593622
end
594623
return ld
595624
end
@@ -608,7 +637,7 @@ function _update_Flastcol!(ld)
608637
ΓUtinvΓ = ld.γ .* transpose(ld.U) ./ transpose(ld.γ)
609638
# length n, ld.F_lastrow of length n-1
610639
if isone(n)
611-
ld.F = fill(ΓUtinvΓ[end, end] * ld.E[end, end], 1, 1)
640+
ld.F[1, 1] = ΓUtinvΓ[end, end] * ld.E[end, end]
612641
else
613642
ld.F[:, end] .= ΓUtinvΓ * ld.E[:, end]
614643
end
@@ -625,14 +654,19 @@ function _update_L!(ld, innerv)
625654
Llastcol[block_start:block_end] .= ld.D[block_start:block_end, block_start:block_end] \ ld.F[block_start:block_end, end]
626655
end
627656
if !innerv
657+
@show ld.D
628658
Llastcol[nl[l]:end] .= ld.D[nl[l]:end, nl[l]:end] \ ld.F[nl[l]:end, end]
629659
end
630660
if isone(n)
631-
ld.L = reshape([Llastcol[1]
661+
ld.L = UpperHessenberg(
662+
reshape([Llastcol[1]
632663
0.0], 2, 1)
664+
)
633665
else
634-
ld.L = [ld.L Llastcol
635-
fill(0.0, 1, n)]
666+
ld.L = UpperHessenberg(
667+
[ld.L Llastcol
668+
fill(0.0, 1, n)]
669+
)
636670
end
637671
return ld
638672
end

test/lal.jl

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,28 @@ using Test
66
# Equation references and identities from:
77
# Freund, R. W., & Nachtigal, N. M. (1994). An Implementation of the QMR Method Based on Coupled Two-Term Recurrences. SIAM Journal on Scientific Computing, 15(2), 313–337. https://doi.org/10.1137/0915022
88

9+
function _append_leading_nonzeros(A, B)
10+
# appends the leading nonzero diagonal and subdiagonal elements in `B` to `A`. This
11+
# grows `A` in size by 1. For instance, if B is [1 2; 3 4], then if `A` is `[0;]`, this
12+
# returns [0 2; 3 4]. If `A` is bigger than `B`, then the off-diagonal elements are
13+
# padded with 0
14+
@assert size(A, 1) size(B, 1)-1
15+
if size(A, 1) == size(B, 1)-1
16+
Aapp = [
17+
A B[1:end-1, end:end]
18+
B[end:end, :]
19+
]
20+
else
21+
zcol = zeros(eltype(A), 1 + size(A, 1) - size(B, 1), 1)
22+
zrow = zeros(eltype(A), 1, 1 + size(A, 2) - size(B, 2))
23+
Aapp = [
24+
A [zcol; B[1:end-1, end:end]]
25+
zrow B[end:end, :]
26+
]
27+
end
28+
return Aapp
29+
end
30+
931
function _iterate_and_collect_lal_intermediates(ld)
1032
# iterates through ld and collects the intermediate matrices by appending the last
1133
# row and column to the matrix being built
@@ -45,13 +67,14 @@ function _iterate_and_collect_lal_intermediates(ld)
4567
U = ld.U[:, :]
4668
L = ld.L[:, :]
4769
else
48-
D = [D ld.D[1:end-1, end]; ld.D[end:end, :]]
49-
E = [E ld.E[1:end-1, end]; ld.E[end:end, :]]
50-
F = [F ld.F[1:end-1, end]; ld.F[end:end, :]]
70+
ivw = IS._VW_block_size(ld)
71+
D = _append_leading_nonzeros(D, ld.D)
72+
E = _append_leading_nonzeros(E, ld.E)
73+
F = _append_leading_nonzeros(F, ld.F)
5174
# F̃ row is not explicitly calculated, so we calculate from F using Lemma 5.1
5275
= [F̃ ld.F̃lastcol; (transpose(F * Γ[1:end-1, 1:end-1]) / Γ[1:end-1, 1:end-1])[end:end, :]]
53-
U = [U ld.U[1:end-1, end]; ld.U[end:end, :]]
54-
L = [L ld.L[1:end-1, end]; ld.L[end:end, :]]
76+
U = _append_leading_nonzeros(U, ld.U)
77+
L = _append_leading_nonzeros(L, ld.L)
5578
end
5679
end
5780

@@ -134,6 +157,34 @@ function test_regular_lal_identities(ld, log; early_exit=false)
134157
end
135158
end
136159

160+
@testset "Block Diagonal Utilities" begin
161+
for T in (Matrix, UpperTriangular)
162+
@testset "$T" begin
163+
A = IS.BlockDiagonal([T([1 2; 3 4]), T(fill(1, 1, 1))])
164+
Bcol = [-1]
165+
Brow = [1]
166+
Bcorner = 0
167+
IS._grow_last_block!(A, Bcol, Brow, Bcorner)
168+
# note that we are converting the container type, so upon conversion to UpperTriangular the sub-diagonals will go to 0 and the equality is satisfied
169+
@test A T([
170+
1 2 0 0
171+
3 4 0 0
172+
0 0 1 -1
173+
0 0 1 0
174+
])
175+
176+
A = IS.BlockDiagonal([T([1 2; 3 4]), T(fill(1, 1, 1))])
177+
IS._start_new_block!(A, 1)
178+
@test A T([
179+
1 2 0 0
180+
3 4 0 0
181+
0 0 1 0
182+
0 0 0 1
183+
])
184+
end
185+
end
186+
end
187+
137188
@testset "A = I" begin
138189
# A = I terminates immediately (because p1 = v1 -> v2 = Ap1 - v1 = 0)
139190
A = Diagonal(fill(1.0, 5))

0 commit comments

Comments
 (0)