1
- # Reference implementation, without consideration of speed.
1
+ # Reference implementation of the generalized Lanczos method, without
2
+ # consideration of speed. The input A can be any Hermitian matrix. The optional
3
+ # argument S is an inner-product that must be both Hermitian and positive
4
+ # definite. Intuitively, Lanczos finds a low-rank approximant A S ≈ Q T Q† S,
5
+ # where T is tridiagonal and Q is orthogonal in the sense that Q† S Q = I. The
6
+ # first column of Q is the initial vector v, which must be normalized. The
7
+ # Lanczos method is useful in two ways. First, eigenvalues of T are
8
+ # representative of extremal eigenvalues of A (in an appropriate S-measure
9
+ # sense). Second, one obtains a very good approximation to the matrix-vector
10
+ # product, f(A S) v ≈ Q f(T) e₁, valid for any function f [1].
11
+ #
12
+ # The generalization of Lanczos to an arbitrary measure S is implemented as
13
+ # follows: Each matrix-vector product A v is replaced by A S v, and each vector
14
+ # inner product w† v is replaced by w† S v. Similar generalizations of Lanczos
15
+ # have been considered in [2] and [3].
16
+ #
17
+ # 1. N. Amsel, T. Chen, A. Greenbaum, C. Musco, C. Musco, Near-optimal
18
+ # approximation of matrix functions by the Lanczos method, (2013)
19
+ # https://arxiv.org/abs/2303.03358v2.
20
+ # 2. H. A. van der Vorst, Math. Comp. 39, 559-561 (1982),
21
+ # https://doi.org/10.1090/s0025-5718-1982-0669648-0
22
+ # 3. M. Grüning, A. Marini, X. Gonze, Comput. Mater. Sci. 50, 2148-2156 (2011),
23
+ # https://doi.org/10.1016/j.commatsci.2011.02.021.
2
24
function lanczos_ref (A, S, v; niters)
3
25
αs = Float64[]
4
26
βs = Float64[]
27
+ vs = typeof (v)[]
28
+
29
+ c = sqrt (real (dot (v, S, v)))
30
+ @assert c ≈ 1 " Initial vector not normalized"
31
+ v /= c
5
32
6
- v /= sqrt (real (dot (v, S, v)))
7
33
w = A * S * v
8
34
α = real (dot (w, S, v))
9
35
w = w - α * v
10
36
push! (αs, α)
37
+ push! (vs, v)
11
38
12
39
for _ in 2 : niters
13
40
β = sqrt (real (dot (w, S, w)))
@@ -19,35 +46,54 @@ function lanczos_ref(A, S, v; niters)
19
46
v = vp
20
47
push! (αs, α)
21
48
push! (βs, β)
49
+ push! (vs, v)
22
50
end
23
51
24
- return SymTridiagonal (αs, βs)
52
+ Q = reduce (hcat, vs)
53
+ T = SymTridiagonal (αs, βs)
54
+ return (; Q, T)
25
55
end
26
56
27
57
28
-
29
- # Use Lanczos iterations to find a truncated tridiagonal representation of A S,
30
- # up to similarity transformation. Here, A is any Hermitian matrix, while S is
31
- # both Hermitian and positive definite. Traditional Lanczos uses the identity
32
- # matrix, S = I. The extension to non-identity matrices S is as follows: Each
33
- # matrix-vector product A v becomes A S v, and each vector inner product w† v
34
- # becomes w† S v. The implementation below follows the notation of Wikipedia,
35
- # and is the most stable of the four variants considered by Paige [1]. Each
36
- # Lanczos iteration requires only one matrix-vector multiplication for A and S,
37
- # respectively.
38
-
39
- # Similar generalizations of Lanczos have been considered in [2] and [3].
58
+ # An optimized implementation of the generalized Lanczos method. See
59
+ # `lanczos_ref` for explanation, and a reference implementation. This optimized
60
+ # implementation follows "the most stable" of the four variants considered by
61
+ # Paige [1], as listed on Wikipedia. Here, however, we allow for an arbitary
62
+ # measure S. In practice, this means that each matrix-vector product A v is
63
+ # replaced by A S v, and each inner product w† v is replaced by w† S v.
64
+ #
65
+ # In this implementation, each Lanczos iteration requires only one matrix-vector
66
+ # multiplication involving A and S, respectively.
67
+ #
68
+ # Details:
69
+ #
70
+ # * The return value is a pair, (T, lhs† Q). The symbol `lhs` denotes "left-hand
71
+ # side" column vectors, packed horizontally into a matrix. If the `lhs`
72
+ # argument is ommitted, its default value will be a matrix of width 0.
73
+ # * If the initial vector `v` is not normalized, then this normalization will be
74
+ # performed automatically, and the scale `√v S v` will be absorbed into the
75
+ # return value `lhs† Q`.
76
+ # * The number of iterations will never exceed length(v). If this limit is hit
77
+ # then, mathematically, the Krylov subspace should be complete, and the matrix
78
+ # T should be exactly similar to the matrix A S. In practice, however,
79
+ # numerical error at finite precision may be severe. Further testing is
80
+ # required to determine whether the Lanczos method is appropriate in this
81
+ # case, where the matrices have modest dimension. (Direct diagonalization may
82
+ # be preferable.)
83
+ # * After `min_iters` iterations, this procedure will estimate the spectral
84
+ # bandwidth Δϵ between extreme eigenvalues of T. Then `Δϵ / resolution` will
85
+ # be an upper bound on the total number of iterations (which includes the
86
+ # initial `min_iters` iterations as well as subsequent ones).
40
87
#
41
88
# 1. C. C. Paige, IMA J. Appl. Math., 373-381 (1972),
42
- # https://doi.org/10.1093%2Fimamat%2F10.3.373.
43
- # 2. H. A. van der Vorst, Math. Comp. 39, 559-561 (1982),
44
- # https://doi.org/10.1090/s0025-5718-1982-0669648-0
45
- # 3. M. Grüning, A. Marini, X. Gonze, Comput. Mater. Sci. 50, 2148-2156 (2011),
46
- # https://doi.org/10.1016/j.commatsci.2011.02.021.
47
- function lanczos (mulA!, mulS!, v; niters)
89
+ # https://doi.org/10.1093%2Fimamat%2F10.3.373.
90
+
91
+ function lanczos (mulA!, mulS!, v; min_iters, resolution= Inf , lhs= zeros (length (v), 0 ), verbose= false )
48
92
αs = Float64[]
49
93
βs = Float64[]
94
+ lhs_adj_Q = Vector{ComplexF64}[]
50
95
96
+ v = copy (v)
51
97
vp = zero (v)
52
98
Sv = zero (v)
53
99
Svp = zero (v)
@@ -56,6 +102,7 @@ function lanczos(mulA!, mulS!, v; niters)
56
102
57
103
mulS! (Sv, v)
58
104
c = sqrt (real (dot (v, Sv)))
105
+ @assert c ≈ 1 " Initial vector not normalized"
59
106
@. v /= c
60
107
@. Sv /= c
61
108
@@ -64,10 +111,36 @@ function lanczos(mulA!, mulS!, v; niters)
64
111
@. w = w - α * v
65
112
mulS! (Sw, w)
66
113
push! (αs, α)
67
-
68
- for _ in 2 : niters
69
- β = sqrt (real (dot (w, Sw)))
70
- iszero (β) && break
114
+ push! (lhs_adj_Q, lhs' * v)
115
+
116
+ # The maximum number of iterations is length(v)-1, because that is the
117
+ # dimension of the full vector space. Break out early if niters has been
118
+ # reached, which will be set according to the spectral bounds Δϵ after
119
+ # iteration i == min_iters.
120
+ niters = typemax (Int)
121
+ for i in 1 : length (v)- 1
122
+ if i == min_iters
123
+ T = SymTridiagonal (αs, βs)
124
+ Δϵ = eigmax (T) - eigmin (T)
125
+ niters = max (min_iters, fld (Δϵ, resolution))
126
+ niters += mod (niters, 2 ) # Round up to nearest even integer
127
+ if verbose
128
+ println (" Δϵ=$Δϵ , niters=$niters " )
129
+ end
130
+ end
131
+
132
+ i >= niters && break
133
+
134
+ # Let β = w† S w. If β < 0 then S is not a valid positive definite
135
+ # measure. If β = 0, this would formally indicate that v is a linear
136
+ # combination of only a subset of the eigenvectors of A S. In this case,
137
+ # it is valid to break early for purposes of approximating the
138
+ # matrix-vector product f(A S) v.
139
+ β² = real (dot (w, Sw))
140
+ iszero (β²) && break
141
+ β² < 0 && error (" S is not a positive definite measure" )
142
+
143
+ β = sqrt (β²)
71
144
@. vp = w / β
72
145
@. Svp = Sw / β
73
146
mulA! (w, Svp)
@@ -77,9 +150,10 @@ function lanczos(mulA!, mulS!, v; niters)
77
150
@. v = vp
78
151
push! (αs, α)
79
152
push! (βs, β)
153
+ push! (lhs_adj_Q, lhs' * v)
80
154
end
81
155
82
- return SymTridiagonal (αs, βs)
156
+ return SymTridiagonal (αs, βs), reduce (hcat, lhs_adj_Q)
83
157
end
84
158
85
159
@@ -107,6 +181,9 @@ function eigbounds(swt, q_reshaped, niters)
107
181
end
108
182
109
183
v = randn (ComplexF64, 2 L)
110
- tridiag = lanczos (mulA!, mulS!, v; niters)
111
- return eigmin (tridiag), eigmax (tridiag)
184
+ Sv = zero (v)
185
+ mulS! (Sv, v)
186
+ v /= sqrt (v' * Sv)
187
+ T, _ = lanczos (mulA!, mulS!, v; min_iters= niters)
188
+ return eigmin (T), eigmax (T)
112
189
end
0 commit comments