Skip to content

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.

For a hands-on demonstration on how to define and use your own Experiment, also have a look at our YouTube tutorial:

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")
    ],
)

Note

Any number of ExperimentSignal lines can be given, and their names, like the uid, are defined by the user. All experimental signal lines must be mapped to logical lines before the experiment can be compiled.

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),
        )

Note

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
HDAWG_MIN_PLAYWAVE_HINT 128
HDAWG_MIN_PLAYZERO_HINT 128
UHFQA_MIN_PLAYWAVE_HINT 64
UHFQA_MIN_PLAYZERO_HINT 64
SHFQA_MIN_PLAYWAVE_HINT 64
SHFQA_MIN_PLAYZERO_HINT 64
SHFSG_MIN_PLAYWAVE_HINT 64
SHFSG_MIN_PLAYZERO_HINT 64
PHASE_RESOLUTION_BITS 24

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.

Note

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.