Source code for quantify_scheduler.operations.control_flow_library

# Repository: https://gitlab.com/quantify-os/quantify-scheduler
# Licensed according to the LICENCE file on the main branch
"""Standard control flow operations for use with the quantify_scheduler."""

from __future__ import annotations

from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING

from quantify_scheduler.operations.operation import Operation

if TYPE_CHECKING:
    from quantify_scheduler.schedules.schedule import Schedule


[docs] class ControlFlowOperation(Operation, metaclass=ABCMeta): """ Control flow operation that can be used as an ``Operation`` in ``Schedule``. This is an abstract class. Each concrete implementation of the control flow operation decides how and when their ``body`` operation is executed. """ @property @abstractmethod
[docs] def body(self) -> Operation | Schedule: """Body of a control flow.""" pass
@body.setter @abstractmethod def body(self, value: Operation | Schedule) -> None: """Body of a control flow.""" pass def __str__(self) -> str: """ Represent the Operation as a string. Returns ------- str description """ return self._get_signature(self.data["control_flow_info"])
[docs] def get_used_port_clocks(self) -> set[tuple[str, str]]: """ Extracts which port-clock combinations are used in this control flow operation. Returns ------- : All (port, clock) combinations that operations in the body of this control flow operation uses. """ return self.body.get_used_port_clocks()
[docs] class LoopOperation(ControlFlowOperation): """ Loop over another operation predefined times. Repeats the operation defined in ``body`` ``repetitions`` times. The actual implementation depends on the backend. Parameters ---------- body Operation to be repeated repetitions Number of repetitions t0 Time offset, by default 0 """ def __init__(self, body: Operation | Schedule, repetitions: int, t0: float = 0.0) -> None: super().__init__(name="LoopOperation") self.data.update( { "control_flow_info": { "body": body, "repetitions": repetitions, "t0": t0, }, } ) self._update() @property
[docs] def body(self) -> Operation | Schedule: """Body of a control flow.""" return self.data["control_flow_info"]["body"]
@body.setter def body(self, value: Operation | Schedule) -> None: """Body of a control flow.""" self.data["control_flow_info"]["body"] = value @property
[docs] def duration(self) -> float: """Duration of a control flow.""" return ( self.data["control_flow_info"]["repetitions"] * self.data["control_flow_info"]["body"].duration )
[docs] class ConditionalOperation(ControlFlowOperation): """ Conditional over another operation. If a preceding thresholded acquisition on ``qubit_name`` results in a "1", the body will be executed, otherwise it will generate a wait time that is equal to the time of the subschedule, to ensure the absolute timing of later operations remains consistent. Parameters ---------- body Operation to be conditionally played qubit_name Name of the qubit on which the body will be conditioned t0 Time offset, by default 0 hardware_buffer_time Time buffer, by default 0 Example ------- A conditional reset can be implemented as follows: .. jupyter-execute:: # relevant imports from quantify_scheduler import Schedule from quantify_scheduler.operations import ConditionalOperation, Measure, X # define conditional reset as a Schedule conditional_reset = Schedule("conditional reset") conditional_reset.add(Measure("q0", feedback_trigger_label="q0")) conditional_reset.add( ConditionalOperation(body=X("q0"), qubit_name="q0"), rel_time=364e-9, ) .. versionadded:: 0.22.0 For some hardware specific implementations, a ``hardware_buffer_time`` might be required to ensure the correct timing of the operations. This will be added to the duration of the ``body`` to prevent overlap with other operations. """ def __init__( self, body: Operation | Schedule, qubit_name: str, t0: float = 0.0, hardware_buffer_time: float = 0.0, ) -> None: super().__init__(name="ConditionalOperation") self.data.update( { "control_flow_info": { "body": body, "qubit_name": qubit_name, "t0": t0, "feedback_trigger_label": qubit_name, "feedback_trigger_address": None, # Filled in at compilation. "hardware_buffer_time": hardware_buffer_time, }, } ) self._update() @property
[docs] def body(self) -> Operation | Schedule: """Body of a control flow.""" return self.data["control_flow_info"]["body"]
@body.setter def body(self, value: Operation | Schedule) -> None: """Body of a control flow.""" self.data["control_flow_info"]["body"] = value @property
[docs] def duration(self) -> float: """Duration of a control flow.""" return ( self.data["control_flow_info"]["body"].duration + self.data["control_flow_info"]["hardware_buffer_time"] )
[docs] class ControlFlowSpec(metaclass=ABCMeta): """ Control flow specification to be used at ``Schedule.add``. The users can specify any concrete control flow with the ``control_flow`` argument to ``Schedule.add``. The ``ControlFlowSpec`` is only a type which by itself cannot be used for the ``control_flow`` argument, use any concrete control flow derived from it. """ @abstractmethod
[docs] def create_operation(self, body: Operation | Schedule) -> Operation | Schedule: """Transform the control flow specification to an operation or schedule.""" pass
[docs] class Loop(ControlFlowSpec): """ Loop control flow specification to be used at ``Schedule.add``. For more information, see ``LoopOperation``. Parameters ---------- repetitions Number of repetitions t0 Time offset, by default 0 """ def __init__(self, repetitions: int, t0: float = 0.0) -> None:
[docs] self.repetitions = repetitions
[docs] self.t0 = t0
[docs] def create_operation(self, body: Operation | Schedule) -> LoopOperation: """Transform the control flow specification to an operation or schedule.""" return LoopOperation(body, self.repetitions, self.t0)
[docs] class Conditional(ControlFlowSpec): """ Conditional control flow specification to be used at ``Schedule.add``. For more information, see ``ConditionalOperation``. Parameters ---------- qubit_name Number of repetitions t0 Time offset, by default 0 """ def __init__(self, qubit_name: str, t0: float = 0.0) -> None:
[docs] self.qubit_name = qubit_name
[docs] self.t0 = t0
[docs] def create_operation(self, body: Operation | Schedule) -> ConditionalOperation: """Transform the control flow specification to an operation or schedule.""" return ConditionalOperation(body, self.qubit_name, self.t0)