Skip to content

Commit 74a891f

Browse files
ytdHuangalbertomercurio
authored andcommitted
Support time-dependent liouvillian (qutip#277)
* support time-dependent `liouvillian` * improve codecov * format files * minor changes
1 parent 7ef933e commit 74a891f

File tree

7 files changed

+72
-68
lines changed

7 files changed

+72
-68
lines changed

src/QuantumToolbox.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import SciMLOperators:
3737
AbstractSciMLOperator,
3838
MatrixOperator,
3939
ScalarOperator,
40+
ScaledOperator,
41+
AddedOperator,
4042
IdentityOperator,
4143
cache_operator,
4244
update_coefficients!,

src/qobj/superoperators.jl

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
Functions for generating (common) quantum super-operators.
33
=#
44

5-
export spre, spost, sprepost, lindblad_dissipator
5+
export spre, spost, sprepost, liouvillian, lindblad_dissipator
66

77
# intrinsic functions for super-operators
8-
# (keep these because they take AbstractMatrix as input)
8+
# (keep these because they take AbstractMatrix as input and ensure the output is sparse matrix)
99
_spre(A::AbstractMatrix, Id::AbstractMatrix) = kron(Id, sparse(A))
1010
_spre(A::AbstractSparseMatrix, Id::AbstractMatrix) = kron(Id, A)
1111
_spost(B::AbstractMatrix, Id::AbstractMatrix) = kron(transpose(sparse(B)), Id)
@@ -14,9 +14,22 @@ _sprepost(A::AbstractMatrix, B::AbstractMatrix) = kron(transpose(sparse(B)), spa
1414
_sprepost(A::AbstractMatrix, B::AbstractSparseMatrix) = kron(transpose(B), sparse(A))
1515
_sprepost(A::AbstractSparseMatrix, B::AbstractMatrix) = kron(transpose(sparse(B)), A)
1616
_sprepost(A::AbstractSparseMatrix, B::AbstractSparseMatrix) = kron(transpose(B), A)
17+
_liouvillian(H::AbstractMatrix, Id::AbstractMatrix) = -1im * (_spre(H, Id) - _spost(H, Id))
18+
19+
# (if input is AbstractSciMLOperator)
20+
_spre(A::MatrixOperator, Id::AbstractMatrix) = MatrixOperator(_spre(A.A, Id))
21+
_spre(A::ScaledOperator, Id::AbstractMatrix) = ScaledOperator(A.λ, _spre(A.L, Id))
22+
_spre(A::AddedOperator, Id::AbstractMatrix) = mapreduce(op -> _spre(op, Id), +, A.ops)
23+
_spost(B::MatrixOperator, Id::AbstractMatrix) = MatrixOperator(_spost(B.A, Id))
24+
_spost(B::ScaledOperator, Id::AbstractMatrix) = ScaledOperator(B.λ, _spost(B.L, Id))
25+
_spost(B::AddedOperator, Id::AbstractMatrix) = mapreduce(op -> _spost(op, Id), +, B.ops)
26+
_liouvillian(H::MatrixOperator, Id::AbstractMatrix) = MatrixOperator(_liouvillian(H.A, Id))
27+
_liouvillian(H::ScaledOperator, Id::AbstractMatrix) = ScaledOperator(H.λ, _liouvillian(H.L, Id))
28+
_liouvillian(H::AddedOperator, Id::AbstractMatrix) = mapreduce(op -> _liouvillian(op, Id), +, H.ops)
29+
# TODO: support `_sprepost`, `sprepost`, and `lindblad_dissipator` for AbstractSciMLOperator (allow c_ops with Vector{QobjEvo})
1730

1831
@doc raw"""
19-
spre(A::QuantumObject, Id_cache=I(size(A,1)))
32+
spre(A::AbstractQuantumObject, Id_cache=I(size(A,1)))
2033
2134
Returns the [`SuperOperator`](@ref) form of `A` acting on the left of the density matrix operator: ``\mathcal{O} \left(\hat{A}\right) \left[ \hat{\rho} \right] = \hat{A} \hat{\rho}``.
2235
@@ -27,14 +40,13 @@ Since the density matrix is vectorized in [`OperatorKet`](@ref) form: ``|\hat{\r
2740
```
2841
(see the section in documentation: [Superoperators and Vectorized Operators](@ref doc:Superoperators-and-Vectorized-Operators) for more details)
2942
30-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when
31-
the same function is applied multiple times with a known Hilbert space dimension.
43+
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
3244
"""
33-
spre(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, Id_cache = I(size(A, 1))) where {T} =
34-
QuantumObject(_spre(A.data, Id_cache), SuperOperator, A.dims)
45+
spre(A::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(A, 1))) where {DT} =
46+
get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator, A.dims)
3547

