helpers#

Helper functions for Qblox backend.

Module Contents#

Classes#

Frequencies

Holds and validates frequencies.

LoopBegin

Operation to indicate the beginning of a loop.

ConditionalBegin

Operation to indicate the beginning of a conditional.

_ControlFlowReturn

An operation that signals the end of the current control flow statement.

Functions#

generate_waveform_data(→ numpy.ndarray)

Generates an array using the parameters specified in data_dict.

generate_waveform_names_from_uuid(→ tuple[str, str])

Generates names for the I and Q parts of the complex waveform based on a unique

generate_uuid_from_wf_data(→ str)

Creates a unique identifier from the waveform data, using a hash. Identical arrays

add_to_wf_dict_if_unique(→ int)

Adds a waveform to the waveform dictionary if it is not yet in there and returns the

generate_waveform_dict(→ dict[str, dict])

Takes a dictionary with complex waveforms and generates a new dictionary with

to_grid_time(→ int)

Convert time value in s to time in ns, and verify that it is aligned with grid time.

is_multiple_of_grid_time(→ bool)

Determine whether a time value in seconds is a multiple of the grid time.

get_nco_phase_arguments(→ int)

Converts a phase in degrees to the int arguments the NCO phase instructions expect.

get_nco_set_frequency_arguments(→ int)

Converts a frequency in Hz to the int argument the NCO set_freq instruction expects.

determine_clock_lo_interm_freqs(→ Frequencies)

From known frequency for the local oscillator or known intermodulation frequency,

