Skip to content

Commit da57ea7

Browse files
TSGutdlfivefifty
andauthored
Implement finite section based adaptive QL (#125)
* basic implementation of finite section based QL * import AbstractCachedMatrix * bug fixes + basic tests work * more tests and lowertriangular layout * simplify loops somewhat * stop checking full finite sections + refactor * refactor before working on matching QL structure * change underlying structs * work towards expanding tau and factors simultaneously * Update infql.jl * explicitly import some lazyarrays functions + type declarations * big style overhaul * Update src/infql.jl Co-authored-by: Sheehan Olver <[email protected]> * tidying, test fix, todo comment * move getindex from here to LazyArrays * Update Project.toml * change Q implementation to be lazy * import the default getQ * bug fix and tidy lazy implementation * fix adjoint * reinstatate some changes * add tests, remove a function * add a test for complex entries * add test for ldiv behavior of Q * LazyVector -> LayoutVector * nzzeros -> last(colsupport * restore non-tridiagonal functionality * Update test_infql.jl * Add matrix tests, remove unnecessary * overrides * lazy multiplication by Q * Update Project.toml --------- Co-authored-by: Sheehan Olver <[email protected]>
1 parent 3ae935a commit da57ea7

File tree

5 files changed

+412
-203
lines changed

5 files changed

+412
-203
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "InfiniteLinearAlgebra"
22
uuid = "cde9dba0-b1de-11e9-2c62-0bab9446c55c"
3-
version = "0.6.17"
3+
version = "0.6.18"
44

55
[deps]
66
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -25,7 +25,7 @@ BlockBandedMatrices = "0.11.5, 0.12"
2525
DSP = "0.7"
2626
FillArrays = "0.13, 1"
2727
InfiniteArrays = "0.12"
28-
LazyArrays = "0.22, 1"
28+
LazyArrays = "1.1.1"
2929
LazyBandedMatrices = "0.8.7"
3030
MatrixFactorizations = "0.9.6, 1"
3131
SemiseparableMatrices = "0.3"

src/InfiniteLinearAlgebra.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ import Base.Broadcast: BroadcastStyle, Broadcasted, broadcasted
1212
import ArrayLayouts: colsupport, rowsupport, triangularlayout, MatLdivVec, triangulardata, TriangularLayout, TridiagonalLayout,
1313
sublayout, _qr, __qr, MatLmulVec, MatLmulMat, AbstractQLayout, materialize!, diagonaldata, subdiagonaldata, supdiagonaldata,
1414
_bidiag_forwardsub!, mulreduce, RangeCumsum, _factorize, transposelayout, ldiv!, lmul!, mul, CNoPivot
15-
import BandedMatrices: BandedMatrix, _BandedMatrix, AbstractBandedMatrix, bandeddata, bandwidths, BandedColumns, bandedcolumns,
15+
import BandedMatrices: BandedMatrix, _BandedMatrix, AbstractBandedMatrix, bandeddata, bandwidths, BandedColumns, bandedcolumns, BandedLayout,
1616
_default_banded_broadcast, banded_similar
1717
import FillArrays: AbstractFill, getindex_value, axes_print_matrix_row
1818
import InfiniteArrays: OneToInf, InfUnitRange, Infinity, PosInfinity, InfiniteCardinal, InfStepRange, AbstractInfUnitRange, InfAxes, InfRanges
1919
import LinearAlgebra: matprod, qr, AbstractTriangular, AbstractQ, adjoint, transpose, AdjOrTrans
2020
import LazyArrays: applybroadcaststyle, CachedArray, CachedMatrix, CachedVector, DenseColumnMajor, FillLayout, ApplyMatrix, check_mul_axes, LazyArrayStyle,
2121
resizedata!, MemoryLayout,
2222
factorize, sub_materialize, LazyLayout, LazyArrayStyle, layout_getindex,
23-
applylayout, ApplyLayout, PaddedLayout, CachedLayout, cacheddata, zero!, MulAddStyle,
24-
LazyArray, LazyMatrix, LazyVector, paddeddata, arguments
25-
import MatrixFactorizations: ul, ul!, _ul, ql, ql!, _ql, QLPackedQ, getL, getR, getU, reflector!, reflectorApply!, QL, QR, QRPackedQ,
23+
applylayout, ApplyLayout, PaddedLayout, CachedLayout, AbstractCachedVector, AbstractCachedMatrix, cacheddata, zero!, MulAddStyle, ApplyArray,
24+
LazyArray, LazyMatrix, LazyVector, paddeddata, arguments, resizedata!
25+
import MatrixFactorizations: ul, ul!, _ul, ql, ql!, _ql, QLPackedQ, getL, getR, getQ, getU, reflector!, reflectorApply!, QL, QR, QRPackedQ,
2626
QRPackedQLayout, AdjQRPackedQLayout, QLPackedQLayout, AdjQLPackedQLayout, LayoutQ
2727

2828
import BlockArrays: AbstractBlockVecOrMat, sizes_from_blocks, _length, BlockedUnitRange, blockcolsupport, BlockLayout, AbstractBlockLayout, BlockSlice

src/banded/hessenbergq.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ function materialize!(L::MatLmulVec{<:HessenbergQLayout{'L'}})
112112
Q, x = L.A,L.B
113113
T = eltype(Q)
114114
t = Array{T}(undef, 2)
115-
nz = nzzeros(x,1)
115+
nz = last(colsupport(x))
116116
for n = 1:length(Q.q)
117117
v = view(x, n:n+1)
118118
mul!(t, Q.q[n], v)
@@ -126,7 +126,7 @@ function materialize!(L::MatLmulVec{<:HessenbergQLayout{'U'}})
126126
Q, x = L.A,L.B
127127
T = eltype(Q)
128128
t = Array{T}(undef, 2)
129-
for n = min(length(Q.q),nzzeros(x,1)):-1:1
129+
for n = min(length(Q.q),last(colsupport(x))):-1:1
130130
v = view(x, n:n+1)
131131
mul!(t, Q.q[n], v)
132132
v .= t

src/infql.jl

Lines changed: 170 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,9 @@ function ql_hessenberg!(B::InfBandedMatrix{TT}; kwds...) where TT
9292
QLHessenberg(_BandedMatrix(H, ℵ₀, l, 1), Vcat( LowerHessenbergQ(F.Q).q, F∞.q))
9393
end
9494

95-
getindex(Q::QLPackedQ{T,<:InfBandedMatrix{T}}, i::Int, j::Int) where T =
96-
(Q'*[Zeros{T}(i-1); one(T); Zeros{T}(∞)])[j]'
97-
getindex(Q::QLPackedQ{<:Any,<:InfBandedMatrix}, I::AbstractVector{Int}, J::AbstractVector{Int}) =
98-
[Q[i,j] for i in I, j in J]
99-
10095
getL(Q::QL, ::NTuple{2,InfiniteCardinal{0}}) = LowerTriangular(Q.factors)
10196
getL(Q::QLHessenberg, ::NTuple{2,InfiniteCardinal{0}}) = LowerTriangular(Q.factors)
10297

103-
# number of structural non-zeros in axis k
104-
nzzeros(A::AbstractArray, k) = size(A,k)
105-
nzzeros(::Zeros, k) = 0
106-
nzzeros(B::Vcat, k) = sum(size.(B.args[1:end-1],k))
107-
nzzeros(B::CachedArray, k) = max(B.datasize[k], nzzeros(B.array,k))
108-
function nzzeros(B::AbstractMatrix, k)
109-
l,u = bandwidths(B)
110-
k == 1 ? size(B,2) + l : size(B,1) + u
111-
end
11298

11399
function materialize!(M::Lmul{<:QLPackedQLayout{<:BandedColumns},<:PaddedLayout})
114100
A,B = M.A,M.B
@@ -123,7 +109,7 @@ function materialize!(M::Lmul{<:QLPackedQLayout{<:BandedColumns},<:PaddedLayout}
123109
D = Afactors.data
124110
for k = 1:
125111
ν = k
126-
allzero = k > nzzeros(B,1) ? true : false
112+
allzero = k > last(colsupport(B)) ? true : false
127113
for j = 1:nB
128114
vBj = B[k,j]
129115
for i = max(1-u):k-1
@@ -157,7 +143,7 @@ function materialize!(M::Lmul{<:AdjQLPackedQLayout{<:BandedColumns},<:PaddedLayo
157143
l,u = bandwidths(Afactors)
158144
D = Afactors.data
159145
@inbounds begin
160-
for k = nzzeros(B,1)+u:-1:1
146+
for k = last(colsupport(B))+u:-1:1
161147
ν = k
162148
for j = 1:nB
163149
vBj = B[k,j]
@@ -175,15 +161,6 @@ function materialize!(M::Lmul{<:AdjQLPackedQLayout{<:BandedColumns},<:PaddedLayo
175161
B
176162
end
177163

178-
function _lmul_cache(A::Union{AbstractMatrix{T},AbstractQ{T}}, x::AbstractVector{S}) where {T,S}
179-
TS = promote_op(matprod, T, S)
180-
lmul!(A, cache(convert(AbstractVector{TS},x)))
181-
end
182-
183-
(*)(A::QLPackedQ{T,<:InfBandedMatrix}, x::AbstractVector) where {T} = _lmul_cache(A, x)
184-
(*)(A::AdjointQtype{T,<:QLPackedQ{T,<:InfBandedMatrix}}, x::AbstractVector) where {T} = _lmul_cache(A, x)
185-
(*)(A::QLPackedQ{T,<:InfBandedMatrix}, x::LazyVector) where {T} = _lmul_cache(A, x)
186-
(*)(A::AdjointQtype{T,<:QLPackedQ{T,<:InfBandedMatrix}}, x::LazyVector) where {T} = _lmul_cache(A, x)
187164

188165

189166
function blocktailiterate(c,a,b, d=c, e=a)
@@ -246,7 +223,7 @@ function lmul!(adjA::AdjointQtype{<:Any,<:QLPackedQ{<:Any,<:InfBlockBandedMatrix
246223
l = 2l+1
247224
u = 2u+1
248225
@inbounds begin
249-
for k = nzzeros(B,1)+u:-1:1
226+
for k = last(colsupport(B))+u:-1:1
250227
ν = k
251228
for j = 1:nB
252229
vBj = B[k,j]
@@ -290,7 +267,7 @@ function materialize!(M::MatLdivVec{<:TriangularLayout{'L','N',BandedColumns{Per
290267
throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal"))
291268
end
292269
data = triangulardata(A)
293-
nz = nzzeros(b,1)
270+
nz = last(colsupport(b))
294271
@inbounds for j in 1:n
295272
iszero(data[j,j]) && throw(SingularException(j))
296273
bj = b[j] = data[j,j] \ b[j]
@@ -333,8 +310,6 @@ function _ql(::SymTridiagonalLayout, ::NTuple{2,OneToInf{Int}}, A, args...; kwds
333310
ql(_BandedMatrix(Hcat(dat, [ev∞,d∞,ev∞] * Ones{T}(1,∞)), ℵ₀, 1, 1), args...; kwds...)
334311
end
335312

336-
337-
338313
# TODO: This should be redesigned as ql(BandedMatrix(A))
339314
# But we need to support dispatch on axes
340315
function _ql(::TridiagonalLayout, ::NTuple{2,OneToInf{Int}}, A, args...; kwds...)
@@ -378,3 +353,169 @@ function LazyBandedMatrices._SymTridiagonal(::Tuple{TriangularLayout{'L', 'N', P
378353
ev = [A[k,k+1] for k=1:m-1]
379354
SymTridiagonal([dv; Fill(dv[end],∞)], [ev; Fill(ev[end],∞)])
380355
end
356+
357+
###
358+
# Experimental adaptive finite section QL
359+
###
360+
mutable struct AdaptiveQLTau{T} <: AbstractCachedVector{T}
361+
data::Vector{T}
362+
M::AbstractMatrix{T}
363+
datasize::Integer
364+
tol::Real
365+
AdaptiveQLTau{T}(D, M, N::Integer, tol) where T = new{T}(D, M, N, tol)
366+
end
367+
mutable struct AdaptiveQLFactors{T} <: AbstractCachedMatrix{T}
368+
data::BandedMatrix{T}
369+
M::AbstractMatrix{T}
370+
datasize::Tuple{Int, Int}
371+
tol::Real
372+
AdaptiveQLFactors{T}(D, M, N::Tuple{Int, Int}, tol) where T = new{T}(D, M, N, tol)
373+
end
374+
375+
size(::AdaptiveQLFactors) = (ℵ₀, ℵ₀)
376+
size(::AdaptiveQLTau) = (ℵ₀, )
377+
378+
# adaptive QL accepts optional tolerance
379+
function ql(A::InfBandedMatrix{T}, tol = eps(float(real(T)))) where T
380+
factors, τ = initialadaptiveQLblock(A, tol)
381+
QL(AdaptiveQLFactors{T}(factors, A, size(factors), tol),AdaptiveQLTau{T}(τ, A, length(τ), tol))
382+
end
383+
384+
# Computes the initial data for the finite section based QL decomposition
385+
function initialadaptiveQLblock(A::AbstractMatrix{T}, tol) where T
386+
maxN = 10000 # Prevent runaway loop
387+
j = 50 # We initialize with a 50 × 50 block that is adaptively expanded
388+
Lerr = one(real(T))
389+
N = j
390+
checkinds = max(1,j-bandwidth(A,1)-bandwidth(A,2))
391+
@inbounds Ls = ql(A[checkinds:N,checkinds:N]).L[2:j-checkinds+1,2:j-checkinds+1]
392+
@inbounds while Lerr > tol
393+
# compute QL for small finite section and large finite section
394+
Ll = ql(A[checkinds:2N,checkinds:2N]).L[2:j-checkinds+1,2:j-checkinds+1]
395+
# compare bottom right sections and stop if desired level of convergence achieved
396+
Lerr = norm(Ll-Ls,2)
397+
if N == maxN
398+
error("Reached max. iterations in adaptive QL without convergence to desired tolerance.")
399+
end
400+
Ls = Ll
401+
N = 2*N
402+
end
403+
F = ql(A[1:(N÷2),1:(N÷2)])
404+
return (F.factors[1:50,1:50], F.τ[1:50])
405+
end
406+
407+
# Resize and filling functions for cached implementation
408+
function resizedata!(K::AdaptiveQLFactors, nm...)
409+
nm = maximum(nm)
410+
νμ = K.datasize[1]
411+
if nm > νμ
412+
olddata = copy(K.data)
413+
K.data = similar(K.data, nm, nm)
414+
K.data[axes(olddata)...] = olddata
415+
inds = νμ:nm
416+
cache_filldata!(K, inds)
417+
K.datasize = size(K.data)
418+
end
419+
K
420+
end
421+
422+
function resizedata!(K::AdaptiveQLTau, nm...)
423+
nm = maximum(nm)
424+
νμ = K.datasize
425+
if nm > νμ
426+
resize!(K.data,nm)
427+
cache_filldata!(K, νμ:nm)
428+
K.datasize = size(K.data,1)
429+
end
430+
K
431+
end
432+
433+
function cache_filldata!(A::AdaptiveQLFactors{T}, inds::UnitRange{Int}) where T
434+
j = maximum(inds)
435+
maxN = 1000*j # Prevent runaway loop
436+
Lerr = one(real(T))
437+
N = j
438+
checkinds = max(1,j-bandwidth(A.M,1)-bandwidth(A.M,2))
439+
@inbounds Ls = ql(A.M[checkinds:N,checkinds:N]).L[2:j-checkinds+1,2:j-checkinds+1]
440+
@inbounds while Lerr > A.tol
441+
# compute QL for small finite section and large finite section
442+
Ll = ql(A.M[checkinds:2N,checkinds:2N]).L[2:j-checkinds+1,2:j-checkinds+1]
443+
# compare bottom right sections and stop if desired level of convergence achieved
444+
Lerr = norm(Ll-Ls,2)
445+
if N == maxN
446+
error("Reached max. iterations in adaptive QL without convergence to desired tolerance.")
447+
end
448+
Ls = Ll
449+
N = 2*N
450+
end
451+
A.data = ql(A.M[1:(N÷2),1:(N÷2)]).factors[1:j,1:j]
452+
end
453+
454+
function cache_filldata!(A::AdaptiveQLTau{T}, inds::UnitRange{Int}) where T
455+
j = maximum(inds)
456+
maxN = 1000*j
457+
Lerr = one(real(T))
458+
N = j
459+
checkinds = max(1,j-bandwidth(A.M,1)-bandwidth(A.M,2))
460+
@inbounds Ls = ql(A.M[checkinds:N,checkinds:N]).L[2:j-checkinds+1,2:j-checkinds+1]
461+
@inbounds while Lerr > A.tol
462+
# compute QL for small finite section and large finite section
463+
Ll = ql(A.M[checkinds:2N,checkinds:2N]).L[2:j-checkinds+1,2:j-checkinds+1]
464+
# compare bottom right sections and stop if desired level of convergence achieved
465+
Lerr = norm(Ll-Ls,2)
466+
if N == maxN
467+
error("Reached max. iterations in adaptive QL without convergence to desired tolerance.")
468+
end
469+
Ls = Ll
470+
N = 2*N
471+
end
472+
A.data = ql(A.M[1:(N÷2),1:(N÷2)]).τ[1:j]
473+
end
474+
475+
# TODO: adaptively build L*b using caching and forward-substitution
476+
*(L::LowerTriangular{T, AdaptiveQLFactors{T}}, b::LayoutVector) where T = ApplyArray(*, L, b)
477+
478+
MemoryLayout(::AdaptiveQLFactors) = LazyBandedLayout()
479+
bandwidths(F::AdaptiveQLFactors) = bandwidths(F.data)
480+
481+
# Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T)
482+
getindex(Q::QLPackedQ{T,<:AdaptiveQLFactors{T}}, i::Int, j::Int) where T =
483+
(Q'*[Zeros{T}(i-1); one(T); Zeros{T}(∞)])[j]'
484+
getindex(Q::QLPackedQ{<:Any,<:AdaptiveQLFactors}, I::AbstractVector{Int}, J::AbstractVector{Int}) =
485+
[Q[i,j] for i in I, j in J]
486+
getindex(Q::QLPackedQ{<:Any,<:AdaptiveQLFactors}, I::Int, J::UnitRange{Int}) =
487+
[Q[i,j] for i in I, j in J]
488+
getindex(Q::QLPackedQ{<:Any,<:AdaptiveQLFactors}, I::UnitRange{Int}, J::Int) =
489+
[Q[i,j] for i in I, j in J]
490+
491+
materialize!(M::Lmul{<:QLPackedQLayout{<:LazyLayout},<:PaddedLayout}) = ApplyArray(*,M.A,M.B)
492+
493+
function materialize!(M::Lmul{<:AdjQLPackedQLayout{<:LazyLayout},<:PaddedLayout})
494+
adjA,B = M.A,M.B
495+
A = parent(adjA)
496+
mA, nA = size(A.factors)
497+
mB, nB = size(B,1), size(B,2)
498+
if mA != mB
499+
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
500+
end
501+
Afactors = A.factors
502+
l,u = bandwidths(Afactors)
503+
l = 2l+1
504+
u = 2u+1
505+
@inbounds begin
506+
for k = last(colsupport(B))+u:-1:1
507+
for j = 1:nB
508+
vBj = B[k,j]
509+
for i = max(1,k-u):k-1
510+
vBj += conj(Afactors[i,k])*B[i,j]
511+
end
512+
vBj = conj(A.τ[k])*vBj
513+
B[k,j] -= vBj
514+
for i = max(1,k-u):k-1
515+
B[i,j] -= Afactors[i,k]*vBj
516+
end
517+
end
518+
end
519+
end
520+
B
521+
end

0 commit comments

Comments
 (0)