Skip to content

Commit 7a6cc50

Browse files
Improve efficiency of generation of Bloch-Redfield tensor and fix documentation typos (#509)
1 parent 52b9148 commit 7a6cc50

File tree

5 files changed

+74
-36
lines changed

5 files changed

+74
-36
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main)
99

10+
- Improve efficiency of `bloch_redfield_tensor` by avoiding unnecessary conversions. ([#509])
11+
1012
## [v0.33.0]
1113
Release date: 2025-07-22
1214

@@ -267,3 +269,4 @@ Release date: 2024-11-13
267269
[#504]: https://github.com/qutip/QuantumToolbox.jl/issues/504
268270
[#506]: https://github.com/qutip/QuantumToolbox.jl/issues/506
269271
[#507]: https://github.com/qutip/QuantumToolbox.jl/issues/507
272+
[#509]: https://github.com/qutip/QuantumToolbox.jl/issues/509

docs/src/users_guide/settings.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Here, we list out each setting along with the specific functions that will use i
1313

1414
- `tidyup_tol::Float64 = 1e-14` : tolerance for [`tidyup`](@ref) and [`tidyup!`](@ref).
1515
- `auto_tidyup::Bool = true` : Automatically tidyup during the following situations:
16-
* Solving for eigenstates, including [`eigenstates`](@ref), [`eigsolve`](@ref), and [`eigsolve_al`](@ref).
16+
* Solving for eigenstates, including [`eigenstates`](@ref), [`eigsolve`](@ref), [`eigsolve_al`](@ref)
17+
* Creating [`bloch_redfield_tensor`](@ref) or [`brterm`](@ref), and solving [`brmesolve`](@ref).
1718
- (to be announced)
1819

1920
## Change default settings

docs/src/users_guide/time_evolution/brmesolve.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ H = -Δ/2.0 * sigmax() - ε0/2 * sigmaz()
162162
163163
ohmic_spectrum(ω) = (ω == 0.0) ? γ1 : γ1 / 2 * (ω / (2 * π)) * (ω > 0.0)
164164
165-
R, U = bloch_redfield_tensor(H, [(sigmax(), ohmic_spectrum)])
165+
R, U = bloch_redfield_tensor(H, ((sigmax(), ohmic_spectrum), ))
166166
167167
R
168168
```
@@ -183,6 +183,8 @@ H = - Δ/2.0 * sigmax() - ϵ0/2.0 * sigmaz()
183183
ohmic_spectrum(ω) = (ω == 0.0) ? γ1 : γ1 / 2 * (ω / (2 * π)) * (ω > 0.0)
184184
185185
a_ops = ((sigmax(), ohmic_spectrum),)
186+
R = bloch_redfield_tensor(H, a_ops; fock_basis = Val(true))
187+
186188
e_ops = [sigmax(), sigmay(), sigmaz()]
187189
188190
# same initial random ket state in QuTiP doc
@@ -192,7 +194,7 @@ e_ops = [sigmax(), sigmay(), sigmaz()]
192194
])
193195
194196
tlist = LinRange(0, 15.0, 1000)
195-
sol = brmesolve(H, ψ0, tlist, a_ops, e_ops=e_ops)
197+
sol = mesolve(R, ψ0, tlist, e_ops=e_ops)
196198
expt_list = real(sol.expect)
197199
198200
# plot the evolution of state on Bloch sphere

src/time_evolution/brmesolve.jl

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,28 @@ function bloch_redfield_tensor(
3737
U = QuantumObject(rst.vectors, Operator(), H.dimensions)
3838
sec_cutoff = float(sec_cutoff)
3939

40-
# in fock basis
41-
R0 = liouvillian(H, c_ops)
40+
H_new = getVal(fock_basis) ? H : QuantumObject(Diagonal(rst.values), Operator(), H.dimensions)
41+
c_ops_new = isnothing(c_ops) ? nothing : map(x -> getVal(fock_basis) ? x : U' * x * U, c_ops)
42+
L0 = liouvillian(H_new, c_ops_new)
4243

43-
# set fock_basis=Val(false) and change basis together at the end
44-
R1 = 0
45-
isempty(a_ops) || (R1 += mapreduce(x -> _brterm(rst, x[1], x[2], sec_cutoff, Val(false)), +, a_ops))
44+
# Check whether we can rotate the terms to the eigenbasis directly in the Hamiltonian space
45+
fock_basis_hamiltonian = getVal(fock_basis) && sec_cutoff == -1
4646

47-
SU = sprepost(U, U') # transformation matrix from eigen basis back to fock basis
47+
R = isempty(a_ops) ? 0 : sum(x -> _brterm(rst, x[1], x[2], sec_cutoff, fock_basis_hamiltonian), a_ops)
48+
49+
# If in fock basis, we need to transform the terms back to the fock basis
50+
# Note: we can transform the terms in the Hamiltonian space only if sec_cutoff is -1
51+
# otherwise, we need to use the SU superoperator below to transform the entire Liouvillian
52+
# at the end, due to the action of M_cut
4853
if getVal(fock_basis)
49-
return R0 + SU * R1 * SU'
54+
if fock_basis_hamiltonian
55+
return L0 + R # Already rotated in the Hamiltonian space
56+
else
57+
SU = sprepost(U, U')
58+
return L0 + SU * R * SU'
59+
end
5060
else
51-
return SU' * R0 * SU + R1, U
61+
return L0 + R, U
5262
end
5363
end
5464

@@ -86,11 +96,21 @@ function brterm(
8696
fock_basis::Union{Bool,Val} = Val(false),
8797
)
8898
rst = eigenstates(H)
89-
term = _brterm(rst, a_op, spectra, sec_cutoff, makeVal(fock_basis))
99+
U = QuantumObject(rst.vectors, Operator(), H.dimensions)
100+
101+
# Check whether we can rotate the terms to the eigenbasis directly in the Hamiltonian space
102+
fock_basis_hamiltonian = getVal(fock_basis) && sec_cutoff == -1
103+
104+
term = _brterm(rst, a_op, spectra, sec_cutoff, fock_basis_hamiltonian)
90105
if getVal(fock_basis)
91-
return term
106+
if fock_basis_hamiltonian
107+
return term # Already rotated in the Hamiltonian space
108+
else
109+
SU = sprepost(U, U')
110+
return SU * term * SU'
111+
end
92112
else
93-
return term, Qobj(rst.vectors, Operator(), rst.dimensions)
113+
return term, U
94114
end
95115
end
96116

@@ -99,7 +119,7 @@ function _brterm(
99119
a_op::T,
100120
spectra::F,
101121
sec_cutoff::Real,
102-
fock_basis::Union{Val{true},Val{false}},
122+
fock_basis_hamiltonian::Union{Bool,Val},
103123
) where {T<:QuantumObject{Operator},F<:Function}
104124
_check_br_spectra(spectra)
105125

@@ -110,9 +130,11 @@ function _brterm(
110130
spectrum = spectra.(skew)
111131

112132
A_mat = U' * a_op.data * U
133+
A_mat_spec = A_mat .* spectrum
134+
A_mat_spec_t = A_mat .* transpose(spectrum)
113135

114-
ac_term = (A_mat .* spectrum) * A_mat
115-
bd_term = A_mat * (A_mat .* trans(spectrum))
136+
ac_term = A_mat_spec * A_mat
137+
bd_term = A_mat * A_mat_spec_t
116138

117139
if sec_cutoff != -1
118140
m_cut = similar(skew)
@@ -124,20 +146,29 @@ function _brterm(
124146
M_cut = @. abs(vec_skew - vec_skew') < sec_cutoff
125147
end
126148

127-
out =
128-
0.5 * (
129-
+ _sprepost(A_mat .* trans(spectrum), A_mat) + _sprepost(A_mat, A_mat .* spectrum) - _spost(ac_term, Id) -
130-
_spre(bd_term, Id)
131-
)
149+
# Rotate the terms to the eigenbasis if possible
150+
if getVal(fock_basis_hamiltonian)
151+
A_mat = U * A_mat * U'
152+
A_mat_spec = U * A_mat_spec * U'
153+
A_mat_spec_t = U * A_mat_spec_t * U'
154+
ac_term = U * ac_term * U'
155+
bd_term = U * bd_term * U'
156+
end
157+
158+
# Remove small values before passing in the Liouville space
159+
if settings.auto_tidyup
160+
tidyup!(A_mat)
161+
tidyup!(A_mat_spec)
162+
tidyup!(A_mat_spec_t)
163+
tidyup!(ac_term)
164+
tidyup!(bd_term)
165+
end
166+
167+
out = (_sprepost(A_mat_spec_t, A_mat) + _sprepost(A_mat, A_mat_spec) - _spost(ac_term, Id) - _spre(bd_term, Id)) / 2
132168

133169
(sec_cutoff != -1) && (out .*= M_cut)
134170

135-
if getVal(fock_basis)
136-
SU = _sprepost(U, U')
137-
return QuantumObject(SU * out * SU', SuperOperator(), rst.dimensions)
138-
else
139-
return QuantumObject(out, SuperOperator(), rst.dimensions)
140-
end
171+
return QuantumObject(out, SuperOperator(), rst.dimensions)
141172
end
142173

143174
@doc raw"""

test/core-test/brmesolve.jl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
A_op = a+a'
66
spectra(x) = (x>0) * 0.5
77
for sec_cutoff in [0, 0.1, 1, 3, -1]
8-
R = bloch_redfield_tensor(H, [(A_op, spectra)], [a^2], sec_cutoff = sec_cutoff, fock_basis = true)
9-
R_eig, evecs = bloch_redfield_tensor(H, [(A_op, spectra)], [a^2], sec_cutoff = sec_cutoff, fock_basis = false)
8+
R = bloch_redfield_tensor(H, ((A_op, spectra),), [a^2], sec_cutoff = sec_cutoff, fock_basis = Val(true))
9+
R_eig, evecs =
10+
bloch_redfield_tensor(H, ((A_op, spectra),), [a^2], sec_cutoff = sec_cutoff, fock_basis = Val(false))
1011
@test isa(R, QuantumObject)
1112
@test isa(R_eig, QuantumObject)
1213
@test isa(evecs, QuantumObject)
@@ -27,7 +28,7 @@ end
2728

2829
# this test applies for limited cutoff
2930
lindblad = lindblad_dissipator(a)
30-
computation = brterm(H, A_op, spectra, sec_cutoff = 1.5, fock_basis = true)
31+
computation = brterm(H, A_op, spectra, sec_cutoff = 1.5, fock_basis = Val(true))
3132
@test isapprox(lindblad, computation, atol = 1e-15)
3233
end
3334

@@ -38,8 +39,8 @@ end
3839
A_op = a+a'
3940
spectra(x) = x>0
4041
for sec_cutoff in [0, 0.1, 1, 3, -1]
41-
R = brterm(H, A_op, spectra, sec_cutoff = sec_cutoff, fock_basis = true)
42-
R_eig, evecs = brterm(H, A_op, spectra, sec_cutoff = sec_cutoff, fock_basis = false)
42+
R = brterm(H, A_op, spectra, sec_cutoff = sec_cutoff, fock_basis = Val(true))
43+
R_eig, evecs = brterm(H, A_op, spectra, sec_cutoff = sec_cutoff, fock_basis = Val(false))
4344
@test isa(R, QuantumObject)
4445
@test isa(R_eig, QuantumObject)
4546
@test isa(evecs, QuantumObject)
@@ -76,8 +77,8 @@ end
7677
a = destroy(N) + destroy(N)^2/2
7778
A_op = a+a'
7879
for spectra in spectra_list
79-
R = brterm(H, A_op, spectra, sec_cutoff = 0.1, fock_basis = true)
80-
R_eig, evecs = brterm(H, A_op, spectra, sec_cutoff = 0.1, fock_basis = false)
80+
R = brterm(H, A_op, spectra, sec_cutoff = 0.1, fock_basis = Val(true))
81+
R_eig, evecs = brterm(H, A_op, spectra, sec_cutoff = 0.1, fock_basis = Val(false))
8182
@test isa(R, QuantumObject)
8283
@test isa(R_eig, QuantumObject)
8384
@test isa(evecs, QuantumObject)
@@ -112,4 +113,4 @@ end
112113

113114
@test all(me.expect .== brme.expect)
114115
end
115-
end;
116+
end

0 commit comments

Comments
 (0)