Source code for qiskit.pulse.instructions.call

# This code is part of Qiskit.
# (C) Copyright IBM 2020, 2021, 2022.
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Call instruction that represents calling a schedule as a subroutine."""

from typing import Optional, Union, Dict, Tuple, Set

from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType
from qiskit.pulse.channels import Channel
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.instructions import instruction

[docs]class Call(instruction.Instruction): """Pulse ``Call`` instruction. The ``Call`` instruction represents the calling of a referenced subroutine (schedule). It enables code reuse both within the pulse representation and hardware (if supported). """ # Prefix to use for auto naming. prefix = "call" def __init__( self, subroutine, value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, name: Optional[str] = None, ): """Define new subroutine. .. note:: Inline subroutine is mutable. This requires special care for modification. Args: subroutine (Union[Schedule, ScheduleBlock]): A program subroutine to be referred to. value_dict: Mapping of parameter object to assigned value. name: Unique ID of this subroutine. If not provided, this is generated based on the subroutine name. Raises: PulseError: If subroutine is not valid data format. """ from qiskit.pulse.schedule import Schedule, ScheduleBlock if not isinstance(subroutine, (Schedule, ScheduleBlock)): raise PulseError(f"Subroutine type {subroutine.__class__.__name__} cannot be called.") value_dict = value_dict or {} # initialize parameter template if subroutine.is_parameterized(): self._arguments = {par: value_dict.get(par, par) for par in subroutine.parameters} assigned_subroutine = subroutine.assign_parameters( value_dict=self.arguments, inplace=False ) else: self._arguments = {} assigned_subroutine = subroutine # create cache data of parameter-assigned subroutine self._assigned_cache = tuple((self._get_arg_hash(), assigned_subroutine)) super().__init__(operands=(subroutine,), name=name or f"{self.prefix}_{subroutine.name}") @property def duration(self) -> Union[int, ParameterExpression]: """Duration of this instruction.""" return self.subroutine.duration @property def channels(self) -> Tuple[Channel]: """Returns the channels that this schedule uses.""" return self.assigned_subroutine().channels # pylint: disable=missing-return-type-doc @property def subroutine(self): """Return attached subroutine. Returns: program (Union[Schedule, ScheduleBlock]): The program referenced by the call. """ return self.operands[0]
[docs] def assigned_subroutine(self): """Returns this subroutine with the parameters assigned. .. note:: This function may be often called internally for class equality check despite its overhead of parameter assignment. The subroutine with parameter assigned is cached based on ``.argument`` hash. Once this argument is updated, new assigned instance will be returned. Note that this update is not mutable operation. Returns: program (Union[Schedule, ScheduleBlock]): Attached program. """ if self._get_arg_hash() != self._assigned_cache[0]: subroutine = self.subroutine.assign_parameters(value_dict=self.arguments, inplace=False) # update cache data self._assigned_cache = tuple((self._get_arg_hash(), subroutine)) else: subroutine = self._assigned_cache[1] return subroutine
@property def parameters(self) -> Set: """Unassigned parameters which determine the instruction behavior.""" params = set() for value in self._arguments.values(): if isinstance(value, ParameterExpression): params |= value.parameters return params @property def arguments(self) -> Dict[ParameterExpression, ParameterValueType]: """Parameters dictionary to be assigned to subroutine.""" return self._arguments @arguments.setter def arguments(self, new_arguments: Dict[ParameterExpression, ParameterValueType]): """Set new arguments. Args: new_arguments: Dictionary of new parameter value mapping to update. Raises: PulseError: When new arguments doesn't match with existing arguments. """ # validation if new_arguments.keys() != self._arguments.keys(): new_arg_names = ", ".join(map(repr, new_arguments.keys())) old_arg_names = ", ".join(map(repr, self.arguments.keys())) raise PulseError( "Key mismatch between new arguments and existing arguments. " f"{new_arg_names} != {old_arg_names}" ) self._arguments = new_arguments def _get_arg_hash(self): """A helper function to generate hash of parameters.""" return hash(tuple(self.arguments.items())) def __eq__(self, other: instruction.Instruction) -> bool: """Check if this instruction is equal to the `other` instruction. Instructions are equal if they share the same type, operands, and channels. """ # type check if not isinstance(other, self.__class__): return False # compare subroutine. assign parameter values before comparison if self.assigned_subroutine() != other.assigned_subroutine(): return False return True def __repr__(self) -> str: return f"{self.__class__.__name__}({self.assigned_subroutine()}, name='{self.name}')"