Skip to content

QProgram QDAC

qprogram-qdac adds QDevil QDAC support to QProgram, the pulse-level quantum programming DSL. The QDAC is a slow high-precision DAC, used most often for flux biasing.

The core DSL knows nothing about any instrument. This package plugs into it through the vendor hooks and contributes three things: a qdac namespace on every program, a capability profile describing what a QDAC channel can do, and .qp serialization for the operations it adds. Importing the package is what turns those on.

What the qdac namespace gives you

Operation What it does
program.qdac.set_offset Hold a DC voltage on a channel until something changes it. The value may be a swept variable.
program.qdac.play Upload an envelope to the channel's waveform engine, with dwell, delay, repetitions, stepped.
program.qdac.set_trigger Arm chassis trigger outputs to fire at a sequence position, for a given duration.
program.qdac.wait_trigger Halt the channel's sequencer until an external trigger arrives on an input port.

Two facts about the instrument are declared in the profile rather than left in a comment. The QDAC has no FPGA, so a swept parameter is re-uploaded from the host between iterations, and qdac-default-v1 puts the enclosing loop host-side. A QDAC channel is single-channel, so the profile carries no IQ waveform tokens at all: an IQ pulse on a QDAC bus is a missing-capability error from qp.validate.

A first program

import qprogram as qp
from qprogram import BusSchema
from qprogram.waveforms import Ramp
from qprogram_qdac import QProgram

schema = BusSchema.flux_tunable_transmon()
flux = schema.q[0].flux

program = QProgram(label="flux-ramp", schema=schema)
program.qdac.set_offset(flux, 0.42)
program.qdac.set_trigger(flux, duration=50, position="start", outputs={1, 2})
program.qdac.play(flux, Ramp(from_amplitude=0.0, to_amplitude=1.0, duration=1000), dwell=100)
program.qdac.wait_trigger(flux, port=3)

print(qp.dumps(program))

That prints the program as .qp text:

#!QProgram 1.0

require qdac 0.1

metadata:
  label: "flux-ramp"

schema:
  element q:
    drive info=IQ
    readout info=IQ+acquires
    flux info=single

body:
  qdac.set_offset q[0].flux 0.42
  qdac.set_trigger q[0].flux 50 outputs=[1, 2]
  qdac.play q[0].flux Ramp(from_amplitude=0.0, to_amplitude=1.0, duration=1000) dwell=100
  qdac.wait_trigger q[0].flux 3

The require qdac 0.1 line is the whole compatibility story. qp.load checks it against the installed extension, and imports this package on demand if the reader never did.

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 QDAC platform will accept Capabilities and profiles
read the .qp wire form of each operation Saving and loading
browse the generated API API reference
map the operations onto QDAC hardware Lowering onto hardware
work on this package Contributing

Status

The vendor protocol version is this package's own version, and .qp files carry it as require qdac <major>.<minor>. A file loads against any installed qprogram-qdac that shares its major version and is no older in minor. Anything else is a ParseError rather than a partial load. The Python API is pre-1.0 and allowed to move; the wire form is the steadier of the two.