Skip to content
32 changes: 32 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,29 @@ 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.


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 +101,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 +247,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