Quickstart
This guide shows you how to go from a Qiskit 2.0 scheduled circuit to sample-precise I/Q waveforms and interactive pulse sheets in minutes.
Prerequisites
Python 3.11+
Qiskit 2.5+ (
pip install "qiskit>=2.5.0,<3")qiskit-waveform-sim (
pip install qiskit-waveform-sim)
Basic Workflow
Create a backend and circuit
from qiskit import QuantumCircuit
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(num_qubits=2, seed=42)
qc = QuantumCircuit(2, 2)
qc.h(0) # Decomposes to rz(π/2) → sx → rz(π/2)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
Transpile and schedule
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
scheduling_method="alap"
)
scheduled_qc = pm.run(qc)
Compile the waveform simulator
from qiskit_waveform_sim import TargetWaveformSimulator
sim = TargetWaveformSimulator(backend.target).compile(scheduled_qc)
Extract sample-precise I/Q snippets
# Get drive channel 0 waveform (first 2500 dt)
d0_snip = sim.get_snippet("d0", start_dt=0, length_dt=2500)
# Get cross-resonance channel waveform
cr_snip = sim.get_snippet("u(0,1)", start_dt=0, length_dt=2500)
# Access waveform data
print(f"Time points: {len(d0_snip.time_ns)}")
print(f"I component: {d0_snip.wave.real[:5]}")
print(f"Q component: {d0_snip.wave.imag[:5]}")
print(f"Frame phase: {d0_snip.phase_rad[:5]}")
Visualize interactively
from qiskit_waveform_sim.visualization import show_pulse_sheet, show_iq_oscilloscope
# Multi-channel Pulse Sheet (schedule view)
show_pulse_sheet(sim, interactive=True)
# Sample-precise I/Q Oscilloscope (waveform view)
show_iq_oscilloscope(sim, "d0", start_dt=0, length_dt=1000, interactive=True)
# Combined view
from qiskit_waveform_sim.visualization import create_combined_view
fig = create_combined_view(sim, "d0", start_dt=0, length_dt=1000)
fig.show()
Custom Pulse Annotations
Attach custom pulse shapes to specific gates using Qiskit 2.0’s BoxOp and Annotation API:
from qiskit_waveform_sim import PulseEnvelopeAnnotation
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
# Override SX gate with custom high-beta DRAG pulse
with qc.box(
duration=160,
unit="dt",
annotations=[PulseEnvelopeAnnotation(shape="drag", amp=0.55, beta=0.25, sigma_ratio=0.2)]
):
qc.sx(0)
qc.measure([0, 1], [0, 1])
# Transpile, schedule, and compile as before...
# The custom annotation will be automatically applied
Export to OpenQASM 3
Serialize circuits with pulse annotations to OpenQASM 3:
from qiskit_waveform_sim import PulseAnnotationSerializer
from qiskit import qasm3
qasm3_str = qasm3.dumps(
scheduled_qc,
annotation_handlers={"pulse_sim.envelope": PulseAnnotationSerializer()}
)
print(qasm3_str)
Next Steps
Tutorial: From Qiskit 2.0 Scheduled Circuit to Frame-Tracked I/Q Waveforms - Complete notebook tutorial
Tutorial: Custom Pulse Annotations and OpenQASM 3 Export - Deep dive into custom pulse annotations
Tutorial: Hardware Export - LabOne Q, RFSoC/QICK, and ARTIQ - Export to LabOne Q, RFSoC, QICK
Core API Reference - Full API reference