Two-Level: Stimulated Three-Pulse Photon Echo

Author

Thomas Ogden

Three short π/2 pulses separated by delays τ and \(T_w\) generate a stimulated photon echo at time \(t = +\tau\) after the third pulse. The defining feature compared to the two-pulse echo (A4) is that coherence information is stored as a population grating during \(T_w\), so the echo amplitude decays with the population lifetime \(T_1\) rather than the coherence lifetime \(T_2\):

\[ A_{\rm echo}(\tau, T_w) \propto e^{-2\tau/T_2}\, e^{-T_w/T_1} \]

A \(T_w\) sweep isolates \(T_1\); a τ sweep (already covered in the photon-echo example) isolates \(T_2\). The stimulated echo is the optical analogue of the NMR stimulated echo (Hahn 1950; Mossberg 1982).

Physical mechanism

  1. π/2 pulse at \(t_1 = -(\tau + T_w)\): creates a ground/excited superposition. Each velocity class begins precessing at its own Doppler- shifted frequency.

  2. Free precession over τ: the macroscopic polarisation \(P \propto \sum_j \rho_{10}^{(j)}\) dephases on the Doppler timescale \(1/\sigma\).

  3. π/2 pulse at \(t_2 = -T_w\): maps the surviving coherences onto a sinusoidal population grating in velocity space. The net coherence vanishes (destructive interference among velocity classes), but the population difference carries the phase record.

  4. Storage interval \(T_w\): the population grating persists. Its amplitude decays as \(e^{-T_w/T_1}\) (spontaneous emission). There is no macroscopic coherence here — this is the defining distinction from the two-pulse echo.

  5. π/2 pulse at \(t_3 = 0\): reads the population grating back into coherences with the same phase record as step 2.

  6. Rephasing over τ: the restored coherences rephase — echo at \(t = +\tau\).

Hard-pulse condition

All three pulses must be hard (spectrally broad enough to rotate every velocity class by exactly π/2):

\[\Omega_{\rm peak} \gg \Delta_{\rm Doppler}\]

Here \(T = 0.025\,\gamma^{-1}\) gives \(\Omega_{\rm peak} = 10\,{\rm rad}\,\gamma\) and \(\Omega_{\rm peak}/\sigma \approx 2.4\), satisfying the condition for all significantly-weighted velocity classes.

Level structure

      |1⟩ ════════════════  (excited)
       │   decay rate 0.1 → T₁ ≈ 1.59 γ⁻¹,  T₂ ≈ 3.18 γ⁻¹
  Ω   │   three π/2 sech pulses (hard-pulse limit)
       │
      |0⟩ ════════════════  (ground)

Inhomogeneous broadening: Gaussian Doppler distribution, \(\sigma \approx 4.2\,{\rm rad}\,\gamma\) (thermal_width = 0.15).

Parameters

Quantity Value Notes
Decay rate \(0.1\) \(\Gamma = 2\pi\times 0.1 \approx 0.63\,{\rm rad}\,\gamma\); \(T_1 \approx 1.59\), \(T_2 \approx 3.18\,\gamma^{-1}\)
Doppler σ \(4.2\,{\rm rad}\,\gamma\) 40 velocity classes over \(\pm 0.75\)
Pulse width \(T = 0.025\,\gamma^{-1}\) hard-pulse: \(\Omega_{\rm peak} = 10\,{\rm rad}\,\gamma\)
Inter-pulse delay \(\tau = 0.5\,\gamma^{-1}\) echo at \(t = +0.5\,\gamma^{-1}\)
Storage interval \(T_w = 1.0\,\gamma^{-1}\) (main) \(e^{-T_w/T_1} \approx 53\%\) amplitude
import math
import numpy as np
import plotly.graph_objects as go
from clerq import mb_solve, plot
from clerq.plot import theme  # register Plotly template
# interaction_strengths = 0.5: clerq 0.13.0 corrected a factor of 2 in the Maxwell
# propagation equation, so 0.5 gives the optical depth that 1.0 gave in earlier versions.
tau = 0.5    # delay between pulse 1–2 and rephasing time after pulse 3
Tw  = 1.0    # storage interval (between pulse 2 and pulse 3)
T   = 0.025  # sech half-width — short hard pulses, Ω_peak >> σ

# Pulse centres:
#   pulse 1 (π/2) at t₁ = −(τ + Tw)
#   pulse 2 (π/2) at t₂ = −Tw
#   pulse 3 (π/2) at t₃ =  0
#   echo            at t_echo = +τ
t1 = -(tau + Tw)   # = −1.5
t2 = -Tw           # = −1.0
t3 =  0.0

