From 7d7c364150a177f1289a4e3835bc35afeb5cfc02 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Tue, 18 Feb 2020 16:55:43 -0500 Subject: [PATCH 01/10] Migrate Delay from a Command to an Instruction, and deprecate DelayInstruction, while maintaining support for the previous API --- qiskit/pulse/__init__.py | 4 +- qiskit/pulse/commands/delay.py | 64 +++--------------- qiskit/pulse/instructions/__init__.py | 1 + qiskit/pulse/instructions/delay.py | 94 +++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 57 deletions(-) create mode 100644 qiskit/pulse/instructions/delay.py diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 83fc5edaf21..4f7fccfb43e 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -136,12 +136,12 @@ from .cmd_def import CmdDef from .commands import (Acquire, AcquireInstruction, FrameChange, PersistentValue, SamplePulse, Snapshot, Kernel, - Discriminator, Delay, ParametricPulse, + Discriminator, ParametricPulse, ParametricInstruction, Gaussian, GaussianSquare, Drag, ConstantPulse, functional_pulse) from .configuration import LoConfig, LoRange from .exceptions import PulseError from .instruction_schedule_map import InstructionScheduleMap -from .instructions import Instruction +from .instructions import Instruction, Delay from .interfaces import ScheduleComponent from .schedule import Schedule diff --git a/qiskit/pulse/commands/delay.py b/qiskit/pulse/commands/delay.py index 24ad8c59ada..78bc8d98a56 100644 --- a/qiskit/pulse/commands/delay.py +++ b/qiskit/pulse/commands/delay.py @@ -12,65 +12,15 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Delay instruction. -""" -from typing import Optional +"""Delay instruction. Deprecated path.""" +import warnings -from qiskit.pulse.channels import Channel -from .instruction import Instruction -from .command import Command - - -class Delay(Command): - """Delay command.""" - - prefix = 'delay' - - def __init__(self, duration: int, name: Optional[str] = None): - """Create a new delay command. - - No other command may be scheduled within a delay command. - - Delays that exist as the last instruction on a channel will be removed. - - Note: Delays are not explicitly sent to backends with the current Qobj - transport layer. They are only used to enforce the correct offset in time - for instructions that will be submitted directly to the backend. - - Args: - duration: Length of time of the delay in terms of dt. - name: Name of delay command for display purposes. - """ - super().__init__(duration=duration) - self._name = Delay.create_name(name) - - # pylint: disable=arguments-differ - def to_instruction(self, channel: Channel, name: str = None) -> 'DelayInstruction': - """Create a delay instruction on the given channel. - - Args: - channel: Channel for delay instruction to be created. - name: Name of delay instruction for display purposes. - - Returns: - DelayInstruction - """ - return DelayInstruction(self, channel, name=name) - # pylint: enable=arguments-differ +from qiskit.pulse.instructions import Delay +from qiskit.pulse.instructions import Instruction class DelayInstruction(Instruction): - """Instruction to add a blocking delay on a `Channel`. - - No other may be scheduled within a delay command. - - Delays that exist as the last instruction on a channel will be removed. - - Note: Delay's are not explicitly sent to backends with the current Qobj - transport layer. They are only used to enforce the correct offset in time - for instructions that will be submitted directly to the backend. - """ + """Deprecated.""" def __init__(self, command: Delay, channel: Delay, name: str = None): """Create a delay instruction from a delay command. @@ -80,4 +30,8 @@ def __init__(self, command: Delay, channel: Delay, name: str = None): channel: Channel for delay instruction to be created. name: Name of delay instruction for display purposes. """ + warnings.warn("The DelayInstruction is deprecated. Use Delay, instead, with channels. " + "For example: DelayInstruction(Delay(5), DriveChannel(0)) -> " + "Delay(5, DriveChannel(0)).", + DeprecationWarning) super().__init__(command, channel, name=name) diff --git a/qiskit/pulse/instructions/__init__.py b/qiskit/pulse/instructions/__init__.py index 820c670c856..2d90c58da01 100644 --- a/qiskit/pulse/instructions/__init__.py +++ b/qiskit/pulse/instructions/__init__.py @@ -21,4 +21,5 @@ An instruction can be added to a :py:class:`~qiskit.pulse.Schedule`, which is a sequence of scheduled Pulse ``Instruction`` s over many channels. """ +from .delay import Delay from .instruction import Instruction diff --git a/qiskit/pulse/instructions/delay.py b/qiskit/pulse/instructions/delay.py new file mode 100644 index 00000000000..e0e1d101455 --- /dev/null +++ b/qiskit/pulse/instructions/delay.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""An instruction for blocking time on a channel; useful for scheduling alignment.""" +import warnings + +from typing import List, Optional, Union + +from qiskit.pulse.channels import PulseChannel +from qiskit.pulse.exceptions import PulseError +from .instruction import Instruction + + +class Delay(Instruction): + """A blocking instruction with no other effect. The delay is used for aligning and scheduling + other instructions. + + Example: + + To schedule an instruction at time = 10, on a channel assigned to the variable ``channel``, + the following could be used:: + + sched = Schedule(name="Delay instruction example") + sched += Delay(10, channel) + sched += Gaussian(duration, amp, sigma, channel) + + The ``channel`` will output no signal from time=0 up until time=10. + """ + + def __init__(self, duration: int, + channel: Optional[PulseChannel] = None, + name: Optional[str] = None): + """Create a new delay instruction. + + No other instruction may be scheduled within a ``Delay``. + + Args: + duration: Length of time of the delay in terms of dt. + channel: The channel that will have the delay. + name: Name of the delay for display purposes. + """ + if channel is None: + warnings.warn("Usage of Delay without specifying a channel is deprecated. For " + "example, Delay(5)(DriveChannel(0)) should be replaced by " + "Delay(5, DriveChannel(0)).", DeprecationWarning) + self._duration = duration + self._channel = channel + super().__init__({'duration': duration}, channel, name) + + @property + def operands(self) -> List[Union[int, PulseChannel]]: + """Return a list of instruction operands.""" + return [self.duration, self.channel] + + @property + def channel(self) -> PulseChannel: + """Return the :py:class:`~qiskit.pulse.channels.Channel` that this instruction is + scheduled on. + """ + return self._channel + + def __call__(self, channel: PulseChannel) -> 'Delay': + """Return new ``Delay`` that is fully instantiated with both ``duration`` and a ``channel``. + + Args: + channel: The channel that will have the delay. + + Return: + Complete and ready to schedule ``Delay``. + + Raises: + PulseError: If ``channel`` has already been set. + """ + warnings.warn("Calling Delay with a channel is deprecated. Instantiate the delay with " + "a channel instead.", DeprecationWarning) + if self._channel is not None: + raise PulseError("The channel has already been assigned as {}.".format(self.channel)) + return Delay(self.duration, channel) + + def __repr__(self): + return "{}({}, {})".format(self.__class__.__name__, + self.duration, + self.channel) From 0fdd44ceada9cd7167e253bb4911c4dcec3dc466 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Tue, 3 Mar 2020 22:18:48 -0700 Subject: [PATCH 02/10] Scattering of bugfixes --- qiskit/assembler/assemble_schedules.py | 4 ++-- qiskit/pulse/instructions/delay.py | 2 +- qiskit/pulse/instructions/instruction.py | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index 0c9cf634d81..55603a73ed3 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -17,7 +17,7 @@ from collections import defaultdict from qiskit.exceptions import QiskitError -from qiskit.pulse import Schedule +from qiskit.pulse import Schedule, Delay from qiskit.pulse.commands import (Command, PulseInstruction, Acquire, AcquireInstruction, DelayInstruction, SamplePulse, ParametricInstruction) from qiskit.qobj import (PulseQobj, QobjHeader, QobjExperimentHeader, @@ -193,7 +193,7 @@ def _assemble_instructions( acquire_instruction_map[(time, instruction.command)].append(instruction) continue - if isinstance(instruction, DelayInstruction): + if isinstance(instruction, (DelayInstruction, Delay)): # delay instructions are ignored as timing is explicit within qobj continue diff --git a/qiskit/pulse/instructions/delay.py b/qiskit/pulse/instructions/delay.py index e0e1d101455..4f89b000d5c 100644 --- a/qiskit/pulse/instructions/delay.py +++ b/qiskit/pulse/instructions/delay.py @@ -56,7 +56,7 @@ def __init__(self, duration: int, "Delay(5, DriveChannel(0)).", DeprecationWarning) self._duration = duration self._channel = channel - super().__init__({'duration': duration}, channel, name) + super().__init__(duration, channel, name=name) @property def operands(self) -> List[Union[int, PulseChannel]]: diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index 4e3d08a4ea8..1472cb1d2fd 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -54,6 +54,7 @@ def __init__(self, duration: Union['Command', int], *channels: List of pulse channels that this instruction operates on. name: Display name for this instruction. """ + self._name = name self._command = None @@ -63,6 +64,7 @@ def __init__(self, duration: Union['Command', int], if name is None: self._name = self.command.name duration = self.command.duration + self._duration = duration self._timeslots = TimeslotCollection(*(Timeslot(Interval(0, duration), channel) for channel in channels if channel is not None)) @@ -100,7 +102,7 @@ def stop_time(self) -> int: @property def duration(self) -> int: """Duration of this instruction.""" - return self.timeslots.duration + return self._duration @property def _children(self) -> Tuple[ScheduleComponent]: From dcfe20deda0e598325d62467583c2046e4bc6f24 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Tue, 3 Mar 2020 22:20:41 -0700 Subject: [PATCH 03/10] Remove extra line --- qiskit/pulse/instructions/instruction.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index 1472cb1d2fd..53a79686836 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -54,7 +54,6 @@ def __init__(self, duration: Union['Command', int], *channels: List of pulse channels that this instruction operates on. name: Display name for this instruction. """ - self._name = name self._command = None From d54e89069f6a117da5deaefbd28d3afbf70e0689 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Wed, 4 Mar 2020 10:24:10 -0700 Subject: [PATCH 04/10] Add reno note update --- ...nstructions-and-commands-aaa6d8724b8a29d3.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml index df88107a748..0884f2f0f70 100644 --- a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml +++ b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml @@ -22,3 +22,18 @@ features: sched += Delay(5, DriveChannel(0)) sched += ShiftPhase(np.pi, DriveChannel(0)) +upgrade: + - | + ``DelayInstruction`` has been deprecated and replaced by + :py:class:`~qiskit.pulse.instruction.Delay`. The migration pattern is: + + Delay()() -> Delay(, ) + DelayInstruction(Delay(), ) + -> Delay(, ) + + Until the deprecation period is over, the previous ``Delay`` syntax of + calling a command on a channel will also be supported: + + Delay()() + + The new ``Delay`` instruction does not support a ``command`` attribute. From fda68db23aef98a0ed85c0ff103aeef2268a5d7e Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Wed, 4 Mar 2020 19:16:02 -0700 Subject: [PATCH 05/10] Add new test --- test/python/pulse/test_instructions.py | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/python/pulse/test_instructions.py diff --git a/test/python/pulse/test_instructions.py b/test/python/pulse/test_instructions.py new file mode 100644 index 00000000000..68f634d6aed --- /dev/null +++ b/test/python/pulse/test_instructions.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +# pylint: disable=invalid-name,unexpected-keyword-arg + +"""Unit tests for pulse instructions.""" + +from qiskit.pulse import Delay, DriveChannel +from qiskit.test import QiskitTestCase + + +class TestDelayCommand(QiskitTestCase): + """Delay tests.""" + + def test_delay(self): + """Test delay.""" + delay = Delay(10, DriveChannel(0), name='test_name') + + self.assertEqual(delay.name, "test_name") + self.assertEqual(delay.duration, 10) + self.assertEqual(delay.operands, [10, DriveChannel(0)]) From 93e4dfe17e9a56debad49e8a20626544d7a355c8 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Mon, 9 Mar 2020 11:39:57 -0600 Subject: [PATCH 06/10] Update qiskit/pulse/instructions/delay.py --- qiskit/pulse/instructions/delay.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/pulse/instructions/delay.py b/qiskit/pulse/instructions/delay.py index 4f89b000d5c..5d84ddf8899 100644 --- a/qiskit/pulse/instructions/delay.py +++ b/qiskit/pulse/instructions/delay.py @@ -54,7 +54,6 @@ def __init__(self, duration: int, warnings.warn("Usage of Delay without specifying a channel is deprecated. For " "example, Delay(5)(DriveChannel(0)) should be replaced by " "Delay(5, DriveChannel(0)).", DeprecationWarning) - self._duration = duration self._channel = channel super().__init__(duration, channel, name=name) From 96bb9f3a93a14880c19b7cccbeda7de5fd46ee17 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Mon, 9 Mar 2020 16:47:48 -0400 Subject: [PATCH 07/10] Fix up releasenote, single : to double :: --- .../unify-instructions-and-commands-aaa6d8724b8a29d3.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml index 0884f2f0f70..d22616a2749 100644 --- a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml +++ b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml @@ -25,14 +25,14 @@ features: upgrade: - | ``DelayInstruction`` has been deprecated and replaced by - :py:class:`~qiskit.pulse.instruction.Delay`. The migration pattern is: + :py:class:`~qiskit.pulse.instruction.Delay`. The migration pattern is:: Delay()() -> Delay(, ) DelayInstruction(Delay(), ) -> Delay(, ) Until the deprecation period is over, the previous ``Delay`` syntax of - calling a command on a channel will also be supported: + calling a command on a channel will also be supported:: Delay()() From 1b3122f3b6afbdee28cefc4e5bc5f0cdeaa4a808 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Mon, 9 Mar 2020 16:52:10 -0400 Subject: [PATCH 08/10] Give instructions a default name --- qiskit/pulse/instructions/instruction.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index de8ae5eac3f..ee78cff817d 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -55,20 +55,22 @@ def __init__(self, duration: Union['commands.Command', int], *channels: List of pulse channels that this instruction operates on. name: Display name for this instruction. """ - self._name = name - self._command = None if not isinstance(duration, int): # TODO: Add deprecation warning once all instructions are migrated self._command = duration if name is None: - self._name = self.command.name + name = self.command.name duration = self.command.duration self._duration = duration self._timeslots = TimeslotCollection(*(Timeslot(Interval(0, duration), channel) for channel in channels if channel is not None)) + if name is None: + name = "{}{}".format(self.__class__.__name__.lower(), str(hex(self.__hash__()))[3:8]) + self._name = name + @property def name(self) -> str: """Name of this instruction.""" @@ -271,8 +273,8 @@ def __eq__(self, other: 'Instruction'): def __hash__(self): if self.command: # Backwards compatibility for Instructions with Commands - return hash((hash(tuple(self.command)), self.channels.__hash__())) - return hash((hash(tuple(self.duration)), self.channels.__hash__())) + return hash(((tuple(self.command)), self.channels.__hash__())) + return hash((self.duration, self.channels.__hash__())) def __add__(self, other: ScheduleComponent) -> Schedule: """Return a new schedule with `other` inserted within `self` at `start_time`.""" From ae8bd12e6ab03499cf2785e2c31ae167bf9f1e96 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Wed, 11 Mar 2020 15:22:27 -0400 Subject: [PATCH 09/10] Update releasenote as a deprecation --- .../unify-instructions-and-commands-aaa6d8724b8a29d3.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml index d22616a2749..227552ae117 100644 --- a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml +++ b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml @@ -22,10 +22,11 @@ features: sched += Delay(5, DriveChannel(0)) sched += ShiftPhase(np.pi, DriveChannel(0)) -upgrade: +deprecation: - | ``DelayInstruction`` has been deprecated and replaced by - :py:class:`~qiskit.pulse.instruction.Delay`. The migration pattern is:: + :py:class:`~qiskit.pulse.instruction.Delay`. This new instruction has been + taken over the previous ``Command`` ``Delay``. The migration pattern is:: Delay()() -> Delay(, ) DelayInstruction(Delay(), ) From 9bc64834a0d35d8ccf2c1357991aefe42f465a58 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Wed, 11 Mar 2020 15:33:55 -0400 Subject: [PATCH 10/10] Fixup release notes typo: deprecation needs to be plural --- .../notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml index 227552ae117..f1eeb595812 100644 --- a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml +++ b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml @@ -22,7 +22,7 @@ features: sched += Delay(5, DriveChannel(0)) sched += ShiftPhase(np.pi, DriveChannel(0)) -deprecation: +deprecations: - | ``DelayInstruction`` has been deprecated and replaced by :py:class:`~qiskit.pulse.instruction.Delay`. This new instruction has been