Inefficient photon detection: Mixing stochastic and deterministic master equations

Copyright (C) 2011 and later, Paul D. Nation & Robert J. Johansson


In [1]:
%pylab inline


Populating the interactive namespace from numpy and matplotlib

In [2]:
from qutip import *

In [3]:
from matplotlib import rcParams
rcParams['font.family'] = 'STIXGeneral'
rcParams['mathtext.fontset'] = 'stix'
rcParams['font.size'] = '14'

Direct photo-detection

Here we follow an example from Wiseman and Milburn, Quantum measurement and control, section. 4.8.1.

Consider cavity that leaks photons with a rate $\kappa$. The dissipated photons are detected with an inefficient photon detector, with photon-detection efficiency $\eta$. The master equation describing this scenario, where a separate dissipation channel has been added for detections and missed detections, is

$\dot\rho = -i[H, \rho] + \mathcal{D}[\sqrt{1-\eta} \sqrt{\kappa} a] + \mathcal{D}[\sqrt{\eta} \sqrt{\kappa}a]$

To describe the photon measurement stochastically, we can unravelling only the dissipation term that corresponds to detections, and leaving the missed detections as a deterministic dissipation term, we obtain [Eq. (4.235) in W&M]

$d\rho = \mathcal{H}[-iH -\eta\frac{1}{2}a^\dagger a] \rho dt + \mathcal{D}[\sqrt{1-\eta} a] \rho dt + \mathcal{G}[\sqrt{\eta}a] \rho dN(t)$

or

$d\rho = -i[H, \rho] dt + \mathcal{D}[\sqrt{1-\eta} a] \rho dt -\mathcal{H}[\eta\frac{1}{2}a^\dagger a] \rho dt + \mathcal{G}[\sqrt{\eta}a] \rho dN(t)$

where

$\displaystyle \mathcal{G}[A] \rho = \frac{A\rho A^\dagger}{\mathrm{Tr}[A\rho A^\dagger]} - \rho$

$\displaystyle \mathcal{H}[A] \rho = A\rho + \rho A^\dagger - \mathrm{Tr}[A\rho + \rho A^\dagger] \rho $

and $dN(t)$ is a Poisson distributed increment with $E[dN(t)] = \eta \langle a^\dagger a\rangle (t)$.

Formulation in QuTiP

In QuTiP we write the stochastic master equation on the form:

$\displaystyle d\rho(t) = -i[H, \rho] dt + \mathcal{D}[B] \rho dt + D_{1}[A]\rho(t) dt + D_{2}[A]\rho(t) d\xi$

where the first two term gives the deterministic master equation (Lindblad form with collapse operator $B$). Here $A = \sqrt{\eta\gamma} a$ and $B = \sqrt{(1-\eta)\gamma} $a.

We can identify

$\displaystyle D_{1}[A]\rho(t) = - \frac{1}{2}\eta\gamma \mathcal{H}[a^\dagger a] \rho(t) = - \frac{1}{2}\mathcal{H}[A^\dagger A] \rho(t) = -\frac{1}{2}\left( A^\dagger A\rho + \rho A^\dagger A - \mathrm{Tr}[A^\dagger A\rho + \rho A^\dagger A] \rho \right)$

$\displaystyle D_{2}[A]\rho(t) = \mathcal{G}[\sqrt{\eta\gamma}a] \rho = \mathcal{G}[A] \rho = \frac{A\rho A^\dagger}{\mathrm{Tr}[A\rho A^\dagger]} - \rho$

and $d\xi = dN(t)$ with a Poisson distribution.


In [4]:
N = 15
w0 = 0.5 * 2 * pi
times = linspace(0, 15, 150)
gamma = 0.1

In [5]:
a = destroy(N)

In [6]:
H = w0 * a.dag() * a

In [7]:
rho0 = fock(N, 5)

In [8]:
e_ops = [a.dag() * a, a + a.dag()]

Define the stochastic terms


In [9]:
# The argument A in the d1 and d2 callback functions is a list with the following
# precomputed superoperators, where c is the stochastic collapse operator given
# to the solver (called once for each operator, if more than one is given)
#
#     A[0] = spre(c)
#     A[1] = spost(c)
#     A[2] = spre(c.dag())
#     A[3] = spost(c.dag())
#     A[4] = spre(n)
#     A[5] = spost(n)
#     A[6] = (spre(c) * spost(c.dag())
#     A[7] = lindblad_dissipator(c)

$\displaystyle D_{1}[A]\rho(t) = -\frac{1}{2}\left( A^\dagger A\rho + \rho A^\dagger A - \mathrm{Tr}[A^\dagger A\rho + \rho A^\dagger A] \rho \right)$


