Tutorial 3) Defining Complex Measurements
This tutorial covers two approaches to building complex measurement routines in arbok-driver:
Dictionary-based composition — defining the full sequence hierarchy from a nested dictionary in a single call
Experiment blueprints — using pre-defined
Experimentclasses to standardise frequently used routines across devices and team members
All code cells are intended to be run in succession. This tutorial assumes you have already set up a Device and ArbokDriver instance as shown in Tutorial 1.
0. Prerequisites
Make sure you have completed Tutorial 1 (Initial Setup) and that the following objects are available in your session:
from arbok_driver import ArbokDriver, Device, Measurement
from arbok_driver.examples.configurations.hardware import opx1000_config
device = Device('device_8q', opx_config=opx1000_config, divider_config={})
my_driver = ArbokDriver('my_driver', device)
1. The Problem: Growing Measurement Complexity
Simple measurements like a charge stability map consist of one or two sub-sequences. As experiments become more sophisticated, a single measurement routine quickly accumulates many sub-sequences with interdependent parameters. A typical coherence measurement such as a CPMG sequence (Carr-Purcell-Meiboom-Gill) requires:
A state initialization stage (parity initialization)
A transit to the control point in gate voltage space
A spin control stage containing multiple nested operations:
An initial pi/2 rotation to place the qubit on the equator
The CPMG refocusing pulses
A final state projection
A transit back from the control point
A readout stage
Adding these components one by one, managing their interdependencies, and ensuring consistent configuration across measurements quickly becomes error-prone, especially when the same routine needs to be deployed on a new device or by a new team member.
arbok-driver provides two solutions to this problem, covered in the following sections.
2. Dictionary-Based Composition
Rather than instantiating and adding sub-sequences individually, arbok-driver allows the entire hierarchy to be defined as a nested dictionary. Each entry specifies:
sequence: theSubSequenceclass to instantiatekwargs: optional keyword arguments passed to the constructorsub_sequences: a nested dictionary of further child sub-sequencesconfig: a device configuration dictionary (used for readout and initialization stages)
The full hierarchy is then constructed in a single call to add_subsequences_from_dict.
from arbok_driver.examples.sequences import (
ToControlPoint, FromControlPoint,
Xstrict, Cpmg, StateProjection
)
from arbok_driver.examples.configurations.sequence import (
parity_init_conf, parity_read_conf
)
cpmg_conf = {
# Initialization stage — device-specific, provided via config
'parity_init': {'config': parity_init_conf},
# Move to the control point in gate voltage space
'to_control': {'sequence': ToControlPoint},
# Spin control stage — contains nested sub-sequences
'spin_control': {
'sub_sequences': {
# Initial pi/2 rotation to place qubit on the Bloch sphere equator
'x_strict': {
'sequence': Xstrict,
'kwargs': {
'target_qubit': 'Q1',
'control_pulse': 'control_pi2'
}
},
# CPMG refocusing pulses
'cpmg': {
'sequence': Cpmg,
'kwargs': {
'target_qubit': 'Q1',
'repetitions': 10, # number of refocusing pulses
't_equator_wait': int(1e3) # free evolution time in ns
}
},
# Final state projection before readout
'state_projection': {
'sequence': StateProjection,
'kwargs': {'target_qubit': 'Q1'}
}
},
# check_step_requirements enables asynchronous conditional execution
# (see Tutorial 4: Asynchronous Measurements)
'kwargs': {'check_step_requirements': True}
},
# Move back from the control point
'from_control': {'sequence': FromControlPoint},
# Readout stage — device-specific, provided via config
'parity_read': {'config': parity_read_conf}
}
# Instantiate the measurement and build the hierarchy from the dictionary
cpmg_meas = Measurement(my_driver, 'cpmg_meas')
cpmg_meas.add_subsequences_from_dict(cpmg_conf)
The full parameter space of the measurement is now available through the QCoDeS interface. You can inspect it with:
cpmg_meas.print_readable_snapshot()
All parameters, including t_equator_wait, repetitions, and any voltage offsets defined in the initialization and readout configurations, are registered as QCoDeS parameters and can be swept or updated directly.
3. The Problem with Raw Dictionaries at Scale
The dictionary approach works well for one-off measurements or exploratory work. However, in a team environment or across multiple devices, raw dictionaries introduce a maintenance problem:
The same dictionary may be copied and modified independently by different users
There is no guarantee that two instances of nominally the same measurement are actually identical
When a sub-sequence is updated or renamed, all copies of the dictionary need to be found and updated manually
For measurements that are run routinely, arbok-driver provides the Experiment class to address this.
4. Experiment Blueprints
An Experiment object encodes the invariant structure of a measurement — the sub-sequence hierarchy, class choices, and default parameters — while leaving the device-specific components as required arguments. This means:
The experimental logic is defined once and versioned centrally
Different devices only need to provide their own initialization and readout configurations
New team members can deploy the same routine on a new device by providing two arguments
The CpmgExperiment blueprint encodes exactly the same structure as the dictionary above:
from arbok_driver.examples.experiments import CpmgExperiment
# Create the identical CPMG measurement from a pre-defined blueprint
# Only the device-specific components need to be provided
cpmg_meas2 = my_driver.create_measurement_from_experiment(
CpmgExperiment(
target_qubit='Q1',
parity_init=parity_init_conf,
parity_read=parity_read_conf
)
)
The resulting cpmg_meas2 object is identical to cpmg_meas in every respect. Verify this by comparing their parameter snapshots:
cpmg_meas2.print_readable_snapshot()
5. Deploying on a Different Device
The real benefit of the Experiment pattern becomes apparent when the same measurement needs to run on a different device. Only the configuration objects change while the sequence structure and all internal logic remain exactly as defined in the blueprint:
# Deploy the same CPMG routine on a different device
# No changes to the experiment blueprint are needed
# cpmg_meas_device2 = my_driver.create_measurement_from_experiment(
# CpmgExperiment(
# target_qubit='Q1',
# init_config=parity_init_conf_device2,
# read_config=parity_read_conf_device2
# )
# )
6. Summary
Approach |
Best suited for |
|---|---|
|
Exploratory measurements, one-off routines, prototyping new sequences |
|
Established routines, multi-device campaigns, team environments |
Both approaches produce identical measurement objects and the same QUA program. The Experiment pattern adds a layer of standardisation that reduces the risk of inconsistencies and makes it easier for new users to get started without understanding the full internal structure of the sequence.
Next steps:
Tutorial 4: Asynchronous Measurements — using step requirements for heralding and conditional execution
Tutorial 5: Parameter Input Streaming — adaptive measurements with the
GenericTuningInterface