Skip to content

Commit cb35d3c

Browse files
Added tests, refined instructions
- added tests relying on proximity of the solutions - rewrote instructions so they are less like the paper and more clear - fixed typos and improved code structure
1 parent 6339ca3 commit cb35d3c

File tree

1 file changed

+49
-20
lines changed

1 file changed

+49
-20
lines changed

tutorials-v5/time-evolution/025_v5_paper-nm_mcsolve.md

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,13 @@ To finally arrive at the Lindblad form, the shift function
4545

4646
$s(t) = 2 | \min \{ 0, \gamma_1(t), \gamma_2(t), ... \} |$,
4747

48-
such that the shifted rates $\Gamma_n (t) = \gamma_n(t) + s(t)$ non-negative.
48+
such that the shifted rates $\Gamma_n (t) = \gamma_n(t) + s(t)$ are non-negative.
4949
Using the regular MCWF method, we obtain the completely positive Lindblad equation
5050

5151
$\dot{\rho}'(t) = - \dfrac{i}{\hbar} [ H(t), \rho'(t) ] + \sum_n \Gamma(t) \mathcal{D}_n[\rho'(t)]$,
5252

53-
so that $\rho'(t) = \mathcal{E} \{\ket{\psi(t)} \bra{\psi(t)}\}$ for the generated trajecotries $\ket{\psi (t)}$.
54-
55-
56-
53+
so that $\rho'(t) = \mathbb{E} \{\ket{\psi(t)} \bra{\psi(t)}\}$ for the generated trajecotries $\ket{\psi (t)}$, where $\mathbb{E}$ is the ensemble average over the trajectories.
54+
This can furthermore be used to finally reconstruct the original states via $\rho(t) = \mathbb{E}\{\mu(t) \ket{\psi(t)} \bra{\psi(t)}\}$.
5755

5856
```python
5957
import matplotlib.pyplot as plt
@@ -152,7 +150,6 @@ A = CubicSpline(tlist, np.complex128(_A))
152150
```python
153151
unitary_gen = liouvillian(H)
154152
dissipator = lindblad_dissipator(sigmam())
155-
me_sol = mesolve([[unitary_gen, A], [dissipator, gamma]], initial_state, tlist)
156153
```
157154

158155
```python
@@ -169,12 +166,18 @@ mc_sol = nm_mcsolve(
169166

170167
## Comparison to other methods
171168

172-
We want to compare this computation with the HEOM and the Bloch-Refield solver.
173-
For these methods we directly apply a spin-boson model and the free reservoir auto-correlation function
169+
We want to compare this computation with the standard `mesolve()`, the HEOM and the Bloch-Refield solver.
170+
For the `mesolve()` we can directly use the operators we have created for `nm_mcsolve()`:
171+
172+
```python
173+
me_sol = mesolve([[unitary_gen, A], [dissipator, gamma]], initial_state, tlist)
174+
```
175+
176+
For the other methods we directly apply a spin-boson model and the free reservoir auto-correlation function
174177

175178
$C(t) = \dfrac{\lambda \Gamma}{2} e^{-i (\omega - \Delta) t - \lambda |t|}$
176179

177-
as well as the same power specture as we used above.
180+
as well as the same power specturm as we used above.
178181
We use the Hamiltonian $H = \omega_0 \sigma_+ \sigma_-$ in the Schrödinger picture and the coupling operator $Q = \sigma_+ + \sigma_-$.
179182
Here, we chose $\omega_0 \gg \Delta$ to ensure validity of the rotating wave approximation.
180183

@@ -204,7 +207,7 @@ heom_bath = qt.heom.BosonicBath(Q, ck_real, vk_real, ck_imag, vk_imag)
204207
heom_sol = qt.heom.heomsolve(H, heom_bath, 10, qt.ket2dm(initial_state), tlist)
205208
```
206209

207-
And secondly, for the Bloch-Redfield solver we can directly use the power spectrum as input:
210+
Secondly, for the Bloch-Redfield solver we can directly use the power spectrum as input:
208211

209212
```python
210213
br_sol = brmesolve(H, initial_state, tlist, a_ops=[(sigmax(), power_spectrum)])
@@ -234,14 +237,14 @@ root4 = root_scalar(lambda t: cgamma(t), method="bisect", bracket=(4, 5)).root
234237
projector = (sigmaz() + qeye(2)) / 2
235238

236239
rho11_me = expect(projector, me_sol.states)
237-
rho11_mc = expect(projector, mc_sol.states[::10])
240+
rho11_mc = expect(projector, mc_sol.states)
238241
rho11_br = expect(projector, br_sol.states)
239242
rho11_heom = expect(projector, heom_states)
240243

241244
plt.plot(tlist, rho11_me, "-", color="orange", label="mesolve")
242245
plt.plot(
243246
tlist[::10],
244-
rho11_mc,
247+
rho11_mc[::10],
245248
"x",
246249
color="blue",
247250
label="nm_mcsolve",
@@ -265,20 +268,20 @@ plt.show()
265268

266269
```python
267270
me_x = expect(sigmax(), me_sol.states)
268-
mc_x = expect(sigmax(), mc_sol.states[::10])
271+
mc_x = expect(sigmax(), mc_sol.states)
269272
heom_x = expect(sigmax(), heom_states)
270273
br_x = expect(sigmax(), br_states)
271274

272275
me_y = expect(sigmay(), me_sol.states)
273-
mc_y = expect(sigmay(), mc_sol.states[::10])
276+
mc_y = expect(sigmay(), mc_sol.states)
274277
heom_y = expect(sigmay(), heom_states)
275278
br_y = expect(sigmay(), br_states)
276279
```
277280

278281
```python
279282
# We smooth the HEOM result because it oscillates quickly and gets hard to see
280-
heom_plot = heom_x * heom_x + heom_y * heom_y
281-
heom_plot = np.convolve(heom_plot, np.array([1 / 11] * 11), mode="valid")
283+
rho01_heom = heom_x * heom_x + heom_y * heom_y
284+
rho01_heom = np.convolve(rho01_heom, np.array([1 / 11] * 11), mode="valid")
282285
heom_tlist = tlist[5:-5]
283286
```
284287

@@ -288,8 +291,8 @@ rho01_mc = mc_x * mc_x + mc_y * mc_y
288291
rho01_br = br_x * br_x + br_y * br_y
289292

290293
plt.plot(tlist, rho01_me, "-", color="orange", label=r"mesolve")
291-
plt.plot(tlist[::10], rho01_mc, "x", color="blue", label=r"nm_mcsolve")
292-
plt.plot(heom_tlist, heom_plot, "--", color="green", label=r"heomsolve")
294+
plt.plot(tlist[::10], rho01_mc[::10], "x", color="blue", label=r"nm_mcsolve")
295+
plt.plot(heom_tlist, rho01_heom, "--", color="green", label=r"heomsolve")
293296
plt.plot(tlist, rho01_br, "-.", color="gray", label=r"brmesolve")
294297

295298
plt.xlabel(r"$t\, /\, \lambda^{-1}$")
@@ -309,11 +312,15 @@ plt.legend()
309312
plt.show()
310313
```
311314

315+
```python
316+
mart_dev = mc_sol.trace - 1
317+
```
318+
312319
```python
313320
plt.plot(tlist, np.zeros_like(tlist), "-", color="orange", label=r"Zero")
314321
plt.plot(
315322
tlist[::10],
316-
1000 * (mc_sol.trace[::10] - 1),
323+
1000 * mart_dev[::10],
317324
"x",
318325
color="blue",
319326
label=r"nm_mcsolve",
@@ -333,6 +340,14 @@ plt.legend()
333340
plt.show()
334341
```
335342

343+
In these plots we notice two things.
344+
First, the result from the Bloch-Redfield equation deviates greatly from the others, showing us that non-Markovian effects are strongly influencing the dynamics.
345+
Second, in the grey areas - when $\gamma(t)$ is negative - the atom state restores coherence.
346+
In the last plot especially we see that during these times, the average influence martingale fluctuates, but is constant otherwise.
347+
Its deviation from unity tells us how well the simulation has converged.
348+
349+
350+
336351
## References
337352

338353
\[1\] [Donvil and Muratore-Ginanneschi. Nat Commun (2022).](https://www.nature.com/articles/s41467-022-31533-8)
@@ -354,5 +369,19 @@ about()
354369
## Testing
355370

356371
```python
357-
# TODO
372+
assert np.allclose(
373+
rho11_me, rho11_heom, atol=1e-3
374+
), "rho11 of mesolve and heomsolve do not agree."
375+
assert np.allclose(
376+
rho11_me, rho11_mc, atol=1e-2
377+
), "rho11 of nm_mcsolve deviates from mesolve too much."
378+
assert np.allclose(
379+
rho01_me[5:-5], rho01_heom, atol=1e-3
380+
), "|rho01|^2 of mesolve and heomsolve do not agree."
381+
assert np.allclose(
382+
rho01_me, rho01_mc, atol=1e-1
383+
), "|rho01|^2 of nm_mcsolve deviates from mesolve too much."
384+
assert (
385+
np.max(mart_dev) < 1e-1
386+
), "MC Simulation has not converged well enough. Average infl. mart. > 1e-1"
358387
```

0 commit comments

Comments
 (0)