Skip to content

Commit 60466cf

Browse files
committed
πŸ— Working Lanczos stub
1 parent 5e41ed3 commit 60466cf

File tree

1 file changed

+151
-1
lines changed

1 file changed

+151
-1
lines changed

β€Žsrc/spectrum.jlβ€Ž

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export spectrum, spectrum_correlation_fft
2-
export SpectrumSolver, ExponentialSeries, PseudoInverse
2+
export SpectrumSolver, ExponentialSeries, PseudoInverse, Lanczos
33

44
abstract type SpectrumSolver end
55

@@ -26,6 +26,21 @@ end
2626

2727
PseudoInverse(; alg::SciMLLinearSolveAlgorithm = KrylovJL_GMRES()) = PseudoInverse(alg)
2828

29+
@doc raw"""
30+
Lanczos(; tol = 1e-8, maxiter = 5000, verbose = 0)
31+
32+
A solver which solves [`spectrum`](@ref) by using a non-symmetric Lanczos variant of the algorithm in [Koch2011](https://www.cond-mat.de/events/correl11/manuscripts/koch.pdf).
33+
The nonsymmetric Lanczos algorithm is adapted from Algorithm 6.6 in [Saad2011](https://www-users.cse.umn.edu/~saad/eig_book_2ndEd.pdf).
34+
The running estimate is updated via a [Wallis-Euler recursion](https://en.wikipedia.org/wiki/Continued_fraction).
35+
"""
36+
struct Lanczos{T<:Real,IT<:Int} <: SpectrumSolver
37+
tol::T
38+
maxiter::IT
39+
verbose::IT
40+
end
41+
42+
Lanczos(; tol = 1e-8, maxiter = 5000, verbose = 0) = Lanczos(tol, maxiter, verbose)
43+
2944
@doc raw"""
3045
spectrum(H::QuantumObject,
3146
Ο‰list::AbstractVector,
@@ -144,6 +159,141 @@ function _spectrum(
144159
return spec
145160
end
146161

162+
function _spectrum(
163+
L::QuantumObject{SuperOperator},
164+
Ο‰list::AbstractVector,
165+
A::QuantumObject{Operator},
166+
B::QuantumObject{Operator},
167+
solver::Lanczos;
168+
kwargs...,
169+
)
170+
check_dimensions(L, A, B)
171+
172+
# Define type shortcuts
173+
fT = _FType(L)
174+
cT = _CType(L)
175+
176+
# Handle input frequency range
177+
Ο‰List = convert(Vector{fT}, Ο‰list) # Convert it to support GPUs and avoid type instabilities
178+
Length = length(Ο‰List)
179+
#spec = Vector{fT}(undef, Length)
180+
181+
# Calculate |v₁> = B|ρss>
182+
ρss = mat2vec(steadystate(L))
183+
vβ‚– = Array{cT}((spre(B) * ρss).data)
184+
185+
# Calculate <w₁| = <I|A
186+
D = prod(L.dimensions)
187+
Ivec = SparseVector(D^2, [1 + n * (D + 1) for n in 0:(D-1)], ones(cT, D)) # same as vec(system_identity_matrix)
188+
wβ‚– = transpose(typeof(vβ‚–)(Ivec)) * spre(A).data
189+
190+
# Store the norm of the Green's function before renormalizing |v₁> and <w₁|
191+
gfNorm = abs(wβ‚– * vβ‚–)
192+
vβ‚– ./= sqrt(gfNorm)
193+
wβ‚– ./= sqrt(gfNorm)
194+
195+
# println(" type: $(typeof(vβ‚–))")
196+
# println(" type: $(typeof(wβ‚–))")
197+
198+
# Current and previous estimates of the spectrum
199+
lanczosFactor = zeros(cT, Length)
200+
lanczosFactor₋₁ = zeros(cT, Length)
201+
202+
# Tridiagonal matrix elements
203+
Ξ±β‚– = cT( 0)
204+
Ξ²β‚– = cT(-1)
205+
Ξ΄β‚– = cT(+1)
206+
207+
# Current and up to second-to-last A and B Euler sequences
208+
Aβ‚‹β‚‚ = ones(cT, Length)
209+
A₋₁ = zeros(cT, Length)
210+
Aβ‚– = zeros(cT, Length)
211+
Bβ‚‹β‚‚ = zeros(cT, Length)
212+
B₋₁ = ones(cT, Length)
213+
Bβ‚– = zeros(cT, Length)
214+
215+
# Maximum norm and residue
216+
maxNorm = zeros(cT, length(Ο‰List))
217+
maxResidue = fT(0.0)
218+
219+
# Previous and next left/right Krylov vectors
220+
v₋₁ = zeros(cT, (D^2, 1))
221+
vβ‚Šβ‚ = zeros(cT, (D^2, 1))
222+
w₋₁ = zeros(cT, (1, D^2))
223+
wβ‚Šβ‚ = zeros(cT, (1, D^2))
224+
225+
# Frequency of renormalization
226+
renormFrequency::typeof(solver.maxiter) = 1
227+
228+
# Loop over the Krylov subspace(s)
229+
for k in 1:solver.maxiter
230+
# k-th diagonal element
231+
Ξ±β‚– = (wβ‚– * L.data) * vβ‚–
232+
233+
# Update A(k), B(k) and continuous fraction; normalization avoids overflow
234+
Aβ‚– .= (-1im .* Ο‰List .+ Ξ±β‚–) .* A₋₁ .- (Ξ²β‚– * Ξ΄β‚–) .* Aβ‚‹β‚‚
235+
Bβ‚– .= (-1im .* Ο‰List .+ Ξ±β‚–) .* B₋₁ .- (Ξ²β‚– * Ξ΄β‚–) .* Bβ‚‹β‚‚
236+
lanczosFactor₋₁ .= lanczosFactor
237+
lanczosFactor .= Aβ‚– ./ Bβ‚–
238+
239+
# Renormalize Euler sequences to avoid overflow
240+
if k % renormFrequency == 0
241+
maxNorm .= max.(abs.(Aβ‚–), abs.(Bβ‚–)) # Note: the MATLAB and C++ codes return the actual complex number
242+
Aβ‚– ./= maxNorm
243+
Bβ‚– ./= maxNorm
244+
A₋₁ ./= maxNorm
245+
B₋₁ ./= maxNorm
246+
end
247+
248+
# Check for convergence
249+
maxResidue = maximum(abs.(lanczosFactor .- lanczosFactor₋₁)) /
250+
max(maximum(abs.(lanczosFactor)), maximum(abs.(lanczosFactor₋₁)))
251+
if maxResidue <= solver.tol
252+
if solver.verbose > 1
253+
println("spectrum(): solver::Lanczos converged after $(k) iterations")
254+
end
255+
break
256+
end
257+
258+
# (k+1)-th left/right vectors, orthogonal to previous ones
259+
# Consider using explicit BLAS calls
260+
vβ‚Šβ‚ .= L.data * vβ‚– .- Ξ±β‚– .* vβ‚– .- Ξ²β‚– .* v₋₁
261+
wβ‚Šβ‚ .= wβ‚– * L.data .- Ξ±β‚– .* wβ‚– .- Ξ΄β‚– .* w₋₁
262+
v₋₁ .= vβ‚–
263+
w₋₁ .= wβ‚–
264+
vβ‚– .= vβ‚Šβ‚
265+
wβ‚– .= wβ‚Šβ‚
266+
267+
# k-th off-diagonal elements
268+
buf = wβ‚– * vβ‚–
269+
Ξ΄β‚– = sqrt(abs(buf))
270+
Ξ²β‚– = buf / Ξ΄β‚–
271+
272+
# Normalize (k+1)-th left/right vectors
273+
vβ‚– ./= Ξ΄β‚–
274+
wβ‚– ./= Ξ²β‚–
275+
276+
# Update everything for the next cycle
277+
Aβ‚‹β‚‚ .= A₋₁
278+
A₋₁ .= Aβ‚–
279+
Bβ‚‹β‚‚ .= B₋₁
280+
B₋₁ .= Bβ‚–
281+
end
282+
283+
if solver.verbose > 0
284+
if maxResidue > solver.tol
285+
println("spectrum(): maxiter = $(solver.maxiter) reached before convergence!")
286+
println("spectrum(): Max residue = $maxResidue")
287+
println("spectrum(): Consider increasing maxiter and/or tol")
288+
end
289+
end
290+
291+
# Restore the norm
292+
lanczosFactor .= gfNorm .* lanczosFactor
293+
294+
return -2 .* real( lanczosFactor )
295+
end
296+
147297
@doc raw"""
148298
spectrum_correlation_fft(tlist, corr; inverse=false)
149299

0 commit comments

Comments
Β (0)