Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions benchmarks/correlations_and_spectrum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ function benchmark_correlations_and_spectrum!(SUITE)

PI_solver = PseudoInverse()

L_solver = Lanczos()

SUITE["Correlations and Spectrum"]["FFT Correlation"] =
@benchmarkable _calculate_fft_spectrum($H, $t_l, $c_ops, $(a'), $a)

Expand All @@ -28,5 +30,8 @@ function benchmark_correlations_and_spectrum!(SUITE)
SUITE["Correlations and Spectrum"]["Spectrum"]["Pseudo Inverse"] =
@benchmarkable spectrum($H, $ω_l, $c_ops, $(a'), $a, solver = $PI_solver)

SUITE["Correlations and Spectrum"]["Spectrum"]["Lanczos"] =
@benchmarkable spectrum($H, $ω_l, $c_ops, $(a'), $a, solver = $L_solver)

return nothing
end
1 change: 1 addition & 0 deletions docs/src/resources/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ spectrum_correlation_fft
spectrum
ExponentialSeries
PseudoInverse
Lanczos
```

## [Entropy and Metrics](@id doc-API:Entropy-and-Metrics)
Expand Down
2 changes: 1 addition & 1 deletion src/QuantumToolbox.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ include("time_evolution/time_evolution_dynamical.jl")

# Others
include("correlations.jl")
include("spectrum.jl")
include("wigner.jl")
include("spin_lattice.jl")
include("arnoldi.jl")
include("entropy.jl")
include("metrics.jl")
include("negativity.jl")
include("steadystate.jl")
include("spectrum.jl")
include("visualization.jl")

# deprecated functions
Expand Down
153 changes: 152 additions & 1 deletion src/spectrum.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export spectrum, spectrum_correlation_fft
export SpectrumSolver, ExponentialSeries, PseudoInverse
export SpectrumSolver, ExponentialSeries, PseudoInverse, Lanczos

abstract type SpectrumSolver end

Expand All @@ -26,6 +26,20 @@

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

@doc raw"""
Lanczos(; tol = 1e-8, maxiter = 5000, verbose = 0)
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).
The nonsymmetric Lanczos algorithm is adapted from Algorithm 6.6 in [Saad2011](https://www-users.cse.umn.edu/~saad/eig_book_2ndEd.pdf).
The running estimate is updated via a [Wallis-Euler recursion](https://en.wikipedia.org/wiki/Continued_fraction).
"""
Base.@kwdef mutable struct Lanczos{T<:Real,IT<:Int} <: SpectrumSolver
tol::T = 1e-8
maxiter::IT = 5000
verbose::IT = 0
steadystate_solver::Union{Nothing,SteadyStateSolver} = nothing
end

@doc raw"""
spectrum(H::QuantumObject,
ωlist::AbstractVector,
Expand All @@ -44,6 +58,7 @@
See also the following list for `SpectrumSolver` docstrings:
- [`ExponentialSeries`](@ref)
- [`PseudoInverse`](@ref)
- [`Lanczos`](@ref)
"""
function spectrum(
H::QuantumObject{HOpType},
Expand Down Expand Up @@ -144,6 +159,142 @@
return spec
end

function _spectrum(

Check warning on line 162 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L162

Added line #L162 was not covered by tests
L::QuantumObject{SuperOperator},
ωlist::AbstractVector,
A::QuantumObject{Operator},
B::QuantumObject{Operator},
solver::Lanczos;
)
check_dimensions(L, A, B)

Check warning on line 169 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L169

Added line #L169 was not covered by tests

# Define type shortcuts
fT = _FType(L)
cT = _CType(L)

Check warning on line 173 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L172-L173

Added lines #L172 - L173 were not covered by tests

# Calculate |v₁> = B|ρss>
ρss =

Check warning on line 176 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L176

Added line #L176 was not covered by tests
solver.steadystate_solver === nothing ? mat2vec(steadystate(L)) :
mat2vec(steadystate(L; solver = solver.steadystate_solver))
vₖ = (spre(B) * ρss).data

Check warning on line 179 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L179

Added line #L179 was not covered by tests

# Define (possibly GPU) vector type
vT = typeof(vₖ)

Check warning on line 182 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L182

Added line #L182 was not covered by tests

# Calculate <w₁| = <I|A
D = prod(L.dimensions)
Ivec = SparseVector(D^2, [1 + n * (D + 1) for n in 0:(D-1)], ones(cT, D)) # same as vec(system_identity_matrix)
wₖ = transpose(vT(Ivec)) * spre(A).data

Check warning on line 187 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L185-L187

Added lines #L185 - L187 were not covered by tests

# Store the norm of the Green's function before renormalizing |v₁> and <w₁|
gfNorm = abs(wₖ * vₖ)
vₖ ./= sqrt(gfNorm)
wₖ ./= sqrt(gfNorm)

Check warning on line 192 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L190-L192

Added lines #L190 - L192 were not covered by tests

# Handle input frequency range
ωList = vT(convert(Vector{fT}, ωlist)) # Make sure they're real frequencies and potentially on GPU
Length = length(ωList)

Check warning on line 196 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L195-L196

Added lines #L195 - L196 were not covered by tests

# Current and previous estimates of the spectrum
lanczosFactor = vT(zeros(cT, Length))
lanczosFactor₋₁ = vT(zeros(cT, Length))

Check warning on line 200 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L199-L200

Added lines #L199 - L200 were not covered by tests

# Tridiagonal matrix elements
αₖ = cT(0)
βₖ = cT(-1)
δₖ = cT(+1)

Check warning on line 205 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L203-L205

Added lines #L203 - L205 were not covered by tests

# Current and up to second-to-last A and B Euler sequences
A₋₂ = vT(ones(cT, Length))
A₋₁ = vT(zeros(cT, Length))
Aₖ = vT(zeros(cT, Length))
B₋₂ = vT(zeros(cT, Length))
B₋₁ = vT(ones(cT, Length))
Bₖ = vT(zeros(cT, Length))

Check warning on line 213 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L208-L213

Added lines #L208 - L213 were not covered by tests

# Maximum norm and residue
maxNorm = vT(zeros(cT, length(ωList)))
maxResidue = fT(0.0)

Check warning on line 217 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L216-L217

Added lines #L216 - L217 were not covered by tests

# Previous and next left/right Krylov vectors
v₋₁ = vT(zeros(cT, D^2))
v₊₁ = vT(zeros(cT, D^2))
w₋₁ = vT(zeros(cT, D^2))'
w₊₁ = vT(zeros(cT, D^2))'

Check warning on line 223 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L220-L223

Added lines #L220 - L223 were not covered by tests

# Frequency of renormalization
renormFrequency::typeof(solver.maxiter) = 1

Check warning on line 226 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L226

Added line #L226 was not covered by tests

# Loop over the Krylov subspace(s)
for k in 1:solver.maxiter

Check warning on line 229 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L229

Added line #L229 was not covered by tests
# k-th diagonal element
w₊₁ = wₖ * L.data
αₖ = w₊₁ * vₖ

Check warning on line 232 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L231-L232

Added lines #L231 - L232 were not covered by tests

# Update A(k), B(k) and continuous fraction; normalization avoids overflow
Aₖ .= (-1im .* ωList .+ αₖ) .* A₋₁ .- (βₖ * δₖ) .* A₋₂
Bₖ .= (-1im .* ωList .+ αₖ) .* B₋₁ .- (βₖ * δₖ) .* B₋₂
lanczosFactor₋₁ .= lanczosFactor
lanczosFactor .= Aₖ ./ Bₖ

Check warning on line 238 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L235-L238

Added lines #L235 - L238 were not covered by tests

# Renormalize Euler sequences to avoid overflow
if k % renormFrequency == 0
maxNorm .= max.(abs.(Aₖ), abs.(Bₖ))
Aₖ ./= maxNorm
Bₖ ./= maxNorm
A₋₁ ./= maxNorm
B₋₁ ./= maxNorm

Check warning on line 246 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L241-L246

Added lines #L241 - L246 were not covered by tests
end

# Check for convergence
maxResidue =

Check warning on line 250 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L250

Added line #L250 was not covered by tests
maximum(abs.(lanczosFactor .- lanczosFactor₋₁)) /
max(maximum(abs.(lanczosFactor)), maximum(abs.(lanczosFactor₋₁)))
if maxResidue <= solver.tol
if solver.verbose > 1
println("spectrum(): solver::Lanczos converged after $(k) iterations")

Check warning on line 255 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L253-L255

Added lines #L253 - L255 were not covered by tests
end
break

Check warning on line 257 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L257

Added line #L257 was not covered by tests
end

# (k+1)-th left/right vectors, orthogonal to previous ones
# Consider using explicit BLAS calls
v₊₁ = L.data * vₖ
v₊₁ .= v₊₁ .- αₖ .* vₖ .- βₖ .* v₋₁
w₊₁ .= w₊₁ .- αₖ .* wₖ .- δₖ .* w₋₁
v₋₁ .= vₖ
w₋₁ .= wₖ
vₖ .= v₊₁
wₖ .= w₊₁

Check warning on line 268 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L262-L268

Added lines #L262 - L268 were not covered by tests

# k-th off-diagonal elements
buf = wₖ * vₖ
δₖ = sqrt(abs(buf))
βₖ = buf / δₖ

Check warning on line 273 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L271-L273

Added lines #L271 - L273 were not covered by tests

# Normalize (k+1)-th left/right vectors
vₖ ./= δₖ
wₖ ./= βₖ

Check warning on line 277 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L276-L277

Added lines #L276 - L277 were not covered by tests

# Update everything for the next cycle
A₋₂ .= A₋₁
A₋₁ .= Aₖ
B₋₂ .= B₋₁
B₋₁ .= Bₖ
end

Check warning on line 284 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L280-L284

Added lines #L280 - L284 were not covered by tests

if solver.verbose > 0 && maxResidue > solver.tol
println("spectrum(): maxiter = $(solver.maxiter) reached before convergence!")
println("spectrum(): Max residue = $maxResidue")
println("spectrum(): Consider increasing maxiter and/or tol")

Check warning on line 289 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L286-L289

Added lines #L286 - L289 were not covered by tests
end

# Restore the norm
lanczosFactor .= gfNorm .* lanczosFactor

Check warning on line 293 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L293

Added line #L293 was not covered by tests

return -2 .* real(lanczosFactor)

Check warning on line 295 in src/spectrum.jl

View check run for this annotation

Codecov / codecov/patch

src/spectrum.jl#L295

Added line #L295 was not covered by tests
end

@doc raw"""
spectrum_correlation_fft(tlist, corr; inverse=false)
Expand Down
4 changes: 4 additions & 0 deletions test/core-test/correlations_and_spectrum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
ω_l2 = range(0, 3, length = 1000)
spec2 = spectrum(H, ω_l2, c_ops, a', a)
spec3 = spectrum(H, ω_l2, c_ops, a', a; solver = PseudoInverse())
spec4 = spectrum(H, ω_l2, c_ops, a', a; solver = Lanczos())

spec1 = spec1 ./ maximum(spec1)
spec2 = spec2 ./ maximum(spec2)
spec3 = spec3 ./ maximum(spec3)
spec4 = spec4 ./ maximum(spec4)

test_func1 = maximum(real.(spec1)) * (0.1 / 2)^2 ./ ((ω_l1 .- 1) .^ 2 .+ (0.1 / 2)^2)
test_func2 = maximum(real.(spec2)) * (0.1 / 2)^2 ./ ((ω_l2 .- 1) .^ 2 .+ (0.1 / 2)^2)
Expand All @@ -26,12 +28,14 @@
@test sum(abs2.(spec2[idxs2] .- test_func2[idxs2])) / sum(abs2.(test_func2[idxs2])) < 0.01
@test all(corr1 .≈ corr2)
@test all(spec2 .≈ spec3)
@test all(spec2 .≈ spec4)

@testset "Type Inference spectrum" begin
@inferred correlation_2op_1t(H, nothing, t_l, c_ops, a', a; progress_bar = Val(false))
@inferred spectrum_correlation_fft(t_l, corr1)
@inferred spectrum(H, ω_l2, c_ops, a', a)
@inferred spectrum(H, ω_l2, c_ops, a', a; solver = PseudoInverse())
@inferred spectrum(H, ω_l2, c_ops, a', a; solver = Lanczos())
end

# tlist and τlist checks
Expand Down
Loading