Defining your Experimental Setup¶
This guide shows you how to create the object describing the experiment setup, which are needed for running the experiment workflows defined in the Applications Library.
Create a DeviceSetup
¶
Start by creating a DeviceSetup
for your experimental setup by following the Device Setup and Descriptor tutorial(add link).
For demonstration purposes, here we create dummy DeviceSetup
containing a SHFQC+ instrument, an HDAWG instrument, and a PQSC instrument, which are used to operate 3 tunable transmon qubits, labelled q0, q1, q2
.
from laboneq_applications.qpu_types.tunable_transmon.demo_qpus import (
tunable_transmon_setup,
)
setup = tunable_transmon_setup(n_qubits=3)
The setup
contains a logical signal group for each qubit labelled with the qubit UID, and each of these qubit signal-line group contains the following signal lines: drive
, drive_ef
, measure
, acquire
, flux
, as shown below.
qubit_signals = {
quid: list(lsg.logical_signals) for quid, lsg in setup.logical_signal_groups.items()
}
qubit_signals
{'q0': ['drive', 'drive_ef', 'measure', 'acquire', 'flux'], 'q1': ['drive', 'drive_ef', 'measure', 'acquire', 'flux'], 'q2': ['drive', 'drive_ef', 'measure', 'acquire', 'flux']}
Let's inspect the connectivity between the instruments and the lines of the qubits:
def get_physical_signal_name(quid, signal_name):
logical_signal = setup.logical_signal_groups[quid].logical_signals[signal_name]
return logical_signal.physical_channel.uid
connections = {
quid: {
sig_name: get_physical_signal_name(quid, sig_name) for sig_name in signals
} for quid, signals in qubit_signals.items()
}
from pprint import pprint
pprint(connections) # noqa: T203
{'q0': {'acquire': 'device_shfqc/qachannels_0_input', 'drive': 'device_shfqc/sgchannels_0_output', 'drive_ef': 'device_shfqc/sgchannels_0_output', 'flux': 'device_hdawg/sigouts_0', 'measure': 'device_shfqc/qachannels_0_output'}, 'q1': {'acquire': 'device_shfqc/qachannels_0_input', 'drive': 'device_shfqc/sgchannels_1_output', 'drive_ef': 'device_shfqc/sgchannels_1_output', 'flux': 'device_hdawg/sigouts_1', 'measure': 'device_shfqc/qachannels_0_output'}, 'q2': {'acquire': 'device_shfqc/qachannels_0_input', 'drive': 'device_shfqc/sgchannels_2_output', 'drive_ef': 'device_shfqc/sgchannels_2_output', 'flux': 'device_hdawg/sigouts_2', 'measure': 'device_shfqc/qachannels_0_output'}}
We see that the three qubits are read out in parallel on the same quantum analyzer (QA) channel of the SHFQC instrument, and that their drive lines are controlled from individual signal generation (SG) channels of the SHFQC instrument. Finally, the flux lines of the qubits are controlled by individual HDAWG outputs.
Define qubits¶
We will show how to create qubit instances from the logical signal groups of the DeviceSetup
defined above. Here, we use the TunableTransmonQubit
class with corresponding TunableTransmonQubitParameters
, but the procedure is the same for any other child class of LabOne Q QuantumElements
class.
Note that, the qubit UIDs must match the names of the logical signal groups define in the DeviceSetup
above, in this case q0, q1, q2
.
from laboneq_applications.qpu_types.tunable_transmon import (
TunableTransmonQubit,
TunableTransmonQubitParameters,
)
qubits = []
for i in range(3):
q = TunableTransmonQubit.from_logical_signal_group(
f"q{i}",
setup.logical_signal_groups[f"q{i}"],
parameters=TunableTransmonQubitParameters()
)
qubits.append(q)
The qubits are instantiated with identical default values of the TunableTransmonQubitParameters
class. Let's see what they are:
qubits[0].parameters
TunableTransmonQubitParameters( │ resonance_frequency_ge=None, │ resonance_frequency_ef=None, │ drive_lo_frequency=None, │ readout_resonator_frequency=None, │ readout_lo_frequency=None, │ readout_integration_delay=2e-08, │ drive_range=10, │ readout_range_out=5, │ readout_range_in=10, │ flux_offset_voltage=0, │ user_defined={}, │ ge_T1=0, │ ge_T2=0, │ ge_T2_star=0, │ ef_T1=0, │ ef_T2=0, │ ef_T2_star=0, │ ge_drive_amplitude_pi=0.2, │ ge_drive_amplitude_pi2=0.1, │ ge_drive_length=5e-08, │ ge_drive_pulse={ │ │ 'function': 'drag', │ │ 'beta': 0, │ │ 'sigma': 0.25 │ }, │ ef_drive_amplitude_pi=0.2, │ ef_drive_amplitude_pi2=0.1, │ ef_drive_length=5e-08, │ ef_drive_pulse={ │ │ 'function': 'drag', │ │ 'beta': 0, │ │ 'sigma': 0.25 │ }, │ readout_amplitude=1.0, │ readout_length=2e-06, │ readout_pulse={ │ │ 'function': 'const' │ }, │ readout_integration_length=2e-06, │ readout_integration_kernels_type='default', │ readout_integration_kernels=None, │ readout_integration_discrimination_thresholds=None, │ reset_delay_length=1e-06, │ spectroscopy_length=5e-06, │ spectroscopy_amplitude=1, │ dc_slot=0, │ dc_voltage_parking=0.0 )
Adjust the values of the parameters to the ones for your quantum device. You can change the value of any of the parameters as shown below for the drive_lo_frequency
parameter:
qubits[0].parameters.drive_lo_frequency = 6e9
If you already the correct qubit parameters stored in an instance of TunableTransmonQubitParameters
(for example, loaded from a file), you can directly pass them to the parameters
argument of TunableTransmonQubit.from_logical_signal_group
, and the qubits will be created with those parameters.
Define quantum operations¶
Next, we need to define the class of quantum operations implementing gates and operations on the qubits defined above. Here, we will use an instance of TunableTransmonOperations
for the tunable transmons defined above.
from laboneq_applications.qpu_types.tunable_transmon import TunableTransmonOperations
quantum_operations = TunableTransmonOperations()
quantum_operations.keys()
['acquire', 'active_reset', 'barrier', 'calibration_traces', 'delay', 'measure', 'passive_reset', 'prepare_state', 'ramsey', 'rx', 'ry', 'rz', 'set_frequency', 'set_readout_amplitude', 'spectroscopy_drive', 'x180', 'x180_ef_reset', 'x90', 'y180', 'y90', 'z180', 'z90']
To learn more about quantum operations and how they are used to create quantum experiments from the qubit parameters, see the tutorial ...
Define the QPU
¶
Finally, we define the quantum processor (QPU) containing the qubits and the corresponding quantum operations.
The qpu
contains the source of ground truth for an experiment and the best state of knowledge of the quantum system that is being operated. This means that the parameters of the qubits and any other parameters of the QPU define the configuration used by all the experiments in the Applications Library.
from laboneq.dsl.quantum import QPU
qpu = QPU(qubits=qubits, quantum_operations=quantum_operations)
Loading From a File¶
The qubits and QPU can also be loaded back from json
files saved by an experiment in the Applications Library. You just need the path to the file:
from laboneq import serializers
serializers.load(path_to_file)
Optional: define a QuantumPlatform
¶
Optionally, you can collect the setup
and the qpu
in an instance of QuantumPlatform
.
from laboneq.dsl.quantum import QuantumPlatform
qt_platform = QuantumPlatform(setup=setup, qpu=qpu)
Demo QuantumPlatform
¶
All of the objects used for demonstration purposes in this notebook can also be more conveniently obtained by instantiating a demo quantum platform provided by the Application Library. This demo platform is useful for quick prototyping in emulation mode.
from laboneq_applications.qpu_types.tunable_transmon.demo_qpus import demo_platform
demo_qt_platform = demo_platform(n_qubits=3)
log_sig_groups = demo_qt_platform.setup.logical_signal_groups
qubit_signals = {
quid: list(lsg.logical_signals) for quid, lsg in log_sig_groups.items()
}
qubit_signals
{'q0': ['drive', 'drive_ef', 'measure', 'acquire', 'flux'], 'q1': ['drive', 'drive_ef', 'measure', 'acquire', 'flux'], 'q2': ['drive', 'drive_ef', 'measure', 'acquire', 'flux']}
demo_qt_platform.qpu.qubits[0].parameters
TunableTransmonQubitParameters( │ resonance_frequency_ge=6500000000.0, │ resonance_frequency_ef=6300000000.0, │ drive_lo_frequency=6400000000, │ readout_resonator_frequency=7100000000.0, │ readout_lo_frequency=7000000000.0, │ readout_integration_delay=2e-08, │ drive_range=10, │ readout_range_out=5, │ readout_range_in=10, │ flux_offset_voltage=0, │ user_defined={}, │ ge_T1=0, │ ge_T2=0, │ ge_T2_star=0, │ ef_T1=0, │ ef_T2=0, │ ef_T2_star=0, │ ge_drive_amplitude_pi=0.8, │ ge_drive_amplitude_pi2=0.4, │ ge_drive_length=5.1e-08, │ ge_drive_pulse={ │ │ 'function': 'drag', │ │ 'beta': 0.01, │ │ 'sigma': 0.21 │ }, │ ef_drive_amplitude_pi=0.7, │ ef_drive_amplitude_pi2=0.3, │ ef_drive_length=5.2e-08, │ ef_drive_pulse={ │ │ 'function': 'drag', │ │ 'beta': 0.01, │ │ 'sigma': 0.21 │ }, │ readout_amplitude=1.0, │ readout_length=2e-06, │ readout_pulse={ │ │ 'function': 'const' │ }, │ readout_integration_length=2e-06, │ readout_integration_kernels_type='default', │ readout_integration_kernels=None, │ readout_integration_discrimination_thresholds=None, │ reset_delay_length=1e-06, │ spectroscopy_length=5e-06, │ spectroscopy_amplitude=1, │ dc_slot=0, │ dc_voltage_parking=0.0 )
demo_qt_platform.qpu.quantum_operations.keys()
['acquire', 'active_reset', 'barrier', 'calibration_traces', 'delay', 'measure', 'passive_reset', 'prepare_state', 'ramsey', 'rx', 'ry', 'rz', 'set_frequency', 'set_readout_amplitude', 'spectroscopy_drive', 'x180', 'x180_ef_reset', 'x90', 'y180', 'y90', 'z180', 'z90']
Connect to Session
¶
Now let's connect to a LabOne Q Session
. Here, we connect in emulation mode. When running on real hardware, connect using do_emulation=False
.
from laboneq.simple import Session
session = Session(setup)
session.connect(do_emulation=True)
[2024.11.07 16:42:00.631] INFO Logging initialized from [Default inline config in laboneq.laboneq_logging] logdir is /builds/qccs/laboneq-applications/docs/sources/tutorials/sources/laboneq_output/log
[2024.11.07 16:42:00.639] INFO VERSION: laboneq 2.41.0
[2024.11.07 16:42:00.643] INFO Connecting to data server at localhost:8004
[2024.11.07 16:42:00.647] INFO Connected to Zurich Instruments LabOne Data Server version 24.10 at localhost:8004
[2024.11.07 16:42:00.667] INFO Configuring the device setup
[2024.11.07 16:42:00.707] INFO The device setup is configured
<laboneq.dsl.session.ConnectionState at 0x7374bb503e30>
Great! You have created everything you need to get started with the measurements. Now, on to experiments!