In [10]:
def d1_rho_func(A, rho_vec):
    n_sum = A[4] + A[5]
    return 0.5 * (- n_sum * rho_vec + expect_rho_vec(n_sum, rho_vec) * rho_vec)

$\displaystyle D_{2}[A]\rho(t) = \frac{A\rho A^\dagger}{\mathrm{Tr}[A\rho A^\dagger]} - \rho$


In [11]:
def d2_rho_func(A, rho_vec):
    e1 = expect_rho_vec(A[6], rho_vec) + 1e-16  # add a small number to avoid division by zero
    return [(A[6] * rho_vec) / e1 - rho_vec]

Highly efficient detection


In [12]:
eta = 0.7
c_ops = [sqrt(1-eta) * sqrt(gamma) * a]  # collapse operator B
sc_ops = [sqrt(eta) * sqrt(gamma) * a]   # stochastic collapse operator A

In [13]:
result_ref = mesolve(H, rho0, times, c_ops+sc_ops, e_ops)

In [14]:
result1 = smesolve(H, rho0, times, c_ops=c_ops, sc_ops=sc_ops, e_ops=e_ops, 
                   ntraj=1, nsubsteps=100, d1=d1_rho_func, d2=d2_rho_func,
                   distribution='poisson', store_measurement=True)


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Elapsed time:   7.81s

In [15]:
result2 = smesolve(H, rho0, times, c_ops=c_ops, sc_ops=sc_ops, e_ops=e_ops, 
                  ntraj=10, nsubsteps=100, d1=d1_rho_func, d2=d2_rho_func,
                  distribution='poisson', store_measurement=True)


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Completed: 10.0%. Elapsed time:   7.68s. Est. remaining time: 00:00:01:09.
Completed: 20.0%. Elapsed time:  15.03s. Est. remaining time: 00:00:01:00.
Completed: 30.0%. Elapsed time:  22.34s. Est. remaining time: 00:00:00:52.
Completed: 40.0%. Elapsed time:  29.67s. Est. remaining time: 00:00:00:44.
Completed: 50.0%. Elapsed time:  37.07s. Est. remaining time: 00:00:00:37.
Completed: 60.0%. Elapsed time:  44.41s. Est. remaining time: 00:00:00:29.
Completed: 70.0%. Elapsed time:  51.75s. Est. remaining time: 00:00:00:22.
Completed: 80.0%. Elapsed time:  59.12s. Est. remaining time: 00:00:00:14.
Completed: 90.0%. Elapsed time:  66.46s. Est. remaining time: 00:00:00:07.
Elapsed time:  73.78s

In [16]:
fig, axes = subplots(2,2, figsize=(12,8), sharex=True)

axes[0,0].plot(times, result1.expect[0], label=r'Stochastic ME (ntraj = 1)', lw=2)
axes[0,0].plot(times, result_ref.expect[0], label=r'Lindblad ME', lw=2)
axes[0,0].set_title("Cavity photon number (ntraj = 1)")
axes[0,0].legend()

axes[0,1].plot(times, result2.expect[0], label=r'Stochatic ME (ntraj = 10)', lw=2)
axes[0,1].plot(times, result_ref.expect[0], label=r'Lindblad ME', lw=2)
axes[0,1].set_title("Cavity photon number (ntraj = 10)")
axes[0,1].legend()

axes[1,0].step(times, np.cumsum(result1.measurement[0].real), lw=2)
axes[1,0].set_title("Cummulative photon detections (ntraj = 1)")
axes[1,1].step(times, np.cumsum(array(result2.measurement).sum(axis=0).real) / 10, lw=2)
axes[1,1].set_title("Cummulative avg. photon detections (ntraj = 10)")

fig.tight_layout()


/usr/local/lib/python3.3/dist-packages/numpy/core/numeric.py:460: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order)

Highly inefficient photon detection


In [17]:
eta = 0.1
c_ops = [sqrt(1-eta) * sqrt(gamma) * a]  # collapse operator B
sc_ops = [sqrt(eta) * sqrt(gamma) * a]   # stochastic collapse operator A

In [18]:
result_ref = mesolve(H, rho0, times, c_ops+sc_ops, e_ops)

In [19]:
result1 = smesolve(H, rho0, times, c_ops=c_ops, sc_ops=sc_ops, e_ops=e_ops, 
                  ntraj=1, nsubsteps=100, d1=d1_rho_func, d2=d2_rho_func,
                  distribution='poisson', store_measurement=True)


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Elapsed time:   8.18s