mb_solve_json = f"""
{{
  "atom": {{
    "num_states": 2,
    "decays": [
      {{"channels": [[0, 1]], "rate": 0.1}}
    ],
    "fields": [
      {{
        "label": "p1",
        "coupled_levels": [[0, 1]],
        "rabi_freq": 1.0,
        "rabi_freq_t_func": "sech",
        "rabi_freq_t_args": {{"n_pi": 0.5, "centre": {t1}, "width": {T}}}
      }},
      {{
        "label": "p2",
        "coupled_levels": [[0, 1]],
        "rabi_freq": 1.0,
        "rabi_freq_t_func": "sech",
        "rabi_freq_t_args": {{"n_pi": 0.5, "centre": {t2}, "width": {T}}}
      }},
      {{
        "label": "p3",
        "coupled_levels": [[0, 1]],
        "rabi_freq": 1.0,
        "rabi_freq_t_func": "sech",
        "rabi_freq_t_args": {{"n_pi": 0.5, "centre": {t3}, "width": {T}}}
      }}
    ]
  }},
  "t_min": {-(tau + Tw + 1.0)},
  "t_max":  {tau + 0.5},
  "t_steps": 1000,
  "z_min": 0.0,
  "z_max": 1.0,
  "z_steps": 20,
  "z_steps_inner": 2,
  "interaction_strengths": [0.5, 0.5, 0.5],
  "velocity_classes": {{
    "thermal_width": 0.15,
    "thermal_delta_min": -0.75,
    "thermal_delta_max":  0.75,
    "thermal_delta_steps": 40
  }},
  "savefile": "mbs-two-stimulated-echo"
}}
"""

mbs = mb_solve.MBSolve().from_json_str(mb_solve_json)
mbs.mbsolve(recalc=False)
print("Done")
Saving MBSolve to mbs-two-stimulated-echo.qu
Done

Output field: stimulated echo at \(t = +\tau\)

The total transmitted field \(|\Omega_{p1}(z_{\rm max},t)| + |\Omega_{p2}(z_{\rm max},t)| + |\Omega_{p3}(z_{\rm max},t)|\) shows three attenuated input pulses and the stimulated echo at \(t = +\tau\). All three pulses drive the same transition so they all experience Beer-Lambert absorption at their respective times.

Vertical dashed lines mark the three pulse centres (\(t_1\), \(t_2\), \(t_3\)) and the echo time \(t_{\rm echo} = +\tau\).

t = mbs.tlist
Omega_in  = (np.abs(mbs.Omegas_zt[0,  0, :]) +
             np.abs(mbs.Omegas_zt[1,  0, :]) +
             np.abs(mbs.Omegas_zt[2,  0, :]))
Omega_out = (np.abs(mbs.Omegas_zt[0, -1, :]) +
             np.abs(mbs.Omegas_zt[1, -1, :]) +
             np.abs(mbs.Omegas_zt[2, -1, :]))

fig = go.Figure(layout=go.Layout(
    title="Stimulated photon echo: transmitted field at z = z_max",
    xaxis_title="time (γ⁻¹)",
    yaxis_title="|Ω| (γ)",
    template="clerq",
))
fig.add_trace(go.Scatter(x=t, y=Omega_in,  name="input  (z = 0)",   line=dict(dash="dot")))
fig.add_trace(go.Scatter(x=t, y=Omega_out, name="output (z = z_max)"))
fig.add_vline(x=t1,   line_dash="dash", line_color="grey",
              annotation_text="π/2 (p1)", annotation_position="top right")
fig.add_vline(x=t2,   line_dash="dash", line_color="grey",
              annotation_text="π/2 (p2)", annotation_position="top right")
fig.add_vline(x=t3,   line_dash="dash", line_color="grey",
              annotation_text="π/2 (p3)", annotation_position="top right")
fig.add_vline(x=tau,  line_dash="dash", line_color="red",
              annotation_text="echo", annotation_position="top right")
fig.show(renderer='notebook_connected')

Macroscopic polarisation: FID, collapse, and revival

