Calibration Reference¶
In this notebook, you'll learn how to calibrate a DeviceSetup
and Experiment
object in LabOne Q in multiple ways, as well as get, set, reset, and serialize your calibration.
Calibration Types¶
Actual calibration data is stored in so called CalibrationItem
objects. These objects are assigned to the calibration property of a Calibratable
object which is a data entity in a DeviceSetup
or Experiment
object tree.
For example, a Calibratable
data entity is a LogicalSignal
or an ExperimentSignal
object. The corresponding CalibrationItem
objects is of type SignalCalibration
.
These are the currently available Calibratable
s and CalibrationItem
s:
Calibratable | CalibrationItem |
---|---|
LogicalSignal |
SignalCalibration |
ExperimentSignal |
SignalCalibration |
Overriding Mechanism¶
The calibration of a DeviceSetup
is considered as the baseline calibration that is used for experiment execution.
This baseline calibration can be overridden non-destructively with calibration on an Experiment
. If a SignalCalibration
is defined i.e. not None
on an ExperimentSignal
, then actual values from that SignalCalibration
are considered while the corresponding values in the SignalCalibration
on the LogicalSignal
are ignored and left unmodified. If there are values in the SignalCalibration
on the ExperimentSignal
that are set to None
, these values are not considered and the corresponding values in the baseline SignalCalibration
on the corresponding LogicalSignal
remain effective.
For example, the oscillator defined for a LogicalSignal
can be overridden by an oscillator in the corresponding ExperimentSignals
's SignalCalibration
. If this SignalCalibration
only defines an oscillator but leaves all other values to None
, only this oscillator will override the baseline oscillator. All values set to None
e.g. like the mixer calibration values will leave the baseline values effective.
Device Calibration¶
You'll start by importing LabOne Q, and defining a descriptor, here with an HDAWG, UHFQA, and PQSC.
# LabOne Q:
from laboneq.simple import *
# pretty printing
from pprint import pprint
descriptor = """\
instrument_list:
HDAWG:
- address: DEV1001
uid: device_hdawg
UHFQA:
- address: DEV2001
uid: device_uhfqa
PQSC:
- address: DEV3001
uid: device_pqsc
connections:
device_hdawg:
- iq_signal: q0/drive_line
ports: [SIGOUTS/0, SIGOUTS/1]
- to: device_uhfqa
port: DIOS/0
device_uhfqa:
- iq_signal: q0/measure_line
ports: [SIGOUTS/0, SIGOUTS/1]
- acquire_signal: q0/acquire_line
device_pqsc:
- to: device_hdawg
port: ZSYNCS/0
"""
Define Uncalibrated DeviceSetup
¶
Using the descriptor, the DeviceSetup
can be created. Initially, it is uncalibrated.
device_setup = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
setup_name="ZI_QCCS",
)
List Calibratable
Data Entities in the DeviceSetup
¶
In order to get a list of all available data entities in a DeviceSetup
that can be calibrated, the following function can be called. The result is a dict where key is the path to the data entity and value is a dict giving the type of the calibration item and a boolean whether there is a calibration set as a value.
device_setup.list_calibratables()
Calibrate DeviceSetup
¶
Below, you'll use different methods to use calibrations, either by setting the calibration directly to the DeviceSetup
or by creating a Calibration
object.
Variant 1: Direct Calibration on Existing DeviceSetup
Object¶
Assigning CalibrationItem
objects to the calibration
property of a Calibratable
:
q0_signals = device_setup.logical_signal_groups["q0"].logical_signals
q0_signals["drive_line"].calibration = SignalCalibration(
oscillator=Oscillator(
uid="drive_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.0, 0.0],
correction_matrix=[
[1.0, 0.0],
[0.0, 1.0],
],
),
# global and static delay of logical signal line: use to align pulses and compensate skew
port_delay=0, # applied to corresponding instrument node, bound to hardware limits
delay_signal=0, # inserted in sequencer code, bound to waveform granularity
)
q0_signals["measure_line"].calibration = SignalCalibration(
oscillator=Oscillator(
uid="measure_osc", frequency=1e8, modulation_type=ModulationType.SOFTWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.0, 0.0],
correction_matrix=[
[1.0, 0.0],
[0.0, 1.0],
],
),
delay_signal=0,
)
q0_signals["acquire_line"].calibration = SignalCalibration(
oscillator=Oscillator(
uid="acquire_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
),
port_delay=0,
delay_signal=0,
)
Assign calibration using forwarded properties on a Calibratable
object. This implicitly sets the CalibrationItem
object on the calibration
property if there is no CalibrationItem
set yet. The forwarded properties on the Calibratable
set the corresponding values in the CalibrationItem
object assigned to the calibration
property of the Calibratable
object:
q0_signals = device_setup.logical_signal_groups["q0"].logical_signals
ls = q0_signals["drive_line"]
ls.oscillator = Oscillator(
uid="drive_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
)
ls.mixer_calibration = MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
)
ls = q0_signals["measure_line"]
ls.oscillator = Oscillator(
uid="measure_osc", frequency=1e8, modulation_type=ModulationType.SOFTWARE
)
ls.mixer_calibration = MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
)
q0_signals["acquire_line"].oscillator = Oscillator(
uid="acquire_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
)
Variant 2: Define Calibration
and Apply to Existing DeviceSetup
Object¶
Define the calibration:
device_calib = Calibration()
device_calib["/logical_signal_groups/q0/drive_line"] = SignalCalibration(
oscillator=Oscillator(
uid="drive_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
)
device_calib["/logical_signal_groups/q0/measure_line"] = SignalCalibration(
oscillator=Oscillator(
uid="measure_osc", frequency=1e8, modulation_type=ModulationType.SOFTWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
)
device_calib["/logical_signal_groups/q0/acquire_line"] = SignalCalibration(
Oscillator(
uid="acquire_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
)
)
Finally, apply calibration:
device_setup.set_calibration(device_calib)
Get, Set, and Reset Device Calibration¶
print("\nGet device calibration:")
device_calib = device_setup.get_calibration()
print(device_calib)
print("\nReset device calibration:")
device_setup.reset_calibration() # clear all calibration items
print(device_setup.get_calibration())
device_setup.reset_calibration(
device_calib
) # clear all calibration items and set given calibration
print("\nSet device calibration:")
device_setup.set_calibration(device_calib)
print(device_setup.get_calibration())
assert device_calib == device_setup.get_calibration()
Experiment Calibration¶
Define Uncalibrated and Unmapped Experiment
¶
exp = Experiment(
uid="My experiment",
signals=[
ExperimentSignal("q0_drive"),
ExperimentSignal("q0_measure"),
ExperimentSignal("q0_acquire"),
],
)
# or with UIDs only
exp = Experiment(
uid="My experiment",
signals=[
"q0_drive",
"q0_measure",
"q0_acquire",
],
)
List Calibratable
Data Entities in the Experiment
¶
In order to get a list of all available data entities in an Experiment
that can be calibrated, the following function can be called. The result is a dict where key is the path to the data entity and value is a dict giving the type of the calibration item and a boolean whether there is a calibration set as a value.
exp.list_calibratables()
Calibrate Experiment¶
Variant 1: Direct Calibration at Experiment
Definition Time¶
The constructor of the ExperimentSignal
accepts calibration values of an ExperimentSignalCalibration
and forwards them internally when assigning a ExperimentSignalCalibration
object to the calibration
property:
exp = Experiment(
uid="My experiment",
signals=[
ExperimentSignal(
uid="q0_drive",
oscillator=Oscillator(frequency=1.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
),
ExperimentSignal(
uid="q0_measure",
oscillator=Oscillator(frequency=1.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
),
ExperimentSignal(uid="q0_acquire", oscillator=Oscillator(frequency=3.0e6)),
],
)
Alternatively, the CalibrationItem
can be given as an object:
exp = Experiment(
uid="My experiment",
signals=[
ExperimentSignal(
uid="q0_drive",
calibration=SignalCalibration(
oscillator=Oscillator(frequency=1.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
),
),
ExperimentSignal(
uid="q0_measure",
calibration=SignalCalibration(
oscillator=Oscillator(frequency=2.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
),
),
ExperimentSignal(
uid="q0_acquire", calibration=SignalCalibration(Oscillator(frequency=3.0e6))
),
],
)
Variant 2: Direct Calibration on Existing Experiment
Object¶
Assigning CalibrationItem
objects to the calibration property of a Calibratable
:
exp.signals["q0_drive"].calibration = SignalCalibration(
oscillator=Oscillator(frequency=1.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
)
exp.signals["q0_measure"].calibration = SignalCalibration(
oscillator=Oscillator(frequency=2.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
)
exp.signals["q0_acquire"].calibration = SignalCalibration(Oscillator(frequency=3.0e6))
Assign calibration using forwarded properties on a Calibratable
object. This implicitly sets the CalibrationItem
object on the calibration
property if there is no CalibrationItem
set yet. The forwarded properties on the Calibratable
set the corresponding values in the CalibrationItem
object assigned to the calibration
property of the Calibratable
object:
exp.reset_calibration()
es = exp.signals["q0_drive"]
es.oscillator = Oscillator(frequency=1.0e6)
es.mixer_calibration = MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
)
es = exp.signals["q0_measure"]
es.oscillator = Oscillator(frequency=2.0e6)
es.mixer_calibration = MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
)
es = exp.signals["q0_acquire"]
es.oscillator = Oscillator(frequency=3.0e6)
Variant 3: Define Calibration
and Apply to Existing Experiment
Object¶
Define calibration:
exp_calib = Calibration()
exp_calib["q0_drive"] = SignalCalibration(
oscillator=Oscillator(frequency=1.0e6),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.02, 0.05],
correction_matrix=[
[1.0, 0.03492077],
[0.0, 1.11178838],
],
),
)
exp_calib["q0_measure"] = SignalCalibration(Oscillator(frequency=2.0e6))
exp_calib["q0_acquire"] = SignalCalibration(Oscillator(frequency=3.0e6))
Apply calibration:
exp.set_calibration(exp_calib)
Get, Set, and Reset Experiment Calibration¶
print("\nGet experiment calibration:")
exp_calib = exp.get_calibration()
print(exp_calib)
print("\nReset experiment calibration:")
exp.reset_calibration() # clear all calibration items
print(exp.get_calibration())
exp.reset_calibration(
exp_calib
) # clear all calibration items and set given calibration
print("\nSet experiment calibration:")
exp.set_calibration(exp_calib)
print(exp.get_calibration())
Modify Experiment Calibration on Experiment¶
Modifying the ExperimentSignalCalibration
object assigned to the calibration
property:
from copy import deepcopy
exp_calib_copy = deepcopy(exp.get_calibration())
exp.signals["q0_drive"].calibration.oscillator.frequency = 3.0
exp.signals["q0_drive"].calibration.voltage_offset = [4]
assert exp_calib == exp.get_calibration() # we modified a reference above
assert exp_calib != exp_calib_copy
assert exp_calib_copy != exp.get_calibration()
exp.set_calibration(exp_calib)
assert exp_calib == exp.get_calibration()
Modifying forwarded properties on ExperimentSignal
from the SignalCalibration
. The forwarded properties set the corresponding values in the SignalCalibration
object assigned to the calibration
property of the ExperimentSignal
object:
exp.signals["q0_drive"].oscillator.frequency = 1.0
exp.signals["q0_drive"].voltage_offsets = [2]
Map Experiment Signals¶
Variant 1: Map Signals at Experiment
Definition Time¶
exp_direct_mapping = Experiment(
uid="My experiment",
signals=[
ExperimentSignal(
"q0_drive",
map_to="/logical_signal_groups/q0/drive_line",
),
ExperimentSignal(
"q0_measure",
map_to="/logical_signal_groups/q0/measure_line",
),
ExperimentSignal(
"q0_acquire",
# it is also possible to reference the logical signal object:
map_to=device_setup.logical_signal_groups["q0"].logical_signals[
"acquire_line"
],
),
],
)
Variant 2: Direct Mapping on Existing Experiment
Object¶
Map signals directly on experiment object by referencing the LogicalSignal
object:
q0 = device_setup.logical_signal_groups["q0"]
exp.map_signal("q0_drive", q0.logical_signals["drive_line"])
exp.map_signal("q0_measure", q0.logical_signals["measure_line"])
exp.map_signal("q0_acquire", q0.logical_signals["acquire_line"])
or by path strings of a LogicalSignal
:
exp.map_signal("q0_drive", "/logical_signal_groups/q0/drive_line")
exp.map_signal("q0_measure", "/logical_signal_groups/q0/measure_line")
exp.map_signal("q0_acquire", "/logical_signal_groups/q0/acquire_line")
Variant 3: Set Entire Signal Map as a Dictionary on Experiment
Object¶
Use dictionary with experiment signal uid to logical signal path strings:
exp.set_signal_map(
{
"q0_drive": "/logical_signal_groups/q0/drive_line",
"q0_measure": "/logical_signal_groups/q0/measure_line",
"q0_acquire": "/logical_signal_groups/q0/acquire_line",
}
)
Use dict with experiment signal uid to LogicalSignal
object:
q0 = device_setup.logical_signal_groups["q0"]
exp.set_signal_map(
{
"q0_drive": q0.logical_signals["drive_line"],
"q0_measure": q0.logical_signals["measure_line"],
"q0_acquire": q0.logical_signals["acquire_line"],
}
)
Alternatively, use the path
property of a LogicalSignal
object:
q0 = device_setup.logical_signal_groups["q0"]
exp.set_signal_map(
{
"q0_drive": q0.logical_signals["drive_line"].path,
"q0_measure": q0.logical_signals["measure_line"].path,
"q0_acquire": q0.logical_signals["acquire_line"].path,
}
)
Get, Set, and Reset Signal Mapping¶
print("\nGet signal map:")
signal_map = exp.get_signal_map()
pprint(signal_map)
pprint(exp.signal_mapping_status)
print("\nReset signal map:")
exp.reset_signal_map() # unmap all signals
pprint(exp.get_signal_map())
pprint(exp.signal_mapping_status)
assert exp.get_signal_map() != signal_map
exp.reset_signal_map(signal_map) # unmap all signals and set given mapping
assert exp.get_signal_map() == signal_map
print("\nSet original signal map again:")
exp.set_signal_map(signal_map)
pprint(exp.get_signal_map())
pprint(exp.signal_mapping_status)
assert exp.get_signal_map() == signal_map
print("\nSet signal map by logical signal UIDs:")
exp.reset_signal_map()
exp.set_signal_map(
{
"q0_drive": "/logical_signal_groups/q0/drive_line",
"q0_measure": "/logical_signal_groups/q0/measure_line",
"q0_acquire": "/logical_signal_groups/q0/acquire_line",
}
)
pprint(exp.get_signal_map())
pprint(exp.signal_mapping_status)
print("\nSet signal map by logical signal objects:")
exp.reset_signal_map()
q0 = device_setup.logical_signal_groups["q0"]
exp.set_signal_map(
{
"q0_drive": q0.logical_signals["drive_line"],
"q0_measure": q0.logical_signals["measure_line"],
"q0_acquire": q0.logical_signals["acquire_line"],
}
)
pprint(exp.get_signal_map())
pprint(exp.signal_mapping_status)
Serialization of Calibration¶
device_calib = device_setup.get_calibration()
device_calib.save("my_device_calib.json")
device_calib_loaded = Calibration.load("my_device_calib.json")
assert device_calib == device_calib_loaded
exp_calib = exp.get_calibration()
exp_calib.save("my_exp_calib.json")
exp_calib_loaded = Calibration.load("my_exp_calib.json")
assert exp_calib == exp_calib_loaded