Zurich Instruments#
Info
The zhinst backend is currently only compatible with python versions 3.8 and 3.9. Please see the installation page for more information.
Introduction#
quantify-scheduler
provides a stateless module: zhinst_backend
,
that abstracts the complexity of setting up experiments using Zurich Instruments hardware.
quantify-scheduler
uses information on the quantum device
and instrument properties to compile a quantify_scheduler.schedules.schedule.Schedule
into waveforms and sequencing instructions suitable for execution on Zurich Instruments hardware.
More information about compilation
can be found in the User Guide.
Using existing programming interfaces provided via zhinst-qcodes and zhinst-toolkit, quantify-scheduler
prepares the instruments that are present in the sec-hardware-description.
Finally, after configuring and running compile_backend()
successfully the instruments are prepared for execution.
The Zurich Instruments backend provides:
Synchronized execution of a program in a UHFQA and an HDAWG through the use of Triggers and Markers.
Automatic generation of the SeqC Sequencer instructions.
Waveform generation and modulation.
Memory-efficient Sequencing with the CommandTable.
Configuration of the relevant settings on all instruments.
Configuration for Triggers and Markers.
Supported Instruments#
The Zurich Instruments backend currently supports the HDAWG and the UHFQA instruments. In addition to the Zurich Instruments devices, the hardware backend supports using microwave sources such as the R&S SGS100A.
Basic paradigm and inner workings#
The Zurich Instruments back end threats a Schedule
as a linear timeline of operations executed on the quantum device and translates this into pulses and acquisitions to be executed on the different input and output channels of an HDAWG and a UHFQA.
In this context a single HDAWG is treated as the master device that sends out a single marker pulse for synchronization at the start of each iteration of the schedule that is used to trigger all other devices.
After the synchronization trigger is given, all devices execute a compiled program for which the timings of all instructions have been calculated.
The compilation from operations at the quantum-device layer to instructions that the hardware can execute is done in several steps.
The zhinst compile_backend()
starts from the ScheduleBase.timing_table
and maps the operations to channels on the hardware using the information specified in the
sec-connectivity.
Corrections for channel latency, as well as moving operations around to ensure all measurements start at the first sample (0) of a clock cycle are also done at this stage.
Once the starting time and sample of each operation are known, the numerical waveforms that have to be uploaded to the hardware can be generated. The numerical waveforms differ from the idealized waveforms of the device description in that they include corrections for effects such as mixer-skewness and linear-dynamical distortions (not implemented yet), and intermediate frequency modulation if required.
Both the CompiledSchedule.hardware_timing_table
and CompiledSchedule.hardware_waveform_dict
are available as properties of the CompiledSchedule
.
In the next step, the clock-accurate Seqc instructions for each awg of each device are determined, as well as the settings (nodes) to be configured including the linking of the numerical waveforms to these nodes.
All of this information is combined in ZISettingsBuilder
s and added to the compiled instructions of the CompiledSchedule
.
Limitations#
There are several limitations to the paradigm and to the current implementation. Some of these are relatively easy to address while others are more fundamental to the paradigm. Here we give an overview of the known limitations. Note that some of these can be quite specific.
Inherent limitations#
There are some inherent limitations to the paradigm of describing the program as a single linear timeline that is started using a single synchronization trigger. These limitations cannot easily be addressed so should be taken into account when thinking about experiments.
Because the synchronization of the HDAWG and the UFHQA relies on a trigger on two devices operating at different clock frequencies one cannot guarantee at what sample within the clock domain the slave device gets triggered. The consequence is that although the triggering is stable within an experiment, the exact time difference (in the number of samples) between the different devices varies between different experiments. This problem is inherent to the triggering scheme and cannot be easily resolved.
The paradigm of a single fixed timeline with a single synchronizing trigger is not compatible with a control loop affecting feedback.
Limitations with the current implementation#
There are also some practical limitations to the implementation. Keep these in mind when operating the hardware.
Real-time modulation is currently not supported, relying on pre-modulated waveforms, it is important to start waveforms at a multiple of the modulation frequency. Sticking to a 10 ns grid, it is recommended to use a modulation frequency of 100 MHz.
All operations need to start at an integer number of samples. Because of the choice of sampling rates of 2.4 GSps (~0.416 ns) and 1.8 GSps (~0.555 ns) it is useful to stick to a 10 ns grid for HDAWG (microwave and flux) pulses and a 40 ns grid for UHFQA (readout) pulses.
Different instructions on the same “awg” cannot start in the same clock cycle. This implies that the readout acquisition delay cannot be 0 (but it can be 40 ns or - 40ns).
All measurements are triggered simultaneously using the
StartQA(QA_INT_ALL, true)
instruction. This implies it is not possible to read out only a specific qubit/channel.All measurements have to start at the same sample within a clock cycle because one can only define a single integration weight per channel. To guarantee this, all operations are shifted around a bit (the measurement fixpoint correction). As a consequence, the reset/initialization operation can sometimes be a bit longer than specified in the schedule.
Because the timing between two devices needs to align over longer schedules, it is important that the clock-rates are accurate. To ensure phase stability, use a 10 MHz shared reference and operate the hardware in external reference mode.
Only a single HDAWG supported as the primary device, other HDAWGs need to be configured as secondary devices.
Multiplexed readout is currently not supported. One can only read out a single channel. (#191)
Hardware compilation configuration#
Old-style hardware config dictionary
The HardwareCompilationConfig
is a DataStructure
that adds validation and structure to the information that was previously stored in the hardware configuration dictionary. The old-style hardware configuration dictionary is still supported, but will be deprecated in the future.
Example old-style hardware configuration
{
"backend": "quantify_scheduler.backends.zhinst_backend.compile_backend",
"latency_corrections": {
"q0:mw-q0.01": 9.5e-08,
"q1:mw-q1.01": 9.5e-08,
"q0:res-q0.ro": -9.5e-08,
"q1:res-q1.ro": -9.5e-08
},
"distortion_corrections": {
"q0:fl-cl0.baseband": {
"filter_func": "scipy.signal.lfilter",
"input_var_name": "x",
"kwargs": {
"b": [
0,
0.25,
0.5
],
"a": [
1
]
},
"clipping_values": [
-2.5,
2.5
],
"sampling_rate": 1000000000.0
}
},
"local_oscillators": [
{
"unique_name": "lo0_ch1",
"instrument_name": "lo0",
"frequency": {
"ch1.frequency": null
},
"frequency_param": "ch1.frequency",
"power": {
"power": 13
}
},
{
"unique_name": "lo0_ch2",
"instrument_name": "lo0",
"frequency": {
"ch2.frequency": null
},
"frequency_param": "ch2.frequency",
"power": {
"ch2.power": 10
}
},
{
"unique_name": "lo1",
"instrument_name": "lo1",
"frequency": {
"frequency": null
},
"frequency_param": "frequency",
"power": {
"power": 16
}
}
],
"devices": [
{
"name": "ic_hdawg0",
"type": "HDAWG8",
"clock_select": 0,
"ref": "int",
"channelgrouping": 0,
"channel_0": {
"port": "q0:mw",
"clock": "q0.01",
"mode": "complex",
"modulation": {
"type": "premod",
"interm_freq": -100000000.0
},
"local_oscillator": "lo0_ch1",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
],
"gain1": 1.0,
"gain2": 1.0,
"mixer_corrections": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": -0.0542,
"dc_offset_q": -0.0328
},
"trigger": null
},
"channel_1": {
"port": "q1:mw",
"clock": "q1.01",
"mode": "complex",
"modulation": {
"type": "premod",
"interm_freq": -100000000.0
},
"local_oscillator": "lo0_ch2",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
],
"gain1": 1.0,
"gain2": 1.0,
"mixer_corrections": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
},
"trigger": null
},
"channel_2": {
"port": "q2:mw",
"clock": "q2.01",
"mode": "complex",
"modulation": {
"type": "premod",
"interm_freq": -100000000.0
},
"local_oscillator": "lo0_ch2",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
],
"gain1": 1.0,
"gain2": 1.0,
"mixer_corrections": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
},
"trigger": null
},
"channel_3": {
"port": "q3:mw",
"clock": "q3.01",
"mode": "complex",
"modulation": {
"type": "premod",
"interm_freq": -100000000.0
},
"local_oscillator": "lo0_ch2",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
],
"gain1": 1.0,
"gain2": 1.0,
"mixer_corrections": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
},
"trigger": null
}
},
{
"name": "ic_uhfqa0",
"type": "UHFQA",
"ref": "ext",
"channel_0": {
"port": "q0:res",
"clock": "q0.ro",
"mode": "real",
"modulation": {
"type": "premod",
"interm_freq": 200000000.0
},
"local_oscillator": "lo1",
"trigger": 2,
"markers": []
}
}
]
}
The zhinst_backend
allows Zurich Instruments to be
configured individually or collectively by enabling master/slave configurations via
Triggers and Markers.
The compilation onto Zurich Instruments hardware is configured by the Hardware Compilation Config.
The configuration file contains parameters about the Instruments, their connectivity to the quantum device, and options used in mapping quantify_scheduler.operations.operation.Operation
s, which act on qubits, onto physical properties of the instruments.
To use the Zurich Instruments backend in compilation, one should pass a valid hardware compilation configuration to the quantum_device.hardware_config
parameter, such that it can be used to generate a full CompilationConfig
using quantum_device.generate_compilation_config()
, which can finally be used to compile a Schedule
using compile()
. The "config_type"
entry specifies to the scheduler that we are using the Zurich Instruments backend (specifically, the quantify_scheduler.backends.zhinst_backend.ZIHardwareCompilationConfig
DataStructure will be parsed).
See the hardware verification tutorial for an example.
Example hardware compilation configuration
import json
from quantify_scheduler.schemas.examples.utils import load_json_example_scheme
from quantify_scheduler.backends.zhinst_backend import ZIHardwareCompilationConfig
hardware_compilation_config = load_json_example_scheme("zhinst_hardware_compilation_config.json")
print(json.dumps(hardware_compilation_config, indent=4, sort_keys=False))
ZIHardwareCompilationConfig.model_validate(hardware_compilation_config)
{
"config_type": "quantify_scheduler.backends.zhinst_backend.ZIHardwareCompilationConfig",
"hardware_description": {
"ic_hdawg0": {
"instrument_type": "HDAWG8",
"ref": "int",
"clock_select": 0,
"channelgrouping": 0,
"channel_0": {
"mode": "complex",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
]
},
"channel_1": {
"mode": "complex",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
]
},
"channel_2": {
"mode": "complex",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
]
},
"channel_3": {
"mode": "complex",
"markers": [
"AWG_MARKER1",
"AWG_MARKER2"
]
}
},
"ic_uhfqa0": {
"instrument_type": "UHFQA",
"ref": "ext",
"channel_0": {
"mode": "real",
"trigger": 2
}
},
"lo0_ch1": {
"instrument_type": "LocalOscillator",
"instrument_name": "lo0",
"frequency_param": "ch1.frequency",
"power": 13
},
"lo0_ch2": {
"instrument_type": "LocalOscillator",
"instrument_name": "lo0",
"frequency_param": "ch2.frequency",
"power_param": "ch2.power",
"power": 10
},
"lo1": {
"instrument_type": "LocalOscillator",
"instrument_name": "lo1",
"frequency_param": "frequency",
"power": 16
},
"iqm_ic_hdawg0_ch0": {
"instrument_type": "IQMixer"
},
"iqm_ic_hdawg0_ch1": {
"instrument_type": "IQMixer"
},
"iqm_ic_hdawg0_ch2": {
"instrument_type": "IQMixer"
},
"iqm_ic_hdawg0_ch3": {
"instrument_type": "IQMixer"
},
"iqm_ic_uhfqa0_ch0": {
"instrument_type": "IQMixer"
}
},
"hardware_options": {
"latency_corrections": {
"q0:mw-q0.01": 9.5e-08,
"q1:mw-q1.01": 9.5e-08,
"q0:res-q0.ro": -9.5e-08,
"q1:res-q1.ro": -9.5e-08
},
"distortion_corrections": {
"q0:fl-cl0.baseband": {
"filter_func": "scipy.signal.lfilter",
"input_var_name": "x",
"kwargs": {
"b": [
0,
0.25,
0.5
],
"a": [
1
]
},
"clipping_values": [
-2.5,
2.5
]
}
},
"modulation_frequencies": {
"q0:mw-q0.01": {
"interm_freq": -100000000.0,
"lo_freq": null
},
"q0:res-q0.ro": {
"interm_freq": 200000000.0,
"lo_freq": null
},
"q1:mw-q1.01": {
"interm_freq": -100000000.0,
"lo_freq": null
},
"q2:mw-q2.01": {
"interm_freq": -100000000.0,
"lo_freq": null
},
"q3:mw-q3.01": {
"interm_freq": -100000000.0,
"lo_freq": null
}
},
"mixer_corrections": {
"q0:mw-q0.01": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": -0.0542,
"dc_offset_q": -0.0328
},
"q1:mw-q1.01": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
},
"q2:mw-q2.01": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
},
"q3:mw-q3.01": {
"amp_ratio": 0.95,
"phase_error": 0.07,
"dc_offset_i": 0.042,
"dc_offset_q": 0.028
}
},
"output_gain": {
"q0:mw-q0.01": {
"gain_I": 1,
"gain_Q": 1
},
"q1:mw-q1.01": {
"gain_I": 1,
"gain_Q": 1
},
"q2:mw-q2.01": {
"gain_I": 1,
"gain_Q": 1
},
"q3:mw-q3.01": {
"gain_I": 1,
"gain_Q": 1
}
}
},
"connectivity": {
"graph": [
[
"ic_hdawg0.channel_0",
"iqm_ic_hdawg0_ch0.if"
],
[
"lo0_ch1.output",
"iqm_ic_hdawg0_ch0.lo"
],
[
"iqm_ic_hdawg0_ch0.rf",
"q0:mw"
],
[
"ic_hdawg0.channel_1",
"iqm_ic_hdawg0_ch1.if"
],
[
"lo0_ch2.output",
"iqm_ic_hdawg0_ch1.lo"
],
[
"iqm_ic_hdawg0_ch1.rf",
"q1:mw"
],
[
"ic_hdawg0.channel_2",
"iqm_ic_hdawg0_ch2.if"
],
[
"lo0_ch2.output",
"iqm_ic_hdawg0_ch2.lo"
],
[
"iqm_ic_hdawg0_ch2.rf",
"q2:mw"
],
[
"ic_hdawg0.channel_3",
"iqm_ic_hdawg0_ch3.if"
],
[
"lo0_ch2.output",
"iqm_ic_hdawg0_ch3.lo"
],
[
"iqm_ic_hdawg0_ch3.rf",
"q3:mw"
],
[
"ic_uhfqa0.channel_0",
"iqm_ic_uhfqa0_ch0.if"
],
[
"lo1.output",
"iqm_ic_uhfqa0_ch0.lo"
],
[
"iqm_ic_uhfqa0_ch0.rf",
"q0:res"
]
]
}
}
ZIHardwareCompilationConfig(config_type=<class 'quantify_scheduler.backends.zhinst_backend.ZIHardwareCompilationConfig'>, hardware_description={'ic_hdawg0': ZIHDAWG8Description(instrument_type='HDAWG8', ref='int', channelgrouping=0, clock_select=0, channel_0=ZIChannelDescription(mode='complex', markers=['AWG_MARKER1', 'AWG_MARKER2'], trigger=None), channel_1=ZIChannelDescription(mode='complex', markers=['AWG_MARKER1', 'AWG_MARKER2'], trigger=None), channel_2=ZIChannelDescription(mode='complex', markers=['AWG_MARKER1', 'AWG_MARKER2'], trigger=None), channel_3=ZIChannelDescription(mode='complex', markers=['AWG_MARKER1', 'AWG_MARKER2'], trigger=None)), 'ic_uhfqa0': ZIUHFQADescription(instrument_type='UHFQA', ref='ext', channel_0=ZIChannelDescription(mode='real', markers=[], trigger=2)), 'lo0_ch1': LocalOscillatorDescription(instrument_type='LocalOscillator', instrument_name='lo0', generic_icc_name=None, frequency_param='ch1.frequency', power_param='power', power=13), 'lo0_ch2': LocalOscillatorDescription(instrument_type='LocalOscillator', instrument_name='lo0', generic_icc_name=None, frequency_param='ch2.frequency', power_param='ch2.power', power=10), 'lo1': LocalOscillatorDescription(instrument_type='LocalOscillator', instrument_name='lo1', generic_icc_name=None, frequency_param='frequency', power_param='power', power=16), 'iqm_ic_hdawg0_ch0': IQMixerDescription(instrument_type='IQMixer'), 'iqm_ic_hdawg0_ch1': IQMixerDescription(instrument_type='IQMixer'), 'iqm_ic_hdawg0_ch2': IQMixerDescription(instrument_type='IQMixer'), 'iqm_ic_hdawg0_ch3': IQMixerDescription(instrument_type='IQMixer'), 'iqm_ic_uhfqa0_ch0': IQMixerDescription(instrument_type='IQMixer')}, hardware_options=ZIHardwareOptions(crosstalk=None, latency_corrections={'q0:mw-q0.01': 9.5e-08, 'q1:mw-q1.01': 9.5e-08, 'q0:res-q0.ro': -9.5e-08, 'q1:res-q1.ro': -9.5e-08}, distortion_corrections={'q0:fl-cl0.baseband': SoftwareDistortionCorrection(filter_func='scipy.signal.lfilter', input_var_name='x', kwargs={'b': [0, 0.25, 0.5], 'a': [1]}, clipping_values=[-2.5, 2.5], sampling_rate=1000000000.0)}, modulation_frequencies={'q0:mw-q0.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q0:res-q0.ro': ModulationFrequencies(interm_freq=200000000.0, lo_freq=None), 'q1:mw-q1.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q2:mw-q2.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q3:mw-q3.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None)}, mixer_corrections={'q0:mw-q0.01': MixerCorrections(dc_offset_i=-0.0542, dc_offset_q=-0.0328, amp_ratio=0.95, phase_error=0.07), 'q1:mw-q1.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07), 'q2:mw-q2.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07), 'q3:mw-q3.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07)}, output_gain={'q0:mw-q0.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q1:mw-q1.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q2:mw-q2.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q3:mw-q3.01': OutputGain(gain_I=1.0, gain_Q=1.0)}), connectivity=Connectivity(graph=<quantify_scheduler.structure.types.Graph object at 0x786078250100>), compilation_passes=[SimpleNodeConfig(name='flatten_schedule', compilation_func=<function flatten_schedule at 0x7860376b9f70>), SimpleNodeConfig(name='zhinst_hardware_compile', compilation_func=<function compile_backend at 0x7860376c00d0>)])
Hardware Description#
The Hardware Description describes the instruments that are used in the setup, along with some instrument-specific settings. The currently supported instruments are:
- class ZIHDAWG4Description(/, **data: Any)[source]
Information needed to specify a HDAWG4 in the
ZIHardwareCompilationConfig
.- channel_0: ZIChannelDescription | None = None[source]
Description of the first channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channel_1: ZIChannelDescription | None = None[source]
Description of the second channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channelgrouping: int[source]
The HDAWG channelgrouping property impacting the amount of HDAWG channels per AWG that must be used.. (default = 0) corresponding to a single sequencer controlling a pair (2) awg outputs.
- clock_select: int[source]
The clock rate divisor which will be used to get the instruments clock rate from the lookup dictionary in quantify_scheduler.backends.zhinst_backend.DEVICE_CLOCK_RATES.
For information see zhinst User manuals, section /DEV…./AWGS/n/TIME Examples: base sampling rate (1.8 GHz) divided by 2^clock_select. (default = 0)
- instrument_type: Literal['HDAWG4'][source]
The instrument type, used to select this datastructure when parsing a
ZIHardwareCompilationConfig
.
- ref: Literal['int'] | Literal['ext'] | Literal['none'][source]
Property that describes if the instrument uses Markers or Triggers. -
int
Enables sending Marker -ext
Enables waiting for Marker -none
Ignores waiting for Marker
- class ZIHDAWG8Description(/, **data: Any)[source]
Information needed to specify a HDAWG8 in the
ZIHardwareCompilationConfig
.- channel_0: ZIChannelDescription | None = None[source]
Description of the first channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channel_1: ZIChannelDescription | None = None[source]
Description of the second channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channel_2: ZIChannelDescription | None = None[source]
Description of the third channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channel_3: ZIChannelDescription | None = None[source]
Description of the fourth channel on this HDAWG (corresponding to 1 or 2 physical output ports).
- channelgrouping: int[source]
The HDAWG channelgrouping property impacting the amount of HDAWG channels per AWG that must be used.. (default = 0) corresponding to a single sequencer controlling a pair (2) awg outputs.
- clock_select: int[source]
The clock rate divisor which will be used to get the instruments clock rate from the lookup dictionary in quantify_scheduler.backends.zhinst_backend.DEVICE_CLOCK_RATES.
For information see zhinst User manuals, section /DEV…./AWGS/n/TIME Examples: base sampling rate (1.8 GHz) divided by 2^clock_select. (default = 0)
- instrument_type: Literal['HDAWG8'][source]
The instrument type, used to select this datastructure when parsing a
ZIHardwareCompilationConfig
.
- ref: Literal['int'] | Literal['ext'] | Literal['none'][source]
Property that describes if the instrument uses Markers or Triggers. -
int
Enables sending Marker -ext
Enables waiting for Marker -none
Ignores waiting for Marker
- class ZIUHFQADescription(/, **data: Any)[source]
Information needed to specify a UHFQA in the
ZIHardwareCompilationConfig
.- channel_0: ZIChannelDescription | None = None[source]
Description of the readout channel on this UHFQA.
- instrument_type: Literal['UHFQA'][source]
The instrument type, used to select this datastructure when parsing a
ZIHardwareCompilationConfig
.
- ref: Literal['int'] | Literal['ext'] | Literal['none'][source]
Property that describes if the instrument uses Markers or Triggers. -
int
Enables sending Marker -ext
Enables waiting for Marker -none
Ignores waiting for Marker
Warning
In order for the backend to find the QCodes Instrument it is required that the keys of the
HardwareDescription map 1-to-1 to the names given to the QCodes Instrument during instantiation, with an ic
prepend.
Example: If the hdawg QCodes Instrument name is “hdawg_dev8831” then the
Device
’sname
is “ic_hdawg_dev8831”
The channels of these instruments are described by
- class ZIChannelDescription(/, **data: Any)[source]
Information needed to specify a ZI Channel in the
ZIHardwareCompilationConfig
.A single ‘channel’ represents a complex output, consisting of two physical I/O channels on the Instrument.
- markers: list[str] = [][source]
Property that specifies which markers to trigger on each sequencer iteration. The values are used as input for the
setTrigger
sequencer instruction.
- mode: Literal['real'] | Literal['complex'][source]
The output mode type.
Local oscillators can also be included by using the following generic datastructure.
- class LocalOscillatorDescription(/, **data: Any)[source]
Information needed to specify a Local Oscillator in the
CompilationConfig
.- _only_default_generic_icc_name(generic_icc_name)[source]
- frequency_param: str = 'frequency'[source]
The QCoDeS parameter that is used to set the LO frequency.
- generic_icc_name: str | None = None[source]
The name of the
GenericInstrumentCoordinatorComponent
corresponding to this Local Oscillator.
- instrument_name: str | None = None[source]
The QCoDeS instrument name corresponding to this Local Oscillator.
- instrument_type: Literal['LocalOscillator'][source]
The field discriminator for this HardwareDescription datastructure.
Connectivity#
The Connectivity
describes how the inputs/outputs of the Zurich Instruments devices are connected to ports on the QuantumDevice
. As described in Connectivity, the connectivity datastructure can be parsed from a list of edges, which are described by a pair of strings that each specify a port on the quantum device, on a HDAWG/UHFQA, or on other auxiliary instruments (like external IQ mixers).
Each input/output node of the HDAWG/UHFQA should be specified in the connectivity as "{instrument_name}.{channel_name}"
. The possible channel names are the same as the allowed fields in the corresponding ZIHardwareDescription
datastructure:
for
"HDAWG4"
:"channel_{0,1}"
,for
"HDAWG8"
:"channel_{0,1,2,3}"
,for
"UHFQA"
:"channel_0"
.
Note
The UHFQA has both an output and an input channel. However, this is currently represented in the connectivity as a single channel that is connected (through IQ mixers) to a port on the quantum device. Both pulses and acquisitions will be assigned to this channel.
The connectivity can be visualized using:
from quantify_scheduler.backends.types.common import Connectivity
connectivity = Connectivity.model_validate(hardware_compilation_config["connectivity"])
connectivity.draw()
<Axes: >
External IQ mixers and local oscillators#
HDAWG/UHFQA channels can be connected to external IQ mixers and local oscillators. To achieve this, you should add a IQMixerDescription
and LocalOscillatorDescription
to the "hardware_description"
part of the hardware compilation config, and specify the connections of the "if"
, "lo"
and "rf"
ports on the IQ mixer in the "connectivity"
part of the hardware compilation config. The compiler will then use this information to assign the pulses and acquisitions to the port on the HDAWG/UHFQA that is connected to the "if"
port on the IQ mixer, and set the local oscillator and intermodulation frequencies accordingly.
1hardware_compilation_cfg = {
2 "config_type": "quantify_scheduler.backends.qblox_backend.ZIHardwareCompilationConfig",
3 "hardware_description": {
4 "ic_hdawg0": {...},
5 "lo1": {
6 "instrument_type": "LocalOscillator",
7 "power": 20
8 },
9 "iq_mixer1": {"instrument_type": "IQMixer"},
10 },
11 "hardware_options": {
12 "modulation_frequencies": {
13 "q1:mw-q1.01": {
14 "lo_freq": 5e9
15 }
16 }
17 },
18 "connectivity": {
19 "graph": [
20 ("ic_hdawg0.channel_0", "iq_mixer1.if"),
21 ("lo1.output", "iq_mixer1.lo"),
22 ("iq_mixer1.rf", "q1:mw"),
23 ]
24 }
25}
Hardware Options#
The ZIHardwareOptions
datastructure contains the settings used in compiling from the quantum-device layer to a set of instructions for the control hardware.
- class ZIHardwareOptions(/, **data: Any)[source]
Datastructure containing the hardware options for each port-clock combination.
Example
Here, the HardwareOptions datastructure is created by parsing a dictionary containing the relevant information.
import pprint from quantify_scheduler.schemas.examples.utils import ( load_json_example_scheme )
from quantify_scheduler.backends.types.zhinst import ( ZIHardwareOptions ) zi_hw_options_dict = load_json_example_scheme( "zhinst_hardware_compilation_config.json")["hardware_options"] pprint.pprint(zi_hw_options_dict) zi_hw_options = ZIHardwareOptions.model_validate(zi_hw_options_dict) zi_hw_options
{'distortion_corrections': {'q0:fl-cl0.baseband': {'clipping_values': [-2.5, 2.5], 'filter_func': 'scipy.signal.lfilter', 'input_var_name': 'x', 'kwargs': {'a': [1], 'b': [0, 0.25, 0.5]}}}, 'latency_corrections': {'q0:mw-q0.01': 9.5e-08, 'q0:res-q0.ro': -9.5e-08, 'q1:mw-q1.01': 9.5e-08, 'q1:res-q1.ro': -9.5e-08}, 'mixer_corrections': {'q0:mw-q0.01': {'amp_ratio': 0.95, 'dc_offset_i': -0.0542, 'dc_offset_q': -0.0328, 'phase_error': 0.07}, 'q1:mw-q1.01': {'amp_ratio': 0.95, 'dc_offset_i': 0.042, 'dc_offset_q': 0.028, 'phase_error': 0.07}, 'q2:mw-q2.01': {'amp_ratio': 0.95, 'dc_offset_i': 0.042, 'dc_offset_q': 0.028, 'phase_error': 0.07}, 'q3:mw-q3.01': {'amp_ratio': 0.95, 'dc_offset_i': 0.042, 'dc_offset_q': 0.028, 'phase_error': 0.07}}, 'modulation_frequencies': {'q0:mw-q0.01': {'interm_freq': -100000000.0, 'lo_freq': None}, 'q0:res-q0.ro': {'interm_freq': 200000000.0, 'lo_freq': None}, 'q1:mw-q1.01': {'interm_freq': -100000000.0, 'lo_freq': None}, 'q2:mw-q2.01': {'interm_freq': -100000000.0, 'lo_freq': None}, 'q3:mw-q3.01': {'interm_freq': -100000000.0, 'lo_freq': None}}, 'output_gain': {'q0:mw-q0.01': {'gain_I': 1, 'gain_Q': 1}, 'q1:mw-q1.01': {'gain_I': 1, 'gain_Q': 1}, 'q2:mw-q2.01': {'gain_I': 1, 'gain_Q': 1}, 'q3:mw-q3.01': {'gain_I': 1, 'gain_Q': 1}}}
ZIHardwareOptions(crosstalk=None, latency_corrections={'q0:mw-q0.01': 9.5e-08, 'q1:mw-q1.01': 9.5e-08, 'q0:res-q0.ro': -9.5e-08, 'q1:res-q1.ro': -9.5e-08}, distortion_corrections={'q0:fl-cl0.baseband': SoftwareDistortionCorrection(filter_func='scipy.signal.lfilter', input_var_name='x', kwargs={'b': [0, 0.25, 0.5], 'a': [1]}, clipping_values=[-2.5, 2.5], sampling_rate=1000000000.0)}, modulation_frequencies={'q0:mw-q0.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q0:res-q0.ro': ModulationFrequencies(interm_freq=200000000.0, lo_freq=None), 'q1:mw-q1.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q2:mw-q2.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None), 'q3:mw-q3.01': ModulationFrequencies(interm_freq=-100000000.0, lo_freq=None)}, mixer_corrections={'q0:mw-q0.01': MixerCorrections(dc_offset_i=-0.0542, dc_offset_q=-0.0328, amp_ratio=0.95, phase_error=0.07), 'q1:mw-q1.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07), 'q2:mw-q2.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07), 'q3:mw-q3.01': MixerCorrections(dc_offset_i=0.042, dc_offset_q=0.028, amp_ratio=0.95, phase_error=0.07)}, output_gain={'q0:mw-q0.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q1:mw-q1.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q2:mw-q2.01': OutputGain(gain_I=1.0, gain_Q=1.0), 'q3:mw-q3.01': OutputGain(gain_I=1.0, gain_Q=1.0)})
- distortion_corrections: dict[str, SoftwareDistortionCorrection] | None = None[source]
Dictionary containing the distortion corrections (values) that should be applied to waveforms on a certain port-clock combination (keys).
- latency_corrections: dict[str, LatencyCorrection] | None = None[source]
Dictionary containing the latency corrections (values) that should be applied to operations on a certain port-clock combination (keys).
- mixer_corrections: dict[str, MixerCorrections] | None = None[source]
Dictionary containing the mixer corrections (values) that should be used for signals on a certain port-clock combination (keys).
- modulation_frequencies: dict[str, ModulationFrequencies] | None = None[source]
Dictionary containing the modulation frequencies (values) that should be used for signals on a certain port-clock combination (keys).
- output_gain: dict[str, OutputGain] | None = None[source]
Dictionary containing the gain settings (values) that should be applied to the outputs that are connected to a certain port-clock combination (keys).
Note
In the Zurich Instruments backend, a LatencyCorrection
is implemented by incrementing the abs_time
of all operations applied to the port-clock combination.