Skip to content

Commit 0da6359

Browse files
akirakyleKrastanov
andauthored
Use FastExpm.jl for matrix exponential of SparseSuperOperator (#112)
--------- Co-authored-by: Stefan Krastanov <[email protected]>
1 parent 0d68b84 commit 0da6359

File tree

6 files changed

+64
-4
lines changed

6 files changed

+64
-4
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
name = "QuantumOpticsBase"
22
uuid = "4f57444f-1401-5e15-980d-4471b28d5678"
3-
version = "0.4.11"
3+
version = "0.4.12"
44

55
[deps]
66
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
77
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
8+
FastExpm = "7868e603-8603-432e-a1a1-694bd70b01f2"
89
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
910
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
1011
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -17,6 +18,7 @@ UnsafeArrays = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6"
1718
[compat]
1819
Adapt = "1, 2, 3.3"
1920
FFTW = "1.2"
21+
FastExpm = "1.1.0"
2022
FillArrays = "0.13, 1"
2123
LRUCache = "1"
2224
QuantumInterface = "0.3.0"

src/operators_dense.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,15 @@ function expect(op::DataOperator{B1,B2}, state::DataOperator{B2,B2}) where {B1,B
226226
result
227227
end
228228

229+
"""
230+
exp(op::DenseOpType)
231+
232+
Operator exponential used, for example, to calculate displacement operators.
233+
Uses LinearAlgebra's `Base.exp`.
234+
235+
If you only need the result of the exponential acting on a vector,
236+
consider using much faster implicit methods that do not calculate the entire exponential.
237+
"""
229238
function exp(op::T) where {B,T<:DenseOpType{B,B}}
230239
return DenseOperator(op.basis_l, op.basis_r, exp(op.data))
231240
end

src/operators_sparse.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Base: ==, *, /, +, -, Broadcast
22
import SparseArrays: sparse
3+
import FastExpm: fastExpm
34

45
const SparseOpPureType{BL,BR} = Operator{BL,BR,<:SparseMatrixCSC}
56
const SparseOpAdjType{BL,BR} = Operator{BL,BR,<:Adjoint{<:Number,<:SparseMatrixCSC}}
@@ -45,6 +46,21 @@ function expect(op::SparseOpPureType{B1,B2}, state::Operator{B2,B2}) where {B1,B
4546
result
4647
end
4748

49+
"""
50+
exp(op::SparseOpType; opts...)
51+
52+
Operator exponential used, for example, to calculate displacement operators.
53+
Uses [`FastExpm.jl.jl`](https://github.com/fmentink/FastExpm.jl) which will return a sparse
54+
or dense operator depending on which is more efficient.
55+
All optional arguments are passed to `fastExpm` and can be used to specify tolerances.
56+
57+
If you only need the result of the exponential acting on a vector,
58+
consider using much faster implicit methods that do not calculate the entire exponential.
59+
"""
60+
function exp(op::T; opts...) where {B,T<:SparseOpType{B,B}}
61+
return SparseOperator(op.basis_l, op.basis_r, fastExpm(op.data; opts...))
62+
end
63+
4864
function permutesystems(rho::SparseOpPureType{B1,B2}, perm) where {B1<:CompositeBasis,B2<:CompositeBasis}
4965
@assert length(rho.basis_l.bases) == length(rho.basis_r.bases) == length(perm)
5066
@assert isperm(perm)
@@ -99,4 +115,4 @@ mul!(result::Bra{B2},b::Bra{B1},M::SparseOpPureType{B1,B2},alpha,beta) where {B1
99115
/(op::EyeOpType,a::T) where {T<:Number} = sparse(op)/a
100116
tensor(a::EyeOpType, b::SparseOpType) = tensor(sparse(a),b)
101117
tensor(a::SparseOpType, b::EyeOpType) = tensor(a,sparse(b))
102-
tensor(a::EyeOpType, b::EyeOpType) = tensor(sparse(a),sparse(b))
118+
tensor(a::EyeOpType, b::EyeOpType) = tensor(sparse(a),sparse(b))

src/superoperators.jl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import QuantumInterface: AbstractSuperOperator
2+
import FastExpm: fastExpm
23

34
"""
45
SuperOperator <: AbstractSuperOperator
@@ -221,10 +222,27 @@ end
221222
"""
222223
exp(op::DenseSuperOperator)
223224
224-
Operator exponential which can for example used to calculate time evolutions.
225+
Superoperator exponential which can, for example, be used to calculate time evolutions.
226+
Uses LinearAlgebra's `Base.exp`.
227+
228+
If you only need the result of the exponential acting on an operator,
229+
consider using much faster implicit methods that do not calculate the entire exponential.
225230
"""
226231
Base.exp(op::DenseSuperOpType) = DenseSuperOperator(op.basis_l, op.basis_r, exp(op.data))
227232

233+
"""
234+
exp(op::SparseSuperOperator; opts...)
235+
236+
Superoperator exponential which can, for example, be used to calculate time evolutions.
237+
Uses [`FastExpm.jl.jl`](https://github.com/fmentink/FastExpm.jl) which will return a sparse
238+
or dense operator depending on which is more efficient.
239+
All optional arguments are passed to `fastExpm` and can be used to specify tolerances.
240+
241+
If you only need the result of the exponential acting on an operator,
242+
consider using much faster implicit methods that do not calculate the entire exponential.
243+
"""
244+
Base.exp(op::SparseSuperOpType; opts...) = SuperOperator(op.basis_l, op.basis_r, fastExpm(op.data; opts...))
245+
228246
# Array-like functions
229247
Base.size(A::SuperOperator) = size(A.data)
230248
@inline Base.axes(A::SuperOperator) = axes(A.data)

test/test_operators.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ OP_cnot = embed(b8, [1,3], op_cnot)
114114
@test reduced(OP_cnot, [1,3])/2. == op_cnot
115115
@test_throws AssertionError embed(b2b2, [1,1], op_cnot)
116116

117-
@test_throws ArgumentError exp(sparse(op1))
117+
b = FockBasis(40)
118+
alpha = 1+5im
119+
H = alpha * create(b) - conj(alpha) * destroy(b)
120+
@test exp(sparse(H); threshold=1e-10) displace(b, alpha)
118121

119122
@test one(b1).data == Diagonal(ones(b1.shape[1]))
120123
@test one(op1).data == Diagonal(ones(b1.shape[1]))

test/test_superoperators.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ end
188188

189189
# tout, ρt = timeevolution.master([0.,1.], ρ₀, H, J; reltol=1e-7)
190190
# @test tracedistance(exp(dense(L))*ρ₀, ρt[end]) < 1e-6
191+
# @test tracedistance(exp(sparse(L))*ρ₀, ρt[end]) < 1e-6
191192

192193
@test dense(spre(op1)) == spre(op1)
193194

@@ -217,4 +218,15 @@ Ldense .+= L
217218
@test_throws ErrorException cos.(Ldense)
218219
@test_throws ErrorException cos.(L)
219220

221+
b = FockBasis(20)
222+
L = liouvillian(identityoperator(b), [destroy(b)])
223+
@test exp(sparse(L)).data exp(dense(L)).data
224+
N = exp(log(2) * sparse(L)) # 50% loss channel
225+
ρ = N * dm(coherentstate(b, 1))
226+
@test (1 - real(tr^2))) < 1e-10 # coherent state remains pure under loss
227+
@test tracedistance(ρ, dm(coherentstate(b, 1/sqrt(2)))) < 1e-10
228+
ρ = N * dm(fockstate(b, 1))
229+
@test (0.5 - real(tr^2))) < 1e-10 # one photon state becomes maximally mixed
230+
@test tracedistance(ρ, normalize(dm(fockstate(b, 0)) + dm(fockstate(b, 1)))) < 1e-10
231+
220232
end # testset

0 commit comments

Comments
 (0)