|
1 | 1 | export spectrum, spectrum_correlation_fft |
2 | | -export SpectrumSolver, ExponentialSeries, PseudoInverse |
| 2 | +export SpectrumSolver, ExponentialSeries, PseudoInverse, Lanczos |
3 | 3 |
|
4 | 4 | abstract type SpectrumSolver end |
5 | 5 |
|
|
26 | 26 |
|
27 | 27 | PseudoInverse(; alg::SciMLLinearSolveAlgorithm = KrylovJL_GMRES()) = PseudoInverse(alg) |
28 | 28 |
|
| 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 | + |
29 | 44 | @doc raw""" |
30 | 45 | spectrum(H::QuantumObject, |
31 | 46 | Οlist::AbstractVector, |
@@ -144,6 +159,141 @@ function _spectrum( |
144 | 159 | return spec |
145 | 160 | end |
146 | 161 |
|
| 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 | + |
147 | 297 | @doc raw""" |
148 | 298 | spectrum_correlation_fft(tlist, corr; inverse=false) |
149 | 299 |
|
|
0 commit comments