open-standards/wai/wai-quantum/docs/pulse.md
Transaction Science 13151abfd7 wai-quantum docs: the pulse page, the READMEs and the feature matrix
docs/pulse.md gives a pulse program, its schedule, its run as a job, its
check and a refusal, all run by the docs checker against the command line
(the receipt a block writes verified), and states the reader's constructs,
the model, the integrator and its bound, what a receipt binds, and the
options. The engine's and the command line's READMEs (with their Simplified
Chinese translations), the service's README, the docs index and the
OpenQASM 3 feature matrix name the pulse layer; the agent docs are
regenerated from the matrix. The reader's module documentation states the
qubit timeline a gate waits for.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 12:05:14 -04:00

7.6 KiB
Raw Permalink Blame History

Pulse and dynamics

A pulse program is OpenQASM 3 whose gates are given as the control signals that make them: OpenPulse calibrations (defcal) on physical qubits, played on frames of ports. The engine reads one into a schedule, integrates the schedule under a Lindblad master equation, and answers as a job of the method pulse: the result is the same bits on every platform, its error is bounded and stated, and its receipt binds the control program, its calibrations, the device and target snapshot it ran on, and the integrator and its step. A pulse result can be checked by anyone by running its request again.

This is a simulation. The engine reaches no device's pulse interface.

A program

OPENQASM 3.0;
defcalgrammar "openpulse";
cal {
  extern port d0;
  extern port a0;
  frame q0 = newframe(d0, 5.0e9, 0);
  frame c0 = newframe(a0, 5.0e9, 0);
}
defcal x $0 { play(q0, gaussian(0.4, 64dt, 16dt)); }
defcal measure $0 -> bit { return capture_v2(c0, 100dt); }
bit c;
x $0;
c = measure $0;
wai-quantum pulse schedule x.qasm --demo-key                              # each frame's timeline, the gate calls, the waveforms
wai-quantum run x.qasm --method pulse --shots 1000 --demo-key             # integrated: the capture's reading, the bound, what the receipt binds
wai-quantum run x.qasm --method pulse --shots 1000 --demo-key --json > job.json
wai-quantum pulse check job.json --demo-key                               # the receipt, the program's digest, and the run again, bit for bit
wai-quantum run x.qasm --method pulse --target synthetic-line-5 --demo-key   # the qubit's frequency, T1 and T2 from a target's calibration
wai-quantum run x.qasm --method pulse --pulse '{"levels": 3, "qubits": {"0": {"anharmonicity_mhz": -220}}}' --demo-key   # three levels: the leakage shown
wai-quantum run x.qasm --method pulse --pulse '{"tolerance": 1e-15}' --demo-key   # exits 1

The last run is refused with J041: the rounding of a schedule this long in double precision is estimated above the tolerance asked, and the message says so rather than giving a number it cannot stand behind.

What the reader takes

qasm::pulse runs the program in order, each calibration's body at its call:

  • Ports reach a qubit by name: d3 drives qubit 3, m3 is its measurement port and a3 its acquisition port. The job's pulse.ports maps other names ({"u0": {"kind": "drive", "qubit": 1}}).
  • Frames are made in cal blocks with newframe(port, frequency, phase) (hertz and radians). Each has a time of its own. A play starts at its frame's time and moves it on by the waveform's length; delay[d] f moves it on; barrier f, g moves both to the later. A gate starts when its qubits are free, all the frames its calibration names together.
  • Waveforms: constant, gaussian, sech, gaussian_square, drag and sine, and explicit samples {s0, s1, …}, real or complex. A template's sample k is taken at t = k − (l − 1)/2 samples, centred and not lifted; the exponential and the sine are the engine's own, so every sample is the same bits everywhere.
  • Frame changes: shift_phase, set_phase, shift_frequency and set_frequency. A frame's carrier runs on continuously across a frequency change.
  • Captures: capture and capture_v0 to capture_v4, each read as a discriminated bit, written to the program's bit when a measurement's calibration returns it.
  • Time is counted in samples of dt (pulse.dt_ns, 0.5 ns unless given). A duration in nanoseconds that is not a whole number of samples is refused (Q458).

What it does not take is refused with a code and its place: Q454 to Q461, listed in the diagnostic codes. The circuit methods keep the same calibrations verbatim and run none of them (Q450, Q451); the pulse reader runs them (Q453).

The model and the bound

quantum_pulse holds each qubit as a ladder of two or three levels in the frame rotating at its frequency, under the rotating-wave approximation, the envelope held for each sample. The drive is what the qubit's drive frames play, at their carriers' phases; at phase 0 a real envelope turns the qubit about +X. Relaxation and pure dephasing come from T1 and T2, and pairs of qubits may have a static ZZ coupling. A capture reads its qubit's state at its first sample and ends that qubit's schedule, since the readout's back-action is not modelled; plays on measurement ports are scheduled and timed and drive nothing.

Within a sample each driven qubit hears one tone, so in the frame of that tone the generator is constant. Each sample is a fixed number of steps of a Taylor series of order 12, then a diagonal phase back to the qubits' frames. Only + − × ÷ and sqrt are used, in a fixed order, so the density matrix is the same bits on x86_64, arm64 and in wasm32; CI pins its digest on each.

The bound: with λ an upper bound on the generator's norm, each step leaves out at most (hλ)^13/13! / (1 − hλ/14) of the state, and the exact evolution is a contraction in the trace norm, so the final state is within T(1 + T) of the exact solution of the model's equation, T the sum over every step. Every probability the result gives is within that, stated as accuracy.truncation_bound. Rounding is stated beside it as an estimate (accuracy.rounding_estimate), not a proof; accuracy.bound is their sum. A step longer than 1/λ, or a schedule whose bound and rounding would pass the tolerance, is refused (J041), naming the steps that would meet it.

The bound is the distance from the model's exact solution; it says nothing of how far the model is from a device. The tests hold the integrator to closed forms within the bound it states: a Rabi turn, the axis a quarter turn of phase gives, a detuned drive, T1 decay and T2 dephasing.

What a receipt binds

A pulse run's result holds, inside what its receipt seals:

Field What it is
pulse.calibration.program the BLAKE3 of the control program's text
pulse.calibration.calibrations the BLAKE3 of its cal, defcal and defcalgrammar statements as written
pulse.calibration.schedule the BLAKE3 of the schedule's JSON
pulse.calibration.device the BLAKE3 of the device's JSON (pulse.device, each parameter with where it came from)
pulse.calibration.target the target snapshot's name, hash and calibration id, when one is named
pulse.integrator the integrator, its order, its step and the steps taken
pulse.density_hash the BLAKE3 of the final density matrix's bits

wai-quantum pulse check, the pulse_check tool on both MCP servers and POST /v1/pulse/check verify the receipt, hold the program's digest to the request's text, and run the request again here, naming every field whose value differs. A pulse result that does not reproduce was not made from that request by this engine.

The options

The job's pulse object (every field optional): dt_ns, levels (2 or 3), tolerance (1e-6 unless given), substeps (chosen so each step times λ is at most 0.5 unless given), qubits (by index: frequency_ghz, rabi_mhz, anharmonicity_mhz, t1_us, t2_us), couplings ([{"qubits": [0, 1], "zz_khz": 50}]) and ports. A qubit's parameters come from pulse.qubits, then the target's calibration, then the defaults: 5 GHz plus 100 MHz per index, a Rabi frequency of 50 MHz at full amplitude, an anharmonicity of −300 MHz, and no decay.

The simulator holds a space of up to 16 (four qubits at two levels, two at three), a frame within 1 GHz of its qubit, one tone per qubit at a time, and schedules of up to 2²² samples.