\(\mathrm{Im}[\rho_{10}(z=0,t)]\) is the velocity-class-averaged coherence. Three stages are visible:

  1. FID after pulse 1: the polarisation builds up then dephases on the Doppler timescale \(1/\sigma \approx 0.24\,\gamma^{-1}\).
  2. Population-grating interval (\(T_w\), between pulse 2 and pulse 3): the net coherence collapses to near zero. The phase information is stored in population, not coherence — no FID is detectable here.
  3. Echo at \(t = +\tau\): pulse 3 reads the population grating back into coherences, which rephase to produce the echo.
fig = plot.coherence(mbs, i=1, j=0, z_idx=0, component="imag")
fig.update_layout(
    title="Im[ρ₁₀(t)] at z = 0 — FID, population-grating storage, echo"
)
fig.add_vline(x=t1,  line_dash="dash", line_color="grey",
              annotation_text="π/2 (p1)", annotation_position="top right")
fig.add_vline(x=t2,  line_dash="dash", line_color="grey",
              annotation_text="π/2 (p2)", annotation_position="top right")
fig.add_vline(x=t3,  line_dash="dash", line_color="grey",
              annotation_text="π/2 (p3)", annotation_position="top right")
fig.add_vline(x=tau, line_dash="dash", line_color="red",
              annotation_text="echo", annotation_position="top right")

# Shade the storage interval T_w
fig.add_vrect(x0=t2, x1=t3, fillcolor="grey", opacity=0.08,
              layer="below", line_width=0,
              annotation_text="T_w (population storage)",
              annotation_position="top left")
fig.show(renderer='notebook_connected')

Coherence during \(T_w\)

The “zero coherence during \(T_w\)” picture has two caveats worth noting:

  1. FID from pulse 2 itself dephases on the same Doppler timescale \(1/\sigma \approx 0.24\,\gamma^{-1}\).
  2. Secondary two-pulse echo from pulses 1 and 2 — a pair of \(\pi/2\) pulses does generate a partial photon echo, peaking at \(t_2 + \tau = -T_w + \tau\). With \(T_w = 1.0\) and \(\tau = 0.5\) this secondary echo sits at \(t = -0.5\,\gamma^{-1}\), only \(0.5\,\gamma^{-1}\) before \(t_3\), so its dephasing tail overlaps the late storage window. For large \(T_w \gg \tau\) this echo has time to fully dephase.

The cell below measures the maximum net coherence in the late storage window (after the FID from pulse 2 has died) and at the echo time.

rho10 = mbs.states_zt[0, :, 1, 0]   # at z = 0
t_arr = mbs.tlist

# Late storage window — after FID from pulse 2 but may overlap secondary echo
# secondary echo from pulses 1+2 is at t2 + tau = -T_w + tau = -0.5
storage_mask = (t_arr > t2 + 0.7) & (t_arr < t3 - 0.05)
rho_storage_max = float(np.abs(rho10[storage_mask]).max())

# Echo peak |ρ₁₀| near t = +τ
echo_mask = (t_arr > tau - 5*T) & (t_arr < tau + 5*T)
rho_echo_max = float(np.abs(rho10[echo_mask]).max())

print(f"|ρ₁₀| max in late T_w window [t₂+0.7, t₃−0.05] : {rho_storage_max:.4f}")
print(f"|ρ₁₀| max at echo (t ≈ +τ)                     : {rho_echo_max:.4f}")
print(f"Suppression ratio (storage / echo)              : {rho_storage_max/rho_echo_max:.3f}")
# The secondary two-pulse echo from pulses 1+2 overlaps this window at T_w=1.0;
# for T_w >> tau the secondary echo fully dephases before t3 and this value drops.
print("(The residual coherence at T_w=1.0 is the tail of the secondary echo")
print(" from pulses 1+2, which peaks at t2+tau=-0.5 and dephases over ~0.5 γ⁻¹.)")
|ρ₁₀| max in late T_w window [t₂+0.7, t₃−0.05] : 0.1841
|ρ₁₀| max at echo (t ≈ +τ)                     : 0.0716
Suppression ratio (storage / echo)              : 2.570
(The residual coherence at T_w=1.0 is the tail of the secondary echo
 from pulses 1+2, which peaks at t2+tau=-0.5 and dephases over ~0.5 γ⁻¹.)

Echo amplitude vs \(T_w\): \(T_1\) measurement

Running the simulation for storage intervals \(T_w \in [1.0, 2.5]\,\gamma^{-1}\) at fixed \(\tau = 0.5\,\gamma^{-1}\) demonstrates the exponential decay:

