Experiment
In this chapter, we provide a brief overview of the functionality of the Experiment
class. Conceptual chapters for gaining an in-depth understanding of how to program your experiments can be found at the following links:
Additionally, a comprehensive list of arguments and details of the Experiment
class can be found in the API documentation.
The Experiment
class is the main interface to define your experiments in LabOne Q.
It allows you to define your pulse sequence using pulses, sections and real- and near-time loops, tell LabOne Q which experimental signal lines the pulses should be played on, set experiment-specific calibrations, determine how you sweep parameters, average over results, set and get arbitrary instrument nodes, and use callback functions.
Experiment Definition
Experiments are defined with a unique ID, “uid”, and with the signal lines to be used in the experiment:
exp = Experiment(
uid="Experiment_Name",
signals=[
ExperimentSignal("Signal_Name_0"),
ExperimentSignal("Signal_Name_1")
],
)
Any number of |
Once you have defined the Experiment
object, a pulse sequence is defined using near-time sweeps (optional), real-time acquisition loops and real-time sweeps, sections, and pulse commands. A minimum of one outer acquire_loop_rt
, one section, and one pulse is required for a valid experiment:
# Experiment definition
exp = Experiment(
uid="Minimum",
signals=[
ExperimentSignal("drive"),
],
)
# Real time acquisition loop
with exp.acquire_loop_rt(
uid="shots",
count=1
):
# The Section
with exp.section(uid="Section_name"):
# Section contents
exp.play(
signal="drive",
pulse=pulse_library.gaussian(
uid = "pulse_name",
length=100e-9,
amplitude=0.5),
)
Near-time sweeps cannot be nested within real-time acquisition loops as discussed here. |
Experiment Calibration
Experiments may, but are not required to, have their own Calibration
applied to them. An experiment calibration is valid only for the experiment it is defined for and overrides the global calibration for the time the experiment runs. The global calibration is not changed and will stay valid through the entire session.
exp.set_calibration(experiment_calibration)
Experimental Signal Map
A map from the ExperimentSignal
lines to logical signal lines must be made before the experiment can be compiled and run. A valid map definition for the above minimal sequence might be:
map = {
"drive": device_setup.logical_signal_groups["q0"].logical_signals["drive_line"]
}
The signal map must be set before compiling or running the experiment:
exp.set_signal_map(map)
Compiling and Running Experiments
To compile or run the experiment, users must create a Session and connect to it. This requires a DeviceSetup
:
session = Session(device_setup=device_setup)
The Session can be connected either to the instruments themselves (the default) or through an emulator (which does not require any connected hardware).
# To connect to an emulator:
session.connect(do_emulation=True)
# To connect to the data sever defined in the `DeviceSetup` and, through it, to the instruments:
session.connect(do_emulation=False)
Compiling Experiments
Users have a choice to compile experiments before running them or to compile and run all at once. Compiling an experiment before the execution can make sense to inspect pulse sequences and timings. An experiment may be compiled without running it:
compiled_exp = session.compile(exp)
Simulation of Output Signals
Compiled experiments can also be simulated to determine the time trace of all involved signals.
The simulation of the output signals can be triggered by using the compiled experiment with the laboneq.simulator.output_simulator.OutputSimulator
class.
The output_simulator example notebook shows how to simulate and plot the signals.
Compiler Options
Users can access advanced compiler options, which modify the compiler’s default behavior via a dictionary of compiler settings. All compiler settings have default values, which support a wide range of experiments. However, specific use cases, such as randomized benchmarking, benefit from optimized settings.
Available compiler settings and their default settings are:
Setting | Default value |
---|---|
|
128 |
|
128 |
|
64 |
|
64 |
|
64 |
|
64 |
|
64 |
|
64 |
|
16 |
|
False |
|
True |
To change the settings, the following syntax can be used:
compiler_settings = {"SHFSG_MIN_PLAYWAVE_HINT": 128, "SHFSG_MIN_PLAYZERO_HINT": 256}
compiled_experiment = my_session.compile(experiment, compiler_settings=compiler_settings)
Running Experiments
To run the experiment, the user has various options:
# Most general, compiles and runs
exp_results = session.run(exp)
# If the experiment has been compiled before
exp_results = session.run(compiled_exp)
# To run the last compiled experiment
exp_results = session.run()
Further information about results access and data handling is covered here.
LabOne Q provides an estimate of the real-time execution time when the experiment is run. If the actual execution time exceeds the estimate by more than 10%, the real-time step is skipped. |
Setting Arbitrary Instrument Node Values
LabOne Q allows the user to set instrument node values on Zurich Instruments devices in the near-time loops of experiments. This can be used to sweep particular nodes on QCCS instruments which would otherwise not be directly accessible in LabOne Q.
The syntax for sweeping an instrument node is similar to the callback function, with the node path and the value (which can also be a sweep parameter) as input arguments:
exp.set_node(path=f'/dev8000/sigouts/0/offset', value=sweep_parameter)
An example experiment is a resonator spectroscopy with different output power levels of the SHFQA. While the frequency sweep is done in a real-time acquisition loop, a near-time sweep is added where the output power node is stepped through the available ranges.
An alternative way is to use the Zurich Instruments Toolkit.