From 1977d76fb7e74cf061589b1388372f83d4b32eab Mon Sep 17 00:00:00 2001 From: knzwnao Date: Sun, 14 Nov 2021 13:23:45 +0900 Subject: [PATCH 01/11] remove deprecated codes --- qiskit/pulse/__init__.py | 2 - qiskit/pulse/builder.py | 137 ------------ qiskit/pulse/channels.py | 32 +-- qiskit/pulse/instructions/call.py | 54 +---- qiskit/pulse/instructions/instruction.py | 93 +------- qiskit/pulse/instructions/play.py | 31 +-- qiskit/pulse/interfaces.py | 249 ---------------------- qiskit/pulse/library/parametric_pulses.py | 36 +--- qiskit/pulse/library/pulse.py | 74 +------ qiskit/pulse/library/waveform.py | 9 - qiskit/pulse/reschedule.py | 27 --- qiskit/pulse/schedule.py | 220 +------------------ qiskit/pulse/transforms/__init__.py | 5 - qiskit/pulse/transforms/alignments.py | 90 +------- 14 files changed, 18 insertions(+), 1041 deletions(-) delete mode 100644 qiskit/pulse/interfaces.py delete mode 100644 qiskit/pulse/reschedule.py diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 1cef1af715b5..74bbb6da168f 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -416,8 +416,6 @@ align_sequential, circuit_scheduler_settings, frequency_offset, - inline, - pad, phase_offset, transpiler_settings, # Macros. diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index fdb4cf1194da..ee382796d54f 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -1126,62 +1126,6 @@ def general_transforms(alignment_context: AlignmentKind) -> ContextManager[None] builder.append_block(current) -@utils.deprecated_functionality -@contextmanager -def inline() -> ContextManager[None]: - """Deprecated. Inline all instructions within this context into the parent context, - inheriting the scheduling policy of the parent context. - - .. warning:: This will cause all scheduling directives within this context - to be ignored. - """ - - def _flatten(block): - for inst in block.blocks: - if isinstance(inst, ScheduleBlock): - yield from _flatten(inst) - else: - yield inst - - builder = _active_builder() - - # set a placeholder - builder.push_context(transforms.AlignLeft()) - try: - yield - finally: - placeholder = builder.pop_context() - for inst in _flatten(placeholder): - builder.append_instruction(inst) - - -@contextmanager -def pad(*chs: chans.Channel) -> ContextManager[None]: # pylint: disable=unused-argument - """Deprecated. Pad all available timeslots with delays upon exiting context. - - Args: - chs: Channels to pad with delays. Defaults to all channels in context - if none are supplied. - - Yields: - None - """ - warnings.warn( - "Context-wise padding is being deprecated. Requested padding is being ignored. " - "Now the pulse builder generate a program in `ScheduleBlock` representation. " - "The padding with delay as a blocker is no longer necessary for this program. " - "However, if you still want delays, you can convert the output program " - "into `Schedule` representation by calling " - "`qiskit.pulse.transforms.target_qobj_transform`. Then, you can apply " - "`qiskit.pulse.transforms.pad` to the converted schedule. ", - DeprecationWarning, - ) - try: - yield - finally: - pass - - @contextmanager def transpiler_settings(**settings) -> ContextManager[None]: """Set the currently active transpiler settings for this context. @@ -1645,87 +1589,6 @@ def snapshot(label: str, snapshot_type: str = "statevector"): append_instruction(instructions.Snapshot(label, snapshot_type=snapshot_type)) -def call_schedule(schedule: Schedule): - """Call a pulse ``schedule`` in the builder context. - - Examples: - - .. jupyter-execute:: - - from qiskit import pulse - from qiskit.pulse import builder - - d0 = pulse.DriveChannel(0) - - sched = pulse.Schedule() - sched += pulse.Play(pulse.Constant(10, 1.0), d0) - - with pulse.build() as pulse_prog: - builder.call_schedule(sched) - - assert pulse_prog == sched - - Args: - Schedule to call. - """ - warnings.warn( - "``call_schedule`` is being deprecated. " - "``call`` function can take both a schedule and a circuit.", - DeprecationWarning, - ) - - call(schedule) - - -def call_circuit(circ: circuit.QuantumCircuit): - """Call a quantum ``circuit`` within the active builder context. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. jupyter-execute:: - - from qiskit import circuit, pulse, schedule, transpile - from qiskit.pulse import builder - from qiskit.test.mock import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - d0 = pulse.DriveChannel(0) - - qc = circuit.QuantumCircuit(2) - qc.cx(0, 1) - qc_transpiled = transpile(qc, optimization_level=3) - sched = schedule(qc_transpiled, backend) - - with pulse.build(backend) as pulse_prog: - # with default settings - builder.call_circuit(qc) - - with pulse.build(backend) as pulse_prog: - with pulse.transpiler_settings(optimization_level=3): - builder.call_circuit(qc) - - assert pulse_prog == sched - - .. note:: Requires the active builder context to have a backend set. - - Args: - Circuit to call. - """ - warnings.warn( - "``call_circuit`` is being deprecated. " - "``call`` function can take both a schedule and a circuit.", - DeprecationWarning, - ) - - call(circ) - - def call( target: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], name: Optional[str] = None, diff --git a/qiskit/pulse/channels.py b/qiskit/pulse/channels.py index d759da39210c..a7230896c12c 100644 --- a/qiskit/pulse/channels.py +++ b/qiskit/pulse/channels.py @@ -25,10 +25,8 @@ import numpy as np -from qiskit.circuit import Parameter -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType +from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.utils import deprecated_functionality class Channel(metaclass=ABCMeta): @@ -71,10 +69,6 @@ def __init__(self, index: int): self._index = index self._hash = hash((self.__class__.__name__, self._index)) - self._parameters = set() - if isinstance(index, ParameterExpression): - self._parameters = index.parameters - @property def index(self) -> Union[int, ParameterExpression]: """Return the index of this channel. The index is a label for a control signal line @@ -112,30 +106,6 @@ def is_parameterized(self) -> bool: """Return True iff the channel is parameterized.""" return isinstance(self.index, ParameterExpression) - @deprecated_functionality - def assign(self, parameter: Parameter, value: ParameterValueType) -> "Channel": - """Return a new channel with the input Parameter assigned to value. - - Args: - parameter: A parameter in this expression whose value will be updated. - value: The new value to bind to. - - Returns: - A new channel with updated parameters. - - Raises: - PulseError: If the parameter is not present in the channel. - """ - if parameter not in self.parameters: - raise PulseError(f"Cannot bind parameters ({parameter}) not present in the channel.") - - new_index = self.index.assign(parameter, value) - if not new_index.parameters: - self._validate_index(new_index) - new_index = int(new_index) - - return type(self)(new_index) - @property def name(self) -> str: """Return the shorthand alias for this channel, which is based on its type and index.""" diff --git a/qiskit/pulse/instructions/call.py b/qiskit/pulse/instructions/call.py index befe05aba345..154a7c75ed7b 100644 --- a/qiskit/pulse/instructions/call.py +++ b/qiskit/pulse/instructions/call.py @@ -12,13 +12,12 @@ """Call instruction that represents calling a schedule as a subroutine.""" -from typing import Optional, Union, Dict, Tuple, Any, Set +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 -from qiskit.pulse.utils import format_parameter_value, deprecated_functionality class Call(instruction.Instruction): @@ -58,7 +57,6 @@ def __init__( value_dict = value_dict or dict() # initialize parameter template - # TODO remove self._parameter_table if subroutine.is_parameterized(): self._arguments = {par: value_dict.get(par, par) for par in subroutine.parameters} assigned_subroutine = subroutine.assign_parameters( @@ -114,56 +112,6 @@ def assigned_subroutine(self): return subroutine - def _initialize_parameter_table(self, operands: Tuple[Any]): - """A helper method to initialize parameter table. - - The behavior of the parameter table of the ``Call`` instruction is slightly different from - other instructions. The actual parameter mapper object is defined only in the - subroutine, thus the call instruction doesn't have operand of ``ParameterExpression`` type. - The parameter table is defined as a mapping of parameter objects to assigned values, - whereas the standard instruction stores the mapping to the operand tuple index. - - Note that this instruction doesn't immediately bind parameter values when the - :meth:`assign_parameters` method is called with the parameter dictionary. - Instead, this instruction separately keeps the parameter values from the subroutine. - This logic enables the compiler to reuse the subroutine with different parameters. - - Args: - operands: List of operands associated with this instruction. - """ - if operands[0].is_parameterized(): - for value in operands[0].parameters: - self._parameter_table[value] = value - - @deprecated_functionality - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "Call": - """Store parameters which will be later assigned to the subroutine. - - Parameter values are not immediately assigned. The subroutine with parameters - assigned according to the populated parameter table will be generated only when - :func:`~qiskit.pulse.transforms.inline_subroutines` function is applied to this - instruction. Note that parameter assignment logic creates a copy of subroutine - to avoid the mutation problem. This function is usually applied by the Qiskit - compiler when the program is submitted to the backend. - - Args: - value_dict: A mapping from Parameters to either numeric values or another - Parameter expression. - - Returns: - Self with updated parameters. - """ - for param_obj, assigned_value in value_dict.items(): - for key_obj, value in self._parameter_table.items(): - # assign value to parameter expression (it can consist of multiple parameters) - if isinstance(value, ParameterExpression) and param_obj in value.parameters: - new_value = format_parameter_value(value.assign(param_obj, assigned_value)) - self._parameter_table[key_obj] = new_value - - return self - def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return any(isinstance(value, ParameterExpression) for value in self.arguments.values()) diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index 4c428bc21f4c..c340dd284a06 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -21,15 +21,11 @@ sched = Schedule() sched += Delay(duration, channel) # Delay is a specific subclass of Instruction """ -import warnings from abc import ABC, abstractproperty -from collections import defaultdict -from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple, Any +from typing import Callable, Iterable, List, Optional, Set, Tuple -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse.channels import Channel from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.utils import format_parameter_value, deprecated_functionality # pylint: disable=missing-return-doc @@ -43,16 +39,12 @@ class Instruction(ABC): def __init__( self, operands: Tuple, - duration: int = None, - channels: Tuple[Channel] = None, name: Optional[str] = None, ): """Instruction initializer. Args: operands: The argument list. - duration: Deprecated. - channels: Deprecated. name: Optional display name for this instruction. Raises: @@ -60,29 +52,10 @@ def __init__( PulseError: If the input ``channels`` are not all of type :class:`Channel`. """ - if duration is not None: - warnings.warn( - "Specifying duration in the constructor is deprecated. " - "Now duration is an abstract property rather than class variable. " - "All subclasses should implement ``duration`` accordingly. " - "See Qiskit-Terra #5679 for more information.", - DeprecationWarning, - ) - - if channels is not None: - warnings.warn( - "Specifying ``channels`` in the constructor is deprecated. " - "All channels should be stored in ``operands``.", - DeprecationWarning, - ) - self._operands = operands self._name = name self._hash = None - self._parameter_table = defaultdict(list) - self._initialize_parameter_table(operands) - for channel in self.channels: if not isinstance(channel, Channel): raise PulseError(f"Expected a channel, got {channel} instead.") @@ -171,17 +144,6 @@ def _instructions(self, time: int = 0) -> Iterable[Tuple[int, "Instruction"]]: """ yield (time, self) - def flatten(self) -> "Instruction": - """Return itself as already single instruction.""" - - warnings.warn( - "`This method is being deprecated. Please use " - "`qiskit.pulse.transforms.flatten` function with this schedule.", - DeprecationWarning, - ) - - return self - def shift(self, time: int, name: Optional[str] = None): """Return a new schedule shifted forward by `time`. @@ -245,59 +207,6 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return any(chan.is_parameterized() for chan in self.channels) - def _initialize_parameter_table(self, operands: Tuple[Any]): - """A helper method to initialize parameter table. - - Args: - operands: List of operands associated with this instruction. - """ - for idx, op in enumerate(operands): - if isinstance(op, ParameterExpression): - for param in op.parameters: - self._parameter_table[param].append(idx) - elif isinstance(op, Channel) and isinstance(op.index, ParameterExpression): - for param in op.index.parameters: - self._parameter_table[param].append(idx) - - @deprecated_functionality - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "Instruction": - """Modify and return self with parameters assigned according to the input. - - Args: - value_dict: A mapping from Parameters to either numeric values or another - Parameter expression. - - Returns: - Self with updated parameters. - """ - new_operands = list(self.operands) - - for parameter in self.parameters: - if parameter not in value_dict: - continue - - value = value_dict[parameter] - op_indices = self._parameter_table[parameter] - for op_idx in op_indices: - param_expr = new_operands[op_idx] - new_operands[op_idx] = format_parameter_value(param_expr.assign(parameter, value)) - - # Update parameter table - entry = self._parameter_table.pop(parameter) - if isinstance(value, ParameterExpression): - for new_parameter in value.parameters: - if new_parameter in self._parameter_table: - new_entry = set(entry + self._parameter_table[new_parameter]) - self._parameter_table[new_parameter] = list(new_entry) - else: - self._parameter_table[new_parameter] = entry - - self._operands = tuple(new_operands) - - return self - def draw( self, dt: float = 1, diff --git a/qiskit/pulse/instructions/play.py b/qiskit/pulse/instructions/play.py index 2bf9a93b1432..2a8536643e76 100644 --- a/qiskit/pulse/instructions/play.py +++ b/qiskit/pulse/instructions/play.py @@ -13,14 +13,13 @@ """An instruction to transmit a given pulse on a ``PulseChannel`` (i.e., those which support transmitted pulses, such as ``DriveChannel``). """ -from typing import Dict, Optional, Union, Tuple, Any, Set +from typing import Optional, Union, Tuple, Set -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType +from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.channels import PulseChannel from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.library.pulse import Pulse from qiskit.pulse.instructions.instruction import Instruction -from qiskit.pulse.utils import deprecated_functionality +from qiskit.pulse.library.pulse import Pulse class Play(Instruction): @@ -76,21 +75,6 @@ def duration(self) -> Union[int, ParameterExpression]: """Duration of this instruction.""" return self.pulse.duration - def _initialize_parameter_table(self, operands: Tuple[Any]): - """A helper method to initialize parameter table. - - Args: - operands: List of operands associated with this instruction. - """ - super()._initialize_parameter_table(operands) - - if any(isinstance(val, ParameterExpression) for val in self.pulse.parameters.values()): - for value in self.pulse.parameters.values(): - if isinstance(value, ParameterExpression): - for param in value.parameters: - # Table maps parameter to operand index, 0 for ``pulse`` - self._parameter_table[param].append(0) - @property def parameters(self) -> Set: """Parameters which determine the instruction behavior.""" @@ -105,15 +89,6 @@ def parameters(self) -> Set: return parameters - @deprecated_functionality - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "Play": - super().assign_parameters(value_dict) - pulse = self.pulse.assign_parameters(value_dict) - self._operands = (pulse, self.channel) - return self - def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return self.pulse.is_parameterized() or super().is_parameterized() diff --git a/qiskit/pulse/interfaces.py b/qiskit/pulse/interfaces.py deleted file mode 100644 index 75de524ab3e4..000000000000 --- a/qiskit/pulse/interfaces.py +++ /dev/null @@ -1,249 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -""" -ScheduleComponent has been deprecated. -It is a common interface for components of schedule (Instruction and Schedule). -""" -import warnings -from abc import ABCMeta, abstractmethod -from typing import Tuple, List, Union, Optional - -from qiskit.pulse.channels import Channel - - -class ScheduleComponent(metaclass=ABCMeta): - """ScheduleComponent has been deprecated. - It has been replaced by `qiskit.pulse.values.Value` and `qiskit.pulse.values.NamedValue``. - Anywhere that currently accepts a ``ScheduleComponent`` should instead accept a - ``Union[Schedule, Instruction]``. - """ - - @property - @abstractmethod - def name(self) -> str: - """Name of ScheduleComponent.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @property - @abstractmethod - def channels(self) -> List[Channel]: - """Return channels used by schedule.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - - pass - - @property - @abstractmethod - def duration(self) -> int: - """Duration of this schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - - pass - - @property - @abstractmethod - def start_time(self) -> int: - """Starting time of this schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - - pass - - @property - @abstractmethod - def stop_time(self) -> int: - """Stopping time of this schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def ch_duration(self, *channels: List[Channel]) -> int: - """Duration of the `channels` in schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def ch_start_time(self, *channels: List[Channel]) -> int: - """Starting time of the `channels` in schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def ch_stop_time(self, *channels: List[Channel]) -> int: - """Stopping of the `channels` in schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @property - @abstractmethod - def _children(self) -> Tuple[Union[int, "ScheduleComponent"]]: - """Child nodes of this schedule component.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @property - @abstractmethod - def instructions(self) -> Tuple[Tuple[int, "Instructions"]]: - """Return iterable for all `Instruction`s in `Schedule` tree.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def flatten(self) -> "ScheduleComponent": - """Return a new schedule which is the flattened schedule contained all `instructions`.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def shift( - self: "ScheduleComponent", time: int, name: Optional[str] = None - ) -> "ScheduleComponent": - """Return a new schedule shifted forward by `time`. - - Args: - time: Time to shift by - name: Name of the new schedule. Defaults to name of parent - """ - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def insert( - self, start_time: int, schedule: "ScheduleComponent", name: Optional[str] = None - ) -> "ScheduleComponent": - """Return a new schedule with `schedule` inserted at `start_time` of `self`. - - Args: - start_time: time to be inserted - schedule: schedule to be inserted - name: Name of the new schedule. Defaults to name of parent - """ - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def append( - self, schedule: "ScheduleComponent", name: Optional[str] = None - ) -> "ScheduleComponent": - """Return a new schedule with `schedule` inserted at the maximum time over - all channels shared between `self` and `schedule`. - - Args: - schedule: schedule to be appended - name: Name of the new schedule. Defaults to name of parent - """ - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def __add__(self, schedule: "ScheduleComponent") -> "ScheduleComponent": - """Return a new schedule with `schedule` inserted within `self` at `start_time`.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def __or__(self, schedule: "ScheduleComponent") -> "ScheduleComponent": - """Return a new schedule which is the union of `self` and `schedule`.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass - - @abstractmethod - def __lshift__(self, time: int) -> "ScheduleComponent": - """Return a new schedule which is shifted forward by `time`.""" - warnings.warn( - "ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", - DeprecationWarning, - ) - pass diff --git a/qiskit/pulse/library/parametric_pulses.py b/qiskit/pulse/library/parametric_pulses.py index 26a0976d576c..47ef158e3e5a 100644 --- a/qiskit/pulse/library/parametric_pulses.py +++ b/qiskit/pulse/library/parametric_pulses.py @@ -42,13 +42,12 @@ class ParametricPulseShapes(Enum): import math import numpy as np -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType +from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.exceptions import PulseError from qiskit.pulse.library import continuous from qiskit.pulse.library.discrete import gaussian, gaussian_square, drag, constant from qiskit.pulse.library.pulse import Pulse from qiskit.pulse.library.waveform import Waveform -from qiskit.pulse.utils import format_parameter_value, deprecated_functionality class ParametricPulse(Pulse): @@ -94,39 +93,6 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return any(_is_parameterized(val) for val in self.parameters.values()) - @deprecated_functionality - def assign( - self, parameter: ParameterExpression, value: ParameterValueType - ) -> "ParametricPulse": - """Assign one parameter to a value, which can either be numeric or another parameter - expression. - """ - return self.assign_parameters({parameter: value}) - - @deprecated_functionality - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "ParametricPulse": - """Return a new ParametricPulse with parameters assigned. - - Args: - value_dict: A mapping from Parameters to either numeric values or another - Parameter expression. - - Returns: - New pulse with updated parameters. - """ - if not self.is_parameterized(): - return self - - new_parameters = {} - for op, op_value in self.parameters.items(): - for parameter, value in value_dict.items(): - if _is_parameterized(op_value) and parameter in op_value.parameters: - op_value = format_parameter_value(op_value.assign(parameter, value)) - new_parameters[op] = op_value - return type(self)(**new_parameters, name=self.name) - def __eq__(self, other: Pulse) -> bool: return super().__eq__(other) and self.parameters == other.parameters diff --git a/qiskit/pulse/library/pulse.py b/qiskit/pulse/library/pulse.py index 19901869e1b7..7c7e73a3d356 100644 --- a/qiskit/pulse/library/pulse.py +++ b/qiskit/pulse/library/pulse.py @@ -17,8 +17,7 @@ from abc import ABC, abstractmethod from typing import Dict, Optional, Any, Tuple, Union -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType -from qiskit.pulse.utils import deprecated_functionality +from qiskit.circuit.parameterexpression import ParameterExpression class Pulse(ABC): @@ -66,31 +65,9 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" raise NotImplementedError - @deprecated_functionality - @abstractmethod - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "Pulse": - """Return a new pulse with parameters assigned. - - Args: - value_dict: A mapping from Parameters to either numeric values or another - Parameter expression. - - Returns: - New pulse with updated parameters. - """ - raise NotImplementedError - def draw( self, - dt: Any = None, # deprecated style: Optional[Dict[str, Any]] = None, - filename: Any = None, # deprecated - interp_method: Any = None, # deprecated - scale: Any = None, # deprecated - interactive: Any = None, # deprecated - draw_title: Any = None, # deprecated backend=None, # importing backend causes cyclic import time_range: Optional[Tuple[int, int]] = None, time_unit: str = "dt", @@ -125,14 +102,6 @@ def draw( the plotters use a given ``axis`` instead of internally initializing a figure object. This object format depends on the plotter. See plotter argument for details. - dt: Deprecated. This argument is used by the legacy pulse drawer. - filename: Deprecated. This argument is used by the legacy pulse drawer. - To save output image, you can call `.savefig` method with - returned Matplotlib Figure object. - interp_method: Deprecated. This argument is used by the legacy pulse drawer. - scale: Deprecated. This argument is used by the legacy pulse drawer. - interactive: Deprecated. This argument is used by the legacy pulse drawer. - draw_title: Deprecated. This argument is used by the legacy pulse drawer. Returns: Visualization output data. @@ -140,46 +109,7 @@ def draw( If matplotlib family is specified, this will be a ``matplotlib.pyplot.Figure`` data. """ # pylint: disable=cyclic-import, missing-return-type-doc - from qiskit.visualization import pulse_drawer_v2, PulseStyle - - legacy_args = { - "dt": dt, - "filename": filename, - "interp_method": interp_method, - "scale": scale, - "interactive": interactive, - "draw_title": draw_title, - } - - active_legacy_args = [] - for name, legacy_arg in legacy_args.items(): - if legacy_arg is not None: - active_legacy_args.append(name) - - if active_legacy_args: - warnings.warn( - "Legacy pulse drawer is deprecated. " - "Specified arguments {dep_args} are deprecated. " - "Please check the API document of new pulse drawer " - "`qiskit.visualization.pulse_drawer_v2`." - "".format(dep_args=", ".join(active_legacy_args)), - DeprecationWarning, - ) - - if filename: - warnings.warn( - "File saving is delegated to the plotter software in new drawer. " - "If you specify matplotlib plotter family to `plotter` argument, " - "you can call `savefig` method with the returned Figure object.", - DeprecationWarning, - ) - - if isinstance(style, PulseStyle): - style = None - warnings.warn( - "Legacy stylesheet is specified. This is ignored in the new drawer. " - "Please check the API documentation for this method." - ) + from qiskit.visualization import pulse_drawer_v2 return pulse_drawer_v2( program=self, diff --git a/qiskit/pulse/library/waveform.py b/qiskit/pulse/library/waveform.py index 7cd26b0b5fb3..b177323c2532 100644 --- a/qiskit/pulse/library/waveform.py +++ b/qiskit/pulse/library/waveform.py @@ -15,10 +15,8 @@ import numpy as np -from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse.exceptions import PulseError from qiskit.pulse.library.pulse import Pulse -from qiskit.pulse.utils import deprecated_functionality class Waveform(Pulse): @@ -115,13 +113,6 @@ def parameters(self) -> Dict[str, Any]: """Return a dictionary containing the pulse's parameters.""" return dict() - @deprecated_functionality - def assign_parameters( - self, value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> "Waveform": - # Waveforms don't accept parameters - return self - def __eq__(self, other: Pulse) -> bool: return ( super().__eq__(other) diff --git a/qiskit/pulse/reschedule.py b/qiskit/pulse/reschedule.py deleted file mode 100644 index c606da6c7b09..000000000000 --- a/qiskit/pulse/reschedule.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# 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. - -"""Deprecated import path. Use `pulse.transforms` instead.""" -import warnings - -# pylint: disable=unused-import -from qiskit.pulse.transforms import ( - align_measures, - add_implicit_acquires, - pad, - compress_pulses, -) - - -warnings.warn( - "The reschedule module has been renamed to transforms. This import path is deprecated." -) diff --git a/qiskit/pulse/schedule.py b/qiskit/pulse/schedule.py index 61479729474c..86da4c03aa6c 100644 --- a/qiskit/pulse/schedule.py +++ b/qiskit/pulse/schedule.py @@ -33,7 +33,7 @@ from qiskit.pulse.channels import Channel from qiskit.pulse.exceptions import PulseError from qiskit.pulse.instructions import Instruction -from qiskit.pulse.utils import instruction_duration_validation, deprecated_functionality +from qiskit.pulse.utils import instruction_duration_validation from qiskit.utils.multiprocessing import is_main_process @@ -144,7 +144,7 @@ def __init__( # These attributes are populated by ``_mutable_insert`` self._timeslots = {} - self.__children = [] + self._children = [] for sched_pair in schedules: try: time, sched = sched_pair @@ -232,27 +232,6 @@ def channels(self) -> Tuple[Channel]: """Returns channels that this schedule uses.""" return tuple(self._timeslots.keys()) - @property - def _children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: - """Deprecated. Return the child schedule components of this ``Schedule`` in the - order they were added to the schedule. - - Notes: - Nested schedules are returned as-is. If you want to collect only instructions, - use py:meth:`~Schedule.instructions` instead. - - Returns: - A tuple, where each element is a two-tuple containing the initial - scheduled time of each ``NamedValue`` and the component - itself. - """ - warnings.warn( - "Schedule._children is now available as the public property " - "Schedule.children. Access to this private property is being deprecated.", - DeprecationWarning, - ) - return tuple(self.__children) - @property def children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: """Return the child schedule components of this ``Schedule`` in the @@ -267,7 +246,7 @@ def children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: scheduled time of each ``NamedValue`` and the component itself. """ - return tuple(self.__children) + return tuple(self._children) @property def instructions(self) -> Tuple[Tuple[int, Instruction]]: @@ -275,7 +254,7 @@ def instructions(self) -> Tuple[Tuple[int, Instruction]]: def key(time_inst_pair): inst = time_inst_pair[1] - return (time_inst_pair[0], inst.duration, sorted(chan.name for chan in inst.channels)) + return time_inst_pair[0], inst.duration, sorted(chan.name for chan in inst.channels) return tuple(sorted(self._instructions(), key=key)) @@ -377,7 +356,7 @@ def _mutable_shift(self, time: int) -> "Schedule": self._duration = self._duration + time self._timeslots = timeslots - self.__children = [(orig_time + time, child) for orig_time, child in self.children] + self._children = [(orig_time + time, child) for orig_time, child in self.children] return self def insert( @@ -408,7 +387,7 @@ def _mutable_insert(self, start_time: int, schedule: ScheduleComponent) -> "Sche schedule: Schedule to mutably insert. """ self._add_timeslots(start_time, schedule) - self.__children.append((start_time, schedule)) + self._children.append((start_time, schedule)) self._parameter_manager.update_parameter_table(schedule) return self @@ -450,18 +429,6 @@ def append( time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name, inplace=inplace) - def flatten(self) -> "Schedule": - """Deprecated.""" - from qiskit.pulse.transforms import flatten - - warnings.warn( - "`This method is being deprecated. Please use " - "`qiskit.pulse.transforms.flatten` function with this schedule.", - DeprecationWarning, - ) - - return flatten(self) - def filter( self, *filter_funcs: Callable, @@ -716,7 +683,7 @@ def replace( new_parameters.update_parameter_table(child) if inplace: - self.__children = new_children + self._children = new_children self._parameter_manager = new_parameters self._renew_timeslots() return self @@ -1027,33 +994,12 @@ def is_schedulable(self) -> bool: return False return True - @property - @deprecated_functionality - @_require_schedule_conversion - def timeslots(self) -> TimeSlots: - """Time keeping attribute.""" - return self.timeslots - @property @_require_schedule_conversion def duration(self) -> int: """Duration of this schedule block.""" return self.duration - @property - @deprecated_functionality - @_require_schedule_conversion - def start_time(self) -> int: - """Starting time of this schedule block.""" - return self.ch_start_time(*self.channels) - - @property - @deprecated_functionality - @_require_schedule_conversion - def stop_time(self) -> int: - """Stopping time of this schedule block.""" - return self.duration - @property def channels(self) -> Tuple[Channel]: """Returns channels that this schedule clock uses.""" @@ -1088,79 +1034,6 @@ def ch_duration(self, *channels: Channel) -> int: """ return self.ch_duration(*channels) - @deprecated_functionality - @_require_schedule_conversion - def ch_start_time(self, *channels: Channel) -> int: - """Return the time of the start of the first instruction over the supplied channels. - - Args: - *channels: Channels within ``self`` to include. - """ - return self.ch_start_time(*channels) - - @deprecated_functionality - @_require_schedule_conversion - def ch_stop_time(self, *channels: Channel) -> int: - """Return maximum start time over supplied channels. - - Args: - *channels: Channels within ``self`` to include. - """ - return self.ch_stop_time(*channels) - - @deprecated_functionality - def shift(self, time: int, name: Optional[str] = None, inplace: bool = True): - """This method will be removed. Temporarily added for backward compatibility. - - .. note:: This method is not supported and being deprecated. - - Args: - time: Time to shift by. - name: Name of the new schedule. Defaults to the name of self. - inplace: Perform operation inplace on this schedule. Otherwise - return a new ``Schedule``. - - Raises: - PulseError: When this method is called. This method is not supported. - """ - raise PulseError( - "Method ``ScheduleBlock.shift`` is not supported as this program " - "representation does not have the notion of instruction " - "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " - "this program to obtain the ``Schedule`` representation supporting " - "this method." - ) - - @deprecated_functionality - def insert( - self, - start_time: int, - block: ScheduleComponent, - name: Optional[str] = None, - inplace: bool = True, - ): - """This method will be removed. Temporarily added for backward compatibility. - - .. note:: This method is not supported and being deprecated. - - Args: - start_time: Time to insert the schedule. - block: Schedule to insert. - name: Name of the new schedule. Defaults to the name of self. - inplace: Perform operation inplace on this schedule. Otherwise - return a new ``Schedule``. - - Raises: - PulseError: When this method is called. This method is not supported. - """ - raise PulseError( - "Method ``ScheduleBlock.insert`` is not supported as this program " - "representation does not have the notion of instruction " - "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " - "this program to obtain the ``Schedule`` representation supporting " - "this method." - ) - def append( self, block: BlockComponent, name: Optional[str] = None, inplace: bool = True ) -> "ScheduleBlock": @@ -1547,21 +1420,7 @@ def wrapper(*args, **kwargs): @_common_method(Schedule, ScheduleBlock) def draw( self, - dt: Any = None, # deprecated style: Optional[Dict[str, Any]] = None, - filename: Any = None, # deprecated - interp_method: Any = None, # deprecated - scale: Any = None, # deprecated - channel_scales: Any = None, # deprecated - plot_all: Any = None, # deprecated - plot_range: Any = None, # deprecated - interactive: Any = None, # deprecated - table: Any = None, # deprecated - label: Any = None, # deprecated - framechange: Any = None, # deprecated - channels: Any = None, # deprecated - show_framechange_channels: Any = None, # deprecated - draw_title: Any = None, # deprecated backend=None, # importing backend causes cyclic import time_range: Optional[Tuple[int, int]] = None, time_unit: str = "dt", @@ -1605,22 +1464,6 @@ def draw( the plotters use a given ``axis`` instead of internally initializing a figure object. This object format depends on the plotter. See plotter argument for details. - dt: Deprecated. This argument is used by the legacy pulse drawer. - filename: Deprecated. This argument is used by the legacy pulse drawer. - To save output image, you can call ``.savefig`` method with - returned Matplotlib Figure object. - interp_method: Deprecated. This argument is used by the legacy pulse drawer. - scale: Deprecated. This argument is used by the legacy pulse drawer. - channel_scales: Deprecated. This argument is used by the legacy pulse drawer. - plot_all: Deprecated. This argument is used by the legacy pulse drawer. - plot_range: Deprecated. This argument is used by the legacy pulse drawer. - interactive: Deprecated. This argument is used by the legacy pulse drawer. - table: Deprecated. This argument is used by the legacy pulse drawer. - label: Deprecated. This argument is used by the legacy pulse drawer. - framechange: Deprecated. This argument is used by the legacy pulse drawer. - channels: Deprecated. This argument is used by the legacy pulse drawer. - show_framechange_channels: Deprecated. This argument is used by the legacy pulse drawer. - draw_title: Deprecated. This argument is used by the legacy pulse drawer. Returns: Visualization output data. @@ -1628,54 +1471,7 @@ def draw( If matplotlib family is specified, this will be a ``matplotlib.pyplot.Figure`` data. """ # pylint: disable=cyclic-import, missing-return-type-doc - from qiskit.visualization import pulse_drawer_v2, SchedStyle - - legacy_args = { - "dt": dt, - "filename": filename, - "interp_method": interp_method, - "scale": scale, - "channel_scales": channel_scales, - "plot_all": plot_all, - "plot_range": plot_range, - "interactive": interactive, - "table": table, - "label": label, - "framechange": framechange, - "channels": channels, - "show_framechange_channels": show_framechange_channels, - "draw_title": draw_title, - } - - active_legacy_args = [] - for name, legacy_arg in legacy_args.items(): - if legacy_arg is not None: - active_legacy_args.append(name) - - if active_legacy_args: - warnings.warn( - "Legacy pulse drawer is deprecated. " - "Specified arguments {dep_args} are deprecated. " - "Please check the API document of new pulse drawer " - "`qiskit.visualization.pulse_drawer_v2`." - "".format(dep_args=", ".join(active_legacy_args)), - DeprecationWarning, - ) - - if filename: - warnings.warn( - "File saving is delegated to the plotter software in new drawer. " - "If you specify matplotlib plotter family to `plotter` argument, " - "you can call `savefig` method with the returned Figure object.", - DeprecationWarning, - ) - - if isinstance(style, SchedStyle): - style = None - warnings.warn( - "Legacy stylesheet is specified. This is ignored in the new drawer. " - "Please check the API documentation for this method." - ) + from qiskit.visualization import pulse_drawer_v2 return pulse_drawer_v2( program=self, diff --git a/qiskit/pulse/transforms/__init__.py b/qiskit/pulse/transforms/__init__.py index 33db6d71e4b3..75b17bff3219 100644 --- a/qiskit/pulse/transforms/__init__.py +++ b/qiskit/pulse/transforms/__init__.py @@ -83,11 +83,6 @@ AlignLeft, AlignRight, AlignSequential, - align_equispaced, - align_func, - align_left, - align_right, - align_sequential, ) from qiskit.pulse.transforms.base_transforms import target_qobj_transform diff --git a/qiskit/pulse/transforms/alignments.py b/qiskit/pulse/transforms/alignments.py index fbdfc800f6e5..f60ab70bf7c1 100644 --- a/qiskit/pulse/transforms/alignments.py +++ b/qiskit/pulse/transforms/alignments.py @@ -19,7 +19,7 @@ from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.exceptions import PulseError from qiskit.pulse.schedule import Schedule, ScheduleComponent -from qiskit.pulse.utils import instruction_duration_validation, deprecated_functionality +from qiskit.pulse.utils import instruction_duration_validation class AlignmentKind(abc.ABC): @@ -364,91 +364,3 @@ def to_dict(self) -> Dict[str, Any]: "duration": self._context_params[0], "func": self._func.__name__, } - - -@deprecated_functionality -def align_left(schedule: Schedule) -> Schedule: - """Align a list of pulse instructions on the left. - - Args: - schedule: Input schedule of which top-level sub-schedules will be rescheduled. - - Returns: - New schedule with input `schedule`` child schedules and instructions - left aligned. - """ - context = AlignLeft() - return context.align(schedule) - - -@deprecated_functionality -def align_right(schedule: Schedule) -> Schedule: - """Align a list of pulse instructions on the right. - - Args: - schedule: Input schedule of which top-level sub-schedules will be rescheduled. - - Returns: - New schedule with input `schedule`` child schedules and instructions - right aligned. - """ - context = AlignRight() - return context.align(schedule) - - -@deprecated_functionality -def align_sequential(schedule: Schedule) -> Schedule: - """Schedule all top-level nodes in parallel. - - Args: - schedule: Input schedule of which top-level sub-schedules will be rescheduled. - - Returns: - New schedule with input `schedule`` child schedules and instructions - applied sequentially across channels - """ - context = AlignSequential() - return context.align(schedule) - - -@deprecated_functionality -def align_equispaced(schedule: Schedule, duration: int) -> Schedule: - """Schedule a list of pulse instructions with equivalent interval. - - Args: - schedule: Input schedule of which top-level sub-schedules will be rescheduled. - duration: Duration of context. This should be larger than the schedule duration. - - Returns: - New schedule with input `schedule`` child schedules and instructions - aligned with equivalent interval. - - Notes: - This context is convenient for writing PDD or Hahn echo sequence for example. - """ - context = AlignEquispaced(duration=duration) - return context.align(schedule) - - -@deprecated_functionality -def align_func(schedule: Schedule, duration: int, func: Callable[[int], float]) -> Schedule: - """Schedule a list of pulse instructions with schedule position defined by the - numerical expression. - - Args: - schedule: Input schedule of which top-level sub-schedules will be rescheduled. - duration: Duration of context. This should be larger than the schedule duration. - func: A function that takes an index of sub-schedule and returns the - fractional coordinate of of that sub-schedule. - The returned value should be defined within [0, 1]. - The pulse index starts from 1. - - Returns: - New schedule with input `schedule`` child schedules and instructions - aligned with equivalent interval. - - Notes: - This context is convenient for writing UDD sequence for example. - """ - context = AlignFunc(duration=duration, func=func) - return context.align(schedule) From f4565ee7698f03afded2c74fe8d44583293a6938 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 12:52:28 +0900 Subject: [PATCH 02/11] cleanup unittests --- test/python/pulse/test_block.py | 60 --- test/python/pulse/test_builder.py | 22 - test/python/pulse/test_parameter_manager.py | 303 ++++++++++++- test/python/pulse/test_parameters.py | 473 -------------------- test/python/pulse/test_pulse_lib.py | 38 ++ test/python/pulse/test_transforms.py | 102 +++-- 6 files changed, 398 insertions(+), 600 deletions(-) delete mode 100644 test/python/pulse/test_parameters.py diff --git a/test/python/pulse/test_block.py b/test/python/pulse/test_block.py index 9f23169cd417..0bffbe7422c1 100644 --- a/test/python/pulse/test_block.py +++ b/test/python/pulse/test_block.py @@ -216,32 +216,6 @@ def test_duration(self): self.assertEqual(block.duration, 350) - def test_timeslots(self): - """Test if correct timeslot is returned with implicit scheduling.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - ref_slots = {self.d0: [(0, 100), (100, 150), (150, 350)], self.d1: [(0, 200)]} - - self.assertDictEqual(block.timeslots, ref_slots) - - def test_start_time(self): - """Test if correct schedule start time is returned with implicit scheduling.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - self.assertEqual(block.start_time, 0) - - def test_stop_time(self): - """Test if correct schedule stop time is returned with implicit scheduling.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - self.assertEqual(block.stop_time, 350) - def test_channels(self): """Test if all channels are returned.""" block = pulse.ScheduleBlock() @@ -267,40 +241,6 @@ def test_channel_duraction(self): self.assertEqual(block.ch_duration(self.d0), 350) self.assertEqual(block.ch_duration(self.d1), 200) - def test_channel_start_time(self): - """Test if correct start time is calculated for each channel.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - self.assertEqual(block.ch_start_time(self.d0), 0) - self.assertEqual(block.ch_start_time(self.d1), 0) - - def test_channel_stop_time(self): - """Test if correct stop time is calculated for each channel.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - self.assertEqual(block.ch_stop_time(self.d0), 350) - self.assertEqual(block.ch_stop_time(self.d1), 200) - - def test_cannot_insert(self): - """Test insert is not supported.""" - block = pulse.ScheduleBlock() - - with self.assertRaises(PulseError): - block.insert(0, pulse.Delay(10, self.d0)) - - def test_cannot_shift(self): - """Test shift is not supported.""" - block = pulse.ScheduleBlock() - for inst in self.test_blocks: - block.append(inst) - - with self.assertRaises(PulseError): - block.shift(10, inplace=True) - def test_cannot_append_schedule(self): """Test schedule cannot be appended. Schedule should be input as Call instruction.""" block = pulse.ScheduleBlock() diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index 712289fb0d8d..f3c74cffe94c 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -182,28 +182,6 @@ def test_align_right(self): self.assertScheduleEqual(schedule, reference) - def test_inline(self): - """Test the inlining context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - - with pulse.build() as schedule: - pulse.delay(3, d0) - with pulse.inline(): - # this alignment will be ignored due to inlining. - with pulse.align_right(): - pulse.delay(5, d1) - pulse.delay(7, d0) - - reference = pulse.Schedule() - # d0 - reference += instructions.Delay(3, d0) - reference += instructions.Delay(7, d0) - # d1 - reference += instructions.Delay(5, d1) - - self.assertScheduleEqual(schedule, reference) - def test_transpiler_settings(self): """Test the transpiler settings context. diff --git a/test/python/pulse/test_parameter_manager.py b/test/python/pulse/test_parameter_manager.py index 5ccffe391fd9..c6a5df6d944e 100644 --- a/test/python/pulse/test_parameter_manager.py +++ b/test/python/pulse/test_parameter_manager.py @@ -16,8 +16,11 @@ from copy import deepcopy +import numpy as np + from qiskit import pulse from qiskit.circuit import Parameter +from qiskit.pulse.exceptions import PulseError, UnassignedDurationError from qiskit.pulse.parameter_manager import ParameterGetter, ParameterSetter from qiskit.pulse.transforms import AlignEquispaced, AlignLeft, inline_subroutines from qiskit.test import QiskitTestCase @@ -122,6 +125,19 @@ def test_get_parameter_from_pulse(self): self.assertSetEqual(visitor.parameters, ref_params) + def test_get_parameter_from_acquire(self): + """Test get parameters from acquire instruction.""" + test_obj = pulse.Acquire( + 16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1) + ) + + visitor = ParameterGetter() + visitor.visit(test_obj) + + ref_params = {self.ch1} + + self.assertSetEqual(visitor.parameters, ref_params) + def test_get_parameter_from_inst(self): """Test get parameters from instruction.""" test_obj = pulse.ShiftPhase(self.phi1 + self.phi2, pulse.DriveChannel(0)) @@ -147,6 +163,19 @@ def test_get_parameter_from_call(self): self.assertSetEqual(visitor.parameters, ref_params) + def test_with_function(self): + """Test ParameterExpressions formed trivially in a function.""" + def get_shift(variable): + return variable - 1 + test_obj = pulse.ShiftPhase(get_shift(self.phi1), self.d1) + + visitor = ParameterGetter() + visitor.visit(test_obj) + + ref_params = {self.phi1, self.ch1} + + self.assertSetEqual(visitor.parameters, ref_params) + def test_get_parameter_from_alignment_context(self): """Test get parameters from alignment context.""" test_obj = AlignEquispaced(duration=self.context_dur + self.dur1) @@ -197,6 +226,21 @@ def test_set_parameter_to_pulse(self): self.assertEqual(assigned, ref_obj) + def test_set_parameter_to_acquire(self): + """Test set parameters to acquire instruction.""" + test_obj = pulse.Acquire( + 16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1) + ) + + value_dict = {self.ch1: 2} + + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(test_obj) + + ref_obj = pulse.Acquire(16000, pulse.AcquireChannel(2), pulse.MemorySlot(2)) + + self.assertEqual(assigned, ref_obj) + def test_set_parameter_to_inst(self): """Test get parameters from instruction.""" test_obj = pulse.ShiftPhase(self.phi1 + self.phi2, pulse.DriveChannel(0)) @@ -229,6 +273,23 @@ def test_set_parameter_to_call(self): self.assertEqual(assigned, ref_obj) + def test_with_function(self): + """Test ParameterExpressions formed trivially in a function.""" + + def get_shift(variable): + return variable - 1 + + test_obj = pulse.ShiftPhase(get_shift(self.phi1), self.d1) + + value_dict = {self.phi1: 1.57, self.ch1: 2} + + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(test_obj) + + ref_obj = pulse.ShiftPhase(get_shift(1.57), pulse.DriveChannel(2)) + + self.assertEqual(assigned, ref_obj) + def test_set_parameter_to_alignment_context(self): """Test get parameters from alignment context.""" test_obj = AlignEquispaced(duration=self.context_dur + self.dur1) @@ -279,17 +340,71 @@ def test_nested_assignment_partial_bind(self): def test_complex_valued_parameter(self): """Test complex valued parameter can be casted to a complex value.""" amp = Parameter("amp") + test_obj = pulse.Constant(duration=160, amp=1j * amp) + + value_dict = {amp: 0.1} + + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(test_obj) + + ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) + + self.assertEqual(assigned, ref_obj) + + def test_complex_value_to_parameter(self): + """Test complex value can be assigned to parameter object.""" + amp = Parameter("amp") + test_obj = pulse.Constant(duration=160, amp=amp) + + value_dict = {amp: 0.1j} + + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(test_obj) + + ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) + + self.assertEqual(assigned, ref_obj) + + def test_complex_parameter_expression(self): + """Test assignment of complex-valued parameter expression to parameter.""" + amp = Parameter("amp") + + mag = Parameter("A") + phi = Parameter("phi") + + test_obj = pulse.Constant(duration=160, amp=amp) + + # generate parameter expression + value_dict = {amp: mag * np.exp(1j * phi)} + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(test_obj) + + # generate complex value + value_dict = {mag: 0.1, phi: 0.5} + visitor = ParameterSetter(param_map=value_dict) + assigned = visitor.visit(assigned) + + # evaluated parameter expression: 0.0877582561890373 + 0.0479425538604203*I + value_dict = {amp: 0.1 * np.exp(0.5j)} + visitor = ParameterSetter(param_map=value_dict) + ref_obj = visitor.visit(test_obj) + + self.assertEqual(assigned, ref_obj) + + def test_invalid_pulse_amplitude(self): + """Test that invalid parameters are still checked upon assignment.""" + amp = Parameter("amp") test_sched = pulse.ScheduleBlock() test_sched.append( pulse.Play( - pulse.Constant(160, amp=1j * amp), + pulse.Constant(160, amp=2 * amp), pulse.DriveChannel(0), ), inplace=True, ) - test_assigned = test_sched.assign_parameters({amp: 0.1}, inplace=False) - self.assertTrue(isinstance(test_assigned.blocks[0].pulse.amp, complex)) + with self.assertRaises(PulseError): + test_sched.assign_parameters({amp: 0.6}, inplace=False) def test_set_parameter_to_complex_schedule(self): """Test get parameters from complicated schedule.""" @@ -339,3 +454,185 @@ def test_set_parameter_to_complex_schedule(self): ) self.assertEqual(assigned, ref_obj) + + +class TestAssignFromProgram(QiskitTestCase): + + def test_attribute_parameters(self): + """Test the ``parameter`` attributes.""" + sigma = Parameter("sigma") + amp = Parameter("amp") + + waveform = pulse.library.Gaussian(duration=128, sigma=sigma, amp=amp) + + block = pulse.ScheduleBlock() + block += pulse.Play(waveform, pulse.DriveChannel(10)) + block.assign_parameters({amp: 0.2, sigma: 4}, inplace=True) + + ref_set = {amp, sigma} + + self.assertSetEqual(block.parameters, ref_set) + + def test_parametric_pulses(self): + """Test Parametric Pulses with parameters determined by ParameterExpressions + in the Play instruction.""" + sigma = Parameter("sigma") + amp = Parameter("amp") + + waveform = pulse.library.Gaussian(duration=128, sigma=sigma, amp=amp) + + block = pulse.ScheduleBlock() + block += pulse.Play(waveform, pulse.DriveChannel(10)) + block.assign_parameters({amp: 0.2, sigma: 4}, inplace=True) + + self.assertEqual(block.blocks[0].pulse.amp, 0.2) + self.assertEqual(block.blocks[0].pulse.sigma, 4.0) + + def test_parameters_from_subroutine(self): + """Test that get parameter objects from subroutines.""" + param1 = Parameter("amp") + waveform = pulse.library.Constant(duration=100, amp=param1) + + program_layer0 = pulse.ScheduleBlock() + program_layer0 += pulse.Play(waveform, pulse.DriveChannel(0)) + + # from call instruction + program_layer1 = pulse.ScheduleBlock() + program_layer1 += pulse.instructions.Call(program_layer0) + self.assertEqual(program_layer1.get_parameters("amp")[0], param1) + + # from nested call instruction + program_layer2 = pulse.Schedule() + program_layer2 += pulse.instructions.Call(program_layer1) + self.assertEqual(program_layer2.get_parameters("amp")[0], param1) + + def test_assign_parameter_to_subroutine(self): + """Test that assign parameter objects to subroutines.""" + param1 = Parameter("amp") + waveform = pulse.library.Constant(duration=100, amp=param1) + + program_layer0 = pulse.ScheduleBlock() + program_layer0 += pulse.Play(waveform, pulse.DriveChannel(0)) + reference = program_layer0.assign_parameters({param1: 0.1}, inplace=False) + + # to call instruction + program_layer1 = pulse.ScheduleBlock() + program_layer1 += pulse.instructions.Call(program_layer0) + target = program_layer1.assign_parameters({param1: 0.1}, inplace=False) + self.assertEqual(inline_subroutines(target), reference) + + # to nested call instruction + program_layer2 = pulse.ScheduleBlock() + program_layer2 += pulse.instructions.Call(program_layer1) + target = program_layer2.assign_parameters({param1: 0.1}, inplace=False) + self.assertEqual(inline_subroutines(target), reference) + + def test_assign_parameter_to_subroutine_parameter(self): + """Test that assign parameter objects to parameter of subroutine.""" + param1 = Parameter("amp") + waveform = pulse.library.Constant(duration=100, amp=param1) + + param_sub1 = Parameter("p1") + param_sub2 = Parameter("p2") + + subroutine = pulse.Schedule() + subroutine += pulse.Play(waveform, pulse.DriveChannel(0)) + reference = subroutine.assign_parameters({param1: 0.6}, inplace=False) + + main_prog = pulse.Schedule() + pdict = {param1: param_sub1 + param_sub2} + main_prog += pulse.instructions.Call(subroutine, value_dict=pdict) + + # parameter is overwritten by parameters + self.assertEqual(len(main_prog.parameters), 2) + target = main_prog.assign_parameters({param_sub1: 0.1, param_sub2: 0.5}, inplace=False) + result = inline_subroutines(target) + + self.assertEqual(result, reference) + + +class TestScheduleTimeslots(QiskitTestCase): + """Test for edge cases of timing overlap on parametrized channels. + + Note that this test is dedicated to `Schedule` since `ScheduleBlock` implicitly + assigns instruction time t0 that doesn't overlap with existing instructions. + """ + + def test_overlapping_pulses(self): + """Test that an error is still raised when overlapping instructions are assigned.""" + param_idx = Parameter("q") + + schedule = pulse.Schedule() + schedule |= pulse.Play( + pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx) + ) + with self.assertRaises(PulseError): + schedule |= pulse.Play( + pulse.Waveform([0.5, 0.5, 0.5, 0.5]), pulse.DriveChannel(param_idx) + ) + + def test_overlapping_on_assignment(self): + """Test that assignment will catch against existing instructions.""" + param_idx = Parameter("q") + + schedule = pulse.Schedule() + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(1)) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx)) + with self.assertRaises(PulseError): + schedule.assign_parameters({param_idx: 1}) + + def test_overlapping_on_expression_assigment_to_zero(self): + """Test constant*zero expression conflict.""" + param_idx = Parameter("q") + + schedule = pulse.Schedule() + schedule |= pulse.Play( + pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx) + ) + schedule |= pulse.Play( + pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(2 * param_idx) + ) + with self.assertRaises(PulseError): + schedule.assign_parameters({param_idx: 0}) + + def test_merging_upon_assignment(self): + """Test that schedule can match instructions on a channel.""" + param_idx = Parameter("q") + + schedule = pulse.Schedule() + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(1)) + schedule = schedule.insert( + 4, pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx)) + ) + schedule.assign_parameters({param_idx: 1}) + + self.assertEqual(schedule.ch_duration(pulse.DriveChannel(1)), 8) + self.assertEqual(schedule.channels, (pulse.DriveChannel(1),)) + + def test_overlapping_on_multiple_assignment(self): + """Test that assigning one qubit then another raises error when overlapping.""" + param_idx1 = Parameter("q1") + param_idx2 = Parameter("q2") + + schedule = pulse.Schedule() + schedule |= pulse.Play( + pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx1) + ) + schedule |= pulse.Play( + pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx2) + ) + schedule.assign_parameters({param_idx1: 2}) + + with self.assertRaises(PulseError): + schedule.assign_parameters({param_idx1: 2}) + + def test_cannot_build_schedule_with_unassigned_duration(self): + """Test we cannot build schedule with parameterized instructions""" + dur = Parameter("dur") + ch = pulse.DriveChannel(0) + + test_play = pulse.Play(pulse.Gaussian(dur, 0.1, dur / 4), ch) + + sched = pulse.Schedule() + with self.assertRaises(UnassignedDurationError): + sched.insert(0, test_play) diff --git a/test/python/pulse/test_parameters.py b/test/python/pulse/test_parameters.py deleted file mode 100644 index 894e0c17503b..000000000000 --- a/test/python/pulse/test_parameters.py +++ /dev/null @@ -1,473 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2020. -# -# 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. - -"""Test cases for parameters used in Schedules.""" -import unittest -import cmath -from copy import deepcopy - -import numpy as np - -from qiskit import pulse, assemble -from qiskit.circuit import Parameter -from qiskit.circuit.parameterexpression import HAS_SYMENGINE -from qiskit.pulse import PulseError -from qiskit.pulse.channels import DriveChannel, AcquireChannel, MemorySlot -from qiskit.pulse.transforms import inline_subroutines -from qiskit.test import QiskitTestCase -from qiskit.test.mock import FakeAlmaden - - -class TestPulseParameters(QiskitTestCase): - """Tests usage of Parameters in qiskit.pulse; specifically in Schedules, - Instructions, and Pulses. - """ - - def setUp(self): - """Just some useful, reusable Parameters and constants.""" - super().setUp() - self.alpha = Parameter("⍺") - self.beta = Parameter("beta") - self.gamma = Parameter("γ") - self.phi = Parameter("ϕ") - self.theta = Parameter("ϑ") - self.amp = Parameter("amp") - self.sigma = Parameter("sigma") - self.qubit = Parameter("q") - self.dur = Parameter("dur") - - self.freq = 4.5e9 - self.shift = 0.2e9 - self.phase = 3.1415 / 4 - - self.backend = FakeAlmaden() - - def test_parameter_attribute_channel(self): - """Test the ``parameter`` attributes.""" - chan = DriveChannel(self.qubit * self.alpha) - self.assertTrue(chan.is_parameterized()) - self.assertEqual(chan.parameters, {self.qubit, self.alpha}) - chan = chan.assign(self.qubit, self.alpha) - self.assertEqual(chan.parameters, {self.alpha}) - chan = chan.assign(self.alpha, self.beta) - self.assertEqual(chan.parameters, {self.beta}) - chan = chan.assign(self.beta, 1) - self.assertFalse(chan.is_parameterized()) - - def test_parameter_attribute_instruction(self): - """Test the ``parameter`` attributes.""" - inst = pulse.ShiftFrequency(self.alpha * self.qubit, DriveChannel(self.qubit)) - self.assertTrue(inst.is_parameterized()) - self.assertEqual(inst.parameters, {self.alpha, self.qubit}) - inst.assign_parameters({self.alpha: self.qubit}) - self.assertEqual(inst.parameters, {self.qubit}) - inst.assign_parameters({self.qubit: 1}) - self.assertFalse(inst.is_parameterized()) - self.assertEqual(inst.parameters, set()) - - def test_parameter_attribute_play(self): - """Test the ``parameter`` attributes.""" - inst = pulse.Play( - pulse.Gaussian(self.dur, self.amp, self.sigma), pulse.DriveChannel(self.qubit) - ) - self.assertTrue(inst.is_parameterized()) - self.assertSetEqual(inst.parameters, {self.dur, self.amp, self.sigma, self.qubit}) - - inst = pulse.Play(pulse.Gaussian(self.dur, 0.1, self.sigma), pulse.DriveChannel(self.qubit)) - self.assertTrue(inst.is_parameterized()) - self.assertSetEqual(inst.parameters, {self.dur, self.sigma, self.qubit}) - - def test_parameter_attribute_schedule(self): - """Test the ``parameter`` attributes.""" - schedule = pulse.Schedule() - self.assertFalse(schedule.is_parameterized()) - schedule += pulse.SetFrequency(self.alpha, DriveChannel(0)) - self.assertEqual(schedule.parameters, {self.alpha}) - schedule += pulse.ShiftFrequency(self.gamma, DriveChannel(0)) - self.assertEqual(schedule.parameters, {self.alpha, self.gamma}) - schedule += pulse.SetPhase(self.phi, DriveChannel(1)) - self.assertTrue(schedule.is_parameterized()) - self.assertEqual(schedule.parameters, {self.alpha, self.gamma, self.phi}) - schedule.assign_parameters({self.phi: self.alpha, self.gamma: self.shift}) - self.assertEqual(schedule.parameters, {self.alpha}) - schedule.assign_parameters({self.alpha: self.beta}) - self.assertEqual(schedule.parameters, {self.beta}) - schedule.assign_parameters({self.beta: 10}) - self.assertFalse(schedule.is_parameterized()) - - def test_straight_schedule_bind(self): - """Nothing fancy, 1:1 mapping.""" - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(self.alpha, DriveChannel(0)) - schedule += pulse.ShiftFrequency(self.gamma, DriveChannel(0)) - schedule += pulse.SetPhase(self.phi, DriveChannel(1)) - schedule += pulse.ShiftPhase(self.theta, DriveChannel(1)) - - schedule.assign_parameters( - { - self.alpha: self.freq, - self.gamma: self.shift, - self.phi: self.phase, - self.theta: -self.phase, - } - ) - - insts = assemble(schedule, self.backend).experiments[0].instructions - GHz = 1e9 # pylint: disable=invalid-name - self.assertEqual(float(insts[0].frequency * GHz), self.freq) - self.assertEqual(float(insts[1].frequency * GHz), self.shift) - self.assertEqual(float(insts[2].phase), self.phase) - self.assertEqual(float(insts[3].phase), -self.phase) - - def test_multiple_parameters(self): - """Expressions of parameters with partial assignment.""" - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(self.alpha + self.beta, DriveChannel(0)) - schedule += pulse.ShiftFrequency(self.gamma + self.beta, DriveChannel(0)) - schedule += pulse.SetPhase(self.phi, DriveChannel(1)) - - # Partial bind - delta = 1e9 - schedule.assign_parameters({self.alpha: self.freq - delta}) - schedule.assign_parameters({self.beta: delta}) - schedule.assign_parameters({self.gamma: self.shift - delta}) - schedule.assign_parameters({self.phi: self.phase}) - - insts = schedule.instructions - self.assertEqual(float(insts[0][1].frequency), self.freq) - self.assertEqual(float(insts[1][1].frequency), self.shift) - self.assertEqual(float(insts[2][1].phase), self.phase) - - def test_with_function(self): - """Test ParameterExpressions formed trivially in a function.""" - - def get_frequency(variable): - return 2 * variable - - def get_shift(variable): - return variable - 1 - - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(get_frequency(self.alpha), DriveChannel(0)) - schedule += pulse.ShiftFrequency(get_shift(self.gamma), DriveChannel(0)) - - schedule.assign_parameters({self.alpha: self.freq / 2, self.gamma: self.shift + 1}) - - insts = schedule.instructions - self.assertEqual(float(insts[0][1].frequency), self.freq) - self.assertEqual(float(insts[1][1].frequency), self.shift) - - def test_substitution(self): - """Test Parameter substitution (vs bind).""" - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(self.alpha, DriveChannel(0)) - - schedule.assign_parameters({self.alpha: 2 * self.beta}) - self.assertEqual(schedule.instructions[0][1].frequency, 2 * self.beta) - schedule.assign_parameters({self.beta: self.freq / 2}) - self.assertEqual(float(schedule.instructions[0][1].frequency), self.freq) - - def test_substitution_with_existing(self): - """Test that substituting one parameter with an existing parameter works.""" - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(self.alpha, DriveChannel(self.qubit)) - - schedule.assign_parameters({self.alpha: 1e9 * self.qubit}) - self.assertEqual(schedule.instructions[0][1].frequency, 1e9 * self.qubit) - schedule.assign_parameters({self.qubit: 2}) - self.assertEqual(float(schedule.instructions[0][1].frequency), 2e9) - - def test_channels(self): - """Test that channel indices can also be parameterized and assigned.""" - schedule = pulse.Schedule() - schedule += pulse.ShiftPhase(self.phase, DriveChannel(2 * self.qubit)) - - schedule.assign_parameters({self.qubit: 4}) - self.assertEqual(schedule.instructions[0][1].channel, DriveChannel(8)) - - def test_acquire_channels(self): - """Test Acquire instruction with multiple channels parameterized.""" - schedule = pulse.Schedule() - schedule += pulse.Acquire(16000, AcquireChannel(self.qubit), MemorySlot(self.qubit)) - schedule.assign_parameters({self.qubit: 1}) - self.assertEqual(schedule.instructions[0][1].channel, AcquireChannel(1)) - self.assertEqual(schedule.instructions[0][1].mem_slot, MemorySlot(1)) - - def test_overlapping_pulses(self): - """Test that an error is still raised when overlapping instructions are assigned.""" - schedule = pulse.Schedule() - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(self.qubit)) - with self.assertRaises(PulseError): - schedule |= pulse.Play(pulse.Waveform([0.5, 0.5, 0.5, 0.5]), DriveChannel(self.qubit)) - - def test_overlapping_on_assignment(self): - """Test that assignment will catch against existing instructions.""" - schedule = pulse.Schedule() - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(1)) - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(self.qubit)) - with self.assertRaises(PulseError): - schedule.assign_parameters({self.qubit: 1}) - - def test_overlapping_on_expression_assigment_to_zero(self): - """Test constant*zero expression conflict.""" - schedule = pulse.Schedule() - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(self.qubit)) - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(2 * self.qubit)) - with self.assertRaises(PulseError): - schedule.assign_parameters({self.qubit: 0}) - - def test_merging_upon_assignment(self): - """Test that schedule can match instructions on a channel.""" - schedule = pulse.Schedule() - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(1)) - schedule = schedule.insert( - 4, pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(self.qubit)) - ) - schedule.assign_parameters({self.qubit: 1}) - self.assertEqual(schedule.ch_duration(DriveChannel(1)), 8) - self.assertEqual(schedule.channels, (DriveChannel(1),)) - - def test_overlapping_on_multiple_assignment(self): - """Test that assigning one qubit then another raises error when overlapping.""" - qubit2 = Parameter("q2") - schedule = pulse.Schedule() - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(self.qubit)) - schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), DriveChannel(qubit2)) - schedule.assign_parameters({qubit2: 2}) - with self.assertRaises(PulseError): - schedule.assign_parameters({self.qubit: 2}) - - def test_play_with_parametricpulse(self): - """Test Parametric Pulses with parameters determined by ParameterExpressions - in the Play instruction.""" - waveform = pulse.library.Gaussian(duration=128, sigma=self.sigma, amp=self.amp) - - schedule = pulse.Schedule() - schedule += pulse.Play(waveform, DriveChannel(10)) - schedule.assign_parameters({self.amp: 0.2, self.sigma: 4}) - - self.backend.configuration().parametric_pulses = ["gaussian", "drag"] - insts = schedule.instructions - self.assertEqual(insts[0][1].pulse.amp, 0.2) - self.assertEqual(insts[0][1].pulse.sigma, 4.0) - - def test_parametric_pulses_parameter_assignment(self): - """Test Parametric Pulses with parameters determined by ParameterExpressions.""" - waveform = pulse.library.GaussianSquare( - duration=1280, sigma=self.sigma, amp=self.amp, width=1000 - ) - waveform = waveform.assign_parameters({self.amp: 0.3, self.sigma: 12}) - self.assertEqual(waveform.amp, 0.3) - self.assertEqual(waveform.sigma, 12) - - waveform = pulse.library.Drag(duration=1280, sigma=self.sigma, amp=self.amp, beta=2) - waveform = waveform.assign_parameters({self.sigma: 12.7}) - self.assertEqual(waveform.amp, self.amp) - self.assertEqual(waveform.sigma, 12.7) - - def test_parametric_pulses_limit_amplitude(self): - """Test that the check for amplitude less than or equal to 1 can be disabled.""" - waveform = pulse.library.Gaussian( - duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=False - ) - self.assertGreater(np.abs(waveform.amp), 1.0) - with self.assertRaises(PulseError): - waveform = pulse.library.Gaussian( - duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=True - ) - - waveform = pulse.library.GaussianSquare( - duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10, limit_amplitude=False - ) - self.assertGreater(np.abs(waveform.amp), 1.0) - with self.assertRaises(PulseError): - waveform = pulse.library.GaussianSquare( - duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10, limit_amplitude=True - ) - - waveform = pulse.library.Drag( - duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=False - ) - self.assertGreater(np.abs(waveform.amp), 1.0) - with self.assertRaises(PulseError): - waveform = pulse.library.Drag( - duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=True - ) - - waveform = pulse.library.Constant(duration=100, amp=1.1 + 0.8j, limit_amplitude=False) - self.assertGreater(np.abs(waveform.amp), 1.0) - with self.assertRaises(PulseError): - waveform = pulse.library.Constant(duration=100, amp=1.1 + 0.8j, limit_amplitude=True) - - @unittest.skip("Not yet supported by ParameterExpression") - def test_complex_value_assignment(self): - """Test that complex values can be assigned to Parameters.""" - waveform = pulse.library.Constant(duration=1280, amp=self.amp) - waveform.assign_parameters({self.amp: 0.2j}) - self.assertEqual(waveform.amp, 0.2j) - - def test_invalid_parametric_pulses(self): - """Test that invalid parameters are still checked upon assignment.""" - schedule = pulse.Schedule() - waveform = pulse.library.Constant(duration=1280, amp=2 * self.amp) - schedule += pulse.Play(waveform, DriveChannel(0)) - with self.assertRaises(PulseError): - waveform.assign_parameters({self.amp: 0.6}) - - def test_get_parameter(self): - """Test that get parameter by name.""" - param1 = Parameter("amp") - param2 = Parameter("amp") - - schedule = pulse.Schedule() - waveform1 = pulse.library.Constant(duration=1280, amp=param1) - waveform2 = pulse.library.Constant(duration=1280, amp=param2) - schedule += pulse.Play(waveform1, DriveChannel(0)) - schedule += pulse.Play(waveform2, DriveChannel(1)) - - self.assertEqual(len(schedule.get_parameters("amp")), 2) - - def test_reference_to_subroutine_params(self): - """Test that get parameter objects from subroutines.""" - param1 = Parameter("amp") - waveform = pulse.library.Constant(duration=100, amp=param1) - - program_layer0 = pulse.Schedule() - program_layer0 += pulse.Play(waveform, DriveChannel(0)) - - # from call instruction - program_layer1 = pulse.Schedule() - program_layer1 += pulse.instructions.Call(program_layer0) - self.assertEqual(program_layer1.get_parameters("amp")[0], param1) - - # from nested call instruction - program_layer2 = pulse.Schedule() - program_layer2 += pulse.instructions.Call(program_layer1) - self.assertEqual(program_layer2.get_parameters("amp")[0], param1) - - def test_assign_parameter_to_subroutine(self): - """Test that assign parameter objects to subroutines.""" - param1 = Parameter("amp") - waveform = pulse.library.Constant(duration=100, amp=param1) - - program_layer0 = pulse.Schedule() - program_layer0 += pulse.Play(waveform, DriveChannel(0)) - reference = deepcopy(program_layer0).assign_parameters({param1: 0.1}) - - # to call instruction - program_layer1 = pulse.Schedule() - program_layer1 += pulse.instructions.Call(program_layer0) - target = deepcopy(program_layer1).assign_parameters({param1: 0.1}) - self.assertEqual(inline_subroutines(target), reference) - - # to nested call instruction - program_layer2 = pulse.Schedule() - program_layer2 += pulse.instructions.Call(program_layer1) - target = deepcopy(program_layer2).assign_parameters({param1: 0.1}) - self.assertEqual(inline_subroutines(target), reference) - - def test_assign_parameter_to_subroutine_parameter(self): - """Test that assign parameter objects to parameter of subroutine.""" - param1 = Parameter("amp") - waveform = pulse.library.Constant(duration=100, amp=param1) - - param_sub1 = Parameter("amp") - param_sub2 = Parameter("phase") - - subroutine = pulse.Schedule() - subroutine += pulse.Play(waveform, DriveChannel(0)) - reference = deepcopy(subroutine).assign_parameters({param1: 0.1 * np.exp(0.5j)}) - - main_prog = pulse.Schedule() - pdict = {param1: param_sub1 * np.exp(1j * param_sub2)} - main_prog += pulse.instructions.Call(subroutine, value_dict=pdict) - - # parameter is overwritten by parameters - self.assertEqual(len(main_prog.parameters), 2) - target = deepcopy(main_prog).assign_parameters({param_sub1: 0.1, param_sub2: 0.5}) - result = inline_subroutines(target) - if not HAS_SYMENGINE: - self.assertEqual(result, reference) - else: - # Because of simplification differences between sympy and symengine when - # symengine is used we get 0.1*exp(0.5*I) instead of the evaluated - # 0.0877582562 + 0.0479425539*I resulting in a failure. When - # symengine is installed manually build the amplitude as a complex to - # avoid this. - reference = pulse.Schedule() - waveform = pulse.library.Constant(duration=100, amp=0.1 * cmath.exp(0.5j)) - reference += pulse.Play(waveform, DriveChannel(0)) - self.assertEqual(result, reference) - - -class TestParameterDuration(QiskitTestCase): - """Tests parametrization of instruction duration.""" - - def test_pulse_duration(self): - """Test parametrization of pulse duration.""" - dur = Parameter("dur") - - test_pulse = pulse.Gaussian(dur, 0.1, dur / 4) - ref_pulse = pulse.Gaussian(160, 0.1, 40) - - self.assertEqual(test_pulse.assign_parameters({dur: 160}), ref_pulse) - - def test_play_duration(self): - """Test parametrization of play instruction duration.""" - dur = Parameter("dur") - ch = pulse.DriveChannel(0) - - test_play = pulse.Play(pulse.Gaussian(dur, 0.1, dur / 4), ch) - test_play.assign_parameters({dur: 160}) - - self.assertEqual(test_play.duration, 160) - - def test_delay_duration(self): - """Test parametrization of delay duration.""" - dur = Parameter("dur") - ch = pulse.DriveChannel(0) - - test_delay = pulse.Delay(dur, ch) - test_delay.assign_parameters({dur: 300}) - - self.assertEqual(test_delay.duration, 300) - - def test_acquire_duration(self): - """Test parametrization of acquire duration.""" - dur = Parameter("dur") - ch = pulse.AcquireChannel(0) - mem_slot = pulse.MemorySlot(0) - - test_acquire = pulse.Acquire(dur, ch, mem_slot=mem_slot) - test_acquire.assign_parameters({dur: 300}) - - self.assertEqual(test_acquire.duration, 300) - - def test_is_parameterized(self): - """Test is parameterized method for parameter duration.""" - dur = Parameter("dur") - ch = pulse.DriveChannel(0) - - test_play = pulse.Play(pulse.Gaussian(dur, 0.1, dur / 4), ch) - - self.assertEqual(test_play.is_parameterized(), True) - - def test_cannot_build_schedule(self): - """Test we cannot build schedule with parameterized instructions""" - dur = Parameter("dur") - ch = pulse.DriveChannel(0) - - test_play = pulse.Play(pulse.Gaussian(dur, 0.1, dur / 4), ch) - - sched = pulse.Schedule() - with self.assertRaises(pulse.exceptions.UnassignedDurationError): - sched.insert(0, test_play) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 88e54b864e41..f03b969e997d 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -278,6 +278,44 @@ def test_hash_generation(self): self.assertListEqual(test_hash, ref_hash) + def test_gaussian_limit_amplitude(self): + """Test that the check for amplitude less than or equal to 1 can be disabled.""" + waveform = Gaussian( + duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=False + ) + self.assertGreater(np.abs(waveform.amp), 1.0) + + with self.assertRaises(PulseError): + Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=True) + + def test_gaussian_square_limit_amplitude(self): + """Test that the check for amplitude less than or equal to 1 can be disabled.""" + waveform = GaussianSquare( + duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10, limit_amplitude=False + ) + self.assertGreater(np.abs(waveform.amp), 1.0) + + with self.assertRaises(PulseError): + GaussianSquare(duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10, limit_amplitude=True) + + def test_drag_limit_amplitude(self): + """Test that the check for amplitude less than or equal to 1 can be disabled.""" + waveform = Drag( + duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=False + ) + self.assertGreater(np.abs(waveform.amp), 1.0) + + with self.assertRaises(PulseError): + Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=True) + + def test_constant_limit_amplitude(self): + """Test that the check for amplitude less than or equal to 1 can be disabled.""" + waveform = Constant(duration=100, amp=1.1 + 0.8j, limit_amplitude=False) + self.assertGreater(np.abs(waveform.amp), 1.0) + + with self.assertRaises(PulseError): + Constant(duration=100, amp=1.1 + 0.8j, limit_amplitude=True) + # pylint: disable=invalid-name,unexpected-keyword-arg diff --git a/test/python/pulse/test_transforms.py b/test/python/pulse/test_transforms.py index 67c6b623ef6d..abaefe8fdac3 100644 --- a/test/python/pulse/test_transforms.py +++ b/test/python/pulse/test_transforms.py @@ -490,6 +490,8 @@ class TestAlignSequential(QiskitTestCase): def test_align_sequential(self): """Test sequential alignment without a barrier.""" + context = transforms.AlignSequential() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) @@ -497,8 +499,7 @@ def test_align_sequential(self): schedule.insert(1, instructions.Delay(3, d0), inplace=True) schedule.insert(4, instructions.Delay(5, d1), inplace=True) schedule.insert(12, instructions.Delay(7, d0), inplace=True) - - schedule = transforms.align_sequential(schedule) + schedule = context.align(schedule) reference = pulse.Schedule() # d0 @@ -511,6 +512,8 @@ def test_align_sequential(self): def test_align_sequential_with_barrier(self): """Test sequential alignment with a barrier.""" + context = transforms.AlignSequential() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) @@ -519,8 +522,7 @@ def test_align_sequential_with_barrier(self): schedule.append(directives.RelativeBarrier(d0, d1), inplace=True) schedule.insert(4, instructions.Delay(5, d1), inplace=True) schedule.insert(12, instructions.Delay(7, d0), inplace=True) - - schedule = transforms.align_sequential(schedule) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, instructions.Delay(3, d0), inplace=True) @@ -536,6 +538,8 @@ class TestAlignLeft(QiskitTestCase): def test_align_left(self): """Test left alignment without a barrier.""" + context = transforms.AlignLeft() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) d2 = pulse.DriveChannel(2) @@ -548,8 +552,8 @@ def test_align_left(self): sched_grouped += instructions.Delay(5, d1) sched_grouped += instructions.Delay(7, d0) schedule.append(sched_grouped, inplace=True) + schedule = context.align(schedule) - schedule = transforms.align_left(schedule) reference = pulse.Schedule() # d0 reference.insert(0, instructions.Delay(3, d0), inplace=True) @@ -563,6 +567,8 @@ def test_align_left(self): def test_align_left_with_barrier(self): """Test left alignment with a barrier.""" + context = transforms.AlignLeft() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) d2 = pulse.DriveChannel(2) @@ -576,7 +582,7 @@ def test_align_left_with_barrier(self): sched_grouped += instructions.Delay(5, d1) sched_grouped += instructions.Delay(7, d0) schedule.append(sched_grouped, inplace=True) - schedule = transforms.remove_directives(transforms.align_left(schedule)) + schedule = transforms.remove_directives(context.align(schedule)) reference = pulse.Schedule() # d0 @@ -595,6 +601,8 @@ class TestAlignRight(QiskitTestCase): def test_align_right(self): """Test right alignment without a barrier.""" + context = transforms.AlignRight() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) d2 = pulse.DriveChannel(2) @@ -608,7 +616,7 @@ def test_align_right(self): sched_grouped += instructions.Delay(7, d0) schedule.append(sched_grouped, inplace=True) - schedule = transforms.align_right(schedule) + schedule = context.align(schedule) reference = pulse.Schedule() # d0 @@ -622,6 +630,8 @@ def test_align_right(self): def test_align_right_with_barrier(self): """Test right alignment with a barrier.""" + context = transforms.AlignRight() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) d2 = pulse.DriveChannel(2) @@ -636,7 +646,7 @@ def test_align_right_with_barrier(self): sched_grouped += instructions.Delay(7, d0) schedule.append(sched_grouped, inplace=True) - schedule = transforms.remove_directives(transforms.align_right(schedule)) + schedule = transforms.remove_directives(context.align(schedule)) reference = pulse.Schedule() # d0 @@ -655,73 +665,77 @@ class TestAlignEquispaced(QiskitTestCase): def test_equispaced_with_short_duration(self): """Test equispaced context with duration shorter than the schedule duration.""" + context = transforms.AlignEquispaced(duration=20) + d0 = pulse.DriveChannel(0) - sched = pulse.Schedule() + schedule = pulse.Schedule() for _ in range(3): - sched.append(Delay(10, d0), inplace=True) - - sched = transforms.align_equispaced(sched, duration=20) + schedule.append(Delay(10, d0), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, Delay(10, d0), inplace=True) reference.insert(10, Delay(10, d0), inplace=True) reference.insert(20, Delay(10, d0), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) def test_equispaced_with_longer_duration(self): """Test equispaced context with duration longer than the schedule duration.""" + context = transforms.AlignEquispaced(duration=50) + d0 = pulse.DriveChannel(0) - sched = pulse.Schedule() + schedule = pulse.Schedule() for _ in range(3): - sched.append(Delay(10, d0), inplace=True) - - sched = transforms.align_equispaced(sched, duration=50) + schedule.append(Delay(10, d0), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, Delay(10, d0), inplace=True) reference.insert(20, Delay(10, d0), inplace=True) reference.insert(40, Delay(10, d0), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) def test_equispaced_with_multiple_channels_short_duration(self): """Test equispaced context with multiple channels and duration shorter than the total duration.""" + context = transforms.AlignEquispaced(duration=20) + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) - sched = pulse.Schedule() - sched.append(Delay(10, d0), inplace=True) - sched.append(Delay(20, d1), inplace=True) - - sched = transforms.align_equispaced(sched, duration=20) + schedule = pulse.Schedule() + schedule.append(Delay(10, d0), inplace=True) + schedule.append(Delay(20, d1), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, Delay(10, d0), inplace=True) reference.insert(0, Delay(20, d1), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) def test_equispaced_with_multiple_channels_longer_duration(self): """Test equispaced context with multiple channels and duration longer than the total duration.""" + context = transforms.AlignEquispaced(duration=30) + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) - sched = pulse.Schedule() - sched.append(Delay(10, d0), inplace=True) - sched.append(Delay(20, d1), inplace=True) - - sched = transforms.align_equispaced(sched, duration=30) + schedule = pulse.Schedule() + schedule.append(Delay(10, d0), inplace=True) + schedule.append(Delay(20, d1), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, Delay(10, d0), inplace=True) reference.insert(10, Delay(20, d1), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) class TestAlignFunc(QiskitTestCase): @@ -734,37 +748,39 @@ def _position(ind): def test_numerical_with_short_duration(self): """Test numerical alignment context with duration shorter than the schedule duration.""" + context = transforms.AlignFunc(duration=20, func=self._position) + d0 = pulse.DriveChannel(0) - sched = pulse.Schedule() + schedule = pulse.Schedule() for _ in range(3): - sched.append(Delay(10, d0), inplace=True) - - sched = transforms.align_func(sched, duration=20, func=self._position) + schedule.append(Delay(10, d0), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(0, Delay(10, d0), inplace=True) reference.insert(10, Delay(10, d0), inplace=True) reference.insert(20, Delay(10, d0), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) def test_numerical_with_longer_duration(self): """Test numerical alignment context with duration longer than the schedule duration.""" + context = transforms.AlignFunc(duration=80, func=self._position) + d0 = pulse.DriveChannel(0) - sched = pulse.Schedule() + schedule = pulse.Schedule() for _ in range(3): - sched.append(Delay(10, d0), inplace=True) - - sched = transforms.align_func(sched, duration=80, func=self._position) + schedule.append(Delay(10, d0), inplace=True) + schedule = context.align(schedule) reference = pulse.Schedule() reference.insert(15, Delay(10, d0), inplace=True) reference.insert(35, Delay(10, d0), inplace=True) reference.insert(55, Delay(10, d0), inplace=True) - self.assertEqual(sched, reference) + self.assertEqual(schedule, reference) class TestFlatten(QiskitTestCase): @@ -772,6 +788,8 @@ class TestFlatten(QiskitTestCase): def test_flatten(self): """Test the flatten transform.""" + context_left = transforms.AlignLeft() + d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) @@ -788,8 +806,8 @@ def test_flatten(self): flattened = transforms.flatten(grouped) # align all the instructions to the left after flattening - flattened = transforms.align_left(flattened) - grouped = transforms.align_left(grouped) + flattened = context_left.align(flattened) + grouped = context_left.align(grouped) reference = pulse.Schedule() # d0 From ac5f91eeeb34139a914fa26de4952bf6890e4800 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 18:48:47 +0900 Subject: [PATCH 03/11] fix format logic --- qiskit/pulse/utils.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index 4efb77d5136b..ba24fb661a50 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -43,33 +43,31 @@ def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: @functools.lru_cache(maxsize=None) def format_parameter_value( operand: ParameterExpression, + decimal: int = 10, ) -> Union[ParameterExpression, complex]: """Convert ParameterExpression into the most suitable data type. Args: operand: Operand value in arbitrary data type including ParameterExpression. + decimal: Number of digit to round returned value. Returns: Value casted to non-parameter data type, when possible. """ - # to evaluate parameter expression object, sympy srepr function is used. - # this function converts the parameter object into string with tiny round error. - # therefore evaluated value is not completely equal to the assigned value. - # however this error can be ignored in practice though we need to be careful for unittests. - # i.e. "pi=3.141592653589793" will be evaluated as "3.14159265358979" - # no DAC that recognizes the resolution of 1e-15 but they are AlmostEqual in tests. - from sympy import srepr - - math_expr = srepr(operand).replace("*I", "j") try: - # value is assigned - evaluated = complex(math_expr) - if not np.iscomplex(evaluated): + # value is assigned. + # note that ParameterExpression directly supports __complex__ via sympy or symengine + evaluated = complex(operand) + # remove truncation error + evaluated = np.round(evaluated, decimals=decimal) + # typecast into most likely data type + if np.isreal(evaluated): evaluated = float(evaluated.real) if evaluated.is_integer(): evaluated = int(evaluated) + return evaluated - except ValueError: + except TypeError: # value is not assigned pass From 4960a114dc07d1af263e84d6f196405462fdf746 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 19:37:10 +0900 Subject: [PATCH 04/11] fix tests --- test/python/pulse/test_parameter_manager.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/python/pulse/test_parameter_manager.py b/test/python/pulse/test_parameter_manager.py index c6a5df6d944e..84053b786694 100644 --- a/test/python/pulse/test_parameter_manager.py +++ b/test/python/pulse/test_parameter_manager.py @@ -281,12 +281,12 @@ def get_shift(variable): test_obj = pulse.ShiftPhase(get_shift(self.phi1), self.d1) - value_dict = {self.phi1: 1.57, self.ch1: 2} + value_dict = {self.phi1: 2.0, self.ch1: 2} visitor = ParameterSetter(param_map=value_dict) assigned = visitor.visit(test_obj) - ref_obj = pulse.ShiftPhase(get_shift(1.57), pulse.DriveChannel(2)) + ref_obj = pulse.ShiftPhase(1.0, pulse.DriveChannel(2)) self.assertEqual(assigned, ref_obj) @@ -457,6 +457,8 @@ def test_set_parameter_to_complex_schedule(self): class TestAssignFromProgram(QiskitTestCase): + """Test managing parameters from programs. Parameter manager is implicitly called. + """ def test_attribute_parameters(self): """Test the ``parameter`` attributes.""" @@ -467,7 +469,6 @@ def test_attribute_parameters(self): block = pulse.ScheduleBlock() block += pulse.Play(waveform, pulse.DriveChannel(10)) - block.assign_parameters({amp: 0.2, sigma: 4}, inplace=True) ref_set = {amp, sigma} @@ -493,11 +494,11 @@ def test_parameters_from_subroutine(self): param1 = Parameter("amp") waveform = pulse.library.Constant(duration=100, amp=param1) - program_layer0 = pulse.ScheduleBlock() + program_layer0 = pulse.Schedule() program_layer0 += pulse.Play(waveform, pulse.DriveChannel(0)) # from call instruction - program_layer1 = pulse.ScheduleBlock() + program_layer1 = pulse.Schedule() program_layer1 += pulse.instructions.Call(program_layer0) self.assertEqual(program_layer1.get_parameters("amp")[0], param1) @@ -511,18 +512,18 @@ def test_assign_parameter_to_subroutine(self): param1 = Parameter("amp") waveform = pulse.library.Constant(duration=100, amp=param1) - program_layer0 = pulse.ScheduleBlock() + program_layer0 = pulse.Schedule() program_layer0 += pulse.Play(waveform, pulse.DriveChannel(0)) reference = program_layer0.assign_parameters({param1: 0.1}, inplace=False) # to call instruction - program_layer1 = pulse.ScheduleBlock() + program_layer1 = pulse.Schedule() program_layer1 += pulse.instructions.Call(program_layer0) target = program_layer1.assign_parameters({param1: 0.1}, inplace=False) self.assertEqual(inline_subroutines(target), reference) # to nested call instruction - program_layer2 = pulse.ScheduleBlock() + program_layer2 = pulse.Schedule() program_layer2 += pulse.instructions.Call(program_layer1) target = program_layer2.assign_parameters({param1: 0.1}, inplace=False) self.assertEqual(inline_subroutines(target), reference) @@ -624,7 +625,7 @@ def test_overlapping_on_multiple_assignment(self): schedule.assign_parameters({param_idx1: 2}) with self.assertRaises(PulseError): - schedule.assign_parameters({param_idx1: 2}) + schedule.assign_parameters({param_idx2: 2}) def test_cannot_build_schedule_with_unassigned_duration(self): """Test we cannot build schedule with parameterized instructions""" From 50c7ee2019940739fb21558c41e79338b42e991e Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 20:46:31 +0900 Subject: [PATCH 05/11] add reno --- ...eprecated-pulse-code-57ec531224e45b5f.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml diff --git a/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml b/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml new file mode 100644 index 000000000000..9150804101c6 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @@ -0,0 +1,61 @@ +--- +upgrade: + - | + :meth:`assign_parameters` method of :class:`qiskit.pulse.instructions.instruction.Instruction` + has been removed and no longer exist as per the deprecation notice from 0.17 release. + :meth:`assign` method of :class:`~qiskit.pulse.channels.Channel`, + :meth:`assign` method and :meth:`assign_parameters` method of :class:`~qiskit.pulse.library.\ + parametric_pulses.ParametricPulse` have also been removed. + These parameters should be assigned from the pulse program (:class:`qiskit.pulse.schedule.\ + Schedule` and :class:`qiskit.pulse.schedule.ScheduleBlock`) rather than operands of + the pulse program instruction. + - | + :meth:`flatten` method of :class:`qiskit.pulse.instructions.instruction.Instruction` and + :class:`qiskit.pulse.schedule.Schedule` has been removed and no longer exist as per the + deprecation notice from 0.17 release. This transformation is defined as a standalone + function in :func:`qiskit.pulse.transforms.canonicalization.flatten`. + - | + :class:`qiskit.pulse.interfaces.ScheduleComponent` has been removed and no longer exist + as per the deprecation notice from 0.15 release. No alternative class will be provided. + - | + Legacy pulse drawer arguments have been removed from :meth:`qiskit.pulse.library.\ + pulse.Pulse#draw`, :meth:`qiskit.pulse.schedule.Schedule#draw` and + :meth:`qiskit.pulse.schedule.ScheduleBlock#draw` and no longer exist as per + the deprecation notice from 0.16 release. Now these draw methods support only + V2 pulse drawer arguments. See method documentations for details. + - | + :mod:`qiskit.pulse.reschedule` has been removed and this import path no longer exist + as per the deprecation notice from 0.14 release. Use :mod:`qiskit.pulse.transforms` instead. + - | + A protecte method :meth:`qiskit.pulse.schedule.Schedule#_children` has been removed and + replaced by a protected instance variable as per the deprecation notice from + 0.17 release. This is now provided as a public method :meth:`qiskit.pulse.schedule.\ + Schedule#children`. + - | + Timeslot relevant methods and properties have been removed and no longer exist in + :class:`qiskit.pulse.schedule.ScheduleBlock` as per the deprecation notice + from 0.17 release. Since this representation doesn't have notion of instruction time t0, + the timeslot information will be available after it is transformed by + :func:`~qiskit.pulse.transforms.canonicalization.block_to_schedule` function. + Corresponding attributes have been provided by the implicit program conversion into + :class:`qiskit.pulse.schedule.Schedule` with the function, but they are no longer supported. + Following attributes are removed: ``timeslots``, ``start_time``, ``stop_time``, + ``ch_start_time``, ``ch_stop_time``, ``shift``, and ``insert``. + - | + Alignment pulse schedule transforms have been removed and no longer exist as per + the deprecation notice from 0.17 relase. These transforms are integrated and implemented + in the :class:`~qiskit.pulse.transforms.alignments.AlignmentKind` context of the schedule block. + Following explicit transform functions are removed: + :func:`qiskit.pulse.transforms.align_equispaced`, + :func:`qiskit.pulse.transforms.align_func`, + :func:`qiskit.pulse.transforms.align_left`, + :func:`qiskit.pulse.transforms.align_right`, + and :func:`qiskit.pulse.transforms.align_sequential`. + - | + Redundant pulse builder commands have been removed and no longer exist as per the + deprecation notice from 0.17. The :func:`qiskit.pulse.builder.call_schedule` and + :func:`qiskit.pulse.builder.call_circuit` command has been integrated into + :func:`~qiskit.pulse.builder.call` command. The :func:`qiskit.pulse.builder.pad` and + :func:`qiskit.pulse.builder.inline` have been just removed. These operations can be + replaced by transforms :func:`qiskit.pulse.transforms.canonicalization.inline_subroutines` + and :func:`qiskit.pulse.transforms.canonicalization.pad` if necessary. From 349cbef9902c3dfa4fd6ed3f90a9fe84206d70af Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 21:16:28 +0900 Subject: [PATCH 06/11] lint / black --- qiskit/pulse/builder.py | 2 -- qiskit/pulse/library/pulse.py | 1 - test/python/pulse/test_parameter_manager.py | 33 +++++++-------------- test/python/pulse/test_pulse_lib.py | 8 ++--- 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index ee382796d54f..adc21cff13e0 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -196,7 +196,6 @@ import contextvars import functools import itertools -import warnings from contextlib import contextmanager from typing import ( Any, @@ -227,7 +226,6 @@ macros, library, transforms, - utils, ) from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock diff --git a/qiskit/pulse/library/pulse.py b/qiskit/pulse/library/pulse.py index 7c7e73a3d356..b4f2b9207866 100644 --- a/qiskit/pulse/library/pulse.py +++ b/qiskit/pulse/library/pulse.py @@ -13,7 +13,6 @@ """Pulses are descriptions of waveform envelopes. They can be transmitted by control electronics to the device. """ -import warnings from abc import ABC, abstractmethod from typing import Dict, Optional, Any, Tuple, Union diff --git a/test/python/pulse/test_parameter_manager.py b/test/python/pulse/test_parameter_manager.py index 84053b786694..41dfe1533ea2 100644 --- a/test/python/pulse/test_parameter_manager.py +++ b/test/python/pulse/test_parameter_manager.py @@ -127,9 +127,7 @@ def test_get_parameter_from_pulse(self): def test_get_parameter_from_acquire(self): """Test get parameters from acquire instruction.""" - test_obj = pulse.Acquire( - 16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1) - ) + test_obj = pulse.Acquire(16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1)) visitor = ParameterGetter() visitor.visit(test_obj) @@ -165,8 +163,10 @@ def test_get_parameter_from_call(self): def test_with_function(self): """Test ParameterExpressions formed trivially in a function.""" + def get_shift(variable): return variable - 1 + test_obj = pulse.ShiftPhase(get_shift(self.phi1), self.d1) visitor = ParameterGetter() @@ -228,9 +228,7 @@ def test_set_parameter_to_pulse(self): def test_set_parameter_to_acquire(self): """Test set parameters to acquire instruction.""" - test_obj = pulse.Acquire( - 16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1) - ) + test_obj = pulse.Acquire(16000, pulse.AcquireChannel(self.ch1), pulse.MemorySlot(self.ch1)) value_dict = {self.ch1: 2} @@ -457,8 +455,7 @@ def test_set_parameter_to_complex_schedule(self): class TestAssignFromProgram(QiskitTestCase): - """Test managing parameters from programs. Parameter manager is implicitly called. - """ + """Test managing parameters from programs. Parameter manager is implicitly called.""" def test_attribute_parameters(self): """Test the ``parameter`` attributes.""" @@ -564,9 +561,7 @@ def test_overlapping_pulses(self): param_idx = Parameter("q") schedule = pulse.Schedule() - schedule |= pulse.Play( - pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx) - ) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx)) with self.assertRaises(PulseError): schedule |= pulse.Play( pulse.Waveform([0.5, 0.5, 0.5, 0.5]), pulse.DriveChannel(param_idx) @@ -587,12 +582,8 @@ def test_overlapping_on_expression_assigment_to_zero(self): param_idx = Parameter("q") schedule = pulse.Schedule() - schedule |= pulse.Play( - pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx) - ) - schedule |= pulse.Play( - pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(2 * param_idx) - ) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx)) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(2 * param_idx)) with self.assertRaises(PulseError): schedule.assign_parameters({param_idx: 0}) @@ -616,12 +607,8 @@ def test_overlapping_on_multiple_assignment(self): param_idx2 = Parameter("q2") schedule = pulse.Schedule() - schedule |= pulse.Play( - pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx1) - ) - schedule |= pulse.Play( - pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx2) - ) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx1)) + schedule |= pulse.Play(pulse.Waveform([1, 1, 1, 1]), pulse.DriveChannel(param_idx2)) schedule.assign_parameters({param_idx1: 2}) with self.assertRaises(PulseError): diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index f03b969e997d..ed497f2d9b01 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -280,9 +280,7 @@ def test_hash_generation(self): def test_gaussian_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" - waveform = Gaussian( - duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=False - ) + waveform = Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=False) self.assertGreater(np.abs(waveform.amp), 1.0) with self.assertRaises(PulseError): @@ -300,9 +298,7 @@ def test_gaussian_square_limit_amplitude(self): def test_drag_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" - waveform = Drag( - duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=False - ) + waveform = Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=False) self.assertGreater(np.abs(waveform.amp), 1.0) with self.assertRaises(PulseError): From defe746869e500755632d170d0abd6cf422d1ab5 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Mon, 15 Nov 2021 21:28:28 +0900 Subject: [PATCH 07/11] remove documentation --- qiskit/pulse/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 74bbb6da168f..a437f8e76e8b 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -275,15 +275,8 @@ .. autosummary:: :toctree: ../stubs/ - ~qiskit.pulse.builder.align_equispaced - ~qiskit.pulse.builder.align_func - ~qiskit.pulse.builder.align_left - ~qiskit.pulse.builder.align_right - ~qiskit.pulse.builder.align_sequential ~qiskit.pulse.builder.circuit_scheduler_settings ~qiskit.pulse.builder.frequency_offset - ~qiskit.pulse.builder.inline - ~qiskit.pulse.builder.pad ~qiskit.pulse.builder.phase_offset ~qiskit.pulse.builder.transpiler_settings From abf0568100bd1e2f8365bf83a7d15b4214d87b23 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Thu, 18 Nov 2021 03:10:12 +0900 Subject: [PATCH 08/11] fix import --- qiskit/pulse/instructions/instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index a9408dc101de..5ddffab05e82 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -22,7 +22,7 @@ sched += Delay(duration, channel) # Delay is a specific subclass of Instruction """ from abc import ABC, abstractmethod -from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple, Any +from typing import Callable, Iterable, List, Optional, Set, Tuple from qiskit.pulse.channels import Channel from qiskit.pulse.exceptions import PulseError From 7476e5897502425fa2bc693793db60ad32f255e2 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Fri, 19 Nov 2021 15:32:16 +0900 Subject: [PATCH 09/11] readd docs --- qiskit/pulse/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index a437f8e76e8b..fa7fc0c1883a 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -275,6 +275,11 @@ .. autosummary:: :toctree: ../stubs/ + ~qiskit.pulse.builder.align_equispaced + ~qiskit.pulse.builder.align_func + ~qiskit.pulse.builder.align_left + ~qiskit.pulse.builder.align_right + ~qiskit.pulse.builder.align_sequential ~qiskit.pulse.builder.circuit_scheduler_settings ~qiskit.pulse.builder.frequency_offset ~qiskit.pulse.builder.phase_offset From cdf106935fd98a7d977fd7068e36aee49804803c Mon Sep 17 00:00:00 2001 From: knzwnao Date: Sat, 20 Nov 2021 19:39:14 +0900 Subject: [PATCH 10/11] readd inline and pad --- qiskit/pulse/__init__.py | 4 ++ qiskit/pulse/builder.py | 58 +++++++++++++++++++ ...eprecated-pulse-code-57ec531224e45b5f.yaml | 5 +- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index fa7fc0c1883a..1cef1af715b5 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -282,6 +282,8 @@ ~qiskit.pulse.builder.align_sequential ~qiskit.pulse.builder.circuit_scheduler_settings ~qiskit.pulse.builder.frequency_offset + ~qiskit.pulse.builder.inline + ~qiskit.pulse.builder.pad ~qiskit.pulse.builder.phase_offset ~qiskit.pulse.builder.transpiler_settings @@ -414,6 +416,8 @@ align_sequential, circuit_scheduler_settings, frequency_offset, + inline, + pad, phase_offset, transpiler_settings, # Macros. diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index de428f5be464..eceea8d6eec4 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -196,6 +196,7 @@ import contextvars import functools import itertools +import warnings from contextlib import contextmanager from typing import ( Any, @@ -226,6 +227,7 @@ macros, library, transforms, + utils, ) from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock @@ -1124,6 +1126,62 @@ def general_transforms(alignment_context: AlignmentKind) -> ContextManager[None] builder.append_block(current) +@utils.deprecated_functionality +@contextmanager +def inline() -> ContextManager[None]: + """Deprecated. Inline all instructions within this context into the parent context, + inheriting the scheduling policy of the parent context. + + .. warning:: This will cause all scheduling directives within this context + to be ignored. + """ + + def _flatten(block): + for inst in block.blocks: + if isinstance(inst, ScheduleBlock): + yield from _flatten(inst) + else: + yield inst + + builder = _active_builder() + + # set a placeholder + builder.push_context(transforms.AlignLeft()) + try: + yield + finally: + placeholder = builder.pop_context() + for inst in _flatten(placeholder): + builder.append_instruction(inst) + + +@contextmanager +def pad(*chs: chans.Channel) -> ContextManager[None]: # pylint: disable=unused-argument + """Deprecated. Pad all available timeslots with delays upon exiting context. + + Args: + chs: Channels to pad with delays. Defaults to all channels in context + if none are supplied. + + Yields: + None + """ + warnings.warn( + "Context-wise padding is being deprecated. Requested padding is being ignored. " + "Now the pulse builder generate a program in `ScheduleBlock` representation. " + "The padding with delay as a blocker is no longer necessary for this program. " + "However, if you still want delays, you can convert the output program " + "into `Schedule` representation by calling " + "`qiskit.pulse.transforms.target_qobj_transform`. Then, you can apply " + "`qiskit.pulse.transforms.pad` to the converted schedule. ", + DeprecationWarning, + ) + try: + yield + finally: + pass + + @contextmanager def transpiler_settings(**settings) -> ContextManager[None]: """Set the currently active transpiler settings for this context. diff --git a/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml b/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml index 9150804101c6..97ede3088975 100644 --- a/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml +++ b/releasenotes/notes/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @@ -55,7 +55,4 @@ upgrade: Redundant pulse builder commands have been removed and no longer exist as per the deprecation notice from 0.17. The :func:`qiskit.pulse.builder.call_schedule` and :func:`qiskit.pulse.builder.call_circuit` command has been integrated into - :func:`~qiskit.pulse.builder.call` command. The :func:`qiskit.pulse.builder.pad` and - :func:`qiskit.pulse.builder.inline` have been just removed. These operations can be - replaced by transforms :func:`qiskit.pulse.transforms.canonicalization.inline_subroutines` - and :func:`qiskit.pulse.transforms.canonicalization.pad` if necessary. + :func:`~qiskit.pulse.builder.call` command. From 43851c1d762436da103a0c2c6479ae8ad7f816c6 Mon Sep 17 00:00:00 2001 From: knzwnao Date: Sat, 20 Nov 2021 19:44:04 +0900 Subject: [PATCH 11/11] readd test --- test/python/pulse/test_builder.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index f3c74cffe94c..712289fb0d8d 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -182,6 +182,28 @@ def test_align_right(self): self.assertScheduleEqual(schedule, reference) + def test_inline(self): + """Test the inlining context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + + with pulse.build() as schedule: + pulse.delay(3, d0) + with pulse.inline(): + # this alignment will be ignored due to inlining. + with pulse.align_right(): + pulse.delay(5, d1) + pulse.delay(7, d0) + + reference = pulse.Schedule() + # d0 + reference += instructions.Delay(3, d0) + reference += instructions.Delay(7, d0) + # d1 + reference += instructions.Delay(5, d1) + + self.assertScheduleEqual(schedule, reference) + def test_transpiler_settings(self): """Test the transpiler settings context.