See also
A Jupyter notebook version of this tutorial can be downloaded here.

[1]:
from qblox_scheduler import HardwareAgent
Getting started with transmon qubits#
In this notebook we will discuss how to get started with transmon qubits using Qblox hardware. The goal of this tutorial is to introduce the most important elements in the hardware- and Device-Under-Test (DUT) configuration files for this QPU architecture. This will be done by taking snippets of the configuration files inside the dependencies/configs folder and discussing the most important concepts. For clarity some of
the details have been omitted, we refer to the configuration files themselves in combination with other tutorials for specific elements of the configuration files.
We consider a simple system containing two capacitively coupled transmons connected to readout resonators.
The hardware configuration file#
To operate the QPU, we will use a cluster with a QRM-RF (slot 4) for microwave readout, a QCM-RF (slot 6) for microwave control and a QCM (slot 14) for optional flux control. If using a “dummy” cluster, this may be specified in the “hardware description” part of the
hw_config.json file as follows. Note that specifying the modules is not required while using an actual instrument.
[2]:
hardware_description = {
"cluster": {
"instrument_type": "Cluster",
"modules": {
"4": {"instrument_type": "QRM_RF"},
"6": {"instrument_type": "QCM_RF"},
"14": {"instrument_type": "QCM"}, # Only required for flux tunable transmons
},
"ref": "internal",
"ip": None,
}
}
In the cell above, the line "ref": "internal" indicates that the cluster will use its own internal 10MHz clock instead of an external one. For setups involving multiple clusters it is recommended to use an external source instead, to avoid timing mistakes during measurements.
The ip-address of the instrument may be specified in the corresponding line. When no ip-address is provided a dummy cluster will be used, which may be used to test the compilation of schedules or other tasks that do not require an actual instrument. To connect to a cluster for the first time and find out its ip-address, check out this tutorial.
After the layout of the cluster has been determined, the connectivity from the cluster to the QPU has to be specified. This is done in the “connectivity” part of the hardware configuration as follows.
[3]:
connectivity = {
"graph": [
["cluster.module4.complex_output_0", "q0:res"], # port notation = <qubit_name>:<port_name>
["cluster.module4.complex_output_0", "q2:res"], # Readout output
["cluster.module4.complex_input_0", "q0:res"],
["cluster.module4.complex_input_0", "q2:res"], # Readout input
["cluster.module6.complex_output_0", "q0:mw"],
["cluster.module6.complex_output_1", "q2:mw"], # MW control
["cluster.module14.real_output_0", "q0:fl"],
["cluster.module14.real_output_2", "q2:fl"], # (Optional) flux control
]
}
In the cell above, in- and outputs of the different modules are linked to qubit ports, written using the syntax <qubit_name>:<port_name>. Note that the qubit- and port names have to be separated by a colon. Making this connection allows the user to operate the instrument by making reference to their QPU, instead of keeping in mind how they connected the cluster.
Note: from this connectivity section, we can see that multiplexing is used to read out two qubits using a single QRM-RF. Because a single mixer is used for the multiplexing, the same Local Oscillator (LO) frequency has to be used for all qubits addressed on this port.
Finally, there are some hardware options that have to be specified prior to starting experiments, related to the analog settings of the instrument.
[4]:
hardware_options = (
{
"output_att": {
"q0:mw-q0.01": 30, # clock notation = <qubit_name>.<clock_name>
"q2:mw-q2.01": 30, # port-clock notation = <port_notation>-<clock_notation>
"q0:res-q0.ro": 34,
"q2:res-q2.ro": 34,
},
"modulation_frequencies": {
"q0:mw-q0.01": {"interm_freq": 80.0e6},
"q2:mw-q2.01": {"interm_freq": 80.0e6},
"q0:res-q0.ro": {"lo_freq": 7.6e9},
"q2:res-q2.ro": {"lo_freq": 7.6e9},
},
},
)
Here, we have introduced the clock notation of <qubit_name>.<clock_name>, where in this case the two names have to be separated by a point. In a similar fashion we define the port-clock pair as <port_notation>-<clock_notation>, where now the names are separated by a hyphen. This final notation allows us to specify analog settings of the instrument while sending signals to multiple qubits from a single module. In the cell above, the following hardware options are specified.
output_attspecifies the analog output attenuation for a specific port and clock.modulation_frequenciesallows the user to either specify the frequency of the LO or the intermediate frequency of the NCO for RF modules. The remaining value will be set automatically by the instrument, according toqubit_frequency = interm_freq + lo_freq.
Combining the contents of the three code cells above yields the hardware configuration file in ./dependencies/configs/hw_config.json and allows for operations of our simple two-qubit QPU using Qblox hardware.
The device-under-test configuration file#
Complementary to the hardware configuration file is the device-under-test (DUT) configuration file. Where the hardware configuration file is used to make the hardware agent aware of the physical layout of the lab (connectivity, fridge layout, etc.), the DUT configuration makes the hardware agent aware of the layout of the QPU and its properties.
The first component of a DUT configuration file contains the “elements” of the device under test.
[5]:
elements = {
"q0": {
"name": "q0",
"element_type": "BasicTransmonElement",
"reset": {"duration": 100e-6, "name": "reset"},
"rxy": {"amp180": 0.12, "duration": 50e-9, "name": "rxy"},
"measure": {"pulse_amp": 0.05, "pulse_duration": 5e-06, "name": "measure"},
"clock_freqs": {"f01": 4.3e9, "readout": 7.4e9, "name": "clock_freqs"},
},
"q2": {...},
}
Here, key-value pairs contain descriptions of the individual (isolated) components of the quantum device, such as qubits. Each value is another dictionary, containing details of the device element. In this case the qubit with the name q0 is a BasicTransmonElement, which is a class containing a description of a general transmon qubit. The rest of the key-value pairs specify parameter settings that will be compiled when a gate is created on that particular device element, as follows.
reset: a passiveResetgate will have a duration of 100 us.rxy: aXgate will have an amplitude of 0.12 a.u. and last for 50 ns.measure: aMeasureoperation will play a pulse with amplitude 0.05 a.u., for a duration of 5 us.
The clock_freqs specify the frequencies of the qubit, such as the the readout frequency or the frequency of its \(\ket{0} \rightarrow \ket{1}\) transition.
In addition to describing the individual elements it is required that we specify how elements are connected to each other on the QPU, this is done using edges.
[6]:
edges = {
"q0_q2": {
"name": "q0_q2",
"edge_type": "CompositeSquareEdge",
"cz": {"square_amp": 0.1, "square_duration": 4e-08, "name": "cz"},
},
"q2_q0": {...},
}
Here key-value pairs contain descriptions of the different inter-element connections on the QPU, for example a connection between qubits q0 and q2. In this case the edge with the name q0_q2 implements a CompositeSquareEdge, which is a class containing a description of a generic connection between two qubits of the BasicTransmonElement type. This particular edge implements a Controlled Z (CZ) rotation by playing a pulse over the flux line of one of the qubits. The final line
specifies the parameter settings that will be compiled when a two-qubit gate is created between qubits q0 and q2. In this case when a CZ gate is created between qubits q0 and q2, it will have an amplitude of 0.1 a.u. and a duration of 40ns.
When combined inside a single .json or .yaml file the elements and edges together make up a complete description of the QPU. This allows the user to add gates operating on qubits to their schedule, as opposed to having to keep track of pulse-level instructions like amplitude or duration. The device-level description can be flexibly extended to suit the user’s needs, for example by adding additional qubit types or inter-qubit connections such as couplers.
The HardwareAgent#
The HardwareAgent is a software representation of the complete experimental setup, meant to conveniently run experiments by (for example) managing pulse delays, output paths and amplitude settings. Using the configuration files discussed above, we can now create an agent that is aware of the physical setup in the lab and properties of the QPU. Create a new HardwareAgent by running the cell below.
[7]:
# Create a new hardware agent based on the configuration files discussed above
hw_agent = HardwareAgent(
hardware_configuration="./dependencies/configs/hw_config.json",
quantum_device_configuration="./dependencies/configs/dut_config.json",
)
# Create an alias for a specific element of the DUT to convenienty interact with its properties
qubit = hw_agent.quantum_device.get_element("q0")
Throughout experiments it is possible to overwrite (gate) parameter settings of the DUT configuration file. For example when configuring the pi-pulse amplitude, the user may want to store a new value to use it in later experiments. This is done in the following way.
[8]:
# Change the amplitude of the pi pulse from 0.12 defined above to 0.15
qubit.rxy.amp180 = 0.15
Afterwards, the new values may be stored inside a new .yaml or .json file by running the following cell.
[9]:
# Store the (changed) DUT configuration file in a new file
hw_agent.quantum_device.to_json_file("./dependencies/configs", add_timestamp=True)
[9]:
'./dependencies/configs/two_flux_tunable_transmons_2025-10-30_00-38-20_UTC.json'
With this information, we are ready to start experiments on superconducting transmon qubits. We recommend starting with a time of flight calibration, to configure the appropriate acquisition delay based on the length of the cables in the setup. If this has been configured previously, we may get started with resonator spectroscopy to find the readout frequencies of the qubit.