HardwareCompilationConfig
migration guide#
The HardwareCompilationConfig
replaces the old-style unvalidated json/dict hardware configuration, adding the validation of the contents and restructuring into "hardware_description"
, "hardware_options"
and "connectivity"
. These parts are described in detail in the Hardware compilation configuration section in the User Guide. Here, we describe how to migrate from the old-style configuration to the new one for the current hardware backends, which each define their own HardwareCompilationConfig
datastructure with backend-specific fields (e.g., QbloxHardwareCompilationConfig
and ZIHardwareCompilationConfig
).
Custom backends
If you have implemented a custom backend, or have added custom instruments and/or options to the Qblox or Zurich Instruments backends, you will need to define your own HardwareCompilationConfig
datastructure, as is already done for the Qblox and Zurich Instruments backends.
Qblox#
The QbloxHardwareCompilationConfig
inherits from the HardwareCompilationConfig
and contains the following backend-specific fields:
The
QbloxHardwareDescription
, which specifies the instruments that are used in the setup, along with their instrument-specific settings. For the Qblox Cluster, we refer to the Hardware description section for the allowed instrument types and settings.The
QbloxHardwareOptions
, which adds some backend-specific options to the genericHardwareOptions
(see Hardware options for more details).The
compilation_passes
field, which specifies the compilation passes that are used to compile the schedule. In the example below, we do not specify the compilation passes, which means that the default passes are used.
Under construction
The new-style QbloxHardwareCompilationConfig
is currently still under construction and subject to change.
To show how to migrate, we first load an example old-style hardware configuration for the Qblox backend:
import rich
from quantify_scheduler.backends.qblox.qblox_hardware_config_old_style import hardware_config as qblox_hardware_config_old_style
rich.print(qblox_hardware_config_old_style)
Show code cell output
{ 'backend': 'quantify_scheduler.backends.qblox_backend.hardware_compile', 'latency_corrections': {'q4:mw-q4.01': 8e-09, 'q5:mw-q5.01': 4e-09}, '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 } }, 'cluster0': { 'ref': 'internal', 'instrument_type': 'Cluster', 'sequence_to_file': False, 'cluster0_module1': { 'instrument_type': 'QCM', 'sequence_to_file': False, 'complex_output_0': { 'lo_name': 'lo0', 'dc_mixer_offset_I': 0.0, 'dc_mixer_offset_Q': 0.0, 'downconverter_freq': None, 'marker_debug_mode_enable': True, 'distortion_correction_latency_compensation': <DistortionCorrectionLatencyEnum.NO_DELAY_COMP: 0>, 'mix_lo': True, 'portclock_configs': [ { 'port': 'q4:mw', 'clock': 'q4.01', 'interm_freq': 200000000.0, 'mixer_amp_ratio': 0.9999, 'mixer_phase_error_deg': -4.2 } ] } }, 'cluster0_module2': { 'instrument_type': 'QCM_RF', 'sequence_to_file': False, 'complex_output_0': { 'lo_freq': None, 'output_att': 4, 'portclock_configs': [ {'port': 'q0:mw', 'clock': 'q0.01', 'interm_freq': 50000000.0}, {'port': 'q5:mw', 'clock': 'q5.01', 'interm_freq': 50000000.0} ] }, 'complex_output_1': { 'lo_freq': 5000000000.0, 'output_att': 6, 'portclock_configs': [{'port': 'q6:mw', 'clock': 'q6.01', 'interm_freq': None}] }, 'digital_output_0': { 'portclock_configs': [{'port': 'q0:switch', 'clock': 'digital'}], 'distortion_correction_latency_compensation': <DistortionCorrectionLatencyEnum.NO_DELAY_COMP: 0> } }, 'cluster0_module3': { 'instrument_type': 'QRM', 'sequence_to_file': False, 'complex_output_0': { 'lo_name': 'lo1', 'dc_mixer_offset_I': -0.054, 'dc_mixer_offset_Q': -0.034, 'input_gain_I': 2, 'input_gain_Q': 3, 'portclock_configs': [ { 'mixer_amp_ratio': 0.9997, 'mixer_phase_error_deg': -4.0, 'port': 'q4:res', 'clock': 'q4.ro', 'interm_freq': None } ] }, 'complex_input_0': { 'portclock_configs': [ { 'port': 'qe0:optical_readout', 'clock': 'qe0.ge0', 'ttl_acq_threshold': 0.5, 'init_gain_awg_path_I': 1.0, 'init_gain_awg_path_Q': 1.0, 'init_offset_awg_path_I': 0.0, 'init_offset_awg_path_Q': 0.0, 'qasm_hook_func': None } ] } }, 'cluster0_module4': { 'instrument_type': 'QRM_RF', 'sequence_to_file': False, 'complex_input_0': { 'lo_freq': None, 'input_att': 10, 'portclock_configs': [{'port': 'q5:res', 'clock': 'q5.ro', 'interm_freq': 50000000.0}] }, 'complex_output_0': { 'lo_freq': 7800000000.0, 'output_att': 12, 'input_att': 4, 'portclock_configs': [{'port': 'q0:res', 'clock': 'q0.ro', 'interm_freq': None}] } }, 'cluster0_module7': { 'instrument_type': 'QCM', 'sequence_to_file': False, 'real_output_0': { 'lo_name': 'lo_real', 'portclock_configs': [{'port': 'q7:mw', 'clock': 'q7.01', 'interm_freq': None}] } }, 'cluster0_module10': { 'instrument_type': 'QCM', 'sequence_to_file': False, 'real_output_0': {'portclock_configs': [{'port': 'q0:fl', 'clock': 'cl0.baseband'}]}, 'real_output_1': {'portclock_configs': [{'port': 'q1:fl', 'clock': 'cl0.baseband'}]}, 'real_output_2': {'portclock_configs': [{'port': 'q2:fl', 'clock': 'cl0.baseband'}]}, 'real_output_3': {'portclock_configs': [{'port': 'q3:fl', 'clock': 'cl0.baseband'}]} }, 'cluster0_module12': { 'instrument_type': 'QCM', 'sequence_to_file': False, 'real_output_0': {'portclock_configs': [{'port': 'q4:fl', 'clock': 'cl0.baseband'}]} } }, 'lo0': {'instrument_type': 'LocalOscillator', 'frequency': None, 'power': 1}, 'lo1': {'instrument_type': 'LocalOscillator', 'frequency': 7200000000.0, 'power': 1}, 'lo_real': {'instrument_type': 'LocalOscillator', 'frequency': 5000000000.0, 'power': 1} }
This config can be migrated to the new QbloxHardwareCompilationConfig
datastructure using the built-in validation, which will recognize the old-style config and convert it to the new-style config:
from quantify_scheduler.backends.qblox_backend import QbloxHardwareCompilationConfig
qblox_hardware_config_new_style = QbloxHardwareCompilationConfig.model_validate(
qblox_hardware_config_old_style
)
rich.print(qblox_hardware_config_new_style)
Show code cell output
QbloxHardwareCompilationConfig( config_type=<class 'quantify_scheduler.backends.qblox_backend.QbloxHardwareCompilationConfig'>, hardware_description={ 'cluster0': ClusterDescription( instrument_type='Cluster', ref='internal', sequence_to_file=False, modules={ 1: QCMDescription( instrument_type='QCM', sequence_to_file=False, complex_output_0=ComplexChannelDescription( marker_debug_mode_enable=True, mix_lo=True, downconverter_freq=None, distortion_correction_latency_compensation=<DistortionCorrectionLatencyEnum.NO_DELAY_COMP: 0> ), complex_output_1=None, real_output_0=None, real_output_1=None, real_output_2=None, real_output_3=None, digital_output_0=None, digital_output_1=None, digital_output_2=None, digital_output_3=None ), 2: QCMRFDescription( instrument_type='QCM_RF', sequence_to_file=False, complex_output_0=None, complex_output_1=None, digital_output_0=DigitalChannelDescription( distortion_correction_latency_compensation=<DistortionCorrectionLatencyEnum.NO_DELAY_COMP: 0> ), digital_output_1=None ), 3: QRMDescription( instrument_type='QRM', sequence_to_file=False, complex_output_0=None, complex_input_0=None, real_output_0=None, real_output_1=None, real_input_0=None, real_input_1=None, digital_output_0=None, digital_output_1=None, digital_output_2=None, digital_output_3=None ), 4: QRMRFDescription( instrument_type='QRM_RF', sequence_to_file=False, complex_output_0=None, complex_input_0=None, digital_output_0=None, digital_output_1=None ), 7: QCMDescription( instrument_type='QCM', sequence_to_file=False, complex_output_0=None, complex_output_1=None, real_output_0=None, real_output_1=None, real_output_2=None, real_output_3=None, digital_output_0=None, digital_output_1=None, digital_output_2=None, digital_output_3=None ), 10: QCMDescription( instrument_type='QCM', sequence_to_file=False, complex_output_0=None, complex_output_1=None, real_output_0=None, real_output_1=None, real_output_2=None, real_output_3=None, digital_output_0=None, digital_output_1=None, digital_output_2=None, digital_output_3=None ), 12: QCMDescription( instrument_type='QCM', sequence_to_file=False, complex_output_0=None, complex_output_1=None, real_output_0=None, real_output_1=None, real_output_2=None, real_output_3=None, digital_output_0=None, digital_output_1=None, digital_output_2=None, digital_output_3=None ) } ), 'iq_mixer_lo0': IQMixerDescription(instrument_type='IQMixer'), 'iq_mixer_lo1': IQMixerDescription(instrument_type='IQMixer'), 'iq_mixer_lo_real': IQMixerDescription(instrument_type='IQMixer'), 'lo0': LocalOscillatorDescription( instrument_type='LocalOscillator', instrument_name=None, generic_icc_name=None, frequency_param='frequency', power_param='power', power=1 ), 'lo1': LocalOscillatorDescription( instrument_type='LocalOscillator', instrument_name=None, generic_icc_name=None, frequency_param='frequency', power_param='power', power=1 ), 'lo_real': LocalOscillatorDescription( instrument_type='LocalOscillator', instrument_name=None, generic_icc_name=None, frequency_param='frequency', power_param='power', power=1 ) }, hardware_options=QbloxHardwareOptions( latency_corrections={'q4:mw-q4.01': 8e-09, 'q5:mw-q5.01': 4e-09}, 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={ 'q4:mw-q4.01': ModulationFrequencies(interm_freq=200000000.0, lo_freq=None), 'q0:mw-q0.01': ModulationFrequencies(interm_freq=50000000.0, lo_freq=None), 'q5:mw-q5.01': ModulationFrequencies(interm_freq=50000000.0, lo_freq=None), 'q6:mw-q6.01': ModulationFrequencies(interm_freq=None, lo_freq=5000000000.0), 'q4:res-q4.ro': ModulationFrequencies(interm_freq=None, lo_freq=7200000000.0), 'q5:res-q5.ro': ModulationFrequencies(interm_freq=50000000.0, lo_freq=None), 'q0:res-q0.ro': ModulationFrequencies(interm_freq=None, lo_freq=7800000000.0), 'q7:mw-q7.01': ModulationFrequencies(interm_freq=None, lo_freq=5000000000.0) }, mixer_corrections={ 'q4:mw-q4.01': MixerCorrections(dc_offset_i=0.0, dc_offset_q=0.0, amp_ratio=0.9999, phase_error=-4.2), 'q4:res-q4.ro': MixerCorrections( dc_offset_i=-0.054, dc_offset_q=-0.034, amp_ratio=0.9997, phase_error=-4.0 ) }, input_gain={'q4:res-q4.ro': ComplexInputGain(gain_I=2, gain_Q=3)}, output_att={'q0:mw-q0.01': 4, 'q5:mw-q5.01': 4, 'q6:mw-q6.01': 6, 'q0:res-q0.ro': 12}, input_att={'q5:res-q5.ro': 10, 'q0:res-q0.ro': 4}, sequencer_options={ 'qe0:optical_readout-qe0.ge0': SequencerOptions( init_offset_awg_path_I=0.0, init_offset_awg_path_Q=0.0, init_gain_awg_path_I=1.0, init_gain_awg_path_Q=1.0, ttl_acq_threshold=0.5, qasm_hook_func=None ) } ), connectivity=Connectivity(graph=<quantify_scheduler.structure.types.Graph object at 0x7e736e3dc5e0>), compilation_passes=[ SimpleNodeConfig( name='compile_long_square_pulses_to_awg_offsets', compilation_func=<function compile_long_square_pulses_to_awg_offsets at 0x7e736e395550> ), SimpleNodeConfig( name='qblox_compile_conditional_playback', compilation_func=<function compile_conditional_playback at 0x7e736e3954c0> ), SimpleNodeConfig( name='qblox_hardware_compile', compilation_func=<function hardware_compile at 0x7e736e3955e0> ) ] )
This new-style config can then be passed to the QuantumDevice
in order to compile a schedule for the Qblox backend, as is shown in the Compilation section of the Tutorial: Compiling to Hardware.
Zurich Instruments#
The ZIHardwareCompilationConfig
inherits from the HardwareCompilationConfig
and contains the following backend-specific fields:
The
QbloxHardwareDescription
, which specifies the instruments that are used in the setup, along with their instrument-specific settings. We refer to the Zurich Instruments Hardware Description section for the allowed instrument types and settings.The
ZIHardwareOptions
, which adds backend-specific options to the genericHardwareOptions
, see Hardware Options for more details.The
compilation_passes
field, which specifies the compilation passes that are used to compile the schedule. In the example below, we do not specify the compilation passes, which means that the default passes are used.
To show how to migrate, we first load an example old-style hardware configuration for the Zurich Instruments backend:
import rich
from quantify_scheduler.backends.zhinst.zhinst_hardware_config_old_style import hardware_config as zhinst_hardware_config_old_style
rich.print(zhinst_hardware_config_old_style)
Show code cell output
{ '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': None}, 'frequency_param': 'ch1.frequency', 'power': {'power': 13} }, { 'unique_name': 'lo0_ch2', 'instrument_name': 'lo0', 'frequency': {'ch2.frequency': None}, 'frequency_param': 'ch2.frequency', 'power': {'ch2.power': 10} }, { 'unique_name': 'lo1', 'instrument_name': 'lo1', 'frequency': {'frequency': None}, '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': None }, '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': None }, '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': None }, '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': None } }, { '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': [] } } ] }
This config can be migrated to the new ZIHardwareCompilationConfig
datastructure using the built-in validation, which will recognize the old-style config and convert it to the new-style config:
from quantify_scheduler.backends.zhinst_backend import ZIHardwareCompilationConfig
zhinst_hardware_config_new_style = ZIHardwareCompilationConfig.model_validate(
zhinst_hardware_config_old_style
)
rich.print(zhinst_hardware_config_new_style)
Show code cell output
ZIHardwareCompilationConfig( config_type=<class 'quantify_scheduler.backends.zhinst_backend.ZIHardwareCompilationConfig'>, hardware_description={ '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 ), '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) ), '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'), 'ic_uhfqa0': ZIUHFQADescription( instrument_type='UHFQA', ref='ext', channel_0=ZIChannelDescription(mode='real', markers=[], trigger=2) ), 'iqm_ic_uhfqa0_ch0': IQMixerDescription(instrument_type='IQMixer') }, hardware_options=ZIHardwareOptions( 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), '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), 'q0:res-q0.ro': ModulationFrequencies(interm_freq=200000000.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 0x7e736e28b7c0>), compilation_passes=[ SimpleNodeConfig( name='zhinst_hardware_compile', compilation_func=<function compile_backend at 0x7e736c66e4c0> ) ] )
This new-style config can then be passed to the QuantumDevice
in order to compile a schedule for the Zurich Instruments backend, as is shown in the Compilation section of the Tutorial: Compiling to Hardware.