|
1 | 1 | # [Solving Problems with Time-dependent Hamiltonians](@id doc-TE:Solving-Problems-with-Time-dependent-Hamiltonians) |
2 | 2 |
|
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