Pulses Module¶
The pulses module provides tools for creating microwave pulse sequences for coherent spin control.
Basic Pulses¶
Basic pulse definitions for NV center control.
Provides π and π/2 pulses for standard quantum gates.
Rotation Convention¶
A pulse with Rabi frequency Ω, duration τ, and phase φ performs the rotation:
R_φ(θ) = exp(-i θ/2 (cos(φ)σ_x + sin(φ)σ_y))
where θ = Ω τ is the rotation angle.
- class sim.pulses.basic.Pulse(shape, amplitude, duration, phase, kwargs=None)[source]¶
Bases:
objectRepresentation of a single MW pulse.
- sim.pulses.basic.pi_pulse(rabi_freq_mhz=1.0, axis='x', shape='rect', **kwargs)[source]¶
Create a π pulse (180° rotation).
- Parameters:
- Returns:
π pulse with calculated duration
- Return type:
Examples
>>> p = pi_pulse(rabi_freq_mhz=10) >>> print(f"Duration: {p.duration*1e9:.1f} ns") Duration: 50.0 ns
>>> # Y-axis rotation >>> p_y = pi_pulse(rabi_freq_mhz=20, axis='y')
- sim.pulses.basic.pi_half_pulse(rabi_freq_mhz=1.0, axis='x', shape='rect', **kwargs)[source]¶
Create a π/2 pulse (90° rotation).
- Parameters:
- Returns:
π/2 pulse with calculated duration
- Return type:
Examples
>>> p = pi_half_pulse(rabi_freq_mhz=10) >>> print(f"Duration: {p.duration*1e9:.1f} ns") Duration: 25.0 ns
- sim.pulses.basic.arbitrary_rotation(theta, phi=0, rabi_freq_mhz=1.0, shape='rect', **kwargs)[source]¶
Create a pulse for arbitrary rotation R_φ(θ).
- Parameters:
- Returns:
Arbitrary rotation pulse
- Return type:
Examples
>>> # π/4 rotation around Y >>> p = arbitrary_rotation(theta=np.pi/4, phi=np.pi/2)
- sim.pulses.basic.composite_pi_pulse(rabi_freq_mhz=1.0, pulse_type='BB1', shape='rect', **kwargs)[source]¶
Create a composite π pulse for error compensation.
- Parameters:
- Returns:
Sequence of pulses comprising the composite pulse
- Return type:
Pulse Class¶
from sim.pulses import Pulse
# Create a pulse
pulse = Pulse(
shape="gaussian", # Envelope shape
amplitude=10, # Rabi frequency in MHz
duration=50e-9, # Duration in seconds
phase=0 # Phase in radians
)
# Access properties
print(pulse.area) # Rotation angle (rad)
print(pulse.is_pi_pulse) # True if area ≈ π
Standard Pulses¶
from sim.pulses import pi_pulse, pi_half_pulse
# π-pulse (180° rotation)
pi = pi_pulse(rabi_freq_mhz=10, axis='x', shape='rect')
# π/2-pulse (90° rotation)
pi2 = pi_half_pulse(rabi_freq_mhz=10, axis='y', shape='gaussian')
Pulse Shapes¶
Pulse envelope shapes for NV center control.
All shape functions return normalized envelopes (max = 1). The actual amplitude is set when creating the pulse.
Available Shapes¶
rectangular : Constant amplitude, sharp edges gaussian : Smooth Gaussian envelope sinc : Sin(x)/x for broadband excitation blackman : Window function with low side lobes drag : DRAG pulse for leakage reduction hermite : Hermite-Gaussian shapes
- sim.pulses.shapes.rectangular(t, duration)[source]¶
Rectangular (square) pulse envelope.
- Parameters:
t (np.ndarray) – Time array in seconds
duration (float) – Pulse duration in seconds
- Returns:
Envelope values (1 inside pulse, 0 outside)
- Return type:
np.ndarray
- sim.pulses.shapes.gaussian(t, duration, sigma=None, truncate=3.0)[source]¶
Gaussian pulse envelope.
- Parameters:
- Returns:
Gaussian envelope (normalized to max 1)
- Return type:
np.ndarray
- sim.pulses.shapes.sinc_pulse(t, duration, zero_crossings=3)[source]¶
Sinc pulse envelope (sin(x)/x).
Good for broadband excitation with controlled frequency profile.
- sim.pulses.shapes.blackman(t, duration)[source]¶
Blackman window pulse envelope.
Very low side lobes, good for frequency selectivity.
- Parameters:
t (np.ndarray) – Time array in seconds
duration (float) – Total pulse duration in seconds
- Returns:
Blackman envelope (normalized to max 1)
- Return type:
np.ndarray
- sim.pulses.shapes.drag(t, duration, sigma=None, beta=0.5)[source]¶
DRAG (Derivative Removal by Adiabatic Gating) pulse.
Reduces leakage to non-computational states by adding a derivative component in quadrature.
- Parameters:
- Returns:
(I_component, Q_component) arrays
- Return type:
Notes
I channel: Gaussian Q channel: Derivative of Gaussian (scaled by beta)
- sim.pulses.shapes.hermite(t, duration, order=0, sigma=None)[source]¶
Hermite-Gaussian pulse envelope.
Higher orders have more oscillations and are useful for selective excitation.
- Parameters:
- Returns:
Hermite-Gaussian envelope (normalized to max 1)
- Return type:
np.ndarray
- sim.pulses.shapes.cosine(t, duration)[source]¶
Cosine (Hann) window pulse envelope.
- Parameters:
t (np.ndarray) – Time array in seconds
duration (float) – Total pulse duration in seconds
- Returns:
Cosine envelope
- Return type:
np.ndarray
- sim.pulses.shapes.tanh_edges(t, duration, rise_time=0.1)[source]¶
Pulse with smooth tanh rise and fall.
Available Shapes¶
"rect"or"rectangular": Square pulse"gaussian": Gaussian envelope"drag": DRAG pulse (derivative removal by adiabatic gate)"blackman": Blackman window"flattop": Flat top with smooth edges
from sim.pulses.shapes import get_envelope
t = np.linspace(0, 100e-9, 1000)
# Gaussian envelope
env = get_envelope("gaussian", t, duration=50e-9, sigma=10e-9)
# DRAG correction
env_drag = get_envelope("drag", t, duration=50e-9, beta=0.5)
Pulse Sequences¶
Standard pulse sequences for NV center experiments.
Implements common sequences for coherence measurements and dynamical decoupling.
Sequence Types¶
- Ramsey: Free induction decay (T2*)
π/2 - τ - π/2
- Spin Echo: Hahn echo (T2)
π/2 - τ/2 - π - τ/2 - π/2
- CPMG: Carr-Purcell-Meiboom-Gill (extended coherence)
π/2 - [τ/2 - π_Y - τ/2]^N - π/2
- XY4/XY8: Dynamical decoupling (robust to pulse errors)
π/2 - [τ - π_X - τ - π_Y - τ - π_X - τ - π_Y]^N - π/2
- class sim.pulses.sequences.PulseSequence(pulses=<factory>, delays=<factory>)[source]¶
Bases:
objectCollection of pulses with timing.
Examples
>>> seq = PulseSequence() >>> seq.add_pulse(pi_half_pulse(10), delay=1e-6) >>> seq.add_pulse(pi_pulse(10), delay=1e-6) >>> seq.add_pulse(pi_half_pulse(10)) >>> print(f"Total duration: {seq.total_duration()*1e6:.1f} μs")
- to_awg(awg)[source]¶
Load the sequence into an AWG interface.
- Parameters:
awg (AWGInterface) – AWG interface to load into
- sim.pulses.sequences.ramsey_sequence(tau, rabi_freq_mhz=1.0, final_phase=0.0, shape='rect')[source]¶
Create a Ramsey sequence: π/2 - τ - π/2.
- Parameters:
- Returns:
Ramsey sequence
- Return type:
Examples
>>> seq = ramsey_sequence(tau=1e-6, rabi_freq_mhz=10) >>> print(seq.total_duration())
- sim.pulses.sequences.spin_echo_sequence(tau, rabi_freq_mhz=1.0, shape='rect')[source]¶
Create a spin echo (Hahn echo) sequence: π/2 - τ/2 - π - τ/2 - π/2.
- Parameters:
- Returns:
Spin echo sequence
- Return type:
- sim.pulses.sequences.cpmg_sequence(tau, n_pulses, rabi_freq_mhz=1.0, shape='rect')[source]¶
Create a CPMG sequence: π/2 - [τ - π_Y - τ]^N - π/2.
- Parameters:
- Returns:
CPMG sequence
- Return type:
- sim.pulses.sequences.xy4_sequence(tau, n_cycles=1, rabi_freq_mhz=1.0, shape='rect')[source]¶
Create an XY4 dynamical decoupling sequence.
Structure: π/2 - [τ - π_X - τ - π_Y - τ - π_X - τ - π_Y]^N - π/2
- Parameters:
- Returns:
XY4 sequence
- Return type:
Notes
XY4 is robust to pulse flip-angle errors due to the alternating X and Y phases.
- sim.pulses.sequences.xy8_sequence(tau, n_cycles=1, rabi_freq_mhz=1.0, shape='rect')[source]¶
Create an XY8 dynamical decoupling sequence.
- Structure: π/2 - [τ - π_X - τ - π_Y - τ - π_X - τ - π_Y -
τ - π_Y - τ - π_X - τ - π_Y - τ - π_X]^N - π/2
- Parameters:
- Returns:
XY8 sequence
- Return type:
Notes
XY8 is more robust than XY4 due to the symmetric phase pattern.
- sim.pulses.sequences.rabi_sequence(duration, rabi_freq_mhz=1.0, shape='rect')[source]¶
Create a Rabi oscillation sequence (single pulse of variable duration).
- Parameters:
- Returns:
Single-pulse Rabi sequence
- Return type:
PulseSequence Class¶
from sim.pulses import PulseSequence, pi_pulse, pi_half_pulse
seq = PulseSequence()
seq.add_pulse(pi_half_pulse(10), delay=1e-6)
seq.add_pulse(pi_pulse(10), delay=1e-6)
seq.add_pulse(pi_half_pulse(10))
print(seq.total_duration())
Standard Sequences¶
Ramsey¶
Free induction decay (T₂* measurement):
from sim.pulses.sequences import ramsey_sequence
seq = ramsey_sequence(
tau=1e-6, # Free evolution time
rabi_freq_mhz=10, # π/2 pulse Rabi frequency
final_phase=0 # Phase of final π/2
)
Spin Echo (Hahn Echo)¶
Refocuses static inhomogeneities (T₂ measurement):
from sim.pulses.sequences import spin_echo_sequence
seq = spin_echo_sequence(
tau=100e-6, # Total evolution time
rabi_freq_mhz=10
)
CPMG¶
Extended coherence with multiple refocusing pulses:
from sim.pulses.sequences import cpmg_sequence
seq = cpmg_sequence(
tau=10e-6, # Inter-pulse delay
n_pulses=8, # Number of π pulses
rabi_freq_mhz=10
)
XY4¶
Dynamical decoupling robust to pulse errors:
from sim.pulses.sequences import xy4_sequence
seq = xy4_sequence(
tau=1e-6, # Inter-pulse delay
n_cycles=4, # Number of XY4 cycles
rabi_freq_mhz=10
)
XY8¶
More robust variant of XY4:
from sim.pulses.sequences import xy8_sequence
seq = xy8_sequence(
tau=1e-6,
n_cycles=2,
rabi_freq_mhz=10
)
Rabi Oscillation¶
Single pulse of variable duration:
from sim.pulses.sequences import rabi_sequence
seq = rabi_sequence(
duration=100e-9, # Pulse duration
rabi_freq_mhz=10
)
Custom Sequences¶
Build arbitrary sequences:
from sim.pulses import PulseSequence, Pulse
def my_sequence(tau, n_pulses, rabi):
seq = PulseSequence()
# Initial π/2
seq.add_pulse(
Pulse(shape="gaussian", amplitude=rabi,
duration=25e-9, phase=0),
delay=tau
)
# Refocusing pulses
for i in range(n_pulses):
phase = np.pi/2 if i % 2 == 0 else 0
seq.add_pulse(
Pulse(shape="gaussian", amplitude=rabi,
duration=50e-9, phase=phase),
delay=tau
)
# Final π/2
seq.add_pulse(
Pulse(shape="gaussian", amplitude=rabi,
duration=25e-9, phase=0)
)
return seq
AWG Integration¶
Load sequences into an AWG interface:
from sim.interfaces import AWGInterface
awg = AWGInterface(sample_rate=1e9)
seq.to_awg(awg)
waveform = awg.get_waveform()