Source code for quantum_mind.sensing.limits
r"""Quantum limits of a phase measurement, and the acceleration sensitivity of an atom interferometer.
A sensor that reads a phase :math:`\varphi` from :math:`N` independent probes (atoms, photons) repeated :math:`\nu`
times cannot do better than the **standard quantum limit**,
.. math:: \Delta\varphi_{\mathrm{SQL}} = \frac{1}{\sqrt{N\nu}} ,
while entangled probes can in principle reach the **Heisenberg limit** :math:`\Delta\varphi_{\mathrm{H}} = 1/(N\sqrt{\nu})`.
Entangled states are fragile, so real instruments sit near the standard quantum limit.
A light-pulse atom interferometer measures a constant acceleration :math:`a` as a phase
:math:`\varphi = k_{\mathrm{eff}}\, a\, T^2`, with :math:`k_{\mathrm{eff}} = 4\pi/\lambda` the effective two-photon wave
number and :math:`T` the time between pulses (Kasevich and Chu, 1992). Its single-shot acceleration sensitivity is therefore
:math:`\Delta a = \Delta\varphi/(C\, k_{\mathrm{eff}} T^2)` with :math:`C` the fringe contrast.
"""
from __future__ import annotations
import numpy as np
__all__ = ['phase_sensitivity', 'atom_interferometer_sensitivity', 'RB87_WAVELENGTH_M']
#: Wavelength of the rubidium-87 D2 line, a common choice for atom interferometers, in metres.
RB87_WAVELENGTH_M = 780.24e-9
[docs]
def phase_sensitivity(n_probes, repeats=1, scaling='sql'):
"""Smallest phase resolvable with ``n_probes`` probes repeated ``repeats`` times.
Parameters
----------
n_probes : float
Number of probes per shot (atoms or photons), at least 1.
repeats : float, optional
Number of independent shots.
scaling : {'sql', 'heisenberg'}, optional
Standard quantum limit :math:`1/\\sqrt{N\\nu}` or Heisenberg limit :math:`1/(N\\sqrt{\\nu})`.
Returns
-------
float
Phase uncertainty in radians.
Raises
------
ValueError
For fewer than one probe or one repeat, or an unknown scaling.
Examples
--------
>>> from quantum_mind.sensing import phase_sensitivity
>>> round(phase_sensitivity(10_000), 4), round(phase_sensitivity(10_000, scaling='heisenberg'), 6)
(0.01, 0.0001)
"""
if n_probes < 1 or repeats < 1:
raise ValueError('need at least one probe and one repeat')
if scaling == 'sql':
return float(1.0 / np.sqrt(n_probes * repeats))
if scaling == 'heisenberg':
return float(1.0 / (n_probes * np.sqrt(repeats)))
raise ValueError("scaling must be 'sql' or 'heisenberg'")
[docs]
def atom_interferometer_sensitivity(n_atoms, interrogation_time, contrast=0.5, wavelength=RB87_WAVELENGTH_M,
scaling='sql'):
"""Single-shot acceleration sensitivity of a light-pulse atom interferometer.
Parameters
----------
n_atoms : float
Atoms detected per shot.
interrogation_time : float
Time ``T`` between pulses in seconds.
contrast : float, optional
Fringe contrast in (0, 1].
wavelength : float, optional
Laser wavelength in metres (default: rubidium D2).
scaling : {'sql', 'heisenberg'}, optional
Returns
-------
float
Acceleration uncertainty per shot in m/s^2.
Examples
--------
>>> from quantum_mind.sensing import atom_interferometer_sensitivity
>>> a = atom_interferometer_sensitivity(1e6, 0.1)
>>> 1e-9 < a < 1e-7
True
"""
if interrogation_time <= 0 or not 0 < contrast <= 1:
raise ValueError('interrogation_time must be positive and contrast in (0, 1]')
k_eff = 4 * np.pi / wavelength
return float(phase_sensitivity(n_atoms, 1, scaling) / (contrast * k_eff * interrogation_time ** 2))