Skip to content

Commit 701f52c

Browse files
committed
add QobjEvo documentation page
1 parent e2e2249 commit 701f52c

File tree

3 files changed

+355
-1
lines changed

3 files changed

+355
-1
lines changed

docs/src/users_guide/QuantumObject/QuantumObject.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ println(isoper(a)) # operator
174174
println(isoperket(a)) # operator-ket
175175
println(isoperbra(a)) # operator-bra
176176
println(issuper(a)) # super operator
177+
println(isconstant(a)) # time-independent or not
177178
println(ishermitian(a)) # Hermitian
178179
println(isherm(a)) # synonym of ishermitian(a)
179180
println(issymmetric(a)) # symmetric

docs/src/users_guide/time_evolution/intro.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
- [Monte-Carlo Solver](@ref doc-TE:Monte-Carlo-Solver)
2020
- [Stochastic Solver](@ref doc-TE:Stochastic-Solver)
2121
- [Solving Problems with Time-dependent Hamiltonians](@ref doc-TE:Solving-Problems-with-Time-dependent-Hamiltonians)
22+
- [Generate QobjEvo](@ref doc-TE:Generate-QobjEvo)
23+
- [QobjEvo fields (attributes)](@ref doc-TE:QobjEvo-fields-(attributes))
24+
- [Using parameters](@ref doc-TE:Using-parameters)
2225

2326
# [Introduction](@id doc-TE:Introduction)
2427

Lines changed: 351 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,353 @@
11
# [Solving Problems with Time-dependent Hamiltonians](@id doc-TE:Solving-Problems-with-Time-dependent-Hamiltonians)
22