\[ A_{\rm echo}(T_w) \propto e^{-T_w/T_1}, \qquad T_1 = \frac{1}{\Gamma} = \frac{1}{2\pi\times {\rm rate}} \approx 1.59\,\gamma^{-1} \]

The range \(T_w \ge 1.0\,\gamma^{-1}\) is used because shorter \(T_w\) values are contaminated by the Hahn echo from pulse-pair (2,3), which appears at \(t = T_w\) and falls inside the measurement window when \(T_w \le 0.75\,\gamma^{-1}\).

Echo amplitude is measured as the peak of the transmitted field \(\sum_i |\Omega_i(z_{\rm max},t)|\) in the window \(t\in[0.2,\,0.75]\,\gamma^{-1}\) (after the three input pulses have passed, before the (2,3) echo at \(t=T_w\)).

rate = 0.1
T1 = 1.0 / (2 * math.pi * rate)   # ≈ 1.592 γ⁻¹
T2 = 2.0 / (2 * math.pi * rate)   # ≈ 3.183 γ⁻¹

# T_w ≥ 1.0: the Hahn echo from pulses (2,3) at t = T_w is outside
# the measurement window [0.2, 0.75], leaving only the stimulated echo.
Tw_values = [1.0, 1.5, 2.0, 2.5]
echo_amps = []

for Tw_val in Tw_values:
    t1_v = -(tau + Tw_val)
    t2_v = -Tw_val
    t_min_v = -(tau + Tw_val + 1.0)
    t_max_v = tau + 0.5
    # Maintain dt ≤ 0.004 so hard pulses (T = 0.025) remain well resolved
    t_steps_v = max(500, int((t_max_v - t_min_v) / 0.004))
    j = f"""
{{
  \"atom\": {{
    \"num_states\": 2,
    \"decays\": [{{\"channels\": [[0, 1]], \"rate\": {rate}}}],
    \"fields\": [
      {{\"label\":\"p1\",\"coupled_levels\":[[0,1]],\"rabi_freq\":1.0,
       \"rabi_freq_t_func\":\"sech\",
       \"rabi_freq_t_args\":{{\"n_pi\":0.5,\"centre\":{t1_v},\"width\":{T}}}}},
      {{\"label\":\"p2\",\"coupled_levels\":[[0,1]],\"rabi_freq\":1.0,
       \"rabi_freq_t_func\":\"sech\",
       \"rabi_freq_t_args\":{{\"n_pi\":0.5,\"centre\":{t2_v},\"width\":{T}}}}},
      {{\"label\":\"p3\",\"coupled_levels\":[[0,1]],\"rabi_freq\":1.0,
       \"rabi_freq_t_func\":\"sech\",
       \"rabi_freq_t_args\":{{\"n_pi\":0.5,\"centre\":0.0,\"width\":{T}}}}}
    ]
  }},
  \"t_min\": {t_min_v},
  \"t_max\": {t_max_v},
  \"t_steps\": {t_steps_v},
  \"z_min\": 0.0, \"z_max\": 1.0, \"z_steps\": 20, \"z_steps_inner\": 2,
  \"interaction_strengths\": [0.5, 0.5, 0.5],
  \"velocity_classes\": {{
    \"thermal_width\": 0.15,
    \"thermal_delta_min\": -0.75,
    \"thermal_delta_max\":  0.75,
    \"thermal_delta_steps\": 40
  }},
  \"savefile\": \"mbs-two-stimulated-echo-Tw{Tw_val:.2f}\"
}}
"""
    m = mb_solve.MBSolve().from_json_str(j)
    m.mbsolve(recalc=False)
    t_v = m.tlist
    # Peak of total output field in the echo window.
    # Window [0.2, 0.75]: after input pulses (last at t=0) and before
    # the (2,3) Hahn echo at t = T_w ≥ 1.0.
    Omega_out = sum(np.abs(m.Omegas_zt[i, -1, :]) for i in range(3))
    echo_mask = (t_v >= 0.2) & (t_v <= 0.75)
    amp = float(Omega_out[echo_mask].max()) if echo_mask.any() else 0.0
    echo_amps.append(amp)
    print(f"T_w = {Tw_val:.1f} γ⁻¹ — echo peak |Ω_out|: {amp:.4f}")
