Pulsed Experiments¶
This tutorial covers common pulsed measurement sequences.
Rabi Oscillations¶
Measure the Rabi frequency by varying pulse duration:
import numpy as np
import matplotlib.pyplot as plt
from sim import HamiltonianBuilder, LindbladSolver
from sim.hamiltonian.terms import ZFS, MicrowaveDrive
from sim.states import ground_state, projector_ms
# Rabi frequency
omega_rabi = 10 # MHz
durations = np.linspace(0, 200e-9, 100) # 0 to 200 ns
populations = []
for t_pulse in durations:
# Time-dependent MW
def mw_amplitude(t):
return omega_rabi if t < t_pulse else 0
H = HamiltonianBuilder()
H.add(ZFS(D=2.87))
H.add(MicrowaveDrive(omega=mw_amplitude))
solver = LindbladSolver(H)
solver.add_t2_dephasing(gamma=1e5)
rho0 = ground_state()
result = solver.evolve(rho0, (0, t_pulse + 1e-9), n_steps=50)
pop = np.trace(projector_ms(+1) @ result.final_state()).real
populations.append(pop)
plt.figure(figsize=(10, 5))
plt.plot(durations * 1e9, populations, 'b-')
plt.xlabel("Pulse Duration (ns)")
plt.ylabel("Population ms=+1")
plt.title(f"Rabi Oscillations (Ω = {omega_rabi} MHz)")
# Expected: cos²(Ωt/2) with period T = 2π/Ω = 100 ns
t_theory = durations * 1e9
pop_theory = np.sin(np.pi * omega_rabi * durations * 1e6)**2
plt.plot(t_theory, pop_theory, 'r--', label='Theory')
plt.legend()
plt.show()
Ramsey Interferometry¶
Measure T₂* and detuning:
from sim.pulses.sequences import ramsey_sequence
tau_values = np.linspace(0, 5e-6, 100) # 0 to 5 μs
signal = []
# Small detuning to see oscillations
detuning = 1 # MHz
for tau in tau_values:
H = HamiltonianBuilder()
H.add(ZFS(D=2.87))
H.add(MicrowaveDrive(omega=20, detuning=detuning)) # Fast π/2
solver = LindbladSolver(H)
solver.add_t2_dephasing(gamma=0.5e6) # T2* = 2 μs
rho = ground_state()
# First π/2
t_pi2 = 12.5e-9 # 12.5 ns for 20 MHz Rabi
result = solver.evolve(rho, (0, t_pi2), n_steps=10)
rho = result.final_state()
# Free evolution (no MW)
H_free = HamiltonianBuilder()
H_free.add(ZFS(D=2.87))
solver_free = LindbladSolver(H_free)
solver_free.add_t2_dephasing(gamma=0.5e6)
result = solver_free.evolve(rho, (0, tau), n_steps=20)
rho = result.final_state()
# Second π/2
result = solver.evolve(rho, (0, t_pi2), n_steps=10)
rho = result.final_state()
pop = np.trace(projector_ms(0) @ rho).real
signal.append(pop)
plt.figure(figsize=(10, 5))
plt.plot(tau_values * 1e6, signal, 'b-')
plt.xlabel("Free Evolution Time τ (μs)")
plt.ylabel("Population ms=0")
plt.title("Ramsey Fringes")
# Fit: A * exp(-τ/T2*) * cos(2π*Δ*τ) + offset
plt.show()
Spin Echo (Hahn Echo)¶
Measure T₂ by refocusing static noise:
tau_values = np.linspace(0, 500e-6, 50) # 0 to 500 μs
echo_signal = []
for tau in tau_values:
H = HamiltonianBuilder()
H.add(ZFS(D=2.87))
H.add(MicrowaveDrive(omega=20))
solver = LindbladSolver(H)
# Pure dephasing (refocusable)
solver.add_t2_dephasing(gamma=1e6)
# T1 relaxation (not refocusable)
solver.add_t1_relaxation(gamma=100)
rho = ground_state()
t_pi2 = 12.5e-9
t_pi = 25e-9
# π/2
result = solver.evolve(rho, (0, t_pi2), n_steps=5)
rho = result.final_state()
# τ/2 free evolution
H_free = HamiltonianBuilder()
H_free.add(ZFS(D=2.87))
solver_free = LindbladSolver(H_free)
solver_free.add_t2_dephasing(gamma=1e6)
solver_free.add_t1_relaxation(gamma=100)
result = solver_free.evolve(rho, (0, tau/2), n_steps=10)
rho = result.final_state()
# π refocusing pulse
result = solver.evolve(rho, (0, t_pi), n_steps=5)
rho = result.final_state()
# τ/2 free evolution
result = solver_free.evolve(rho, (0, tau/2), n_steps=10)
rho = result.final_state()
# π/2 readout
result = solver.evolve(rho, (0, t_pi2), n_steps=5)
rho = result.final_state()
pop = np.trace(projector_ms(0) @ rho).real
echo_signal.append(pop)
plt.figure(figsize=(10, 5))
plt.plot(tau_values * 1e6, echo_signal, 'b-o')
plt.xlabel("Total Evolution Time τ (μs)")
plt.ylabel("Echo Amplitude")
plt.title("Spin Echo Decay")
plt.show()
CPMG Dynamical Decoupling¶
Extend coherence with multiple refocusing pulses:
n_pulses_list = [1, 2, 4, 8, 16, 32]
tau_fixed = 100e-6 # Total evolution time
coherence_vs_n = []
for n in n_pulses_list:
tau_segment = tau_fixed / (2 * n)
# ... similar pulse sequence with n π-pulses ...
# (Implementation similar to spin echo but with loop)
# Placeholder for demonstration
# In reality, more pulses = better coherence preservation
T2_eff = 200e-6 * np.sqrt(n) # Approximate scaling
coherence = np.exp(-tau_fixed / T2_eff)
coherence_vs_n.append(coherence)
plt.figure(figsize=(8, 5))
plt.semilogy(n_pulses_list, coherence_vs_n, 'bo-')
plt.xlabel("Number of π-pulses")
plt.ylabel("Coherence")
plt.title("CPMG: Coherence vs Number of Pulses")
plt.grid(True)
plt.show()
XY8 Sequence for AC Sensing¶
The XY8 sequence is sensitive to AC fields at specific frequencies:
# Sensing frequency f_sense
# Maximum sensitivity when τ = 1/(2*f_sense)
f_sense = 1e6 # 1 MHz AC field
tau = 1 / (2 * f_sense) # 500 ns
# XY8 filter function peaks at f_sense and odd harmonics
freqs = np.linspace(0, 5e6, 1000)
filter_function = np.sinc(freqs * tau)**2 # Simplified
plt.figure(figsize=(10, 5))
plt.plot(freqs / 1e6, filter_function)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Sensitivity")
plt.axvline(f_sense / 1e6, color='r', linestyle='--', label='Target frequency')
plt.title("XY8 Filter Function")
plt.legend()
plt.show()