qblox_backend ============= .. py:module:: quantify_scheduler.backends.qblox_backend .. autoapi-nested-parse:: Compiler backend for Qblox hardware. Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: quantify_scheduler.backends.qblox_backend.OperationTimingInfo quantify_scheduler.backends.qblox_backend.ConditionalAddress quantify_scheduler.backends.qblox_backend.QbloxHardwareCompilationConfig quantify_scheduler.backends.qblox_backend._LocalOscillatorCompilationConfig quantify_scheduler.backends.qblox_backend._ClusterCompilationConfig quantify_scheduler.backends.qblox_backend._ClusterModuleCompilationConfig quantify_scheduler.backends.qblox_backend._SequencerCompilationConfig quantify_scheduler.backends.qblox_backend.ChannelPath Functions ~~~~~~~~~ .. autoapisummary:: quantify_scheduler.backends.qblox_backend._replace_long_square_pulses_recursively quantify_scheduler.backends.qblox_backend._replace_long_square_pulses quantify_scheduler.backends.qblox_backend._all_conditional_acqs_and_control_flows_and_latch_reset quantify_scheduler.backends.qblox_backend._set_conditional_address_map quantify_scheduler.backends.qblox_backend._insert_latch_reset quantify_scheduler.backends.qblox_backend.compile_conditional_playback quantify_scheduler.backends.qblox_backend.compile_long_square_pulses_to_awg_offsets quantify_scheduler.backends.qblox_backend.hardware_compile quantify_scheduler.backends.qblox_backend._all_abs_times_ops_with_voltage_offsets_pulses quantify_scheduler.backends.qblox_backend._add_clock_freqs_to_set_clock_frequency quantify_scheduler.backends.qblox_backend.validate_non_overlapping_stitched_pulse quantify_scheduler.backends.qblox_backend._exists_pulse_starting_before_current_end quantify_scheduler.backends.qblox_backend._raise_if_pulses_overlap_on_same_port_clock quantify_scheduler.backends.qblox_backend._get_pulse_start_ends quantify_scheduler.backends.qblox_backend._operation_end quantify_scheduler.backends.qblox_backend._check_nco_operations_on_nco_time_grid quantify_scheduler.backends.qblox_backend._check_nco_operations_on_nco_time_grid_recursively quantify_scheduler.backends.qblox_backend._check_nco_grid_timing quantify_scheduler.backends.qblox_backend._is_nco_operation .. py:function:: _replace_long_square_pulses_recursively(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule) -> quantify_scheduler.operations.operation.Operation | None Generate a dict referring to long square pulses to replace in the schedule. This function generates a mapping (dict) from the keys in the :meth:`~quantify_scheduler.schedules.schedule.ScheduleBase.operations` dict to a list of indices, which refer to entries in the `"pulse_info"` list that describe a square pulse. :param operation: An operation, possibly containing long square pulses. .. py:function:: _replace_long_square_pulses(operation: quantify_scheduler.operations.operation.Operation, square_pulse_idx_to_replace: list[int]) -> quantify_scheduler.operations.operation.Operation | None Replace any square pulses indicated by pulse_idx_map by a ``long_square_pulse``. :param operation: Operation to be replaced. :param square_pulse_idx_to_replace: A list of indices in the pulse info to be replaced. :returns: * *operation* -- The operation to be replaced. If returns ``None``, the operation does not need to be replaced in the schedule or control flow. * *square_pulse_idx_to_replace* -- The pulse indices that need to be replaced in the operation. .. py:function:: _all_conditional_acqs_and_control_flows_and_latch_reset(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, time_offset: float, accumulator: List[Tuple[float, quantify_scheduler.operations.operation.Operation]]) -> None .. py:class:: OperationTimingInfo Timing information for an Operation. .. py:attribute:: start :type: float start time of the operation. .. py:attribute:: end :type: float end time of the operation. .. py:method:: from_operation_and_schedulable(operation: quantify_scheduler.operations.operation.Operation, schedulable: quantify_scheduler.schedules.schedule.Schedulable) -> OperationTimingInfo :classmethod: Create an ``OperationTimingInfo`` from an operation and a schedulable. .. py:method:: overlaps_with(operation_timing_info: OperationTimingInfo) -> bool Check if this operation timing info overlaps with another. .. py:class:: ConditionalAddress Container for conditional address data. .. py:attribute:: portclocks :type: set[tuple[str, str]] .. py:attribute:: address :type: int .. py:function:: _set_conditional_address_map(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, conditional_address_map: collections.defaultdict[str, ConditionalAddress]) -> None .. py:function:: _insert_latch_reset(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, abs_time_relative_to_schedule: float, schedule: quantify_scheduler.schedules.schedule.Schedule, conditional_address_map: collections.defaultdict[str, ConditionalAddress]) -> None .. py:function:: compile_conditional_playback(schedule: quantify_scheduler.schedules.schedule.Schedule, config: quantify_scheduler.structure.model.DataStructure | dict) -> quantify_scheduler.schedules.schedule.Schedule Compiles conditional playback. This compiler pass will determine the mapping between trigger labels and trigger addresses that the hardware will use. The feedback trigger address is stored under the key ``feedback_trigger_address`` in ``pulse_info`` and in ``acquisition_info`` of the corresponding operation. A valid conditional playback consists of two parts: (1) a conditional acquisition or measure, and (2) a conditional control flow. The first should always be followed by the second, else an error is raised. A conditional acquisition sends a trigger after the acquisition ends and if the acquisition crosses a certain threshold. Each sequencer that is subscribed to this trigger will increase their *latch* counters by one. To ensure the latch counters contain either 0 or 1 trigger counts, a :class:`~quantify_scheduler.backends.qblox.operations.pulse_library.LatchReset` operation is inserted right after the start of a conditional acquisition, on all sequencers. If this is not possible (e.g. due to concurring operations), a :class:`RuntimeError` is raised. :param schedule: The schedule to compile. :returns: The returned schedule is a reference to the original ``schedule``, but updated. :rtype: Schedule :raises RuntimeError: - If a conditional acquisitions/measures is not followed by a conditional control flow. - If a conditional control flow is not preceded by a conditional acquisition/measure. - If the compilation pass is unable to insert :class:`~quantify_scheduler.backends.qblox.operations.pulse_library.LatchReset` on all sequencers. .. py:function:: compile_long_square_pulses_to_awg_offsets(schedule: quantify_scheduler.schedules.schedule.Schedule, config: quantify_scheduler.structure.model.DataStructure | dict) -> quantify_scheduler.schedules.schedule.Schedule Replace square pulses in the schedule with long square pulses. Introspects operations in the schedule to find square pulses with a duration longer than :class:`~quantify_scheduler.backends.qblox.constants.PULSE_STITCHING_DURATION`. Any of these square pulses are converted to :func:`~quantify_scheduler.backends.qblox.operations.pulse_factories.long_square_pulse`, which consist of AWG voltage offsets. If any operations are to be replaced, a deepcopy will be made of the schedule, which is returned by this function. Otherwise the original unmodified schedule will be returned. :param schedule: A :class:`~quantify_scheduler.schedules.schedule.Schedule`, possibly containing long square pulses. :type schedule: Schedule :returns: **schedule** -- The schedule with square pulses longer than :class:`~quantify_scheduler.backends.qblox.constants.PULSE_STITCHING_DURATION` replaced by :func:`~quantify_scheduler.backends.qblox.operations.pulse_factories.long_square_pulse`. If no replacements were done, this is the original unmodified schedule. :rtype: Schedule .. py:function:: hardware_compile(schedule: quantify_scheduler.schedules.schedule.Schedule, config: quantify_scheduler.backends.graph_compilation.CompilationConfig) -> quantify_scheduler.schedules.schedule.CompiledSchedule Generate qblox hardware instructions for executing the schedule. The principle behind the overall compilation is as follows: For every instrument in the hardware configuration, we instantiate a compiler object. Then we assign all the pulses/acquisitions that need to be played by that instrument to the compiler, which then compiles for each instrument individually. This function then returns all the compiled programs bundled together in a dictionary with the QCoDeS name of the instrument as key. :param schedule: The schedule to compile. It is assumed the pulse and acquisition info is already added to the operation. Otherwise an exception is raised. :param config: Compilation config for :class:`~quantify_scheduler.backends.graph_compilation.QuantifyCompiler`. :returns: The compiled schedule. .. py:class:: QbloxHardwareCompilationConfig(/, **data: Any) Bases: :py:obj:`quantify_scheduler.backends.types.common.HardwareCompilationConfig` Datastructure containing the information needed to compile to the Qblox backend. This information is structured in the same way as in the generic :class:`~quantify_scheduler.backends.types.common.HardwareCompilationConfig`, but contains fields for hardware-specific settings. .. py:attribute:: config_type :type: Type[QbloxHardwareCompilationConfig] A reference to the :class:`~quantify_scheduler.backends.types.common.HardwareCompilationConfig` DataStructure for the Qblox backend. .. py:attribute:: hardware_description :type: Dict[str, Union[quantify_scheduler.backends.types.qblox.QbloxHardwareDescription, quantify_scheduler.backends.types.common.HardwareDescription]] Description of the instruments in the physical setup. .. py:attribute:: hardware_options :type: quantify_scheduler.backends.types.qblox.QbloxHardwareOptions Options that are used in compiling the instructions for the hardware, such as :class:`~quantify_scheduler.backends.types.common.LatencyCorrection` or :class:`~quantify_scheduler.backends.types.qblox.SequencerOptions`. .. py:attribute:: allow_off_grid_nco_ops :type: Optional[bool] :value: None Flag to allow NCO operations to play at times that are not aligned with the NCO grid. .. py:attribute:: compilation_passes :type: List[quantify_scheduler.backends.graph_compilation.SimpleNodeConfig] The list of compilation nodes that should be called in succession to compile a schedule to instructions for the Qblox hardware. .. py:method:: _validate_connectivity_channel_names() -> QbloxHardwareCompilationConfig .. py:method:: _warn_mix_lo_false() -> QbloxHardwareCompilationConfig .. py:method:: from_old_style_hardware_config(data: Any) -> Any :classmethod: Convert old style hardware config dict to new style before validation. .. py:method:: _validate_connectivity_graph_structure() -> QbloxHardwareCompilationConfig Validate connectivity graph structure. .. py:method:: _extract_instrument_compilation_configs(portclocks_used: set[tuple]) -> Dict[str, Any] Extract an instrument compiler config for each instrument mentioned in ``hardware_description``. Each instrument config has a similar structure than ``QbloxHardwareCompilationConfig``, but contains only the settings related to their related instrument. Each config must contain at least one portclock referenced in ``portclocks_used``, otherwise the config is deleted. .. py:method:: _get_all_portclock_to_path_and_lo_name_to_path(portclocks_used: set[tuple[str, str]], cluster_configs: dict[str, _ClusterCompilationConfig], lo_configs: dict[str, _LocalOscillatorCompilationConfig]) -> None .. py:class:: _LocalOscillatorCompilationConfig(/, **data: Any) Bases: :py:obj:`quantify_scheduler.structure.model.DataStructure` Configuration values for a :class:`quantify_scheduler.backends.qblox.instrument_compilers.LocalOscillatorCompiler`. .. py:attribute:: hardware_description :type: quantify_scheduler.backends.types.common.LocalOscillatorDescription Description of the physical setup of this local oscillator. .. py:attribute:: frequency :type: Union[float, None] :value: None The frequency of this local oscillator. .. py:class:: _ClusterCompilationConfig(/, **data: Any) Bases: :py:obj:`quantify_scheduler.structure.model.DataStructure` Configuration values for a :class:`~.ClusterCompiler`. .. py:attribute:: hardware_description :type: quantify_scheduler.backends.types.qblox.ClusterDescription Description of the physical setup of this cluster. .. py:attribute:: hardware_options :type: quantify_scheduler.backends.types.qblox.QbloxHardwareOptions Options that are used in compiling the instructions for the hardware. .. py:attribute:: portclock_to_path :type: Dict[str, ChannelPath] Mapping between portclocks and their associated channel name paths (e.g. cluster0.module1.complex_output_0). .. py:attribute:: lo_to_path :type: Dict[str, ChannelPath] Mapping between lo names and their associated channel name paths (e.g. cluster0.module1.complex_output_0). .. py:attribute:: allow_off_grid_nco_ops :type: Optional[bool] :value: None Flag to allow NCO operations to play at times that are not aligned with the NCO grid. .. py:method:: _extract_module_compilation_configs() -> Dict[int, _ClusterModuleCompilationConfig] .. py:class:: _ClusterModuleCompilationConfig(/, **data: Any) Bases: :py:obj:`quantify_scheduler.structure.model.DataStructure` Configuration values for a :class:`~.ClusterModuleCompiler`. .. py:attribute:: hardware_description :type: quantify_scheduler.backends.types.qblox.ClusterModuleDescription Description of the physical setup of this module. .. py:attribute:: hardware_options :type: quantify_scheduler.backends.types.qblox.QbloxHardwareOptions Options that are used in compiling the instructions for the hardware. .. py:attribute:: portclock_to_path :type: Dict[str, ChannelPath] Mapping between portclocks and their associated channel name paths (e.g. cluster0.module1.complex_output_0). .. py:attribute:: lo_to_path :type: Dict[str, ChannelPath] Mapping between lo names and their associated channel name paths (e.g. cluster0.module1.complex_output_0). .. py:attribute:: allow_off_grid_nco_ops :type: Optional[bool] :value: None Flag to allow NCO operations to play at times that are not aligned with the NCO grid. .. py:method:: _extract_sequencer_compilation_configs() -> Dict[int, _SequencerCompilationConfig] .. py:method:: _validate_hardware_distortion_corrections_mode() -> _ClusterModuleCompilationConfig .. py:method:: _validate_input_gain_mode() -> _ClusterModuleCompilationConfig .. py:class:: _SequencerCompilationConfig(/, **data: Any) Bases: :py:obj:`quantify_scheduler.structure.model.DataStructure` Configuration values for a :class:`~.SequencerCompiler`. .. py:attribute:: hardware_description :type: Union[quantify_scheduler.backends.types.qblox.ComplexChannelDescription, quantify_scheduler.backends.types.qblox.RealChannelDescription, quantify_scheduler.backends.types.qblox.DigitalChannelDescription] Information needed to specify a complex/real/digital input/output. .. py:attribute:: sequencer_options :type: quantify_scheduler.backends.types.qblox.SequencerOptions Configuration options for this sequencer. .. py:attribute:: portclock :type: str Portclock associated to this sequencer. .. py:attribute:: channel_name :type: str Channel name associated to this sequencer. .. py:attribute:: latency_correction :type: quantify_scheduler.backends.types.common.LatencyCorrection Latency correction that should be applied to operations on this sequencer. .. py:attribute:: distortion_correction :type: Union[quantify_scheduler.backends.types.common.SoftwareDistortionCorrection, None] Distortion corrections that should be applied to waveforms on this sequencer. .. py:attribute:: lo_name :type: Union[str, None] Local oscilator associated to this sequencer. .. py:attribute:: modulation_frequencies :type: quantify_scheduler.backends.types.common.ModulationFrequencies Modulation frequencies associated to this sequencer. .. py:attribute:: mixer_corrections :type: Union[quantify_scheduler.backends.types.qblox.QbloxMixerCorrections, None] Mixer correction settings. .. py:attribute:: allow_off_grid_nco_ops :type: Optional[bool] :value: None Flag to allow NCO operations to play at times that are not aligned with the NCO grid. .. py:class:: ChannelPath Path of a sequencer channel. .. py:attribute:: cluster_name :type: str .. py:attribute:: module_name :type: str .. py:attribute:: channel_name :type: str .. py:attribute:: module_idx :type: int .. py:method:: from_path(path: str) -> ChannelPath :classmethod: Instantiate a `ChannelPath` object from a path string. .. py:function:: _all_abs_times_ops_with_voltage_offsets_pulses(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, time_offset: float, accumulator: List[Tuple[float, quantify_scheduler.operations.operation.Operation]]) -> None .. py:function:: _add_clock_freqs_to_set_clock_frequency(schedule: quantify_scheduler.schedules.schedule.Schedule, operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule | None = None) -> None .. py:function:: validate_non_overlapping_stitched_pulse(schedule: quantify_scheduler.schedules.schedule.Schedule, **_: Any) -> None Raise an error when pulses overlap, if at least one contains a voltage offset. Since voltage offsets are sometimes used to construct pulses (see e.g. :func:`.long_square_pulse`), overlapping these with regular pulses in time on the same port-clock can lead to undefined behaviour. Note that for each schedulable, all pulse info entries with the same port and clock count as one pulse for that port and clock. This is because schedulables, starting before another schedulable has finished, could affect the waveforms or offsets in the remaining time of that other schedulable. :param schedule: A :class:`~quantify_scheduler.schedules.schedule.Schedule`, possibly containing long square pulses. :type schedule: Schedule :returns: **schedule** -- A :class:`~quantify_scheduler.schedules.schedule.Schedule`, possibly containing long square pulses. :rtype: Schedule :raises RuntimeError: If the schedule contains overlapping pulses (containing voltage offsets) on the same port and clock. .. py:function:: _exists_pulse_starting_before_current_end(abs_times_and_operations: list[Tuple[float, quantify_scheduler.operations.operation.Operation]], current_idx: int) -> Tuple[float, quantify_scheduler.operations.operation.Operation] | Literal[False] .. py:function:: _raise_if_pulses_overlap_on_same_port_clock(abs_time_a: float, op_a: quantify_scheduler.operations.operation.Operation, abs_time_b: float, op_b: quantify_scheduler.operations.operation.Operation) -> None Raise an error if any pulse operations overlap on the same port-clock. A pulse here means a waveform or a voltage offset. .. py:function:: _get_pulse_start_ends(abs_time: float, operation: quantify_scheduler.operations.operation.Operation) -> dict[str, tuple[float, float]] .. py:function:: _operation_end(abs_time_and_operation: Tuple[float, quantify_scheduler.operations.operation.Operation]) -> float .. py:function:: _check_nco_operations_on_nco_time_grid(schedule: quantify_scheduler.schedules.schedule.Schedule, **_: Any) -> quantify_scheduler.schedules.schedule.Schedule Check whether NCO operations are on the 4ns time grid _and_ sub-schedules (including control-flow) containing NCO operations start/end on the 4 ns time grid. .. py:function:: _check_nco_operations_on_nco_time_grid_recursively(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, schedulable: quantify_scheduler.schedules.schedule.Schedulable | None = None, parent_control_flow_op: quantify_scheduler.operations.control_flow_library.ControlFlowOperation | None = None) -> bool Check whether NCO operations, or Schedules/ControlFlowOperations containing NCO operations, align with the NCO grid. :param operation: The Operation or Schedule to be checked. :type operation: Operation | Schedule :param schedulable: The Schedulable the operation is a part of. None if it is the top-level Schedule. :type schedulable: Schedulable | None, optional :param parent_control_flow_op: The ControlFlowOperation that the operation is part of, if any. This is used to create the correct error message. :type parent_control_flow_op: ControlFlowOperation | None, optional :returns: True if the operation is a, or contains NCO operation(s), else False. :rtype: bool .. py:function:: _check_nco_grid_timing(operation: quantify_scheduler.operations.operation.Operation | quantify_scheduler.schedules.schedule.Schedule, schedulable: quantify_scheduler.schedules.schedule.Schedulable | None, parent_control_flow_op: quantify_scheduler.operations.control_flow_library.ControlFlowOperation | None = None) -> None Assumes `operation` is a, or contains NCO operation(s), and checks the alignment of the `operation` with the NCO grid. .. py:function:: _is_nco_operation(operation: quantify_scheduler.operations.operation.Operation) -> bool