3648
@doc raw"""
37-
spost(B::QuantumObject, Id_cache=I(size(B,1)))
49+
spost(B::AbstractQuantumObject, Id_cache=I(size(B,1)))
3850
3951
Returns the [`SuperOperator`](@ref) form of `B` acting on the right of the density matrix operator: ``\mathcal{O} \left(\hat{B}\right) \left[ \hat{\rho} \right] = \hat{\rho} \hat{B}``.
4052
@@ -45,11 +57,10 @@ Since the density matrix is vectorized in [`OperatorKet`](@ref) form: ``|\hat{\r
4557
```
4658
(see the section in documentation: [Superoperators and Vectorized Operators](@ref doc:Superoperators-and-Vectorized-Operators) for more details)
4759
48-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when
49-
the same function is applied multiple times with a known Hilbert space dimension.
60+
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
5061
"""
51-
spost(B::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, Id_cache = I(size(B, 1))) where {T} =
52-
QuantumObject(_spost(B.data, Id_cache), SuperOperator, B.dims)
62+
spost(B::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(B, 1))) where {DT} =
63+
get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator, B.dims)
5364

5465
@doc raw"""
5566
sprepost(A::QuantumObject, B::QuantumObject)
@@ -84,21 +95,21 @@ Returns the Lindblad [`SuperOperator`](@ref) defined as
8495
\hat{O}^\dagger \hat{O} \hat{\rho} - \hat{\rho} \hat{O}^\dagger \hat{O} \right)
8596
```
8697
87-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when
88-
the same function is applied multiple times with a known Hilbert space dimension.
98+
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
8999
90-
See also [`spre`](@ref) and [`spost`](@ref).
100+
See also [`spre`](@ref), [`spost`](@ref), and [`sprepost`](@ref).
91101
"""
92102
function lindblad_dissipator(O::QuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(O, 1))) where {DT}
93103
Od_O = O' * O
94-
return sprepost(O, O') - spre(Od_O, Id_cache) / 2 - spost(Od_O, Id_cache) / 2
104+
return sprepost(O, O') - (spre(Od_O, Id_cache) + spost(Od_O, Id_cache)) / 2
95105
end
106+
# TODO: suppport collapse operator given as QobjEvo-type
96107

97108
# It is already a SuperOperator
98109
lindblad_dissipator(O::QuantumObject{DT,SuperOperatorQuantumObject}, Id_cache = nothing) where {DT} = O
99110

100111
@doc raw"""
101-
liouvillian(H::QuantumObject, c_ops::Union{Nothing,AbstractVector,Tuple}=nothing, Id_cache=I(prod(H.dims)))
112+
liouvillian(H::AbstractQuantumObject, c_ops::Union{Nothing,AbstractVector,Tuple}=nothing, Id_cache=I(prod(H.dims)))
102113
103114
Construct the Liouvillian [`SuperOperator`](@ref) for a system Hamiltonian ``\hat{H}`` and a set of collapse operators ``\{\hat{C}_n\}_n``:
104115
@@ -117,20 +128,18 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr
117128
See also [`spre`](@ref), [`spost`](@ref), and [`lindblad_dissipator`](@ref).
118129
"""
119130
function liouvillian(
120-
H::QuantumObject{MT1,OpType1},
131+
H::AbstractQuantumObject{DT,OpType},
121132
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing,
122133
Id_cache = I(prod(H.dims)),
123-
) where {MT1<:AbstractMatrix,OpType1<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}}
134+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}}
124135
L = liouvillian(H, Id_cache)
125136
if !(c_ops isa Nothing)
126-
for c_op in c_ops
127-
L += lindblad_dissipator(c_op, Id_cache)
128-
end
137+
L += mapreduce(lindblad_dissipator, +, c_ops)
129138
end
130139
return L
131140
end
132141

