1
+ const MAX_TRIDIAG_CHOL_N = 2 ^ 21 - 1 # Maximum allowable size of Cholesky factor before terminating to prevent OutOfMemory errors without convergence
2
+ mutable struct LazySymTridiagonalReverseCholeskyFactors{T,M1,M2} <: LazyMatrix{T}
3
+ const A:: M1 # original matrix
4
+ const L:: M2 # LN
5
+ const ε:: T # adaptive tolerance
6
+ N:: Int # size of L used for approximating Ln
7
+ n:: Int # size of approximated finite section
8
+ end # this should behave like a lower Bidiagonal matrix
9
+ function LazySymTridiagonalReverseCholeskyFactors (A, N, n, L, ε)
10
+ require_one_based_indexing (A)
11
+ M1, M2 = typeof (A), typeof (L)
12
+ T = eltype (L)
13
+ return LazySymTridiagonalReverseCholeskyFactors {T,M1,M2} (A, L, convert (T, ε), N, n)
14
+ end
15
+
16
+ function getproperty (C:: ReverseCholesky{<:Any,<:LazySymTridiagonalReverseCholeskyFactors} , d:: Symbol ) # mimic getproperty(::ReverseCholesky{<:Any, <:Bidiagonal}, ::Symbol)
17
+ Cfactors = getfield (C, :factors )
18
+ # Cuplo = 'L'
19
+ if d == :U
20
+ return Cfactors'
21
+ elseif d == :L || d == :UL
22
+ return Cfactors
23
+ else
24
+ return getfield (C, d)
25
+ end
26
+ end
27
+ MemoryLayout (:: Type{<:LazySymTridiagonalReverseCholeskyFactors} ) = BidiagonalLayout {LazyLayout,LazyLayout} ()
28
+
29
+ size (L:: LazySymTridiagonalReverseCholeskyFactors ) = size (L. A)
30
+ axes (L:: LazySymTridiagonalReverseCholeskyFactors ) = axes (L. A)
31
+ Base. eltype (L:: Type{LazySymTridiagonalReverseCholeskyFactors} ) = eltype (L. L)
32
+
33
+ copy (L:: LazySymTridiagonalReverseCholeskyFactors ) = LazySymTridiagonalReverseCholeskyFactors (copy (L. A), copy (L. L), L. ε, L. N, L. n)
34
+ copy (U:: Adjoint{T,<:LazySymTridiagonalReverseCholeskyFactors} ) where {T} = copy (parent (U))'
35
+
36
+ LazyBandedMatrices. bidiagonaluplo (L:: LazySymTridiagonalReverseCholeskyFactors ) = ' L'
37
+
38
+ """
39
+ InfiniteBoundsAccessError <: Exception
40
+
41
+ Struct for defining an error when accessing a `LazySymTridiagonalReverseCholeskyFactors` object outside of the
42
+ maximum allowable finite section of size `$MAX_TRIDIAG_CHOL_N × $MAX_TRIDIAG_CHOL_N `.
43
+ """
44
+ struct InfiniteBoundsAccessError <: Exception
45
+ i:: Int
46
+ j:: Int
47
+ end
48
+ function Base. showerror (io:: IO , err:: InfiniteBoundsAccessError )
49
+ print (io, " InfiniteBoundsAccessError: Tried to index reverse Cholesky factory at index (" , err. i, " , " , err. j)
50
+ print (io, " ), outside of the maximum allowable finite section of size (" , MAX_TRIDIAG_CHOL_N, " × " , MAX_TRIDIAG_CHOL_N, " )" )
51
+ end
52
+
53
+ function getindex (L:: LazySymTridiagonalReverseCholeskyFactors , i:: Int , j:: Int )
54
+ max (i, j) > MAX_TRIDIAG_CHOL_N && throw (InfiniteBoundsAccessError (i, j))
55
+ T = eltype (L)
56
+ if j > i
57
+ return zero (T)
58
+ elseif max (i, j) > L. n
59
+ _expand_factor! (L, max (i, j))
60
+ return L. L[i, j]
61
+ else
62
+ return L. L[i, j]
63
+ end
64
+ end
65
+
66
+ function reversecholesky_layout (:: SymTridiagonalLayout , :: NTuple{2,OneToInf{Int}} , A, :: NoPivot ; kwds... )
67
+ a, b = A. dv, A. ev
68
+ T = promote_type (eltype (a), eltype (b), eltype (b[1 ] / a[1 ])) # could also use promote_op(/, eltype(a), eltype(b)), but promote_op is fragile apparently
69
+ tol = eps (real (T)) # no good way to pass this as a keyword currently, so just hardcode it
70
+ L = Bidiagonal ([zero (T)], T[], :L )
71
+ chol = LazySymTridiagonalReverseCholeskyFactors (A, 1 , 1 , L, tol)
72
+ _expand_factor! (chol, 2 ^ 4 ) # initialise with 2^4
73
+ return ReverseCholesky (chol, ' L' , 0 )
74
+ end
75
+
76
+ function _expand_factor! (L:: LazySymTridiagonalReverseCholeskyFactors , n)
77
+ L. n ≥ n && return L
78
+ return __expand_factor! (L:: LazySymTridiagonalReverseCholeskyFactors , n)
79
+ end
80
+
81
+ function compute_ξ (LL:: LazySymTridiagonalReverseCholeskyFactors )
82
+ #=
83
+ We can show that ||LN' Pn inv(LN') Vb|| = |bN| ||ξ||, where
84
+ ξₙ = LN[n, n]νₙ,
85
+ ξᵢ = LN[i, i]νᵢ + LN[i+1, i]νᵢ₊₁, i = 1, 2, …, n-1, where
86
+ νN = 1/LN[N, N]
87
+ νᵢ = -(LN[i+1, i] / LN[i, i]) νᵢ₊₁, i = 1, 2, …, N-1.
88
+ =#
89
+ L, N, n = LL. L, LL. N, LL. n
90
+ ν = inv (L[N, N])
91
+ for i in (N- 1 ): - 1 : n
92
+ ν *= - (L[i+ 1 , i] / L[i, i])
93
+ end
94
+ ξ = (L[n, n] * ν)^ 2
95
+ for i in (n- 1 ): - 1 : 1
96
+ ξ′ = L[i+ 1 , i] * ν
97
+ ν *= - (L[i+ 1 , i] / L[i, i])
98
+ ξ′ += L[i, i] * ν
99
+ ξ += ξ′^ 2
100
+ end
101
+ bN = LL. A[N, N+ 1 ]
102
+ scale = iszero (bN) ? one (bN) : abs (bN)
103
+ return scale * sqrt (ξ) # could maybe just return sqrt(ξ), but maybe bN helps for scaling?
104
+ end
105
+
106
+ function has_converged (LL:: LazySymTridiagonalReverseCholeskyFactors )
107
+ ξ = compute_ξ (LL)
108
+ return ξ ≤ LL. ε
109
+ end
110
+
111
+ function _resize_factor! (L:: LazySymTridiagonalReverseCholeskyFactors , N= 2 L. N)
112
+ L. N = N
113
+ resize! (L. L. dv, L. N)
114
+ resize! (L. L. ev, L. N - 1 )
115
+ return L
116
+ end
117
+
118
+ function _finite_revchol! (L:: LazySymTridiagonalReverseCholeskyFactors )
119
+ # Computes the reverse Cholesky factorisation of L.A[1:L.N, 1:L.N]
120
+ N = L. N
121
+ a, b = L. A. dv, L. A. ev
122
+ ℓa, ℓb = L. L. dv, L. L. ev
123
+ ℓa[N] = sqrt (a[N])
124
+ for i in (N- 1 ): - 1 : 1
125
+ ℓb[i] = b[i] / ℓa[i+ 1 ]
126
+ ℓa[i] = sqrt (a[i] - ℓb[i]^ 2 )
127
+ end
128
+ return L
129
+ end
130
+
131
+ function __expand_factor! (L:: LazySymTridiagonalReverseCholeskyFactors , n)
132
+ L. N > MAX_TRIDIAG_CHOL_N && return L
133
+ L. n = n
134
+ L. N < L. n && _resize_factor! (L, 2 n)
135
+ while ! has_converged (L) && L. N ≤ MAX_TRIDIAG_CHOL_N
136
+ _resize_factor! (L)
137
+ _finite_revchol! (L)
138
+ end
139
+ ! has_converged (L) && L. N > MAX_TRIDIAG_CHOL_N && @warn " Reverse Cholesky algorithm failed to converge. Returned results may not be accurate." maxlog = 1
140
+ return L
141
+ end
0 commit comments