Skip to content

Commit bbeaf85

Browse files
author
Pawel Latawiec
committed
Add LimitedMemoryMatrix for storage
1 parent 901d096 commit bbeaf85

File tree

6 files changed

+183
-85
lines changed

6 files changed

+183
-85
lines changed

src/IterativeSolvers.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ include("history.jl")
1414
include("hessenberg.jl")
1515

1616
# Krylov subspace methods
17+
include("limited_memory_matrices.jl")
1718
include("lal.jl")
1819

1920
# Linear solvers

src/lal.jl

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ mutable struct LookAheadLanczosDecomp{OpT, OptT, VecT, MatT, ElT, ElRT}
4141
q::VecT
4242
::VecT
4343
::VecT
44-
P::MatT
45-
Q::MatT
44+
P::LimitedMemoryMatrix{ElT, MatT}
45+
Q::LimitedMemoryMatrix{ElT, MatT}
4646

4747
# V-W sequence
4848
v::VecT
4949
w::VecT
5050
::VecT
5151
::VecT
52-
V::MatT
53-
W::MatT
52+
V::LimitedMemoryMatrix{ElT, MatT}
53+
W::LimitedMemoryMatrix{ElT, MatT}
5454

5555
# matrix-vector products
5656
Ap::VecT
@@ -153,8 +153,8 @@ function LookAheadLanczosDecomp(
153153
q = similar(v)
154154
= similar(v)
155155
= similar(v)
156-
P = similar(v, size(v, 1), 0)
157-
Q = similar(v, size(v, 1), 0)
156+
P = LimitedMemoryMatrix(similar(v, size(v, 1), 0), max_block_size)
157+
Q = LimitedMemoryMatrix(similar(v, size(v, 1), 0), max_block_size)
158158
Ap = similar(v)
159159
Atq = similar(v)
160160
qtAp = zero(elT)
@@ -163,8 +163,8 @@ function LookAheadLanczosDecomp(
163163

164164
= similar(v)
165165
= similar(v)
166-
V = reshape(copy(v), size(v, 1), 1)
167-
W = reshape(copy(w), size(v, 1), 1)
166+
V = LimitedMemoryMatrix(copy(v), max_block_size)
167+
W = LimitedMemoryMatrix(copy(w), max_block_size)
168168
w̃tṽ = zero(elT)
169169

170170
wtv = transpose(w) * v
@@ -451,10 +451,6 @@ function _update_U!(ld, innerp)
451451
# U is upper triangular matrix in decomposition of recurrence relation for P-Q sequence
452452
# updates last column of U
453453
n, mk, k, kstar = ld.n, ld.mk, ld.k, ld.kstar
454-
#idx_offset = mk[kstar]-1
455-
idx_offset = 0
456-
# TODO
457-
# we only store the entries from mk[kstar] to n-1
458454
ld.U = UpperTriangular(
459455
[
460456
ld.U fill(0.0, n-1, 1)
@@ -463,12 +459,12 @@ function _update_U!(ld, innerp)
463459
)
464460

465461
for i = kstar:k-1
466-
block_start = mk[i]-idx_offset
467-
block_end = mk[i+1]-1-idx_offset
462+
block_start = mk[i]
463+
block_end = mk[i+1]-1
468464
ld.U[block_start:block_end, end] .= ld.E[block_start:block_end, block_start:block_end] \ ld.F̃lastcol[block_start:block_end]
469465
end
470466
if !innerp && !isone(n)
471-
ld.U[mk[k]-idx_offset:end-1, end] .= ld.E[mk[k]:end, mk[k]:end] \ ld.F̃lastcol[mk[k]-idx_offset:end]
467+
ld.U[mk[k]:end-1, end] .= ld.E[mk[k]:end, mk[k]:end] \ ld.F̃lastcol[mk[k]:end]
472468
end
473469
return ld
474470
end
@@ -478,11 +474,11 @@ function _update_p̂q̂_common!(ld)
478474
mk, k, kstar = ld.mk, ld.k, ld.kstar
479475
copyto!(ld.p̂, ld.v)
480476
copyto!(ld.q̂, ld.w)
481-
#idx_offset = mk[kstar]-1
482-
idx_offset = 0
483477
for i = mk[kstar]:mk[k]-1 # TODO: OPTIMIZE gemv! (or 5-arg mul!)
484-
axpy!(-ld.U[i-idx_offset, end], ld.P[:, i-idx_offset], ld.p̂)
485-
axpy!(-ld.U[i-idx_offset, end] * ld.γ[end] / ld.γ[i-idx_offset], ld.Q[:, i-idx_offset], ld.q̂)
478+
if ld.U[i, end] != 0
479+
axpy!(-ld.U[i, end], ld.P[:, i], ld.p̂)
480+
axpy!(-ld.U[i, end] * ld.γ[end] / ld.γ[i], ld.Q[:, i], ld.q̂)
481+
end
486482
end
487483
end
488484
function _update_Gnm1!(ld)
@@ -527,10 +523,11 @@ function _update_pq_regular!(ld)
527523
n, mk, k, kstar = ld.n, ld.mk, ld.k, ld.kstar
528524
copyto!(ld.p, ld.p̂)
529525
copyto!(ld.q, ld.q̂)
530-
idx_offset = 0
531526
for i = mk[k]:n-1 # TODO: OPTIMIZE gemv! (or 5-arg mul!)
532-
axpy!(-ld.U[i-idx_offset, end], ld.P[:, i], ld.p)
533-
axpy!(-ld.U[i-idx_offset, end] * ld.γ[n] / ld.γ[i - idx_offset], ld.Q[:, i], ld.q)
527+
if ld.U[i, end] != 0
528+
axpy!(-ld.U[i, end], ld.P[:, i], ld.p)
529+
axpy!(-ld.U[i, end] * ld.γ[n] / ld.γ[i], ld.Q[:, i], ld.q)
530+
end
534531
end
535532
return ld
536533
end
@@ -540,11 +537,13 @@ function _update_pq_inner!(ld)
540537
n, mk, k, kstar = ld.n, ld.mk, ld.k, ld.kstar
541538
copyto!(ld.p, ld.p̂)
542539
copyto!(ld.q, ld.q̂)
543-
idx_offset = 0
544540
for i = mk[k]:n-1 # TODO: OPTIMIZE gemv!
545-
ld.U[i-idx_offset, end] = _u(i, n, mk[k])
546-
axpy!(-_u(i, n, mk[k]), ld.P[:, i], ld.p)
547-
axpy!(-_u(i, n, mk[k]) * ld.γ[n] / ld.γ[i - idx_offset], ld.Q[:, i], ld.q)
541+
u = _u(i, n, mk[k])
542+
ld.U[i, end] = u
543+
if u != 0
544+
axpy!(-u, ld.P[:, i], ld.p)
545+
axpy!(-u * ld.γ[n] / ld.γ[i], ld.Q[:, i], ld.q)
546+
end
548547
end
549548
return ld
550549
end
@@ -558,8 +557,8 @@ function _matvec_pq!(ld, retry=false)
558557
ld.P[:, end] .= ld.p
559558
ld.Q[:, end] .= ld.q
560559
else
561-
ld.P = [ld.P ld.p]
562-
ld.Q = [ld.Q ld.q]
560+
hcat!(ld.P, ld.p)
561+
hcat!(ld.Q, ld.q)
563562
end
564563
mul!(ld.Ap, ld.A, ld.p)
565564
ld.qtAp = transpose(ld.q) * ld.Ap
@@ -647,11 +646,11 @@ function _update_v̂ŵ_common!(ld)
647646
# 5.2.6
648647
l, lstar, nl, n = ld.l, ld.lstar, ld.nl, ld.n
649648

650-
# idx_offset = nl[lstar]-1
651-
idx_offset = 0
652649
for i = nl[lstar]:nl[l]-1 # TODO: OPTIMIZE gemv! (or 5-arg mul!)
653-
axpy!(-ld.L[i-idx_offset, n], ld.V[:, i-idx_offset], ld.Ap)
654-
axpy!(-ld.L[i-idx_offset, n] * ld.γ[n] / ld.γ[i-idx_offset], ld.W[:, i-idx_offset], ld.Atq)
650+
if ld.L[i, n] != 0
651+
axpy!(-ld.L[i, n], ld.V[:, i], ld.Ap)
652+
axpy!(-ld.L[i, n] * ld.γ[n] / ld.γ[i], ld.W[:, i], ld.Atq)
653+
end
655654
end
656655
return ld
657656
end
@@ -691,11 +690,11 @@ function _update_vw_regular!(ld)
691690
n, l, lstar, nl, k, mk = ld.n, ld.l, ld.lstar, ld.nl, ld.k, ld.mk
692691
copyto!(ld.ṽ, ld.Ap)
693692
copyto!(ld.w̃, ld.Atq)
694-
#idx_offset = nl[lstar]-1
695-
idx_offset = 0
696693
for i = nl[l]:n # TODO: OPTIMIZE gemv! (or 5-arg mul!)
697-
axpy!(-ld.L[i-idx_offset, end], ld.V[:, i], ld.ṽ)
698-
axpy!(-ld.L[i-idx_offset, end] * ld.γ[n] / ld.γ[i - idx_offset], ld.W[:, i], ld.w̃)
694+
if ld.L[i, end] != 0
695+
axpy!(-ld.L[i, end], ld.V[:, i], ld.ṽ)
696+
axpy!(-ld.L[i, end] * ld.γ[n] / ld.γ[i], ld.W[:, i], ld.w̃)
697+
end
699698
end
700699
return ld
701700
end
@@ -705,12 +704,13 @@ function _update_vw_inner!(ld)
705704
n, l, k, lstar, nl, mk = ld.n, ld.l, ld.k, ld.lstar, ld.nl, ld.mk
706705
copyto!(ld.ṽ, ld.Ap)
707706
copyto!(ld.w̃, ld.Atq)
708-
#idx_offset = nl[lstar]-1
709-
idx_offset = 0
710707
for i = nl[l]:n # TODO: OPTIMIZE gemv!
711-
ld.L[i-idx_offset, end] = _l(i, n, nl[l])
712-
axpy!(-_l(i, n, nl[l]), ld.V[:, i], ld.ṽ)
713-
axpy!(-_l(i, n, nl[l]) * ld.γ[n] / ld.γ[i - idx_offset], ld.W[:, i], ld.w̃)
708+
ll = _l(i, n, nl[l])
709+
ld.L[i, end] = ll
710+
if ll != 0
711+
axpy!(-_l(i, n, nl[l]), ld.V[:, i], ld.ṽ)
712+
axpy!(-_l(i, n, nl[l]) * ld.γ[n] / ld.γ[i], ld.W[:, i], ld.w̃)
713+
end
714714
end
715715
return ld
716716
end
@@ -743,8 +743,8 @@ function _update_vw!(ld)
743743
ld.v = ld./ ld.ρ
744744
ld.w = ld./ ld.ξ
745745
ld.wtv = ld.w̃tṽ / (ld.ρ * ld.ξ)
746-
ld.V = [ld.V ld.v]
747-
ld.W = [ld.W ld.w]
746+
hcat!(ld.V, ld.v)
747+
hcat!(ld.W, ld.w)
748748
return ld
749749
end
750750

src/limited_memory_matrices.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""
2+
LimitedMemoryMatrix
3+
4+
A matrix which keeps only the last `memory` columns. Access outside these columns throws an error. Thus, the underlying storage is a matrix of size `(N, memory)` where `N` is the size of the first dimension
5+
"""
6+
mutable struct LimitedMemoryMatrix{T, V<:AbstractMatrix{T}} <: AbstractMatrix{T}
7+
value::V
8+
memory::Int
9+
hsize::Int
10+
11+
function LimitedMemoryMatrix{T, V}(value::V, memory, hsize) where {T, V<:AbstractMatrix{T}}
12+
@assert Base.require_one_based_indexing(value)
13+
return new{T, V}(value, memory, hsize)
14+
end
15+
end
16+
17+
function LimitedMemoryMatrix(mat::AbstractMatrix{T}, memory) where T
18+
value = similar(mat, size(mat, 1), memory)
19+
value[:, end-size(mat, 2)+1:end] .= mat
20+
return LimitedMemoryMatrix{T, typeof(value)}(value, memory, size(mat, 2))
21+
end
22+
function LimitedMemoryMatrix(vec::AbstractVector{T}, memory) where T
23+
value = similar(vec, size(vec, 1), memory)
24+
value[:, end] .= vec
25+
return LimitedMemoryMatrix{T, typeof(value)}(value, memory, 1)
26+
end
27+
28+
Base.size(A::LimitedMemoryMatrix) = (size(A.value, 1), A.hsize)
29+
Base.similar(A::LimitedMemoryMatrix) = LimitedMemoryMatrix(similar(A.value), A.memory, A.hsize)
30+
Base.similar(A::LimitedMemoryMatrix, ::Type{T}) where T = LimitedMemoryMatrix(similar(A.value, T), A.memory, A.hsize)
31+
Base.isempty(A::LimitedMemoryMatrix) = size(A, 1) == 0 || size(A, 2) == 0
32+
33+
Base.@propagate_inbounds function Base.setindex!(A::LimitedMemoryMatrix, v, i::Integer, j::Integer)
34+
lowest_stored_index = size(A, 2) - A.memory + 1
35+
if j < lowest_stored_index || j > size(A, 2)
36+
throw(
37+
ArgumentError(
38+
"Cannot set index ($i, $j) out of memory range, memory kept from $(max(1, lowest_stored_index)) to $(size(A, 2)))"
39+
)
40+
)
41+
else
42+
jj = j - lowest_stored_index + 1
43+
A.value[i, jj] = v
44+
end
45+
return v
46+
end
47+
48+
Base.@propagate_inbounds function Base.getindex(A::LimitedMemoryMatrix, i::Integer, j::Integer)
49+
lowest_stored_index = size(A, 2) - A.memory + 1
50+
if j < lowest_stored_index || j > size(A, 2)
51+
throw(
52+
ArgumentError(
53+
"Cannot get index ($i, $j) out of memory range, memory kept from $(max(1, lowest_stored_index)) to $(size(A, 2)))"
54+
)
55+
)
56+
else
57+
@inbounds v = A.value[i, j - lowest_stored_index + 1]
58+
end
59+
return v
60+
end
61+
62+
function Base.show(io::IO, ::MIME"text/plain", A::LimitedMemoryMatrix) where {T}
63+
print(io, Base.dims2string(size(A)), " ")
64+
Base.showarg(io, A.value, true)
65+
end
66+
67+
"""
68+
hcat!(A::LimitedMemoryMatrix, B::AbstractVector)
69+
70+
Concatenates the vector `B` in-place to `A`, shifting the stored columns and increasing the
71+
size of `A`
72+
"""
73+
function hcat!(A::LimitedMemoryMatrix, B::AbstractVector)
74+
@assert size(B, 1) == size(A, 1)
75+
A.hsize += 1
76+
A.value[:, 1:end-1] .= A.value[:, 2:end]
77+
A.value[:, end] .= B
78+
return A
79+
end
80+
81+
"""
82+
LimitedMemoryBandedMatrix
83+
84+
A matrix which keeps only the last `memory` columns. Access outside these columns throws an
85+
error. Additionally, the matrix is understood to be banded, such that only values
86+
`band_offset` from the diagonal are non-zero, where the band is of width `band_width`. Thus, the underlying storage is a matrix of size `(band_width, memory)`
87+
"""
88+
mutable struct LimitedMemoryBandedMatrix{T, V<:AbstractMatrix{T}} <: AbstractMatrix{T}
89+
value::V
90+
memory::Int
91+
size::Tuple{Int, Int}
92+
band_offset::Int
93+
band_width::Int
94+
end

0 commit comments

Comments
 (0)