Saving MBSolve to mbs-two-stimulated-echo-Tw1.00.qu
T_w = 1.0 γ⁻¹ — echo peak |Ω_out|: 1.5223
Saving MBSolve to mbs-two-stimulated-echo-Tw1.50.qu
T_w = 1.5 γ⁻¹ — echo peak |Ω_out|: 1.0720
Saving MBSolve to mbs-two-stimulated-echo-Tw2.00.qu
T_w = 2.0 γ⁻¹ — echo peak |Ω_out|: 0.8760
Saving MBSolve to mbs-two-stimulated-echo-Tw2.50.qu
T_w = 2.5 γ⁻¹ — echo peak |Ω_out|: 0.6413
Tw_arr = np.array(Tw_values)
amps   = np.array(echo_amps)

# Fit normalised to T_w = 1.0 (first data point)
Tw_fine  = np.linspace(Tw_arr[0], Tw_arr[-1] + 0.5, 200)
analytic = amps[0] * np.exp(-(Tw_fine - Tw_arr[0]) / T1)

fig = go.Figure(layout=go.Layout(
    title=f"Stimulated echo amplitude vs T_w — T₁ = {T1:.2f} γ⁻¹",
    xaxis_title="T_w (γ⁻¹)",
    yaxis_title="peak |Ω(z_max, t)| in echo window",
    template="clerq",
))
fig.add_trace(go.Scatter(
    x=Tw_arr, y=amps, mode="markers+lines", name="simulation",
    marker=dict(size=8)
))
fig.add_trace(go.Scatter(
    x=Tw_fine, y=analytic, mode="lines",
    name=f"e^(−ΔT_w/T₁),  T₁ = {T1:.2f} γ⁻¹",
    line=dict(dash="dash")
))
fig.show(renderer='notebook_connected')

# Print deviations from analytic
print("T_w (γ⁻¹)  |  measured  |  analytic  |  deviation")
for tw, amp in zip(Tw_arr, amps):
    expected = amps[0] * np.exp(-(tw - Tw_arr[0]) / T1)
    print(f"  {tw:.1f}       |   {amp:.4f}   |   {expected:.4f}   |  {(amp-expected)/expected*100:+.1f}%")
T_w (γ⁻¹)  |  measured  |  analytic  |  deviation
  1.0       |   1.5223   |   1.5223   |  +0.0%
  1.5       |   1.0720   |   1.1119   |  -3.6%
  2.0       |   0.8760   |   0.8121   |  +7.9%
  2.5       |   0.6413   |   0.5932   |  +8.1%

Pulse-area sanity check

Each of the three input fields should carry a π/2 pulse area at \(z = 0\).

for i, label in enumerate(["p1 (π/2)", "p2 (π/2)", "p3 (π/2)"]):
    area = np.trapezoid(mbs.Omegas_zt[i, 0, :].real, mbs.tlist) / np.pi
    print(f"Field {label}: area = {area:.4f} π")
    assert abs(area - 0.5) < 0.05, f"Expected 0.5π, got {area:.4f}π"
Field p1 (π/2): area = 0.5000 π
Field p2 (π/2): area = 0.5000 π
Field p3 (π/2): area = 0.5000 π

Summary

  • Pulse 1 (π/2) creates a superposition; the macroscopic polarisation free-induction decays on the Doppler timescale \(1/\sigma\).
  • Pulse 2 (π/2) converts coherences into a population grating in velocity space; the net coherence vanishes.
  • During \(T_w\) the grating persists with amplitude \(e^{-T_w/T_1}\). No coherence is detectable — a key signature of the stimulated echo.
  • Pulse 3 (π/2) reads the grating back into coherences.
  • Echo at \(t = +\tau\): coherences rephase and the medium emits a coherent burst.
  • The \(T_w\)-sweep directly measures \(T_1\); a τ-sweep (as in the two-pulse photon echo example) measures \(T_2\). Together they give the full relaxation picture.

Applications include quantum memory characterisation, optical spectroscopy of inhomogeneously broadened systems (rare-earth-doped crystals, atomic vapours), and the optical analogue of NMR relaxation measurements.

References

  1. E. L. Hahn, Spin Echoes, Phys. Rev. 80, 580 (1950). Original NMR stimulated echo.
  2. T. W. Mossberg, Time-domain frequency-selective optical data storage, Opt. Lett. 7, 77 (1982). Optical stimulated echo; \(T_1/T_2\) separation.
  3. N. A. Kurnit, I. D. Abella, S. R. Hartmann, Observation of a Photon Echo, PRL 13, 567 (1964). First optical photon echo (context).
  4. L. Allen, J. H. Eberly, Optical Resonance and Two-Level Atoms (Dover, 1987), Ch. 9.