Skip to content

Commit a5f6819

Browse files
committed
support time-dependent liouvillian
1 parent 3207e18 commit a5f6819

File tree

5 files changed

+40
-53
lines changed

5 files changed

+40
-53
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(c_op -> lindblad_dissipator(c_op), +, 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/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)