133-
liouvillian(H::QuantumObject{<:AbstractMatrix,OperatorQuantumObject}, Id_cache::Diagonal = I(prod(H.dims))) =
134-
-1im * (spre(H, Id_cache) - spost(H, Id_cache))
142+
liouvillian(H::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache::Diagonal = I(prod(H.dims))) where {DT} =
143+
get_typename_wrapper(H)(_liouvillian(H.data, Id_cache), SuperOperator, H.dims)
135144

136-
liouvillian(H::QuantumObject{<:AbstractMatrix,SuperOperatorQuantumObject}, Id_cache::Diagonal) = H
145+
liouvillian(H::AbstractQuantumObject{DT,SuperOperatorQuantumObject}, Id_cache::Diagonal) where {DT} = H

src/time_evolution/mesolve.jl

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,7 @@ function _generate_mesolve_kwargs(e_ops, progress_bar::Val{false}, tlist, kwargs
4242
end
4343

4444
_mesolve_make_L_QobjEvo(H::QuantumObject, c_ops) = QobjEvo(liouvillian(H, c_ops); type = SuperOperator)
45-
function _mesolve_make_L_QobjEvo(H::Tuple, c_ops)
46-
c_ops isa Nothing && return QobjEvo(H; type = SuperOperator, f = liouvillian)
47-
return QobjEvo((H..., mapreduce(op -> lindblad_dissipator(op), +, c_ops)); type = SuperOperator, f = liouvillian)
48-
end
49-
_mesolve_make_L_QobjEvo(H::QuantumObjectEvolution{DT,OperatorQuantumObject}, c_ops) where {DT<:AbstractSciMLOperator} =
50-
throw(
51-
ArgumentError(
52-
"This function does not support the data type of time-dependent Operator `H` currently. Try to provide `H` as a time-dependent SuperOperator or Tuple instead.",
53-
),
54-
)
55-
function _mesolve_make_L_QobjEvo(
56-
H::QuantumObjectEvolution{DT,SuperOperatorQuantumObject},
57-
c_ops,
58-
) where {DT<:AbstractSciMLOperator}
59-
c_ops isa Nothing && return H
60-
return H + QobjEvo((mapreduce(op -> lindblad_dissipator(op), +, c_ops)))
61-
end
45+
_mesolve_make_L_QobjEvo(H::Union{QuantumObjectEvolution,Tuple}, c_ops) = liouvillian(QobjEvo(H), c_ops)
6246

6347
@doc raw"""
6448
mesolveProblem(

src/time_evolution/time_evolution.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export TimeEvolutionSol, TimeEvolutionMCSol, TimeEvolutionSSESol
22

3-
export liouvillian, liouvillian_floquet, liouvillian_generalized
3+
export liouvillian_floquet, liouvillian_generalized
44

55
const DEFAULT_ODE_SOLVER_OPTIONS = (abstol = 1e-8, reltol = 1e-6, save_everystep = false, save_end = true)
66
const DEFAULT_SDE_SOLVER_OPTIONS = (abstol = 1e-2, reltol = 1e-2, save_everystep = false, save_end = true)

test/core-test/quantum_objects.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,15 @@
6565
end
6666

6767
@testset "Operator and SuperOperator" begin
68+
N = 10
69+
A = Qobj(rand(ComplexF64, N, N))
70+
B = Qobj(rand(ComplexF64, N, N))
71+
ρ = rand_dm(N) # random density matrix
72+
@test mat2vec(A * ρ * B) spre(A) * spost(B) * mat2vec(ρ) sprepost(A, B) * mat2vec(ρ) # we must make sure this equality holds !
73+
6874
a = sprand(ComplexF64, 100, 100, 0.1)
6975
a2 = Qobj(a)
7076
a3 = Qobj(a, type = SuperOperator)
71-
7277
@test isket(a2) == false
7378
@test isbra(a2) == false
7479
@test isoper(a2) == true

test/core-test/quantum_objects_evo.jl

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -160,25 +160,37 @@
160160
# @test X_warn == X3
161161
# end
162162

163-
@testset "Time Dependent Operators" begin
163+
@testset "Time Dependent Operators and SuperOperators" begin
164164
N = 10
165165
a = destroy(N)
166166
coef1(p, t) = exp(-1im * p.ω1 * t)
167167
coef2(p, t) = sin(p.ω2 * t)
168+
t = rand()
169+
p = (ω1 = rand(), ω2 = rand())
170+
171+
# Operator
172+
H_td = QobjEvo(((a, coef1), a' * a, (a', coef2)))
173+
H_ti = coef1(p, t) * a + a' * a + coef2(p, t) * a'
174+
ψ = rand_ket(N)
175+
@test H_td(p, t) H_ti
176+
@test H_td(ψ, p, t) H_ti * ψ
177+
@test isconstant(a) == true
178+
@test isconstant(H_td) == false
179+
@test isconstant(QobjEvo(a)) == true
180+
@test isoper(H_td) == true
181+
182+
# SuperOperator
183+
c_ops = [sqrt(rand()) * a]
184+
L_td = liouvillian(H_td, c_ops)
185+
L_td2 = -1im * spre(H_td) + 1im * spost(H_td) + lindblad_dissipator(c_ops[1])
186+
ρvec = mat2vec(rand_dm(N))
187+
@test L_td(p, t) L_td2(p, t)
188+
@test L_td(ρvec, p, t) L_td2(ρvec, p, t)
189+
@test isconstant(L_td) == false
190+
@test issuper(L_td) == true
168191

169192
@test_throws MethodError QobjEvo([[a, coef1], a' * a, [a', coef2]])
170-
171-
op1 = QobjEvo(((a, coef1), a' * a, (a', coef2)))
172-
op1 = QobjEvo(((a, coef1), a' * a, (a', coef2)))
173-
174-
p = (ω1 = 1, ω2 = 2)
175-
@test op1(p, 0.1) coef1(p, 0.1) * a + a' * a + coef2(p, 0.1) * a'
176-
177-
ψ = fock(N, 1)
178-
@test op1(ψ, p, 0.1) (coef1(p, 0.1) * a + a' * a + coef2(p, 0.1) * a') * ψ
179-
180-
@test isconstant(a) == true
181-
@test isconstant(op1) == false
182-
@test isconstant(Qobj(a)) == true
193+
@test_throws ArgumentError H_td(ρvec, p, t)
194+
@test_throws ArgumentError L_td(ψ, p, t)
183195
end
184196
end

test/core-test/time_evolution.jl

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,18 +205,9 @@
205205
# @test sol_sse.expect ≈ sol_sse_td.expect atol = 1e-2 * length(tlist)
206206

207207
H_td2 = QobjEvo(H_td)
208-
L_td = QobjEvo(H_td, type = SuperOperator, f = liouvillian)
208+
L_td = liouvillian(H_td2)
209209

210210
sol_se_td2 = sesolve(H_td2, ψ0, tlist, e_ops = e_ops, progress_bar = Val(false), params = p)
211-
@test_throws ArgumentError mesolve(
212-
H_td2,
213-
ψ0,
214-
tlist,
215-
c_ops,
216-
e_ops = e_ops,
217-
progress_bar = Val(false),
218-
params = p,
219-
)
220211
sol_me_td2 = mesolve(L_td, ψ0, tlist, c_ops, e_ops = e_ops, progress_bar = Val(false), params = p)
221212
sol_mc_td2 = mcsolve(
222213
H_td2,
@@ -253,6 +244,7 @@
253244
@inferred mesolve(H, ψ0, tlist, c_ops, e_ops = e_ops, saveat = tlist, progress_bar = Val(false))
254245
@inferred mesolve(H, ψ0, tlist, (a, a'), e_ops = (a' * a, a'), progress_bar = Val(false)) # We test the type inference for Tuple
255246
@inferred mesolve(H_td, ψ0, tlist, c_ops, e_ops = e_ops, progress_bar = Val(false), params = p)
247+
@inferred mesolve(H_td2, ψ0, tlist, c_ops, e_ops = e_ops, progress_bar = Val(false), params = p)
256248
@inferred mesolve(L_td, ψ0, tlist, c_ops, e_ops = e_ops, progress_bar = Val(false), params = p)
257249
end
258250

0 commit comments

Comments
 (0)