Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
34 changes: 34 additions & 0 deletions src/factorizations/matrixalgebrakit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ for f in
:eig_full, :eig_vals, :eigh_full, :eigh_vals,
:left_polar, :right_polar,
:project_hermitian, :project_antihermitian, :project_isometric,
:exponential,
]
f! = Symbol(f, :!)
@eval function MAK.default_algorithm(::typeof($f!), ::Type{T}; kwargs...) where {T <: AbstractTensorMap}
Expand Down Expand Up @@ -49,6 +50,7 @@ for f! in (
:qr_null!, :lq_null!,
:svd_vals!, :eig_vals!, :eigh_vals!,
:project_hermitian!, :project_antihermitian!, :project_isometric!,
:exponential!,
)
@eval function MAK.$f!(t::AbstractTensorMap, N, alg::AbstractAlgorithm)
$(f! in (:eig_vals!, :eigh_vals!, :project_hermitian!, :project_antihermitian!) && :(LinearAlgebra.checksquare(t)))
Expand All @@ -62,6 +64,31 @@ for f! in (
end
end

# Exponential with Tuple
function MAK.exponential!((τ, t)::Tuple{E, T}, N, alg::AbstractAlgorithm) where {E <: Number, T <: AbstractTensorMap}
LinearAlgebra.checksquare(t)
foreachblock(t, N) do _, (tblock, Nblock)
Nblock′ = exponential!((τ, tblock), Nblock, alg)
# deal with the case where the output is not the same as the input
Nblock === Nblock′ || copy!(Nblock, Nblock′)
return nothing
end
return N
end

# Default algorithm for exponential with Tuple
MAK.exponential!((τ, t)::Tuple{E, T}) where {E <: Number, T <: DiagonalTensorMap} = MAK.exponential!((τ, t), DefaultAlgorithm())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a line that I'm a bit sceptical about. I don't know why this has to be included, since I think it should be covered by the functions above, but if I don't include this, the algorithm that is selected for DiagonalTensorMaps is the default (LinearAlgebra.exp!) instead of the desired DiagonalAlgorithm.

MAK.exponential!(t::Tuple{E, T}, ::DefaultAlgorithm) where {E <: Number, T <: Diagonal} = MAK.exponential!(t, DiagonalAlgorithm())
MAK.exponential!(t::Tuple{E, T1}, out::T2, ::DefaultAlgorithm) where {E <: Number, T1 <: Diagonal, T2 <: Diagonal} = MAK.exponential!(t, out, DiagonalAlgorithm())
Comment on lines +81 to +82

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.

@sanderdemeyer sanderdemeyer Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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


function MAK.default_algorithm(::typeof(exponential!), ::Type{Tuple{E, T}}; kwargs...) where {E <: Number, T}

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 MAK.default_algorithm(::typeof(exponential!), ::Type{Tuple{E, T}}; kwargs...) where {E <: Number, T}
function MAK.default_algorithm(::typeof(exponential!), ::Type{Tuple{E, T}}; kwargs...) where {E <: Number, T <: AbstractTensorMap}

Although I think it might be better to replace this in MAK: https://github.com/QuantumKitHub/MatrixAlgebraKit.jl/blob/11096c1667ca0c08cb15eaa7e8388a55a0abe24a/src/interface/exponential.jl#L37-L39
should just forward to the non-tuple case, which will then select the default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What is then your suggested change in MAK? Because there we don't specify anything (in both exponential!(t) as exponential!((tau,t)) ). Do you want to restrict it to A <: AbstractMatrix there?

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.

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_algorithm(exponential!, B; kwargs...)
end
function default_algorithm(::typeof(exponential!), ::Type{Tuple{A, B}}; kwargs...) where {A, B}
    return default_algorithm(exponential!, B; kwargs...)
end

Something like this?

return MAK.default_algorithm(exponential!, blocktype(T); kwargs...)
end

function MAK.copy_input(::typeof(exponential), (τ, t)::Tuple{E, T}) where {E <: Number, T <: AbstractTensorMap}
return (τ, copy_oftype(t, factorisation_scalartype(exponential, t)))
end

MAK.zero!(t::AbstractTensorMap) = zerovector!(t)

# Default algorithm
Expand All @@ -76,6 +103,7 @@ for f in [
:left_polar, :right_polar,
:left_orth, :right_orth, :left_null, :right_null,
:project_hermitian, :project_antihermitian, :project_isometric,
:exponential,
]
f! = Symbol(f, :!)
@eval MAK.$f!(t::AbstractTensorMap, alg::DefaultAlgorithm) =
Expand Down Expand Up @@ -221,3 +249,9 @@ MAK.initialize_output(::typeof(project_antihermitian!), tsrc::AbstractTensorMap,
tsrc
MAK.initialize_output(::typeof(project_isometric!), tsrc::AbstractTensorMap, ::AbstractAlgorithm) =
similar(tsrc)

# Exponential
# ----------------
MAK.initialize_output(::typeof(exponential!), t::AbstractTensorMap, ::AbstractAlgorithm) = t
MAK.initialize_output(::typeof(exponential!), (τ, t)::Tuple{Number, AbstractTensorMap}, ::AbstractAlgorithm) = t
MAK.initialize_output(::typeof(exponential!), (τ, t)::Tuple{T1, AbstractTensorMap{T2}}, ::AbstractAlgorithm) where {T1 <: Complex, T2 <: Real} = similar(t, complex(eltype(t)))
10 changes: 1 addition & 9 deletions src/tensors/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -416,15 +416,7 @@ function Base.:(/)(t1::AbstractTensorMap, t2::AbstractTensorMap)
return t
end

# TensorMap exponentation:
function exp!(t::TensorMap)
domain(t) == codomain(t) ||
error("Exponential of a tensor only exist when domain == codomain.")
for (c, b) in blocks(t)
copy!(b, LinearAlgebra.exp!(b))
end
return t
end
@deprecate exp!(t) exponential!(t)

# Sylvester equation with TensorMap objects:
function LinearAlgebra.sylvester(A::AbstractTensorMap, B::AbstractTensorMap, C::AbstractTensorMap)
Expand Down
64 changes: 64 additions & 0 deletions test/tensors/exponential.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Test, TestExtras
using TensorKit, MatrixAlgebraKit
using Random

spaces = [ℂ^4] #, Vect[U1Irrep](0 => 1, 1 => 2), Vect[SU2Irrep](0 => 1, 1 // 2 => 1)]
scalartypes = [Float64, ComplexF32] #, ComplexF64]
algorithms = [MatrixFunctionViaLA(), MatrixFunctionViaEig(DefaultAlgorithm()), MatrixFunctionViaEigh(DefaultAlgorithm())]

@timedtestset "exponential for Hermitian matrices with $space, scalartype(A) = $st1, scalartype(τ) = $st2" for space in spaces, st1 in scalartypes, st2 in scalartypes
A = randn(st1, space, space)
A = project_hermitian!(A)
τ = rand(st2)

expA = @constinferred exponential(A)
expτA = @constinferred exponential((τ, A))

for alg in algorithms
expA2 = @constinferred exponential(A, alg)
expτA2 = @constinferred exponential((τ, A), alg)

@test expA ≈ expA2
@test expτA ≈ expτA2
end
end

@timedtestset "exponential! for general matrices for $space, scalartype(A) = $st1, scalartype(τ) = $st2" for space in spaces, st1 in scalartypes, st2 in scalartypes
A = randn(st1, space, space)
τ = rand(st2)

@test exponential!(copy(A)) == exponential!((1.0, copy(A)))

A2 = exponential!((τ, A))
if st1 <: Real && st2 <: Complex
@test objectid(A2) != objectid(A)
else
@test objectid(A2) == objectid(A)
end

expτA = exponential!((τ, copy(A)))
expminτA = exponential!((-τ, copy(A)))
@test expτA * expminτA ≈ id(scalartype(expτA), space)
@test expτA ≈ inv(expminτA)
end

@timedtestset "exponential! for diagonal matrices for $space, scalartype(A) = $st1, scalartype(τ) = $st2" for space in spaces, st1 in scalartypes, st2 in scalartypes
A = DiagonalTensorMap(rand(st1, reduceddim(space)), space)
τ = rand(st2)

exponential!(copy(A))
@test exponential!(copy(A)) ≈ exponential!((1.0, copy(A))) #, DiagonalAlgorithm())

A2 = @constinferred exponential!((τ, A))
@test A2 isa DiagonalTensorMap
if st1 <: Real && st2 <: Complex
@test objectid(A2) != objectid(A)
else
@test objectid(A2) == objectid(A)
end

expτA = exponential!((τ, copy(A)))
expminτA = exponential!((-τ, copy(A)))
@test expτA * expminτA ≈ id(scalartype(expτA), space)
@test expτA ≈ inv(expminτA)
end
Loading