Hi, I’m trying to efficiently obtain the exponential of a complex symmetric tridiagonal matrix (SymTridiagonal
).
Unfortunately, exp
is neither defined for SymTridiagonal
nor Tridiagonal
. I believe this is because eigen
is not defined for those matrices (although eigen
is defined for real-valued symmetric tridiagonal matrices, although not complex ones). An MWE:
julia> using LinearAlgebra
julia> n = 3
3
julia> St = SymTridiagonal(-2ones(n), ones(n-1))
3×3 SymTridiagonal{Float64,Array{Float64,1}}:
-2.0 1.0 ⋅
1.0 -2.0 1.0
⋅ 1.0 -2.0
julia> T = Tridiagonal(ones(n-1), -2ones(n), ones(n-1))
3×3 Tridiagonal{Float64,Array{Float64,1}}:
-2.0 1.0 ⋅
1.0 -2.0 1.0
⋅ 1.0 -2.0
julia> exp(St) # similar error for exp(complex(St))
ERROR: MethodError: no method matching exp(::SymTridiagonal{Float64,Array{Float64,1}})
Closest candidates are:
exp(::BigFloat) at mpfr.jl:619
exp(::Missing) at math.jl:1167
exp(::Complex{Float16}) at math.jl:1115
...
Stacktrace:
[1] top-level scope at REPL[5]:1
julia> exp(T) # similar error for exp(complex(T))
ERROR: MethodError: no method matching exp(::Tridiagonal{Float64,Array{Float64,1}})
Closest candidates are:
exp(::BigFloat) at mpfr.jl:619
exp(::Missing) at math.jl:1167
exp(::Complex{Float16}) at math.jl:1115
...
Stacktrace:
[1] top-level scope at REPL[6]:1
julia> eigen(St)
Eigen{Float64,Float64,Array{Float64,2},Array{Float64,1}}
values:
3-element Array{Float64,1}:
-3.414213562373093
-1.9999999999999987
-0.5857864376269051
vectors:
3×3 Array{Float64,2}:
-0.5 -0.707107 0.5
0.707107 -9.42055e-16 0.707107
-0.5 0.707107 0.5
julia> eigen(T) # similar error for eigen(complex(St)) and eigen(complex(T))
ERROR: MethodError: no method matching eigen!(::Tridiagonal{Float64,Array{Float64,1}}; permute=true, scale=true, sortby=LinearAlgebra.eigsortby)
Closest candidates are:
eigen!(::SymTridiagonal{#s664,V} where V<:AbstractArray{#s664,1} where #s664<:Union{Float32, Float64}) at /build/julia-98cBbp/julia-1.4.1+dfsg/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:232 got unsupported keyword arguments "permute", "scale", "sortby"
eigen!(::SymTridiagonal{#s664,V} where V<:AbstractArray{#s664,1} where #s664<:Union{Float32, Float64}, ::UnitRange) at /build/julia-98cBbp/julia-1.4.1+dfsg/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:235 got unsupported keyword arguments "permute", "scale", "sortby"
eigen!(::SymTridiagonal{#s664,V} where V<:AbstractArray{#s664,1} where #s664<:Union{Float32, Float64}, ::Real, ::Real) at /build/julia-98cBbp/julia-1.4.1+dfsg/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:240 got unsupported keyword arguments "permute", "scale", "sortby"
...
Stacktrace:
[1] eigen(::Tridiagonal{Float64,Array{Float64,1}}; permute::Bool, scale::Bool, sortby::typeof(LinearAlgebra.eigsortby)) at /build/julia-98cBbp/julia-1.4.1+dfsg/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/eigen.jl:237
[2] eigen(::Tridiagonal{Float64,Array{Float64,1}}) at /build/julia-98cBbp/julia-1.4.1+dfsg/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/eigen.jl:235
[3] top-level scope at REPL[8]:1
This might seem like a corner case, but SymTridiagonal
matrices can be quite useful for solving 1D problems in Quantum Mechanics. An example application follows:
using LinearAlgebra
D²(n, Δx) = SymTridiagonal(-2ones(n), ones(n-1)) / Δx^2
function hamiltonian(x, V; m=1, ħ=1)
# Assuming x is equally spaced
Δx = x[2] - x[1]
T = -(ħ^2 / 2m) * D²(length(x), Δx)
return T + V
end
ħ = 1
ω₀ = 1
m = 1
t = 1
L = 5
Δx =0.1
x = -(L-Δx):Δx:(L-Δx)
V = Diagonal(m * ω₀^2 * x.^2 / 2);
H = hamiltonian(x, V, m=m);
typeof(H) # => SymTridiagonal{Float64,Array{Float64,1}}
exp(-im * H * t/ ħ)
# =>
# ERROR: MethodError: no method matching exp(::SymTridiagonal{Complex{Float64},Array{Complex{Float64},1}})
# Closest candidates are:
# exp(::BigFloat) at mpfr.jl:619
# exp(::Missing) at math.jl:1167
# exp(::Complex{Float16}) at math.jl:1115
# ...
# Stacktrace:
# [1] top-level scope at REPL[74]:1
I can do exp(Array(-im * H * t/ ħ))
, but then I would lose the nice structure of H
. Is there a more efficient way of doing this?