Programmatic Device Setup Construction¶
The device setup object is used by LabOne Q to retrieve all information relating to the dataserver and the instruments connected to it from.
Once complete, the device setup object exposes LogicalSignals
and an interface to apply persistent Calibration
changes to them or other calibratables contained in the device setup.
In this notebook, you will learn how to define DeviceSetup
objects for different instruments setups by using the recent programmatic construction functionality of LabOne Q.
Imports¶
LabOne Q standard imports
from laboneq.simple import *
Device Setup Initialization and Dataserver¶
We begin by initializing a mostly empty DeviceSetup
instance
device_setup = DeviceSetup("my_setup_1")
The add_dataserver
functionality allows to set the details of the connection to the dataserver.
device_setup.add_dataserver(host="111.22.33.44", port="8004")
Note, that this information can also be provided during the above initialization step.
Instruments¶
We can add individual instruments to the device setup
device_setup.add_instruments(SHFQC(uid="device_shfqc", address="dev12345"))
print(device_setup)
Note, that the resulting device setup has no physical channels defined yet.
Connections¶
We directly add a logical signal line in the form of create_connection
device_setup.add_connections(
"device_shfqc",
create_connection(to_signal="q0/drive_line", ports="SGCHANNELS/0/OUTPUT"),
)
print(device_setup)
Note, that a logical signal line as well as the connected physical channel were added to the device setup from this connection.
Multiple Connections¶
We can successively add additional connections
device_setup.add_connections(
"device_shfqc",
create_connection(to_signal="q1/drive_line", ports="SGCHANNELS/1/OUTPUT"),
)
print(device_setup)
or add multiple connections at once
device_setup.add_connections(
"device_shfqc",
create_connection(to_signal="q0/measure_line", ports=["QACHANNELS/0/OUTPUT"]),
create_connection(to_signal="q0/acquire_line", ports=["QACHANNELS/0/INPUT"]),
create_connection(to_signal="q1/measure_line", ports=["QACHANNELS/0/OUTPUT"]),
create_connection(to_signal="q1/acquire_line", ports=["QACHANNELS/0/INPUT"]),
)
print(device_setup)
which allows for some programmatic constructions like.
inds = [2, 3]
device_setup.add_connections(
"device_shfqc",
*[
create_connection(
to_signal=f"q{_}/drive_line", ports=[f"SGCHANNELS/{_}/OUTPUT"]
)
for _ in inds
],
*[
create_connection(to_signal=f"q{_}/measure_line", ports=["QACHANNELS/0/OUTPUT"])
for _ in inds
],
*[
create_connection(to_signal=f"q{_}/acquire_line", ports=["QACHANNELS/0/INPUT"])
for _ in inds
],
)
print(device_setup)
connections can only be added if they do not exist already
try:
device_setup.add_connections(
"device_shfqc",
create_connection(to_signal="q0/drive_line", ports="SGCHANNELS/0/OUTPUT"),
)
except:
print("LabOneQException as expected")
Setups with Multiple Instruments¶
We want to add another instrument and define logical signal lines from its ports. Here, we add a HDAWG to the setup as well as a PQSC.
device_setup.add_instruments(
HDAWG(uid="device_hdawg", address="dev8765"),
PQSC(uid="device_pqsc", address="dev10123", reference_clock_source="internal"),
)
The PQSC uses ZSYNC connections to synchronize between the SHFQC and the HDAWG.
We can use the add_connections
method to define also ZSYNC connectivity between instruments.
device_setup.add_connections(
"device_pqsc",
create_connection(to_instrument="device_shfqc", ports="ZSYNCS/0"),
create_connection(to_instrument="device_hdawg", ports="ZSYNCS/10"),
)
print(device_setup)
Finally, we define the additional LogicalSignal
lines on the HDAWG
device_setup.add_connections(
"device_hdawg",
*[
create_connection(to_signal=f"q{_}/flux_line", ports=f"SIGOUTS/{_}")
for _ in range(4)
],
)
print(device_setup)
Device Setup at Scale¶
We can combine the above methods to define large scale device setups 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(
"pqsc", create_connection(to_instrument=f"shfsg_{i}", ports=f"ZSYNCS/{i}")
)
scaled_setup.add_connections(
f"shfsg_{i}",
*[
create_connection(
to_signal=f"q{i*8+_}/drive_line", 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(
"pqsc", create_connection(to_instrument=f"hdawg_{i}", ports=f"ZSYNCS/{i+8}")
)
scaled_setup.add_connections(
f"hdawg_{i}",
*[
create_connection(to_signal=f"q{i*8+_}/flux_line", 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}"))
scaled_setup.add_connections(
"pqsc", create_connection(to_instrument=f"shfqa_{i}", ports=f"ZSYNCS/{i+16}")
)
for j in range(4):
scaled_setup.add_connections(
f"shfqa_{i}",
*[
create_connection(
to_signal=f"q{i*32+j*8+_}/measure_line",
ports=f"QACHANNELS/{j}/OUTPUT",
)
for _ in range(8)
],
*[
create_connection(
to_signal=f"q{i*32+j*8+_}/acquire_line",
ports=f"QACHANNELS/{j}/INPUT",
)
for _ in range(8)
],
)
We verify that we have defined the logical signal lines 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())
Note
- Significantly higher qubit counts are only possible using a QHUB for synchronization
- The above example assumes regularity in the wiring. Functionality to readapt existing connections e.g. to swap out two experimental lines, is not yet implemented.
- The index logic in the above can be simplified further.
Setups with Gen1 Instruments¶
The definition of sets of gen1 instruments is done also with the above methods.
gen1_setup = DeviceSetup("gen1_qzilla")
gen1_setup.add_dataserver(host="111.22.33.44", port="8004")
gen1_setup.add_instruments(
HDAWG(uid="hdawg", address="dev8768"),
UHFQA(uid="uhfqa", address="dev2890"),
PQSC(uid="pqsc", address="dev10001", reference_clock_source="external"),
)
We need, however to account the different connections between the instruments. Specifically, in such setups only the HDAWG has a ZSYNC connection to the PQSC,
gen1_setup.add_connections(
"pqsc", create_connection(to_instrument="hdawg", ports="ZSYNCS/0")
)
while the UHFQA instrument connects to the HDAWG via a DIOS port.
gen1_setup.add_connections(
"hdawg", create_connection(to_instrument="device_uhfqa", ports="DIOS/0")
)
Furthermore, in such setup the I and Q signal components of the same logical signal are located at different physical ports. We account for this by handing a list of both physical ports to the ports argument.
gen1_setup.add_connections(
"hdawg",
create_connection(to_signal="q0/drive_line", ports=["SIGOUTS/0", "SIGOUTS/1"]),
)
gen1_setup.add_connections(
"uhfqa",
create_connection(to_signal="q0/acquire_line", ports=["SIGOUTS/0", "SIGOUTS/1"]),
)