generate_port_clock_to_device_map(→ dict[tuple[str, ...)

Generates a mapping that specifies which port-clock combinations belong to which

_get_control_flow_begin(...)

_get_control_flow_end(...)

_get_list_of_operations_for_op_info_creation(→ None)

assign_pulse_and_acq_info_to_devices(schedule, ...)

Traverses the schedule and generates OpInfo objects for every pulse and

calc_from_units_volt(→ float | None)

Helper method to calculate the offset from mV or V.

single_scope_mode_acquisition_raise(sequencer_0, ...)

Raises an error stating that only one scope mode acquisition can be used per module.

_preprocess_legacy_hardware_config(→ dict[str, Any])

Modify a legacy hardware config into a form that is compatible with the current backend.

_generate_new_style_hardware_compilation_config(→ dict)

Generate a new-style QbloxHardwareCompilationConfig from an old-style hardware config.

generate_waveform_data(data_dict: dict, sampling_rate: float, duration: float | None = None) numpy.ndarray[source]#

Generates an array using the parameters specified in data_dict.

Parameters:
  • data_dict (dict) – The dictionary that contains the values needed to parameterize the waveform. data_dict['wf_func'] is then called to calculate the values.

  • sampling_rate (float) – The sampling rate used to generate the time axis values.

  • duration (float or None, optional) – The duration of the waveform in seconds. This parameter can be used if data_dict does not contain a 'duration' key. By default None.

Returns:

wf_data – The (possibly complex) values of the generated waveform. The number of values is determined by rounding to the nearest integer.

Return type:

np.ndarray

Raises:

TypeError – If data_dict does not contain a 'duration' entry and duration is None.

generate_waveform_names_from_uuid(uuid: Any) tuple[str, str][source]#

Generates names for the I and Q parts of the complex waveform based on a unique identifier for the pulse/acquisition.

Parameters:

uuid – A unique identifier for a pulse/acquisition.

Returns:

  • uuid_I – Name for the I waveform.

  • uuid_Q – Name for the Q waveform.

generate_uuid_from_wf_data(wf_data: numpy.ndarray, decimals: int = 12) str[source]#

Creates a unique identifier from the waveform data, using a hash. Identical arrays yield identical strings within the same process.

Parameters:
  • wf_data – The data to generate the unique id for.

  • decimals – The number of decimal places to consider.

Returns:

A unique identifier.

add_to_wf_dict_if_unique(wf_dict: dict[str, Any], waveform: numpy.ndarray) int[source]#

Adds a waveform to the waveform dictionary if it is not yet in there and returns the uuid and index. If it is already present it simply returns the uuid and index.

Parameters:
  • wf_dict – The waveform dict in the format expected by the sequencer.

  • waveform – The waveform to add.

Returns:

  • Dict[str, Any] – The (updated) wf_dict.

  • str – The uuid of the waveform.

  • int – The index.

generate_waveform_dict(waveforms_complex: dict[str, numpy.ndarray]) dict[str, dict][source]#

Takes a dictionary with complex waveforms and generates a new dictionary with real valued waveforms with a unique index, as required by the hardware.

Parameters:

waveforms_complex – Dictionary containing the complex waveforms. Keys correspond to a unique identifier, value is the complex waveform.

Returns:

A dictionary with as key the unique name for that waveform, as value another dictionary containing the real-valued data (list) as well as a unique index. Note that the index of the Q waveform is always the index of the I waveform +1.

Return type:

dict[str, dict]

Examples

import numpy as np
from quantify_scheduler.backends.qblox.helpers import generate_waveform_dict

complex_waveforms = {12345: np.array([1, 2])}
generate_waveform_dict(complex_waveforms)

# {'12345_I': {'data': [1, 2], 'index': 0},
# '12345_Q': {'data': [0, 0], 'index': 1}}
{'12345_I': {'data': [1, 2], 'index': 0},
 '12345_Q': {'data': [0, 0], 'index': 1}}
to_grid_time(time: float, grid_time_ns: int = constants.GRID_TIME) int[source]#

Convert time value in s to time in ns, and verify that it is aligned with grid time.

Takes a float value representing a time in seconds as used by the schedule, and returns the integer valued time in nanoseconds that the sequencer uses.

The time value needs to be aligned with grid time, i.e., needs to be a multiple of GRID_TIME, within a tolerance of 1 picosecond.

Parameters:
  • time – A time value in seconds.

  • grid_time_ns – The grid time to use in nanoseconds.

Returns:

The integer valued nanosecond time.

Raises:

ValueError – If time is not a multiple of GRID_TIME within the tolerance.

is_multiple_of_grid_time(time: float, grid_time_ns: int = constants.GRID_TIME) bool[source]#

Determine whether a time value in seconds is a multiple of the grid time.

Within a tolerance as defined by to_grid_time().

Parameters:
  • time – A time value in seconds.

  • grid_time_ns – The grid time to use in nanoseconds.

Returns:

True if time is a multiple of the grid time, False otherwise.

get_nco_phase_arguments(phase_deg: float) int[source]#

Converts a phase in degrees to the int arguments the NCO phase instructions expect. We take phase_deg modulo 360 to account for negative phase and phase larger than 360.

Parameters:

phase_deg – The phase in degrees

Returns:

The int corresponding to the phase argument.

get_nco_set_frequency_arguments(frequency_hz: float) int[source]#

Converts a frequency in Hz to the int argument the NCO set_freq instruction expects.

Parameters:

frequency_hz – The frequency in Hz.

Returns:

The frequency expressed in steps for the NCO set_freq instruction.

Raises:

ValueError – If the frequency_hz is out of range.

class Frequencies[source]#

Holds and validates frequencies.

clock: float[source]#
LO: float | None = None[source]#
IF: float | None = None[source]#
validate()[source]#

Validates frequencies.

determine_clock_lo_interm_freqs(freqs: Frequencies, downconverter_freq: float | None = None, mix_lo: bool | None = True) Frequencies[source]#

From known frequency for the local oscillator or known intermodulation frequency, determine any missing frequency, after optionally applying downconverter_freq to the clock frequency.

If mix_lo is True, the following relation is obeyed: \(f_{RF} = f_{LO} + f_{IF}\).

If mix_lo is False, \(f_{RF} = f_{LO}\) is upheld.

Warning

Using downconverter_freq requires custom Qblox hardware, do not use otherwise.

Parameters:
  • freqs (Frequencies) – Frequencies object containing clock, local oscillator (LO) and Intermodulation frequency (IF), the frequency of the numerically controlled oscillator (NCO).

  • downconverter_freq (Optional[float]) – Frequency for downconverting the clock frequency, using: \(f_\mathrm{out} = f_\mathrm{downconverter} - f_\mathrm{in}\).

  • mix_lo (bool) – Flag indicating whether IQ mixing is enabled with the LO.

Returns:

Frequencies object containing the determined LO and IF frequencies and the optionally downconverted clock frequency.

Warns:
  • RuntimeWarning – In case downconverter_freq is set equal to 0, warns to unset via null/None instead.

  • RuntimeWarning – In case LO is overridden to clock due to mix_lo being False

Raises:
  • ValueError – In case downconverter_freq is less than 0.

  • ValueError – In case downconverter_freq is less than clock_freq.

  • ValueError – In case mix_lo is True and neither LO frequency nor IF has been supplied.

  • ValueError – In case mix_lo is True and both LO frequency and IF have been supplied and do not adhere to \(f_{RF} = f_{LO} + f_{IF}\).

generate_port_clock_to_device_map(device_compilers: dict[str, Any]) dict[tuple[str, str], str][source]#

Generates a mapping that specifies which port-clock combinations belong to which device.

Here, device means a top-level entry in the hardware config, e.g. a Cluster, not which module within the Cluster.

Each port-clock combination may only occur once.

Parameters:

device_compilers – Dictionary containing compiler configs.

Returns:

A dictionary with as key a tuple representing a port-clock combination, and as value the name of the device. Note that multiple port-clocks may point to the same device.

Raises:

ValueError – If a port-clock combination occurs multiple times in the hardware configuration.

class LoopBegin(repetitions: int, t0: float = 0)[source]#

Bases: quantify_scheduler.operations.operation.Operation

Operation to indicate the beginning of a loop.

Parameters:
  • repetitions (int) – number of repetitions

  • t0 (float, optional) – time offset, by default 0

class ConditionalBegin(qubit_name: str, feedback_trigger_address: int, t0: float)[source]#

Bases: quantify_scheduler.operations.operation.Operation

Operation to indicate the beginning of a conditional.

Parameters:
  • qubit_name – The name of the qubit to condition on.

  • feedback_trigger_address – Feedback trigger address

  • t0 – Time offset, by default 0

class_name[source]#
_get_control_flow_begin(control_flow_operation: quantify_scheduler.operations.control_flow_library.ControlFlowOperation) quantify_scheduler.operations.operation.Operation[source]#
class _ControlFlowReturn(t0: float = 0)[source]#

Bases: quantify_scheduler.operations.operation.Operation

An operation that signals the end of the current control flow statement.

Cannot be added to Schedule manually.

Parameters:

t0 (float, optional) – time offset, by default 0

_get_control_flow_end(control_flow_operation: quantify_scheduler.operations.control_flow_library.ControlFlowOperation) quantify_scheduler.operations.operation.Operation[source]#
_get_list_of_operations_for_op_info_creation(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, time_offset: float, accumulator: list[tuple[float, quantify_scheduler.operations.operation.Operation]]) None[source]#
assign_pulse_and_acq_info_to_devices(schedule: quantify_scheduler.schedules.schedule.Schedule, device_compilers: dict[str, quantify_scheduler.backends.qblox.instrument_compilers.ClusterCompiler])[source]#

Traverses the schedule and generates OpInfo objects for every pulse and acquisition, and assigns it to the correct ClusterCompiler.

Parameters:
  • schedule – The schedule to extract the pulse and acquisition info from.

  • device_compilers – Dictionary containing InstrumentCompilers as values and their names as keys.

  • portclock_to_path – Dictionary containing the hardware path to connected to each portclock.

Raises:
  • RuntimeError – This exception is raised then the function encountered an operation that has no pulse or acquisition info assigned to it.

  • KeyError – This exception is raised when attempting to assign a pulse with a port-clock combination that is not defined in the hardware configuration.

  • KeyError – This exception is raised when attempting to assign an acquisition with a port-clock combination that is not defined in the hardware configuration.

calc_from_units_volt(voltage_range, name: str, param_name: str, offset: float | None) float | None[source]#

Helper method to calculate the offset from mV or V. Then compares to given voltage range, and throws a ValueError if out of bounds.

Parameters:
  • voltage_range – The range of the voltage levels of the device used.

  • name – The name of the device used.

  • param_name – The name of the offset parameter this method is using.

  • offset – The value of the offset parameter this method is using.

Returns:

The normalized offsets.

Raises:

RuntimeError – When a unit range is given that is not supported, or a value is given that falls outside the allowed range.

single_scope_mode_acquisition_raise(sequencer_0, sequencer_1, module_name)[source]#

Raises an error stating that only one scope mode acquisition can be used per module.

Parameters:
  • sequencer_0 – First sequencer which attempts to use the scope mode acquisition.

  • sequencer_1 – Second sequencer which attempts to use the scope mode acquisition.

  • module_name – Name of the module.

Raises:

ValueError – Always raises the error message.

_preprocess_legacy_hardware_config(hardware_config: dict[str, Any]) dict[str, Any][source]#

Modify a legacy hardware config into a form that is compatible with the current backend.

_generate_new_style_hardware_compilation_config(old_style_config: dict) dict[source]#

Generate a new-style QbloxHardwareCompilationConfig from an old-style hardware config.

Parameters:

old_style_config – Old-style hardware config.

Returns:

New-style hardware compilation config dictionary.

Return type:

dict