In [20]:
result2 = smesolve(H, rho0, times, c_ops=c_ops, sc_ops=sc_ops, e_ops=e_ops, 
                  ntraj=10, nsubsteps=100, d1=d1_rho_func, d2=d2_rho_func,
                  distribution='poisson', store_measurement=True)


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Completed: 10.0%. Elapsed time:   7.93s. Est. remaining time: 00:00:01:11.
Completed: 20.0%. Elapsed time:  15.63s. Est. remaining time: 00:00:01:02.
Completed: 30.0%. Elapsed time:  23.37s. Est. remaining time: 00:00:00:54.
Completed: 40.0%. Elapsed time:  31.12s. Est. remaining time: 00:00:00:46.
Completed: 50.0%. Elapsed time:  38.89s. Est. remaining time: 00:00:00:38.
Completed: 60.0%. Elapsed time:  46.66s. Est. remaining time: 00:00:00:31.
Completed: 70.0%. Elapsed time:  54.41s. Est. remaining time: 00:00:00:23.
Completed: 80.0%. Elapsed time:  62.16s. Est. remaining time: 00:00:00:15.
Completed: 90.0%. Elapsed time:  69.92s. Est. remaining time: 00:00:00:07.
Elapsed time:  77.61s

In [21]:
fig, axes = subplots(2,2, figsize=(12,8), sharex=True)

axes[0,0].plot(times, result1.expect[0], label=r'Stochastic ME (ntraj = 1)', lw=2)
axes[0,0].plot(times, result_ref.expect[0], label=r'Lindblad ME', lw=2)
axes[0,0].set_title("Cavity photon number (ntraj = 1)")
axes[0,0].legend()

axes[0,1].plot(times, result2.expect[0], label=r'Stochatic ME (ntraj = 10)', lw=2)
axes[0,1].plot(times, result_ref.expect[0], label=r'Lindblad ME', lw=2)
axes[0,1].set_title("Cavity photon number (ntraj = 10)")
axes[0,1].legend()

axes[1,0].step(times, np.cumsum(result1.measurement[0].real), lw=2)
axes[1,0].set_title("Cummulative photon detections (ntraj = 1)")
axes[1,1].step(times, np.cumsum(array(result2.measurement).sum(axis=0).real) / 10, lw=2)
axes[1,1].set_title("Cummulative avg. photon detections (ntraj = 10)")

fig.tight_layout()


Inefficient homodyne detection

The stochastic master equation for inefficient homodyne detection, when unravaling the detection part of the master equation

$\dot\rho = -i[H, \rho] + \mathcal{D}[\sqrt{1-\eta} \sqrt{\kappa} a] + \mathcal{D}[\sqrt{\eta} \sqrt{\kappa}a]$,

is given in W&M as

$d\rho = -i[H, \rho]dt + \mathcal{D}[\sqrt{1-\eta} \sqrt{\kappa} a] \rho dt + \mathcal{D}[\sqrt{\eta} \sqrt{\kappa}a] \rho dt + \mathcal{H}[\sqrt{\eta} \sqrt{\kappa}a] \rho d\xi$

where $d\xi$ is the Wiener increment. This can be described as a standard homodyne detection with efficiency $\eta$ together with a stochastic dissipation process with collapse operator $\sqrt{(1-\eta)\kappa} a$. Alternatively we can combine the two deterministic terms on standard Lindblad for and obtain the stochastic equation (which is the form given in W&M)

$d\rho = -i[H, \rho]dt + \mathcal{D}[\sqrt{\kappa} a]\rho dt + \sqrt{\eta}\mathcal{H}[\sqrt{\kappa}a] \rho d\xi$

Below we solve these two equivalent equations with QuTiP


In [22]:
rho0 = coherent(N, sqrt(5))

Form 1: Standard homodyne with deterministic dissipation on Lindblad form


In [41]:
eta = 0.95
c_ops = [sqrt(1-eta) * sqrt(gamma) * a]  # collapse operator B
sc_ops = [sqrt(eta) * sqrt(gamma) * a]   # stochastic collapse operator A

In [42]:
result_ref = mesolve(H, rho0, times, c_ops+sc_ops, e_ops)

In [46]:
result = smesolve(H, rho0, times, c_ops, sc_ops, e_ops, ntraj=125, nsubsteps=100,
                  method='homodyne', store_measurement=True, m_ops=[[a + a.dag()]])


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Completed: 10.4%. Elapsed time:  63.19s. Est. remaining time: 00:00:09:04.
Completed: 20.0%. Elapsed time: 122.39s. Est. remaining time: 00:00:08:09.
Completed: 30.4%. Elapsed time: 186.49s. Est. remaining time: 00:00:07:06.
Completed: 40.0%. Elapsed time: 245.76s. Est. remaining time: 00:00:06:08.
Completed: 50.4%. Elapsed time: 309.89s. Est. remaining time: 00:00:05:04.
Completed: 60.0%. Elapsed time: 369.27s. Est. remaining time: 00:00:04:06.
Completed: 70.4%. Elapsed time: 433.56s. Est. remaining time: 00:00:03:02.
Completed: 80.0%. Elapsed time: 492.06s. Est. remaining time: 00:00:02:03.
Completed: 90.4%. Elapsed time: 555.41s. Est. remaining time: 00:00:00:58.
Elapsed time: 613.69s

