Copyright (C) 2011 and later, Paul D. Nation & Robert J. Johansson
In this notebook we test the qutip stochastic master equation solver (smesolve) with a few textbook examples taken from the book Quantum Optics, by Walls and Milburn, section 6.7.
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from qutip import *
Stochastic master equation in Milburn's formulation
dρ(t)=dN(t)G[a]ρ(t)−dtγH[12a†a]ρ(t)
where
G[A]ρ=AρA†Tr[AρA†]−ρ
H[A]ρ=12(Aρ+ρA†−Tr[Aρ+ρA†]ρ)
and dN(t) is a Poisson distributed increment with E[dN(t)]=γ⟨a†a⟩(t)dt.
In QuTiP we write the stochastic master equation on the form (in the interaction picture, with no deterministic dissipation):
dρ(t)=D1[A]ρ(t)dt+D2[A]ρ(t)dW
where A=√γa, so we can identify
D1[A]ρ(t)=−12γH[a†a]ρ(t)=−γ12(a†aρ+ρa†a−Tr[a†aρ+ρa†a]ρ)=−12(A†Aρ+ρA†A−Tr[A†Aρ+ρA†A]ρ)
D2[A]ρ(t)=G[a]ρ=AρA†Tr[AρA†]−ρ
and
dW=dN(t)
and A=√γa is the collapse operator including the rate of the process as a coefficient in the operator.
N = 10
w0 = 0.5 * 2 * np.pi
times = np.linspace(0, 15, 150)
dt = times[1] - times[0]
gamma = 0.25
A = 2.5
ntraj = 50
nsubsteps = 50
a = destroy(N)
x = a + a.dag()
H = w0 * a.dag() * a
#rho0 = coherent(N, 5)
rho0 = fock(N, 5)
c_ops = [np.sqrt(gamma) * a]
e_ops = [a.dag() * a, x]
result_ref = mesolve(H, rho0, times, c_ops, e_ops)
plot_expectation_values(result_ref);
D1[a,ρ]=−γ12(a†aρ+ρa†a−Tr[a†aρ+ρa†a])→−12({A†A}L+{A†A}R)ρv+E[({A†A}L+{A†A}R)ρv]
D2[A,ρ(t)]=AρA†Tr[AρA†]−ρ→ALA†RρvE[ALA†Rρv]−ρv
result = photocurrent_mesolve(H, rho0, times, c_ops=[], sc_ops=c_ops, e_ops=e_ops,
ntraj=ntraj, nsubsteps=nsubsteps,
store_measurement=True, noise=1234)
10.0%. Run time: 0.52s. Est. time left: 00:00:00:04 20.0%. Run time: 1.11s. Est. time left: 00:00:00:04 30.0%. Run time: 1.66s. Est. time left: 00:00:00:03 40.0%. Run time: 2.34s. Est. time left: 00:00:00:03 50.0%. Run time: 2.95s. Est. time left: 00:00:00:02 60.0%. Run time: 3.51s. Est. time left: 00:00:00:02 70.0%. Run time: 4.09s. Est. time left: 00:00:00:01 80.0%. Run time: 4.64s. Est. time left: 00:00:00:01 90.0%. Run time: 5.27s. Est. time left: 00:00:00:00 Total run time: 5.85s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.step(times, dt * m.real)
photocurrentmesolve does not take custom noise, but you can set the seed.
result = photocurrent_mesolve(H, rho0, times, c_ops=[], sc_ops=c_ops, e_ops=e_ops,
ntraj=ntraj, nsubsteps=nsubsteps, store_measurement=True, noise=1234)
10.0%. Run time: 0.53s. Est. time left: 00:00:00:04 20.0%. Run time: 1.04s. Est. time left: 00:00:00:04 30.0%. Run time: 1.98s. Est. time left: 00:00:00:04 40.0%. Run time: 2.59s. Est. time left: 00:00:00:03 50.0%. Run time: 3.19s. Est. time left: 00:00:00:03 60.0%. Run time: 3.69s. Est. time left: 00:00:00:02 70.0%. Run time: 4.28s. Est. time left: 00:00:00:01 80.0%. Run time: 4.80s. Est. time left: 00:00:00:01 90.0%. Run time: 5.57s. Est. time left: 00:00:00:00 Total run time: 6.07s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.step(times, dt * m.real)
H = w0 * a.dag() * a + A * (a + a.dag())
result_ref = mesolve(H, rho0, times, c_ops, e_ops)
Stochastic master equation for homodyne in Milburn's formulation
dρ(t)=−i[H,ρ(t)]dt+γD[a]ρ(t)dt+dW(t)√γH[a]ρ(t)
where D is the standard Lindblad dissipator superoperator, and H is defined as above, and dW(t) is a normal distributed increment with E[dW(t)]=√dt.
In QuTiP format we have:
dρ(t)=−i[H,ρ(t)]dt+D1[A]ρ(t)dt+D2[A]ρ(t)dW
where A=√γa, so we can identify
D1[A]ρ(t)=γD[a]ρ(t)=D[A]ρ(t)
L = liouvillian(H, c_ops=c_ops).data
def d1_rho_func(t, rho_vec):
return cy.spmv(L, rho_vec)
D2[A]ρ(t)=√γH[a]ρ(t)=Aρ+ρA†−Tr[Aρ+ρA†]ρ→(AL+A†R)ρv−Tr[(AL+A†R)ρv]ρv
n_sum = spre(c_ops[0]) + spost(c_ops[0].dag())
n_sum_data = n_sum.data
def d2_rho_func(t, rho_vec):
e1 = cy.cy_expect_rho_vec(n_sum_data, rho_vec, False)
out = np.zeros((1,len(rho_vec)),dtype=complex)
out += cy.spmv(n_sum_data, rho_vec) - e1 * rho_vec
return out
result = general_stochastic(ket2dm(rho0), times, d1=d1_rho_func, d2=d2_rho_func,
e_ops=[spre(op) for op in e_ops], ntraj=ntraj, solver="platen",
m_ops=[spre(a + a.dag())], dW_factors=[1/np.sqrt(gamma)],
nsubsteps=nsubsteps, store_measurement=True, map_func=parallel_map)
10.0%. Run time: 8.20s. Est. time left: 00:00:01:13 20.0%. Run time: 15.30s. Est. time left: 00:00:01:01 30.0%. Run time: 25.41s. Est. time left: 00:00:00:59 40.0%. Run time: 33.66s. Est. time left: 00:00:00:50 50.0%. Run time: 40.31s. Est. time left: 00:00:00:40 60.0%. Run time: 48.43s. Est. time left: 00:00:00:32 70.0%. Run time: 56.63s. Est. time left: 00:00:00:24 80.0%. Run time: 66.07s. Est. time left: 00:00:00:16 90.0%. Run time: 72.94s. Est. time left: 00:00:00:08 100.0%. Run time: 79.44s. Est. time left: 00:00:00:00 Total run time: 79.46s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.plot(times, m[:, 0].real, 'b', alpha=0.025)
plt.plot(times, result_ref.expect[1], 'k', lw=2);
plt.ylim(-15, 15)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0].real, 'r', lw=2);
result = smesolve(H, rho0, times, [], c_ops, e_ops, ntraj=ntraj, nsubsteps=nsubsteps, solver="pc-euler",
method='homodyne', store_measurement=True)
10.0%. Run time: 2.19s. Est. time left: 00:00:00:19 20.0%. Run time: 3.66s. Est. time left: 00:00:00:14 30.0%. Run time: 4.94s. Est. time left: 00:00:00:11 40.0%. Run time: 6.23s. Est. time left: 00:00:00:09 50.0%. Run time: 7.52s. Est. time left: 00:00:00:07 60.0%. Run time: 8.99s. Est. time left: 00:00:00:05 70.0%. Run time: 10.36s. Est. time left: 00:00:00:04 80.0%. Run time: 11.65s. Est. time left: 00:00:00:02 90.0%. Run time: 12.92s. Est. time left: 00:00:00:01 Total run time: 14.22s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.plot(times, m[:, 0].real / np.sqrt(gamma), 'b', alpha=0.025)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0].real / np.sqrt(gamma), 'r', lw=2);
plt.plot(times, result_ref.expect[1], 'k', lw=2)
[<matplotlib.lines.Line2D at 0x7ff799003b38>]
result = smesolve(H, rho0, times, [], c_ops, e_ops, ntraj=ntraj, nsubsteps=nsubsteps, solver="pc-euler",
method='homodyne', store_measurement=True, noise=result.noise)
10.0%. Run time: 1.36s. Est. time left: 00:00:00:12 20.0%. Run time: 2.67s. Est. time left: 00:00:00:10 30.0%. Run time: 4.13s. Est. time left: 00:00:00:09 40.0%. Run time: 5.47s. Est. time left: 00:00:00:08 50.0%. Run time: 6.82s. Est. time left: 00:00:00:06 60.0%. Run time: 8.62s. Est. time left: 00:00:00:05 70.0%. Run time: 9.94s. Est. time left: 00:00:00:04 80.0%. Run time: 11.32s. Est. time left: 00:00:00:02 90.0%. Run time: 12.64s. Est. time left: 00:00:00:01 Total run time: 16.34s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.plot(times, m[:, 0].real / np.sqrt(gamma), 'b', alpha=0.025)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0].real / np.sqrt(gamma), 'r', lw=2);
plt.plot(times, result_ref.expect[1], 'k', lw=2)
[<matplotlib.lines.Line2D at 0x7ff798e2b860>]
e_ops = [a.dag() * a, a + a.dag(), -1j * (a - a.dag())]
result_ref = mesolve(H, rho0, times, c_ops, e_ops)
Stochastic master equation for heterodyne in Milburn's formulation
dρ(t)=−i[H,ρ(t)]dt+γD[a]ρ(t)dt+1√2dW1(t)√γH[a]ρ(t)+1√2dW2(t)√γH[−ia]ρ(t)
where D is the standard Lindblad dissipator superoperator, and H is defined as above, and dWi(t) is a normal distributed increment with E[dWi(t)]=√dt.
In QuTiP format we have:
dρ(t)=−i[H,ρ(t)]dt+D1[A]ρ(t)dt+D(1)2[A]ρ(t)dW1+D(2)2[A]ρ(t)dW2
where A=√γa, so we can identify
D1[A]ρ=γD[a]ρ=D[A]ρ
#def d1_rho_func(A, rho_vec):
# return A[7] * rho_vec
L = liouvillian(H, c_ops=c_ops).data
def d1_rho_func(t, rho_vec):
return cy.spmv(L, rho_vec)
D(1)2[A]ρ=1√2√γH[a]ρ=1√2H[A]ρ=1√2(Aρ+ρA†−Tr[Aρ+ρA†]ρ)→1√2{(AL+A†R)ρv−Tr[(AL+A†R)ρv]ρv}
D(2)2[A]ρ=1√2√γH[−ia]ρ=1√2H[−iA]ρ=−i√2(Aρ−ρA†−Tr[Aρ−ρA†]ρ)→−i√2{(AL−A†R)ρv−Tr[(AL−A†R)ρv]ρv}
n_sump = spre(c_ops[0]) + spost(c_ops[0].dag())
n_sump_data = n_sump.data/np.sqrt(2)
n_summ = spre(c_ops[0]) - spost(c_ops[0].dag())
n_summ_data = -1.0j*n_summ.data/np.sqrt(2)
def d2_rho_func(A, rho_vec):
out = np.zeros((2,len(rho_vec)),dtype=complex)
e1 = cy.cy_expect_rho_vec(n_sump_data, rho_vec, False)
out[0,:] += cy.spmv(n_sump_data, rho_vec) - e1 * rho_vec
e1 = cy.cy_expect_rho_vec(n_summ_data, rho_vec, False)
out[1,:] += cy.spmv(n_summ_data, rho_vec) - e1 * rho_vec
return out
#def d2_rho_func(t, rho_vec):
# e1 = cy.cy_expect_rho_vec(n_sum_data, rho_vec, False)
# out = np.zeros((1,len(rho_vec)),dtype=complex)
# out += cy.spmv(n_sum_data, rho_vec) - e1 * rho_vec
# return out
result = general_stochastic(ket2dm(rho0), times, d1=d1_rho_func, d2=d2_rho_func,
e_ops=[spre(op) for op in e_ops], solver="platen", # order=1
ntraj=ntraj, nsubsteps=nsubsteps, len_d2=2,
m_ops=[spre(a + a.dag()), (-1j)*spre(a - a.dag())],
dW_factors=[2/np.sqrt(gamma), 2/np.sqrt(gamma)],
store_measurement=True, map_func=parallel_map)
10.0%. Run time: 23.13s. Est. time left: 00:00:03:28 20.0%. Run time: 38.60s. Est. time left: 00:00:02:34 30.0%. Run time: 58.09s. Est. time left: 00:00:02:15 40.0%. Run time: 72.07s. Est. time left: 00:00:01:48 50.0%. Run time: 89.31s. Est. time left: 00:00:01:29 60.0%. Run time: 103.07s. Est. time left: 00:00:01:08 70.0%. Run time: 121.63s. Est. time left: 00:00:00:52 80.0%. Run time: 138.52s. Est. time left: 00:00:00:34 90.0%. Run time: 152.94s. Est. time left: 00:00:00:16 100.0%. Run time: 165.88s. Est. time left: 00:00:00:00 Total run time: 165.96s
plot_expectation_values([result, result_ref])
(<Figure size 576x288 with 3 Axes>, array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7ff798d45668>], [<matplotlib.axes._subplots.AxesSubplot object at 0x7ff798e76d30>], [<matplotlib.axes._subplots.AxesSubplot object at 0x7ff79981beb8>]], dtype=object))
for m in result.measurement:
plt.plot(times, m[:, 0].real, 'r', alpha=0.025)
plt.plot(times, m[:, 1].real, 'b', alpha=0.025)
plt.ylim(-20, 20)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0].real, 'r', lw=2);
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,1].real, 'b', lw=2);
plt.plot(times, result_ref.expect[1], 'k', lw=2);
plt.plot(times, result_ref.expect[2], 'k', lw=2);
result = smesolve(H, rho0, times, [], c_ops, e_ops, ntraj=ntraj, nsubsteps=nsubsteps, solver="milstein", # order=1
method='heterodyne', store_measurement=True)
10.0%. Run time: 1.90s. Est. time left: 00:00:00:17 20.0%. Run time: 4.26s. Est. time left: 00:00:00:17 30.0%. Run time: 7.62s. Est. time left: 00:00:00:17 40.0%. Run time: 10.65s. Est. time left: 00:00:00:15 50.0%. Run time: 13.26s. Est. time left: 00:00:00:13 60.0%. Run time: 15.16s. Est. time left: 00:00:00:10 70.0%. Run time: 18.11s. Est. time left: 00:00:00:07 80.0%. Run time: 20.96s. Est. time left: 00:00:00:05 90.0%. Run time: 23.19s. Est. time left: 00:00:00:02 Total run time: 26.30s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.plot(times, m[:, 0, 0].real / np.sqrt(gamma), 'r', alpha=0.025)
plt.plot(times, m[:, 0, 1].real / np.sqrt(gamma), 'b', alpha=0.025)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0,0].real / np.sqrt(gamma), 'r', lw=2);
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0,1].real / np.sqrt(gamma), 'b', lw=2);
plt.plot(times, result_ref.expect[1], 'k', lw=2);
plt.plot(times, result_ref.expect[2], 'k', lw=2);
result = smesolve(H, rho0, times, [], c_ops, e_ops, ntraj=ntraj, nsubsteps=nsubsteps, solver="milstein", # order=1
method='heterodyne', store_measurement=True, noise=result.noise)
10.0%. Run time: 2.00s. Est. time left: 00:00:00:18 20.0%. Run time: 3.83s. Est. time left: 00:00:00:15 30.0%. Run time: 6.04s. Est. time left: 00:00:00:14 40.0%. Run time: 8.52s. Est. time left: 00:00:00:12 50.0%. Run time: 10.81s. Est. time left: 00:00:00:10 60.0%. Run time: 12.66s. Est. time left: 00:00:00:08 70.0%. Run time: 14.80s. Est. time left: 00:00:00:06 80.0%. Run time: 16.94s. Est. time left: 00:00:00:04 90.0%. Run time: 18.78s. Est. time left: 00:00:00:02 Total run time: 20.38s
plot_expectation_values([result, result_ref]);
for m in result.measurement:
plt.plot(times, m[:, 0, 0].real / np.sqrt(gamma), 'r', alpha=0.025)
plt.plot(times, m[:, 0, 1].real / np.sqrt(gamma), 'b', alpha=0.025)
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0,0].real / np.sqrt(gamma), 'r', lw=2);
plt.plot(times, np.array(result.measurement).mean(axis=0)[:,0,1].real / np.sqrt(gamma), 'b', lw=2);
plt.plot(times, result_ref.expect[1], 'k', lw=2);
plt.plot(times, result_ref.expect[2], 'k', lw=2);
plt.axis('tight')
plt.ylim(-25, 25);
from qutip.ipynbtools import version_table
version_table()
Software | Version |
---|---|
QuTiP | 4.4.0.dev0+21a52e95 |
Numpy | 1.16.0 |
SciPy | 1.2.0 |
matplotlib | 3.0.2 |
Cython | 0.29.3 |
Number of CPUs | 2 |
BLAS Info | OPENBLAS |
IPython | 7.2.0 |
Python | 3.6.7 (default, Oct 22 2018, 11:32:17) [GCC 8.2.0] |
OS | posix [linux] |
Fri Feb 01 13:43:38 2019 JST |