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?