3-
This page is still under construction, please visit [API](@ref doc-API) first.
3+
```@setup QobjEvo
4+
using QuantumToolbox
5+
6+
using CairoMakie
7+
CairoMakie.enable_only_mime!(MIME"image/svg+xml"())
8+
```
9+
10+
## [Generate QobjEvo](@id doc-TE:Generate-QobjEvo)
11+
12+
In the previous examples of solving time evolution, we assumed that the systems under consideration were described by time-independent Hamiltonians. However, many systems have explicit time dependence in either the Hamiltonian, or the collapse operators (`c_ops`) describing coupling to the environment, and sometimes both components might depend on time. The time evolution solvers such as [`sesolve`](@ref), [`mesolve`](@ref), etc., are all capable of handling time-dependent Hamiltonians and collapse operators.
13+
14+
`QuantumToolbox.jl` uses [`QuantumObjectEvolution`](@ref) (or the abbreviated synonym: [`QobjEvo`](@ref)) to represent time-dependent quantum operators, and its `data` field (attribute) takes advantage of [`SciMLOperators.jl`](https://docs.sciml.ai/SciMLOperators/stable/).
15+
16+
To generate a time-dependent operator [`QobjEvo`](@ref), one needs to specify a time-independent [`Qobj`](@ref) together with a time-dependent coefficient function with the form `coef(p, t)`, namely
17+
18+
```@example QobjEvo
19+
coef(p, t) = sin(t)
20+
21+
H_t = QobjEvo(sigmax(), coef)
22+
```
23+
24+
!!! warning "The inputs of coefficient function"
25+
Please note that although we didn't use the argument `p` in the definition of `coef`, we still need to put a dummy input `p` in the declaration of `coef`. We will describe how to use the parameter `p` in the section [Using parameters](@ref doc-TE:Using-parameters).
26+
27+
The [`QobjEvo`](@ref) can also be generated by specifying many pairs of time-independent [`Qobj`](@ref) and time-dependent coefficient function. For instance, we will look at a case with the total Hamiltonian ``\hat{H}(t)`` can be separate into time-independent part (``\hat{H}_0``) and a summation of many time-dependent operators, which takes the form:
28+
29+
```math
30+
\hat{H}(t) = \hat{H}_0 + \sum_j f_j(t) \hat{H}_j.
31+
```
32+
33+
The following code sets up this problem by using a `Tuple` which contains many time-dependent pairs `(H_j, f_j)`, namely
34+
35+
```@example QobjEvo
36+
H0 = qeye(2)
37+
38+
H1 = sigmax()
39+
f1(p, t) = sin(t)
40+
41+
H2 = sigmay()
42+
f2(p, t) = cos(t)
43+
44+
H3 = sigmaz()
45+
f3(p, t) = 9 * exp(-(t / 5)^2)
46+
47+
H_tuple = (
48+
H0,
49+
(H1, f1),
50+
(H2, f2),
51+
(H3, f3),
52+
)
53+
54+
H_t = QobjEvo(H_tuple)
55+
```
56+
57+
This is equivalent by generating the [`QobjEvo`](@ref) separately and `+` (or `sum`) them up:
58+
59+
```@example QobjEvo
60+
H_t == H0 + QobjEvo(H1, f1) + QobjEvo(H2, f2) + QobjEvo(H3, f3)
61+
```
62+
63+
Most solvers will accept any format that could be made into a [`QobjEvo`](@ref) for the Hamiltonian. All of the followings are equivalent:
64+
65+
```julia
66+
sol = mesolve(H_t, ...)
67+
sol = mesolve(H_tuple, ...)
68+
```
69+
70+
Collapse operators `c_ops` only accept a list where each element is either a [`Qobj`](@ref) or a [`QobjEvo`](@ref). For example, in the following call:
71+
72+
```julia
73+
γ1 = sqrt(0.1)
74+
γ2(p, t) = sqrt(sin(t))
75+
c_ops = (
76+
γ1 * sigmaz(),
77+
QobjEvo(sigmax() + sigmay(), γ2)
78+
)
79+
80+
sol = mesolve(H_t, ..., c_ops, ...)
81+
```
82+
83+
[`mesolve`](@ref) will see `2` collapse operators: ``\sqrt{0.1} \hat{\sigma}_z`` and ``\sqrt{\sin(t)} (\hat{\sigma}_x + \hat{\sigma}_y)``.
84+
85+
As an example, we will look at a case with a time-dependent Hamiltonian of the form ``\hat{H}(t) = \hat{H}_0 + f_1(t) \hat{H}_1``, where ``f_1(t) = A \exp \left[ -(t / \sigma)^2 \right]`` is the time-dependent driving strength. The following code sets up the problem
86+
87+
```@example QobjEvo
88+
N = 2 # Set where to truncate Fock state for cavity
89+
90+
# basis states for Atom
91+
u = basis(3, 0) # u state
92+
e = basis(3, 1) # excited state
93+
g = basis(3, 2) # ground state
94+
95+
# operators
96+
ge = tensor(qeye(N), g * e') # |g><e|
97+
ue = tensor(qeye(N), u * e') # |u><e|
98+
uu = tensor(qeye(N), u * u') # |u><u|
99+
gg = tensor(qeye(N), g * g') # |g><g|
100+
a = tensor(destroy(N), qeye(3))
101+
102+
# Hamiltonian
103+
g = 5 # coupling strength
104+
H0 = -g * (ge' * a + a' * ge)
105+
H1 = ue' + ue
106+
f1(p, t) = 9 * exp(-(t / 5)^2)
107+
H_t = QobjEvo(
108+
(
109+
H0,
110+
(H1, f1)
111+
)
112+
)
113+
114+
# Build collapse operators
115+
κ = 1.5 # Cavity decay rate
116+
γ0 = 6 # Atomic decay rate
117+
c_ops = [
118+
sqrt(κ) * a,
119+
sqrt(4 * γ0 / 9) * ge, # Use Rb branching ratio of 4/9 e -> g
120+
sqrt(5 * γ0 / 9) * ue # 5/9 e -> u
121+
]
122+
123+
tlist = LinRange(0, 4, 200) # Define time vector
124+
ψ0 = tensor(basis(N, 0), u) # Define initial state
125+
126+
# Build observables
127+
e_ops = [
128+
a' * a,
129+
uu,
130+
gg
131+
]
132+
133+
# solve dynamics
134+
exp_me = mesolve(H_t, ψ0, tlist, c_ops, e_ops = e_ops; progress_bar = Val(false)).expect
135+
exp_mc = mcsolve(H_t, ψ0, tlist, c_ops, e_ops = e_ops; progress_bar = Val(false)).expect
136+
137+
# plot by CairoMakie.jl
138+
fig = Figure(size = (500, 350))
139+
ax = Axis(fig[1, 1], xlabel = L"Time $t$", ylabel = "expectation value")
140+
lines!(ax, tlist, real(exp_me[1,:]), label = L"$\hat{a}^\dagger \hat{a}$ (mesolve)", color = :red, linestyle = :solid)
141+
lines!(ax, tlist, real(exp_mc[1,:]), label = L"$\hat{a}^\dagger \hat{a}$ (mcsolve)", color = :red, linestyle = :dash)
142+
lines!(ax, tlist, real(exp_me[2,:]), label = L"$|u \rangle\langle u|$ (mesolve)", color = :green, linestyle = :solid)
143+
lines!(ax, tlist, real(exp_mc[2,:]), label = L"$|u \rangle\langle u|$ (mcsolve)", color = :green, linestyle = :dash)
144+
lines!(ax, tlist, real(exp_me[3,:]), label = L"$|g \rangle\langle g|$ (mesolve)", color = :blue, linestyle = :solid)
145+
lines!(ax, tlist, real(exp_mc[3,:]), label = L"$|g \rangle\langle g|$ (mcsolve)", color = :blue, linestyle = :dash)
146+
147+
axislegend(ax, position = :rc)
148+
149+
fig
150+
```
151+
152+
The result from [`mesolve`](@ref) is identical to that shown in the examples, the [`mcsolve`](@ref) however will be noticeably off, suggesting we should increase the number of trajectories `ntraj` for this example.
153+
154+
In addition, we can also consider the decay of a simple Harmonic oscillator with time-varying decay rate ``\gamma_1(t)``
155+
156+
```@example QobjEvo
157+
N = 10 # number of basis states
158+
a = destroy(N)
159+
H = a' * a
160+
161+
ψ0 = basis(N, 9) # initial state
162+
163+
γ0 = 0.5
164+
γ1(p, t) = sqrt(γ0 * exp(-t))
165+
c_ops_ti = [sqrt(γ0) * a] # time-independent collapse term
166+
c_ops_td = [QobjEvo(a, γ1)] # time-dependent collapse term
167+
168+
e_ops = [a' * a]
169+
170+
tlist = LinRange(0, 10, 100)
171+
exp_ti = mesolve(H, ψ0, tlist, c_ops_ti, e_ops = e_ops; progress_bar = Val(false)).expect[1,:]
172+
exp_td = mesolve(H, ψ0, tlist, c_ops_td, e_ops = e_ops; progress_bar = Val(false)).expect[1,:]
173+
174+
# plot by CairoMakie.jl
175+
fig = Figure(size = (500, 350))
176+
ax = Axis(fig[1, 1], xlabel = L"Time $t$", ylabel = L"\langle \hat{a}^\dagger \hat{a} \rangle")
177+
lines!(ax, tlist, real(exp_ti), label = L"\gamma_0", linestyle = :solid)
178+
lines!(ax, tlist, real(exp_td), label = L"\gamma_1(t) = \gamma_0 e^{-t}", linestyle = :dash)
179+
180+
axislegend(ax, position = :rt)
181+
182+
fig
183+
```
184+
185+
## [QobjEvo fields (attributes)](@id doc-TE:QobjEvo-fields-(attributes))
186+
187+
[`QuantumObjectEvolution`](@ref) as a time-dependent quantum system, as its main functionality creates a [`QuantumObject`](@ref) at a specified time ``t``:
188+
189+
```@example QobjEvo
190+
coef(p, t) = sin(π * t)
191+
Ht = QobjEvo(sigmaz(), coef)
192+
193+
Ht(0.25)
194+
```
195+
196+
[`QuantumObjectEvolution`](@ref) shares a lot of properties with the [`QuantumObject`](@ref):
197+
198+
```@example QobjEvo
199+
Ht.data
200+
```
201+
202+
```@example QobjEvo
203+
Ht.type
204+
```
205+
206+
```@example QobjEvo
207+
Ht.dims
208+
```
209+
210+
The properties of a [`QuantumObjectEvolution`](@ref) can also be retrieved using several functions with inputting [`QuantumObjectEvolution`](@ref):
211+
212+
```@example QobjEvo
213+
size(Ht)
214+
```
215+
216+
```@example QobjEvo
217+
shape(Ht) # synonym of size(H)
218+
```
219+
220+
```@example QobjEvo
221+
length(Ht)
222+
```
223+
224+
```@example QobjEvo
225+
eltype(Ht) # element type
226+
```
227+
228+
```@example QobjEvo
229+
println(isket(Ht)) # ket
230+
println(isbra(Ht)) # bra
231+
println(isoper(Ht)) # operator
232+
println(isoperket(Ht)) # operator-ket
233+
println(isoperbra(Ht)) # operator-bra
234+
println(issuper(Ht)) # super operator
235+
println(isconstant(Ht)) # time-independent or not
236+
println(ishermitian(Ht)) # Hermitian
237+
println(isherm(Ht)) # synonym of ishermitian(a)
238+
println(issymmetric(Ht)) # symmetric
239+
println(isposdef(Ht)) # positive definite (and Hermitian)
240+
```
241+
242+
[`QobjEvo`](@ref) follow the same mathematical operations rules as [`Qobj`](@ref). They can be added, subtracted and multiplied with scalar, [`Qobj`](@ref) and [`QobjEvo`](@ref). They also support [`adjoint`](@ref), [`transpose`](@ref), and [`conj`](@ref) methods, and can be used for [`SuperOperator`](@ref) transformation:
243+
244+
```@example QobjEvo
245+
coef(p, t) = sin(π * t)
246+
Ht = QobjEvo(sigmaz(), coef)
247+
248+
coef2(p, t) = exp(-t)
249+
c_op = QobjEvo(destroy(2), coef2)
250+
251+
L1 = -1im * (spre(Ht) - spost(Ht'))
252+
L1 += lindblad_dissipator(c_op)
253+
```
254+
255+
Or equivalently:
256+
```@example QobjEvo
257+
L2 = liouvillian(Ht, [c_op])
258+
```
259+
260+
```@example QobjEvo
261+
t = rand()
262+
L1(t) == L2(t)
263+
```
264+
265+
!!! note "Optimization for superoperator transformation"
266+
Although the value of `L1` and `L2` here is equivalent, one can observe that the structure of `L2.data` is much simpler than `L1.data`, which means that it will be more efficient to solve the time evolution using `L2` instead of `L1`. Therefore, we recommend to use [`liouvillian`](@ref) or [`lindblad_dissipator`](@ref) to generate time-dependent [`SuperOperator`](@ref) because these functions have been optimized.
267+
268+
## [Using parameters](@id doc-TE:Using-parameters)
269+
270+
Until now, the coefficients were only functions of time `t`. In the definition of ``f_1(t) = A \exp \left[ -(t / \sigma)^2 \right]`` in the previous example, the driving amplitude ``A`` and width of the gaussian driving term ``\sigma`` were hardcoded with their numerical values. This is fine for problems that are specialized, or that we only want to run once. However, in many cases, we would like to study the same problem with a range of parameters and don't have to worry about manually changing the values on each run. `QuantumToolbox.jl` allows you to accomplish this by adding extra parameters `p` to coefficient functions that make the [`QobjEvo`](@ref). For instance, instead of explicitly writing `9` for ``A`` and `5` for ``\sigma``, we can either specify them with `p` as a `Vector` or `NamedTuple`:
271+
272+
```@example QobjEvo
273+
# specify p as a Vector
274+
f1_v(p, t) = p[1] * exp(-(t / p[2])^2)
275+
276+
H_t_v = QobjEvo(sigmaz(), f1_v)
277+
278+
p_v = [9, 5] # 1st element represents A; 2nd element represents σ
279+
t = 1
280+
H_t_v(p_v, t)
281+
```
282+
283+
```@example QobjEvo
284+
# specify p as a NamedTuple
285+
f1_n(p, t) = p.A * exp(-(t / p.σ)^2)
286+
287+
H_t_n = QobjEvo(sigmaz(), f1_n)
288+
289+
p_n = (A = 9, σ = 5)
290+
t = 1
291+
H_t_n(p_n, t)
292+
```
293+
294+
```@example QobjEvo
295+
t = rand()
296+
H_t_v(p_v, t) == H_t_n(p_n, t)
297+
```
298+
299+
!!! note "Custom structure of parameter"
300+
For more advanced usage, any custom `struct` of parameter `p` can be used.
301+
302+
When solving time evolutions, the solvers take an single keyword argument `params`, which will be directly passed to input `p` of the coefficient functions to build the time dependent Hamiltonian and collapse operators. For example, with `p::Vector`:
303+
304+
```@example QobjEvo
305+
f1(p, t) = p[1] * cos(p[2] * t)
306+
f2(p, t) = p[3] * sin(p[4] * t)
307+
γ(p, t) = sqrt(p[5] * exp(-p[6] * t))
308+
p_total = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
309+
310+
H_t = sigmaz() + QobjEvo(sigmax(), f1) + QobjEvo(sigmay(), f2)
311+
312+
c_ops = [
313+
QobjEvo(destroy(2), γ)
314+
]
315+
316+
e_ops = [sigmaz()]
317+
318+
ψ0 = basis(2, 0)
319+
tlist = LinRange(0, 10, 20)
320+
sol1 = mesolve(H_t, ψ0, tlist, c_ops, params = p_total, e_ops = e_ops; progress_bar = Val(false))
321+
```
322+
323+
Similarly, with `p::NamedTuple`:
324+
325+
```@example QobjEvo
326+
f1(p, t) = p.A1 * cos(p.ω1 * t)
327+
f2(p, t) = p.A2 * sin(p.ω2 * t)
328+
γ(p, t) = sqrt(p.A3 * exp(-p.γ0 * t))
329+
p_total = (
330+
A1 = 0.1,
331+
ω1 = 0.2,
332+
A2 = 0.3,
333+
ω2 = 0.4,
334+
A3 = 0.5,
335+
γ0 = 0.6
336+
)
337+
338+
H_t = sigmaz() + QobjEvo(sigmax(), f1) + QobjEvo(sigmay(), f2)
339+
340+
c_ops = [
341+
QobjEvo(destroy(2), γ)
342+
]
343+
344+
e_ops = [sigmaz()]
345+
346+
ψ0 = basis(2, 0)
347+
tlist = LinRange(0, 10, 20)
348+
sol2 = mesolve(H_t, ψ0, tlist, c_ops, params = p_total, e_ops = e_ops; progress_bar = Val(false))
349+
```
350+
351+
```@example QobjEvo
352+
sol1.expect == sol2.expect
353+
```

0 commit comments

Comments
 (0)