In [47]:
plot_expectation_values([result, result_ref]);



In [48]:
fig, ax = subplots(figsize=(8,4))

for m in result.measurement:
    ax.plot(times, m[:, 0].real, 'b', alpha=0.025)

ax.plot(times, result_ref.expect[1], 'b', lw=2);

ax.set_ylim(-15, 15)
ax.set_xlim(0, times.max())
ax.set_xlabel('time', fontsize=12)
ax.plot(times, array(result.measurement).mean(axis=0)[:,0].real, 'k', lw=2);


Form 2: Combined homodyne with deterministic dissipation for missed detection events

$\displaystyle D_{1}[A]\rho(t) = \mathcal{D}[\kappa a]\rho(t) = \mathcal{D}[A]\rho(t)$


In [49]:
def d1_rho_func(A, rho_vec):
    return A[7] * rho_vec

$\displaystyle D_{2}[A]\rho(t) = \sqrt{\eta} \mathcal{H}[\sqrt{\kappa} a]\rho(t) = \sqrt{\eta} \mathcal{H}[A]\rho(t) = \sqrt{\eta}(A\rho + \rho A^\dagger - \mathrm{Tr}[A\rho + \rho A^\dagger] \rho) \rightarrow \sqrt{\eta} \left((A_L + A_R^\dagger)\rho_v - \mathrm{Tr}[(A_L + A_R^\dagger)\rho_v] \rho_v\right)$


In [50]:
def d2_rho_func(A, rho_vec):
    n_sum = A[0] + A[3]
    e1 = expect_rho_vec(n_sum, rho_vec)
    return [sqrt(eta) * (n_sum * rho_vec - e1 * rho_vec)]

In [51]:
c_ops = []
sc_ops = [sqrt(gamma) * a]

In [52]:
result_ref = mesolve(H, rho0, times, c_ops+sc_ops, e_ops)

In [53]:
result = smesolve(H, rho0, times, [], sc_ops, e_ops, 
                  ntraj=25, nsubsteps=100, d1=d1_rho_func, d2=d2_rho_func, m_ops=[[a + a.dag()]],
                  distribution='normal', store_measurement=True)


Completed:  0.0%. Elapsed time:   0.00s. Est. remaining time: 00:00:00:00.
Completed: 12.0%. Elapsed time:  15.71s. Est. remaining time: 00:00:01:55.
Completed: 20.0%. Elapsed time:  25.84s. Est. remaining time: 00:00:01:43.
Completed: 32.0%. Elapsed time:  40.97s. Est. remaining time: 00:00:01:27.
Completed: 40.0%. Elapsed time:  51.07s. Est. remaining time: 00:00:01:16.
Completed: 52.0%. Elapsed time:  66.23s. Est. remaining time: 00:00:01:01.
Completed: 60.0%. Elapsed time:  76.26s. Est. remaining time: 00:00:00:50.
Completed: 72.0%. Elapsed time:  91.35s. Est. remaining time: 00:00:00:35.
Completed: 80.0%. Elapsed time: 101.42s. Est. remaining time: 00:00:00:25.
Completed: 92.0%. Elapsed time: 116.57s. Est. remaining time: 00:00:00:10.
Elapsed time: 126.82s

In [54]:
plot_expectation_values([result, result_ref]);



In [55]:
fig, ax = subplots(figsize=(8,4))

for m in result.measurement:
    ax.plot(times, m[:, 0].real, 'b', alpha=0.05)

ax.plot(times, result_ref.expect[1], 'b', lw=2);

ax.set_ylim(-15, 15)
ax.set_xlim(0, times.max())
ax.set_xlabel('time', fontsize=12)
ax.plot(times, array(result.measurement).mean(axis=0)[:,0].real, 'k', lw=2);


Versions


In [56]:
from qutip.ipynbtools import version_table

version_table()


Out[56]:
SoftwareVersion
IPython2.0.0-dev
SciPy0.13.0.dev-7c6d92e
OSposix [linux]
QuTiP2.3.0.dev-73cd13c
Python3.3.1 (default, Apr 17 2013, 22:30:32) [GCC 4.7.3]
Numpy1.8.0.dev-b307a8a
Cython0.19.1
matplotlib1.4.x
Fri Sep 27 14:40:00 2013 JST