Skip to content

QProgram Qblox

Qblox extensions for QProgram, a Python DSL for pulse-level quantum experiments.

The core DSL describes what is portable across instruments. This package describes what a Qblox cluster adds on top: acquisitions without a readout pulse, marker and trigger lines, and thresholded-acquisition setup on the QCM and QRM sequencers. Importing the package registers all of it. Nothing in the core changes.

What the qblox namespace gives you

Every operation is reached as program.qblox.<name>(...) and writes one qblox.<name> line into a .qp file.

Operation What it does
acquire Integrate a readout bus against weights without playing a pulse. Returns a MeasurementHandle.
set_markers Drive the four marker outputs from a 4-character mask, for example "0001".
set_trigger Emit a trigger of a given duration on selected outputs, at the start or end of the operation.
wait_trigger Block the sequencer until a trigger arrives on an input port, up to a timeout.
set_acquisition_threshold Set the state-discrimination threshold of a readout bus.
set_acquisition_rotation Rotate the integrated IQ point before it meets the threshold. Radians, as everywhere in the DSL.

The last two take effect off the sequencer, as slow-control parameter writes at execution time. Both accept an Expression, so a loop can sweep them the way a discrimination is normally calibrated.

Alongside the operations, the package ships QBLOX_DEFAULT_V1, the capability profile that says what a qblox-driven bus accepts, and QbloxMixin, the typed property that gives editors something to complete.

A first program

import math

import qprogram as qp
from qprogram.buses import BusSchema
from qprogram.waveforms import IQDrag, IQPair, Square

from qprogram_qblox import QProgram  # qprogram.QProgram with a typed .qblox namespace

schema = BusSchema.transmon()
q = schema.q

program = QProgram(label="rotation_calibration", schema=schema)
angle = program.variable("angle", units="rad")

with program.average(shots=1000):
    with program.sweep(angle).from_linspace(0.0, math.pi, 21):
        program.qblox.set_acquisition_rotation(q[0].readout, angle)
        program.play(q[0].drive, "pi_pulse")
        program.sync()
        m0 = program.qblox.acquire(q[0].readout, "weights", fields=(qp.MeasurementField.STATE,))

# Bind calibrated waveforms at the very end; the program itself only names them.
resolved = program.with_waveforms(
    {
        "pi_pulse": IQDrag(amplitude=0.5, duration=40, sigma=8, beta=0.1),
        "weights": IQPair(Square(1.0, 2000), Square(1.0, 2000)),
    }
)

result = qp.simulate(resolved)
population = result.get(m0, field=qp.MeasurementField.STATE)
print(population.dims, population.shape)  # ('angle',) (21,)

qp.simulate is the reference software executor from the core package. It runs qblox operations generically: qblox.acquire produces measurement records, the rest leave the simulated outcome alone. A Qblox platform is a drop-in for the same call and lowers each operation onto real sequencers.

Where to go next

If you want to ... Read
install the package and run something Getting started
see every operation and its arguments Operations
know which programs a qblox platform accepts Capabilities and profiles
read and write .qp files that use qblox operations Saving and loading
browse the generated API API reference
turn these operations into sequencer code Lowering onto hardware
work on this package Contributing

How it plugs in

The core package defines three hooks and this one uses all three at import time: a runtime namespace registered with QProgram.register_vendor, a typed mixin for editors, and the serialization registry that maps each operation class to its .qp spelling. A fourth line, the qprogram.vendors entry point in pyproject.toml, lets qprogram.load import this package on demand when a file's header carries require qblox 0.1.

Because all four are declarations rather than patches, the same program can also carry operations from other vendor packages. Getting started shows how to combine them.

Status

The package is pre-1.0 and tracks the core DSL, so the Python API is allowed to move. The wire format is steadier: a .qp file that requires qblox 0.1 loads against any installed version that shares its major number and is no older in minor, and anything else is a ParseError rather than a silent partial load.