Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
eb913cb
implement exponential
sanderdemeyer Nov 6, 2025
a3dc04d
update on exponential
sanderdemeyer Nov 12, 2025
c4564ee
Merge branch 'QuantumKitHub:main' into exponential
sanderdemeyer Nov 12, 2025
8dc3ecd
remove comment
sanderdemeyer Nov 12, 2025
d9fb748
Merge branch 'exponential' of https://github.com/sanderdemeyer/Matrix…
sanderdemeyer Nov 12, 2025
5095cdb
comments
sanderdemeyer Nov 13, 2025
89dfa23
change name of decompositions.jl to matrixfunctions.jl
sanderdemeyer Nov 19, 2025
996ecb5
revert name change
sanderdemeyer Nov 19, 2025
dc78eb0
Merge branch 'main' into exponential
sanderdemeyer Nov 19, 2025
f220035
general comments
sanderdemeyer Nov 20, 2025
c68afad
bug fix
sanderdemeyer Nov 20, 2025
95ddb06
avoid allocation in diagonal case
sanderdemeyer Nov 20, 2025
5d6f4f3
Merge branch 'main' into exponential
sanderdemeyer Nov 26, 2025
c8e811c
include exponentiali(tau, A)
sanderdemeyer Nov 26, 2025
0229417
remove simple test case and make the test more general
sanderdemeyer Nov 26, 2025
cbbf813
fix formatting
sanderdemeyer Nov 26, 2025
d08d545
add docs
sanderdemeyer Dec 1, 2025
720ada5
remove a bunch of allocations and clean up
lkdvos Dec 3, 2025
d738c22
Merge branch 'main' into exponential
lkdvos Dec 3, 2025
be111ea
introduce `map_diagonal` to simplify and relax types
lkdvos Dec 3, 2025
c760a47
rework tests
lkdvos Dec 3, 2025
d0d14e1
revert wrong filename changes
lkdvos Dec 3, 2025
cf98bd4
avoid running non-GPU tests through buildkite
lkdvos Dec 3, 2025
1536eb4
correct wrong in-place assumptions
lkdvos Dec 3, 2025
349800e
fixes part II
lkdvos Dec 3, 2025
28b5bc5
exponentialr instead of exponentiali
sanderdemeyer May 14, 2026
c313009
Merge branch 'main' into exponential
sanderdemeyer May 14, 2026
55794da
fix default algorithms
sanderdemeyer Jun 10, 2026
e04d94c
Merge branch 'exponential' of https://github.com/sanderdemeyer/Matrix…
sanderdemeyer Jun 10, 2026
04f08ab
fix default algorithms 2
sanderdemeyer Jun 10, 2026
bfcd6ca
Fix merge
sanderdemeyer Jun 11, 2026
3c3124f
fix default exponential algorithm
sanderdemeyer Jun 11, 2026
04a1436
Merge branch 'main' into exponential
sanderdemeyer Jun 11, 2026
2d736f6
add packages to tests
sanderdemeyer Jun 11, 2026
89f02c3
Update ext/MatrixAlgebraKitGenericSchurExt.jl
sanderdemeyer Jun 12, 2026
dba342f
Update test/genericlinearalgebra/exponential.jl
sanderdemeyer Jun 12, 2026
ed4228e
Update test/genericlinearalgebra/exponential.jl
sanderdemeyer Jun 12, 2026
329f493
change name of `exponentialr` to `exponential`
sanderdemeyer Jun 12, 2026
62b448b
fix formatting
sanderdemeyer Jun 12, 2026
1cd993e
Fix `check_scalar`
sanderdemeyer Jun 12, 2026
cdec4d5
Merge branch 'main' into exponential
sanderdemeyer Jun 12, 2026
dc9d48e
Update `check_scalar`
sanderdemeyer Jun 12, 2026
386670d
Merge branch 'exponential' of https://github.com/sanderdemeyer/Matrix…
sanderdemeyer Jun 12, 2026
f160b21
remove redundant `ishermitian` checks
sanderdemeyer Jun 12, 2026
08085c9
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
c3ba3be
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
32670d7
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
03ce510
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
70f692f
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
67b2faf
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
6589265
change from two-argument to one-argument `check_input`
sanderdemeyer Jun 12, 2026
f508b00
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
1c1c394
Update src/implementations/exponential.jl
sanderdemeyer Jun 12, 2026
e28ea8c
Remove undefined exports
sanderdemeyer Jun 13, 2026
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
7 changes: 7 additions & 0 deletions ext/MatrixAlgebraKitGenericSchurExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ function MatrixAlgebraKit.default_eig_algorithm(
return QRIteration(; driver, kwargs...)
end

function MatrixAlgebraKit.default_exponential_algorithm(
type::Type{T}; kwargs...
) where {T <: StridedMatrix{<:GSFloat}}
eig_alg = MatrixAlgebraKit.default_eig_algorithm(type; kwargs...)
return MatrixFunctionViaEig(eig_alg)
end

function geev!(::GS, A::AbstractMatrix, Dd::AbstractVector, V::AbstractMatrix; kwargs...)
D, Vmat = GenericSchur.eigen!(A)
copyto!(Dd, D)
Expand Down
7 changes: 7 additions & 0 deletions src/MatrixAlgebraKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export left_polar, right_polar
export left_polar!, right_polar!
export left_orth, right_orth, left_null, right_null
export left_orth!, right_orth!, left_null!, right_null!
export exponential, exponential!

export Householder, Native_HouseholderQR, Native_HouseholderLQ
export DivideAndConquer, SafeDivideAndConquer, QRIteration, Bisection, Jacobi, SVDViaPolar
Expand All @@ -40,6 +41,7 @@ export LAPACK_HouseholderQR, LAPACK_HouseholderLQ, LAPACK_Simple, LAPACK_Expert,
export GLA_HouseholderQR, GLA_QRIteration, GS_QRIteration
export LQViaTransposedQR
export PolarViaSVD, PolarNewton
export MatrixFunctionViaLA, MatrixFunctionViaEig, MatrixFunctionViaEigh
export DefaultAlgorithm
export DiagonalAlgorithm
export NativeBlocked
Expand Down Expand Up @@ -95,9 +97,12 @@ include("common/matrixproperties.jl")

include("yalapack.jl")
include("algorithms.jl")

include("interface/projections.jl")
include("interface/decompositions.jl")
include("interface/truncation.jl")
include("interface/matrixfunctions.jl")

include("interface/qr.jl")
include("interface/lq.jl")
include("interface/svd.jl")
Expand All @@ -107,6 +112,7 @@ include("interface/gen_eig.jl")
include("interface/schur.jl")
include("interface/polar.jl")
include("interface/orthnull.jl")
include("interface/exponential.jl")

include("implementations/projections.jl")
include("implementations/truncation.jl")
Expand All @@ -119,6 +125,7 @@ include("implementations/gen_eig.jl")
include("implementations/schur.jl")
include("implementations/polar.jl")
include("implementations/orthnull.jl")
include("implementations/exponential.jl")

include("common/gauge.jl") # needs to be defined after the functions are

Expand Down
20 changes: 20 additions & 0 deletions src/common/view.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ See also [`diagview`](@ref).

diagonal(v::AbstractVector) = Diagonal(v)

"""
map_diagonal!(f, dst, src...)

Map the scalar function `f` over all elements of the diagonal of `src...`, returning
a diagonal result.

See also [`map_diagonal!`](@ref).
"""
map_diagonal(f, src, srcs...) = diagonal(f.(diagview(src), map(diagview, srcs)...))

"""
map_diagonal!(f, dst, src...)

Map the scalar function `f` over all elements of the diagonal of `src...`,
into the diagonal elements of destination `dst`.

See also [`map_diagonal`](@ref).
"""
map_diagonal!(f, dst, src, srcs...) = (diagview(dst) .= f.(diagview(src), map(diagview, srcs)...); dst)

# triangularind
function lowertriangularind(A::AbstractMatrix)
Base.require_one_based_indexing(A)
Expand Down
129 changes: 129 additions & 0 deletions src/implementations/exponential.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Inputs
# ------
function copy_input(::typeof(exponential), A::AbstractMatrix)
return copy!(similar(A, float(eltype(A))), A)
end

copy_input(::typeof(exponential), A::Diagonal) = copy(A)
copy_input(::typeof(exponential), (τ, A)::Tuple{Number, AbstractMatrix}) = (τ, copy!(similar(A, float(eltype(A))), A))
copy_input(::typeof(exponential), (τ, A)::Tuple{Number, Diagonal}) = τ, copy(A)

function check_input(::typeof(exponential!), A::AbstractMatrix, expA::AbstractMatrix, alg::AbstractAlgorithm)
m = LinearAlgebra.checksquare(A)
@check_size(expA, (m, m))
@check_scalar(expA, A)
return nothing
end

function check_input(::typeof(exponential!), A::AbstractMatrix, expA::AbstractMatrix, alg::MatrixFunctionViaEigh)
m = LinearAlgebra.checksquare(A)
@check_size(expA, (m, m))
@check_scalar(expA, A)
return nothing
end
Comment on lines +18 to +23

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this one covered by the previous definition with alg::AbstractAlgorithm?


function check_input(::typeof(exponential!), A::AbstractMatrix, expA::AbstractMatrix, ::DiagonalAlgorithm)
m = LinearAlgebra.checksquare(A)
@assert isdiag(A)
@assert expA isa Diagonal
@check_size(expA, (m, m))
@check_scalar(expA, A)
return nothing
end

function check_input(::typeof(exponential!), (τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::AbstractAlgorithm)
m = LinearAlgebra.checksquare(A)
@check_size(expA, (m, m))
@check_scalar(expA, A, (τ isa Real) ? identity : complex)
return nothing
end

function check_input(::typeof(exponential!), (τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, ::DiagonalAlgorithm)
m = LinearAlgebra.checksquare(A)
@assert isdiag(A)
@assert expA isa Diagonal
@check_size(expA, (m, m))
@check_scalar(expA, A, (τ isa Real) ? identity : complex)
return nothing
end

# Outputs
# -------
initialize_output(::typeof(exponential!), A::AbstractMatrix, ::AbstractAlgorithm) = A
initialize_output(::typeof(exponential!), (τ, A)::Tuple{T, AbstractMatrix}, ::AbstractAlgorithm) where {T <: Real} = A
initialize_output(::typeof(exponential!), (τ, A)::Tuple{Number, AbstractMatrix}, ::AbstractAlgorithm) = complex(A)

# Implementation
# --------------
function exponential!(A, expA, alg::MatrixFunctionViaLA)
check_input(exponential!, A, expA, alg)
A = LinearAlgebra.exp!(A)
A === expA || copy!(expA, A)
return expA
end

function exponential!(A, expA, alg::MatrixFunctionViaEigh)
check_input(exponential!, A, expA, alg)
D, V = eigh_full!(A, alg.eigh_alg)
expD = map_diagonal!(x -> exp(x / 2), D, D)
VexpD = rmul!(V, expD)
return mul!(expA, VexpD, V')
Comment on lines +68 to +70

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right. I think you do x -> exp(x / 2) with the intention to then do mul(expA, VexpD, VexpD') ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably rename the variables slightly, but V and expD share memory so the two are in fact the same

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return mul!(expA, VexpD, V')
return mul!(expA, VexpD, VexpD')

end
Comment on lines +65 to +71

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function exponential!(A, expA, alg::MatrixFunctionViaEigh)
check_input(exponential!, A, expA, alg)
D, V = eigh_full!(A, alg.eigh_alg)
expD = map_diagonal!(x -> exp(x / 2), D, D)
VexpD = rmul!(V, expD)
return mul!(expA, VexpD, V')
end
exponential!(A, expA, alg::MatrixFunctionViaEigh) = exponential!((1, A), expA, alg)


function exponential!(A::AbstractMatrix, expA::AbstractMatrix, alg::MatrixFunctionViaEig)
check_input(exponential!, A, expA, alg)
D, V = eig_full!(A, alg.eig_alg)
expD = map_diagonal!(exp, D, D)
iV = inv(V)
VexpD = rmul!(V, expD)
if eltype(A) <: Real
expA .= real.(VexpD * iV)
else
mul!(expA, VexpD, iV)
end
return expA
end
Comment on lines +73 to +85

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function exponential!(A::AbstractMatrix, expA::AbstractMatrix, alg::MatrixFunctionViaEig)
check_input(exponential!, A, expA, alg)
D, V = eig_full!(A, alg.eig_alg)
expD = map_diagonal!(exp, D, D)
iV = inv(V)
VexpD = rmul!(V, expD)
if eltype(A) <: Real
expA .= real.(VexpD * iV)
else
mul!(expA, VexpD, iV)
end
return expA
end
exponential!(A::AbstractMatrix, expA::AbstractMatrix, alg::MatrixFunctionViaEig) = exponential!((1, A), expA, alg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do 1, one(eltype(A)), or true?


function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::MatrixFunctionViaLA)
check_input(exponential!, (τ, A), expA, alg)
expA .= A .* τ
return LinearAlgebra.exp!(expA)
end
Comment on lines +87 to +91

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid code duplication:

Suggested change
function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::MatrixFunctionViaLA)
check_input(exponential!, (τ, A), expA, alg)
expA .= A .* τ
return LinearAlgebra.exp!(expA)
end
exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::MatrixFunctionViaLA) = exponential!(A * τ, expA, alg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::MatrixFunctionViaLA)
check_input(exponential!, (τ, A), expA, alg)
expA .= A .* τ
return LinearAlgebra.exp!(expA)
end
function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::AbstractAlgorithm)
expA .= A .* τ
return exponential!(expA, expA, alg)
end

I think this way we avoid one more allocation, and I immediately relaxed the alg::AbstractAlgortihm signature since I guess this is a good default implementation? (Note that while we could modify A itself, this might fail because of eltype issues, so it has to be expA.


function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA::AbstractMatrix, alg::MatrixFunctionViaEigh)
check_input(exponential!, (τ, A), expA, alg)
D, V = eigh_full!(A, alg.eigh_alg)
expD = map_diagonal(x -> exp(x * τ), D)
VexpD = V * expD
if eltype(A) <: Real && eltype(τ) <: Real
return expA .= real.(VexpD * V')
else
return mul!(expA, VexpD, V')
end
Comment on lines +96 to +102

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if eltype(A) <: Real && eltype(τ) <: Real, everything should remain real, so no real call is necessary.

Suggested change
expD = map_diagonal(x -> exp(x * τ), D)
VexpD = V * expD
if eltype(A) <: Real && eltype(τ) <: Real
return expA .= real.(VexpD * V')
else
return mul!(expA, VexpD, V')
end
if eltype(A) <: Real && eltype(τ) <: Real
V .*= exp.(transpose(diagview(D)) .* τ ./ 2)
return mul!(expA, V, V')
else
VexpD = V .* exp.(transpose(diagview(D)) .* τ)
return mul!(expA, VexpD, V')
end

end

function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA, alg::MatrixFunctionViaEig)
check_input(exponential!, (τ, A), expA, alg)
D, V = eig_full!(A, alg.eig_alg)
expD = map_diagonal!(x -> exp(x * τ), D, D)
iV = inv(V)
VexpD = rmul!(V, expD)
if eltype(A) <: Real && eltype(τ) <: Real
expA .= real.(VexpD * iV)
return expA
else
return mul!(expA, VexpD, iV)
end
Comment on lines +108 to +116

@Jutho Jutho Jun 15, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expD = map_diagonal!(x -> exp(x * τ), D, D)
iV = inv(V)
VexpD = rmul!(V, expD)
if eltype(A) <: Real && eltype(τ) <: Real
expA .= real.(VexpD * iV)
return expA
else
return mul!(expA, VexpD, iV)
end
if eltype(A) <: Real && eltype(τ) <: Real
VexpD = V .* exp.(transpose(diagview(D)) .* τ)
expAc = rdiv!(VexpD, LinearAlgebra.lu!(V))
return expA .= real.(expAc)
else
expA .= V .* exp.(transpose(D) .* τ)
return rdiv!(expA, LinearAlgebra.lu!(V))
end

end

# Diagonal logic
# --------------
function exponential!(A, expA, alg::DiagonalAlgorithm)
check_input(exponential!, A, expA, alg)
return map_diagonal!(exp, expA, A)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually like the broadcasting version more. Together with the suggestions above, I think this removes the need for map_diagonal:

Suggested change
return map_diagonal!(exp, expA, A)
diagview(expA) .= exp.(diagview(A))
return expA

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I suggested/introduced the map_diagonal to have a specialization point in case we end up using this for SectorVector/DiagonalTensorMap as well. (Currently broadcasting for those doesn't actually work on GPU, since it falls back to scalar indexing). Happy to remove this if you prefer though, but then we probably should investigate properly implementing the broadcasting :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with map_diagonal! also, and then we should probably use it more consistently. We have to make sure that this one then works well on the GPU, and supports overwriting input, e.g. like map_diagonal!(exp, D, D).

But maybe we should actually just call exponential!(D, D, DiagonalAlgorithm())?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea, that way I guess we can fully remove any array-specific part of this code and have it be generic?

end

function exponential!((τ, A)::Tuple{Number, AbstractMatrix}, expA, alg::DiagonalAlgorithm)
check_input(exponential!, (τ, A), expA, alg)
return map_diagonal!(x -> exp(x * τ), expA, A)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return map_diagonal!(x -> exp(x * τ), expA, A)
diagview(expA) .= exp.(diagview(A) .* τ)
return expA

end
43 changes: 43 additions & 0 deletions src/interface/exponential.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Exponential functions
# --------------

"""
exponential(A; kwargs...) -> expA
exponential(A, alg::AbstractAlgorithm) -> expA
exponential!(A, [expA]; kwargs...) -> expA
exponential!(A, [expA], alg::AbstractAlgorithm) -> expA
exponential((τ,A); kwargs...) -> expτA
exponential((τ,A), alg::AbstractAlgorithm) -> expτA
exponential!((τ,A), [expA]; kwargs...) -> expτA
exponential!((τ,A), [expA], alg::AbstractAlgorithm) -> expτA

Compute the exponential of the square matrix `A` or `τ*A`,

!!! note
The bang method `exponential!` optionally accepts the output structure and
possibly destroys the input matrix `A`. Always use the return value of the function
as it may not always be possible to use the provided `expA` as output.
"""
@functiondef exponential

# Algorithm selection
# -------------------
default_exponential_algorithm(A; kwargs...) = default_exponential_algorithm(typeof(A); kwargs...)
function default_exponential_algorithm(T::Type; kwargs...)
return MatrixFunctionViaLA(; kwargs...)
end
function default_exponential_algorithm(::Type{T}; kwargs...) where {T <: Diagonal}
return DiagonalAlgorithm(; kwargs...)
end

for f in (:exponential!,)
@eval function default_algorithm(::typeof($f), ::Type{A}; kwargs...) where {A}
return default_exponential_algorithm(A; kwargs...)
end
end

for f in (:exponential!,)
@eval function default_algorithm(::typeof($f), ::Tuple{A, B}; kwargs...) where {A, B}
return default_exponential_algorithm(B; kwargs...)
end
end
Comment on lines +33 to +43

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to do these via an @eval block since it is only one function:

Suggested change
for f in (:exponential!,)
@eval function default_algorithm(::typeof($f), ::Type{A}; kwargs...) where {A}
return default_exponential_algorithm(A; kwargs...)
end
end
for f in (:exponential!,)
@eval function default_algorithm(::typeof($f), ::Tuple{A, B}; kwargs...) where {A, B}
return default_exponential_algorithm(B; kwargs...)
end
end
function default_algorithm(::typeof(exponential!), ::Type{A}; kwargs...) where {A}
return default_exponential_algorithm(A; kwargs...)
end
function default_algorithm(::typeof(exponential!), ::Tuple{A, B}; kwargs...) where {A, B}
return default_exponential_algorithm(B; kwargs...)
end

39 changes: 39 additions & 0 deletions src/interface/matrixfunctions.jl
Comment thread
sanderdemeyer marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ================================
# EXPONENTIAL ALGORITHMS
# ================================
"""
MatrixFunctionViaLA()

Algorithm type to denote finding the exponential of `A` via the implementation of `LinearAlgebra`.
"""
@algdef MatrixFunctionViaLA

"""
MatrixFunctionViaEigh()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MatrixFunctionViaEigh()
MatrixFunctionViaEigh(eigh_alg)


Algorithm type to denote finding the exponential `A` by computing the hermitian eigendecomposition of `A`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Algorithm type to denote finding the exponential `A` by computing the hermitian eigendecomposition of `A`.
Algorithm type for computing a function of a matrix by computing its hermitian eigenvalue decomposition and applying the function to the eigenvalues.

The `eigh_alg` specifies which hermitian eigendecomposition implementation to use.
"""
struct MatrixFunctionViaEigh{A <: AbstractAlgorithm} <: AbstractAlgorithm
eigh_alg::A
end
function Base.show(io::IO, alg::MatrixFunctionViaEigh)
print(io, "MatrixFunctionViaEigh(")
_show_alg(io, alg.eigh_alg)
return print(io, ")")
end

"""
MatrixFunctionViaEig()

Algorithm type to denote finding the exponential `A` by computing the eigendecomposition of `A`.
The `eig_alg` specifies which eigendecomposition implementation to use.
Comment on lines +27 to +30

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MatrixFunctionViaEig()
Algorithm type to denote finding the exponential `A` by computing the eigendecomposition of `A`.
The `eig_alg` specifies which eigendecomposition implementation to use.
MatrixFunctionViaEig(eig_alg)
Algorithm type for computing a function of a matrix by computing its eigenvalue decomposition and applying the function to the eigenvalues.
The `eig_alg` specifies which eigendecomposition implementation to use.

"""
struct MatrixFunctionViaEig{A <: AbstractAlgorithm} <: AbstractAlgorithm
eig_alg::A
end
function Base.show(io::IO, alg::MatrixFunctionViaEig)
print(io, "MatrixFunctionViaEig(")
_show_alg(io, alg.eig_alg)
return print(io, ")")
end
1 change: 0 additions & 1 deletion src/matrixfunctions.jl

This file was deleted.

88 changes: 88 additions & 0 deletions test/exponential.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using MatrixAlgebraKit
using Test
using TestExtras
using StableRNGs
using MatrixAlgebraKit: diagview
using LinearAlgebra
using LinearAlgebra: exp

BLASFloats = (Float32, Float64, ComplexF32, ComplexF64)
GenericFloats = (Float16, ComplexF16, BigFloat, Complex{BigFloat})

@testset "exponential! for T = $T" for T in BLASFloats
rng = StableRNG(123)
m = 54

A = LinearAlgebra.normalize!(randn(rng, T, m, m))
Ac = copy(A)
expA = LinearAlgebra.exp(A)

expA2 = @constinferred exponential(A)
@test expA ≈ expA2
@test A == Ac

algs = (MatrixFunctionViaLA(), MatrixFunctionViaEig(LAPACK_Simple()))
@testset "algorithm $alg" for alg in algs
expA2 = @constinferred exponential(A, alg)
@test expA ≈ expA2
@test A == Ac
end

@test_throws DomainError exponential(A; alg = MatrixFunctionViaEigh(LAPACK_QRIteration()))
end

@testset "exponential! for T = $T" for T in BLASFloats
rng = StableRNG(123)
m = 54

A = randn(rng, T, m, m)
τ = randn(rng, T)
Ac = copy(A)

Aτ = A * τ
expAτ = LinearAlgebra.exp(Aτ)

expAτ2 = @constinferred exponential((τ, A))
@test expAτ ≈ expAτ2
@test A == Ac

algs = (MatrixFunctionViaLA(), MatrixFunctionViaEig(LAPACK_Simple()))
@testset "algorithm $alg" for alg in algs
expAτ2 = @constinferred exponential((τ, A), alg)
@test expAτ ≈ expAτ2
@test A == Ac
end

@test_throws DomainError exponential((τ, A); alg = MatrixFunctionViaEigh(LAPACK_QRIteration()))
end

@testset "exponential! for Diagonal{$T}" for T in (BLASFloats..., GenericFloats...)
rng = StableRNG(123)
m = 54

A = Diagonal(randn(rng, T, m))
τ = randn(rng, T)
Ac = copy(A)

expA = LinearAlgebra.exp(A)

expA2 = @constinferred exponential(A)
@test expA ≈ expA2
@test A == Ac
end

@testset "exponential! for Diagonal{$T}" for T in (BLASFloats..., GenericFloats...)
rng = StableRNG(123)
m = 1

A = Diagonal(randn(rng, T, m))
τ = randn(rng, T)
Ac = copy(A)

Aτ = A * τ
expAτ = LinearAlgebra.exp(Aτ)

expAτ2 = @constinferred exponential((τ, A))
@test expAτ ≈ expAτ2
@test A == Ac
end
Loading
Loading