Device Setup¶
Components and Purpose¶
The device setup class in LabOne Q allows you to represent all information on the hardware of your experimental setup. This includes
- Instruments i.e. Zurich Instruments' control electronics including
- Serial numbers of the instruments
- User-defined ids to reference each instrument
- Upgradable device options
- Connections between instruments for signal synchronization (e.g. ZSync)
- Definition of signal lines, qubits, etc.
- Signal types
- Logical signal lines
- Physical ports
- Other connection options
- (Optimal) Information on LabOne data server information used to connect to the instruments
- User-defined id to reference the data server
- IP address
- Port numbers
Additional electronics may be part of the setup, such as DC sources or radio-frequency signal generators. These are not included in the descriptor since LabOne Q does not provide drivers for these instruments. Nevertheless, LabOne Q provides an interface to control such instruments in the normal workflow via user-defined callback functions.
Usage Scenarios and Learning Goals¶
When using LabOne Q to run experiments, one will typically define a DeviceSetup
instance once in the very beginning and later only load and modify such objects when needed.
The following sections of this tutorial will guide you through the technical details of the device setup class and demonstrate how to define and use its components. You will find these concepts likely useful when...
- creating
DeviceSetup
objects whose components you need to alter often within your notebook - or when defining large device setups programmatically at scale
After you gained some familiarity with the device setup class itself we will learn the details of the device descriptor. The device descriptor is a string-based input format the you can use to...
- load and/or store an initial definition of a
DeviceSetup
object (without any calibration settings applied) - or initialize device setups of typically small to medium scale
In the following we will examine examples of different device setups and finally take a look at some device setup templates that can be loaded and used for as starting point and for prototyping.
Initialization and Data Server¶
After importing the DeviceSetup
class from the standard LabOne Q import ...
from laboneq.simple import *
you can instantiate a device setup object ...
device_setup = DeviceSetup("ZI_QCCS")
and then add information about the data server the instruments of this setup are connected to.
device_setup.add_dataserver(
host="111.22.33.44",
port="8004",
)
Instrument Objects¶
The instruments of a device setup are represented by instances of a corresponding instrument class. These objects contain
- a user-defined id
- the serial number of the instrument
- the specific model and upgradeable options (see the documentation)
- incoming and outgoing connections (see below)
The following example shows how to instantiate a SHFQC+ qubit controller in 6-channel configuration with enabled Output Router and Adder option.
SHFQC(uid="shfqc", address="dev12345", device_options="SHFQC6/RTR/16W")
The add_instruments
functionality can be used to include such instrument objects in the device setup instance.
With the following example we assemble a device setup consisting of a SHFSG, a SHFQA, and a PQSC using the internal reference clock of the latter.
device_setup.add_instruments(
SHFQA(uid="shfqa", address="dev12000", device_options="SHFQA4"),
SHFSG(uid="shfsg", address="dev12001", device_options="SHFSG8/RTR"),
PQSC(uid="pqsc", address="dev10001", reference_clock_source="internal"),
)
Note that adding instruments with an existing id is not permitted.
try:
device_setup.add_instruments(PQSC(uid="pqsc"))
except:
print("LabOneQException as expected: Instrument already in device setup")
Sampling Rates¶
The sampling rate of the instruments in a setup depends on the specific combination of instruments it comprises.
Setup | Sampling rates |
---|---|
HDAWG + UHFQA | HDAWG: 2.4 GSa/s, UHFQA: 1.8 GSa/s |
HDAWG + SHF instruments | 2.0 GSa/s |
UHFQA + SHF instruments | not supported |
Connections¶
Connections represent either internal connections between instruments (ZSYNC) or signal connections to the quantum element (e.g. logical signal lines to qubits).
Connection objects can be generated with the create_connection
helper function ...
drive_line = create_connection(
to_signal="q0/drive", ports="SGCHANNELS/0/OUTPUT", type="iq"
)
and added to a device setup instance with the add_connection
function.
device_setup.add_connections("shfsg", drive_line)
Note, the following
- The connection's origin is specified in the
add_connection function
while it's target (here: the logical signal line) is an argument ofcreate_connection
. - When defining connections for a specific instrument/port refer to this table for possible options
Multiple connections from the same instrument can be added at once.
device_setup.add_connections(
"shfqa",
create_connection(to_signal="q0/measure", ports="QACHANNELS/0/OUTPUT", type="iq"),
create_connection(
to_signal="q0/acquire", ports="QACHANNELS/0/INPUT", type="acquire"
),
)
Defining internal ZSYNC connections is no longer necessary and should be avoided.
Note, that a common pitfall is a mismatch between the ZSYNC port definition in the device setup and the physical wiring on the PQSC. If the specified port and instrument pairs do not match the actual setup configuration, the software will return an error.
Accessing Objects in the Device Setup¶
The device setup allows to retrieve individual instruments...
device_setup.instrument_by_uid("shfqa")
or specific logical signal lines.
device_setup.logical_signal_by_uid("q0/measure")
In the next tutorial you will furthermore learn about the Calibration
object that is used to apply settings to instrument nodes.
The get_calibration
functionality of the device setup allows to retrieve such an objects containing the current settings.
device_setup.get_calibration()
Device Descriptor¶
The device descriptor is a string format that you can use to define an initialize a device setup.
The following shows an example of a device descriptor and how it is used to construct a DeviceSetup
instance equivalent to that constructed programmatically above.
Also see the options supported by the device descriptor format for a complete list.
descriptor = """\
instruments:
PQSC:
- address: dev10001
uid: pqsc
options: PQSC
SHFQA:
- address: dev12000
uid: shfqa
options: SHFQA4
SHFSG:
- address: dev12001
uid: shfsg
options: SHFSG8/RTR
connections:
shfsg:
- iq_signal: q0/drive
ports: SGCHANNELS/0/OUTPUT
shfqa:
- iq_signal: q0/measure
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q0/acquire
ports: QACHANNELS/0/INPUT
"""
device_setup_from_descriptor = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
)
Note, that instrument options are passed via the keyword options
.
This will become mandatory in future versions of LabOne Q.
See the available instrument options for details.
Storing and Loading Device Setups¶
The save
functionality can be used to serialize a DeviceSetup
instance into the human readable json format and store it to a file.
from laboneq.simple import save, load
save(device_setup_from_descriptor, "saved_device_setup.json")
Correspondingly, the load
functionality reconstructs a DeviceSetup
object from a json file.
loaded_device_setup = load("saved_device_setup.json")
Examples¶
Programmatic Setup Generation¶
Below we show how to construct a large scale device setup for controlling 64 qubits programmatically.
scaled_setup = DeviceSetup("my_scaled_setup")
scaled_setup.add_dataserver(host="111.22.33.44", port="8004")
scaled_setup.add_instruments(
PQSC(uid="pqsc", address="dev10001", reference_clock_source="internal")
)
Add drive line signals using an SHFSG for each eight qubits.
for i in range(8):
scaled_setup.add_instruments(SHFSG(uid=f"shfsg_{i}", address=f"dev1212{i}"))
scaled_setup.add_connections(
f"shfsg_{i}",
*[
create_connection(
to_signal=f"q{i * 8 + _}/drive", ports=f"SGCHANNELS/{_}/OUTPUT"
)
for _ in range(8)
],
)
Likewise, add flux line signals using an HDAWG for eight qubits.
for i in range(8):
scaled_setup.add_instruments(HDAWG(uid=f"hdawg_{i}", address=f"dev876{i}"))
scaled_setup.add_connections(
f"hdawg_{i}",
*[
create_connection(to_signal=f"q{i * 8 + _}/flux", ports=f"SIGOUTS/{_}")
for _ in range(8)
],
)
Define pairs of measure and acquire signals together, 8 qubits per QA unit with 4 QA units per SHFQA.
for i in range(2):
scaled_setup.add_instruments(SHFQA(uid=f"shfqa_{i}", address=f"dev1234{i}"))
for j in range(4):
scaled_setup.add_connections(
f"shfqa_{i}",
*[
create_connection(
to_signal=f"q{i * 32 + j * 8 + _}/measure",
ports=f"QACHANNELS/{j}/OUTPUT",
)
for _ in range(8)
],
*[
create_connection(
to_signal=f"q{i * 32 + j * 8 + _}/acquire",
ports=f"QACHANNELS/{j}/INPUT",
)
for _ in range(8)
],
)
We verify that we have defined the logical signal groups q0 to q63, each with a drive, flux, measure, and acquire line, respectively.
for lsg in scaled_setup.logical_signal_groups:
print(lsg, *scaled_setup.logical_signal_groups[lsg].logical_signals.keys())
descriptor = """\
dataservers:
my_qccs_dataserver:
host: 111.22.33.44
port: 8004
instruments:
HDAWG:
- address: DEV8000
uid: hdawg
options: HDAWG8/CNT/MF/ME/PC/SKW/IQ
UHFQA:
- address: DEV2000
uid: uhfqa
options: UHFQA/AWG/DIG/RUB
PQSC:
- address: DEV10000
uid: pqsc
options: PQSC
connections:
hdawg:
- iq_signal: q0/drive
ports: [SIGOUTS/0, SIGOUTS/1]
- rf_signal: q0/flux
ports: [SIGOUTS/2]
- to: uhfqa
port: DIOS/0
uhfqa:
- iq_signal: q0/measure
ports: [SIGOUTS/0, SIGOUTS/1]
- acquire_signal: q0/acquire
"""
device_setup_pqsc_hdawg_uhfqa = DeviceSetup.from_descriptor(
descriptor,
)
Note, that a device descriptor can include information on the dataserver as shown above.
However, this information will be overwritten whenever the arguments server_host
/server_port
are provided to the from_descriptor
method.
Device Descriptor for PQSC and SHF Instruments¶
For setups comprising instruments of the SHF series (SHFSG, SHFQA, SHFQC), the descriptor looks similar to before. Due to the integrated frequency up- and down-conversion, the SHF instruments require only single output ports per logical signal line.
descriptor = """\
instruments:
SHFSG:
- address: DEV12000
uid: shfsg
options: SHFSG8/RTR
SHFQA:
- address: DEV12001
uid: shfqa
options: SHFQA2/16W
PQSC:
- address: DEV10000
uid: pqsc
options: PQSC
connections:
shfsg:
- iq_signal: q0/drive
ports: SGCHANNELS/0/OUTPUT
shfqa:
- iq_signal: q0/measure
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q0/acquire
ports: QACHANNELS/0/INPUT
"""
device_setup_pqsc_shf_instruments = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
)
Device Descriptor for Single HDAWG and Single UHFQA¶
Similarly to device setups with a PQSC, the descriptor contains information about the instruments, the connections and the logical signal lines. All available keywords for e.g. signal types stay the same.
descriptor = """\
instruments:
HDAWG:
- address: dev8000
uid: hdawg
options: HDAWG8/CNT/MF/ME/PC/SKW/IQ
UHFQA:
- address: dev2000
uid: uhfqa
options: UHFQA/AWG/DIG/RUB
connections:
hdawg:
- iq_signal: q0/drive
ports: [SIGOUTS/0, SIGOUTS/1]
- to: uhfqa
port: DIOS/0
uhfqa:
- iq_signal: q0/measure
ports: [SIGOUTS/0, SIGOUTS/1]
- acquire_signal: q0/acquire
"""
device_setup_small = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
)
Device Descriptor for a Single HDAWG¶
The descriptor for a standalone instrument setup such as a single HDAWG is shown below.
descriptor = """\
instruments:
HDAWG:
- address: dev8000
uid: hdawg
options: HDAWG8/CNT/MF/ME/PC/SKW/IQ
connections:
hdawg:
- iq_signal: q0/drive
ports: [SIGOUTS/0, SIGOUTS/1]
- rf_signal: q0/flux
ports: [SIGOUTS/2]
"""
device_single_hdawg = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
)
In such a setup, the HDAWG uses its internal reference clock by default.