Visualize and export the pulse schedule of a QICK
asm_v2 program before it is sent to an RFSoC, so you can confirm timing,
durations, amplitudes and sweeps are what you intended. Works online (connected)
and fully offline (from a saved config or a compiled-program pickle).
QCVT reads a compiled program (an AveragerProgramV2 instance) and turns it
into a sweep-aware Schedule, expressed entirely in microseconds, that drives
the plots and exports. Timing is taken directly from QICK's own time parameters
(t_params) and pulse lengths (get_length()), and Delay instructions are
accumulated to recover absolute times — so what you see matches what the board
plays, even across generators and readouts running at different clock rates.
Verified against qick 0.2.388, 0.2.406, and 0.2.422 (CI matrix). QCVT reaches
into qick internals (macro_list, t_params, pulse/envelope dicts); if a newer
qick release breaks extraction, pin to a verified version or open an issue.
pip install -e . # core: matplotlib, numpy
pip install -e ".[qick]" # also install qick (needed to build programs / load soccfg)
pip install -e ".[pickle]" # cloudpickle for loading compiled-program pickles
pip install -e ".[dev]" # pytest + qick + cloudpicklefrom qcvt import show_schedule, review_schedule, extract_schedule
prog = YourProgram(soccfg, reps=1, final_delay=0, cfg=config)
show_schedule(prog, title="My experiment") # interactive; no files written
# Pre-submit gate: save a PNG, optionally prompt before acquire()
ok = review_schedule(prog, save_dir="qcvt_reviews/my_exp", show=True, confirm=True)
if not ok:
raise RuntimeError("aborted")
# Verification: raise instead of silently skipping ambiguous macros
sched = extract_schedule(prog, strict=True)from qcvt import visualize_all
outputs = visualize_all(prog, out_dir="output/", title="Qubit spectroscopy",
show_amplitude=True)
# outputs -> {schedule_png, edges_state_csv, edges_state_png}from qcvt import visualize_from_pickle
prog, ax = visualize_from_pickle("compiled_program.pkl", output_path="schedule.png")from qcvt import save_soccfg_to_json, load_soccfg_from_json, show_schedule
# once, while connected:
save_soccfg_to_json(soc, "qick_config.json")
# later, offline:
soccfg = load_soccfg_from_json("qick_config.json")
prog = YourProgram(soccfg, reps=1, final_delay=0, cfg=config)
show_schedule(prog)See examples/run_offline_example.py for a complete, runnable example (it uses the
bundled examples/qick_config.json).
qcvt --pickle prog.pkl --out-dir ./out --show-amplitudeWrites schedule.png and edges_state.csv/.png. Use --no-table-png to skip the
table image, --strict for fail-fast extraction, and --no-suppress-off to keep
CW cleanup pulses whose names end in off.
- One lane per generator and per readout channel, on a shared microsecond axis.
- Each pulse as a labelled bar; readout integration windows as green bars.
- Periodic (CW) pulses hatched and extended to the next event on their channel.
- Swept parameters (time, length, gain) drawn as translucent ranges and tagged in the pulse label; an optional amplitude panel shows gain sweeps as a band.
- Swept-gain pulses are drawn at the sweep endpoint with the largest magnitude (QICK gains are signed, so sweeps like -0.6..0.6 work), making the pulse visible at its largest extent; the amplitude panel shows the full |gain| min→max band, which reaches 0 when a sweep crosses zero.
- Time origin: by default the axis is the absolute program timeline (which
includes any initial delay from
_initialize()). Passtime_origin="body"(CLI:--time-origin body) toplot_pulse_schedule,show_schedule,review_scheduleorvisualize_allto place t = 0 at the start of the loop body — matching how times read inside your_body(). This affects plots only; the state edge-matrix export always stays on the absolute timeline. - Correct amplitudes for all QICK pulse styles:
const— rectanglearb— curved envelopes (gaussian, DRAG, arbitrary I/Q) sampled at the DAC rateflat_top— rising ramp + plateau + falling ramp (QICK's three-segment convention)
- Multi-timescale programs (ns qubit pulses next to µs readout / ms CW):
- set
t0_us/max_time_usto zoom the viewing window - short pulses that would be invisible get a tick + duration callout
- when length dynamic range is large, an automatic zoom inset focuses on the short pulses
- set
| Function | Returns | Description |
|---|---|---|
show_schedule(prog, ...) |
None |
Interactive display (no files saved) |
review_schedule(prog, save_dir=..., ...) |
bool |
Pre-submit gate: save PNG, optional confirm/abort before acquire |
visualize_all(prog, out_dir, ...) |
dict |
Schedule PNG + state edge matrix + table PNG |
plot_pulse_schedule(prog, ...) |
ax or (ax, ax_amp) |
Draw the schedule (and optional amplitude panel) |
visualize_from_pickle(path, ...) |
(prog, ax) |
Load a compiled-program pickle and plot |
extract_schedule(prog, strict=False, suppress_off_pulses=True) |
Schedule |
Sweep-aware, microsecond schedule model |
export_edge_matrix_csv(prog, prefix, t0, t1, ...) |
str |
On/off state edge matrix CSV |
csv_to_table_png(csv, png, title) |
None |
Render a CSV as a highlighted table |
save_soccfg_to_json(soc, path) |
None |
Save RFSoC config for offline use |
load_soccfg_from_json(path) |
QickConfig |
Load config (requires qick) |
The package is organized into qcvt.model (schedule extraction), qcvt.plotting,
qcvt.export and qcvt.io.
- The program must be compiled (an
AveragerProgramV2compiles on construction). - A single iteration of each loop is drawn; swept values are annotated and their ranges shown rather than unrolled.
- Extraction is best-effort by default: a macro that fails to parse is skipped with a
warning rather than aborting the whole schedule. Pass
strict=True(or usewith strict_mode():) to raiseQCVTErrorinstead — preferred for pre-submit verification. Unhandled timed macros (anything witht_paramsthat QCVT does not recognize) warn or raise the same way; untimed macros (register ops, loop control, labels) are ignored by design. resync()advances the time reference by at most its argument (at runtime it appliesmax(0, t - elapsed)), so times drawn after aResyncare upper bounds. A warning is emitted (orQCVTErrorin strict mode) when a program contains one.- By default, non-periodic pulses whose names end in
off/turnoffare hidden when they share a timestamp with a periodic pulse on the same channel (a common CW cleanup convention). Passsuppress_off_pulses=Falseto keep them.
QICK separates compilation from execution: AveragerProgramV2 resolves all
timing at construction, in pure software, given only a QickConfig. The board
is required to play pulses, never to decide when they play.
pip install -e ".[dev]"
pytest tests/ -vtests/test_power_rabi_timing.py compiles a full Power_rabi program against the
bundled examples/qick_config.json and asserts pulse-to-pulse offsets to within
tProc cycle quantization. Any timing regression is catchable on a laptop.
MIT.
