Skip to content

Commit 12b07bc

Browse files
lcapellutotaalexandermergify[bot]
authored
Command/Instruction unification (#3862)
* Begin command and instruction unification within Pulse. Instructions will migrate into the new instructions submodule of pulse. This begins by moving only the Instruction class. The Instruction ``command`` attribute is deprecated in favor of ``operands``, and references to commands are updated. * Remove deprecation warning from commands/instruction.py because the commands/__init__ can have a deprecation warning after the rest is implemented * Revert commands/__init__ because changes there are not necessary. * Fill in first positional argument in instruction. What was command is now just 'duration', and any other operands only exist for an instruction instance * Continue adding documentation and notes Fix up hash function * Fix type hint from List[Channel] to Channel * Update repr to match new init signature * Update qiskit/pulse/instructions/__init__.py Co-Authored-By: Thomas Alexander <thomasalexander2718@gmail.com> * Update init file for the instruction module to be more clear about the operands * Make Instruction an ABC * update instruction comment * Update docs * Update year in copyright header * Revert Instruction as ABC because the Instructions which haven't been migrated yet do not support the abstractmethods * Update __eq__ to support both styles until migration is complete * Update __hash__ to support both styles until migration is complete * Fixup style * Update TODO comment * Use self.command instead of init arg if the first arg is a command, for readability * Make repr unchanged for nonmigrated commands * Small bugfix * Update instructions documentation * Relax requirement for channel operand * API reference to Instruction in release notes * Fix releasenote * Silly reno style Co-authored-by: Thomas Alexander <thomasalexander2718@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent fdd5603 commit 12b07bc

File tree

5 files changed

+339
-249
lines changed

5 files changed

+339
-249
lines changed

qiskit/pulse/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,14 @@
134134
from .channels import (DriveChannel, MeasureChannel, AcquireChannel,
135135
ControlChannel, RegisterSlot, MemorySlot)
136136
from .cmd_def import CmdDef
137-
from .commands import (Instruction, Acquire, AcquireInstruction, FrameChange,
137+
from .commands import (Acquire, AcquireInstruction, FrameChange,
138138
PersistentValue, SamplePulse, Snapshot, Kernel,
139139
Discriminator, Delay, ParametricPulse,
140140
ParametricInstruction, Gaussian,
141141
GaussianSquare, Drag, ConstantPulse, functional_pulse)
142142
from .configuration import LoConfig, LoRange
143143
from .exceptions import PulseError
144144
from .instruction_schedule_map import InstructionScheduleMap
145+
from .instructions import Instruction
145146
from .interfaces import ScheduleComponent
146147
from .schedule import Schedule

qiskit/pulse/commands/instruction.py

+3-248
Original file line numberDiff line numberDiff line change
@@ -12,252 +12,7 @@
1212
# copyright notice, and modified files need to carry a notice indicating
1313
# that they have been altered from the originals.
1414

15-
"""
16-
Instruction = Leaf node of schedule.
17-
"""
18-
import warnings
15+
"""Instruction = Leaf node of schedule. Deprecated path."""
16+
# pylint: disable=unused-import
1917

20-
from typing import Tuple, List, Iterable, Callable, Optional
21-
22-
from qiskit.pulse.channels import Channel
23-
from qiskit.pulse.interfaces import ScheduleComponent
24-
from qiskit.pulse.schedule import Schedule
25-
from qiskit.pulse.timeslots import Interval, Timeslot, TimeslotCollection
26-
27-
# pylint: disable=missing-return-doc,missing-type-doc
28-
29-
30-
class Instruction(ScheduleComponent):
31-
"""An abstract class for leaf nodes of schedule."""
32-
33-
def __init__(self, command, *channels: List[Channel],
34-
name: Optional[str] = None):
35-
"""Instruction initializer.
36-
37-
Args:
38-
command: Pulse command to schedule
39-
*channels: List of pulse channels to schedule with command
40-
name: Name of Instruction
41-
"""
42-
self._command = command
43-
self._name = name if name else self._command.name
44-
45-
duration = command.duration
46-
47-
self._timeslots = TimeslotCollection(*(Timeslot(Interval(0, duration), channel)
48-
for channel in channels if channel is not None))
49-
50-
channels = self.channels
51-
52-
@property
53-
def name(self) -> str:
54-
"""Name of this instruction."""
55-
return self._name
56-
57-
@property
58-
def command(self):
59-
"""The associated command.
60-
61-
Returns: Command
62-
"""
63-
return self._command
64-
65-
@property
66-
def channels(self) -> Tuple[Channel]:
67-
"""Returns channels that this schedule uses."""
68-
return self.timeslots.channels
69-
70-
@property
71-
def timeslots(self) -> TimeslotCollection:
72-
"""Occupied time slots by this instruction."""
73-
return self._timeslots
74-
75-
@property
76-
def start_time(self) -> int:
77-
"""Relative begin time of this instruction."""
78-
return self.timeslots.start_time
79-
80-
@property
81-
def stop_time(self) -> int:
82-
"""Relative end time of this instruction."""
83-
return self.timeslots.stop_time
84-
85-
@property
86-
def duration(self) -> int:
87-
"""Duration of this instruction."""
88-
return self.timeslots.duration
89-
90-
@property
91-
def _children(self) -> Tuple[ScheduleComponent]:
92-
"""Instruction has no child nodes."""
93-
return ()
94-
95-
@property
96-
def instructions(self) -> Tuple[Tuple[int, 'Instruction']]:
97-
"""Iterable for getting instructions from Schedule tree."""
98-
return tuple(self._instructions())
99-
100-
def ch_duration(self, *channels: List[Channel]) -> int:
101-
"""Return duration of the supplied channels in this Instruction.
102-
103-
Args:
104-
*channels: Supplied channels
105-
"""
106-
return self.timeslots.ch_duration(*channels)
107-
108-
def ch_start_time(self, *channels: List[Channel]) -> int:
109-
"""Return minimum start time for supplied channels.
110-
111-
Args:
112-
*channels: Supplied channels
113-
"""
114-
return self.timeslots.ch_start_time(*channels)
115-
116-
def ch_stop_time(self, *channels: List[Channel]) -> int:
117-
"""Return maximum start time for supplied channels.
118-
119-
Args:
120-
*channels: Supplied channels
121-
"""
122-
return self.timeslots.ch_stop_time(*channels)
123-
124-
def _instructions(self, time: int = 0) -> Iterable[Tuple[int, 'Instruction']]:
125-
"""Iterable for flattening Schedule tree.
126-
127-
Args:
128-
time: Shifted time of this node due to parent
129-
130-
Yields:
131-
Tuple[int, ScheduleComponent]: Tuple containing time `ScheduleComponent` starts
132-
at and the flattened `ScheduleComponent`
133-
"""
134-
yield (time, self)
135-
136-
def flatten(self) -> 'Instruction':
137-
"""Return itself as already single instruction."""
138-
return self
139-
140-
def union(self, *schedules: List[ScheduleComponent], name: Optional[str] = None) -> 'Schedule':
141-
"""Return a new schedule which is the union of `self` and `schedule`.
142-
143-
Args:
144-
*schedules: Schedules to be take the union with this Instruction.
145-
name: Name of the new schedule. Defaults to name of self
146-
"""
147-
if name is None:
148-
name = self.name
149-
return Schedule(self, *schedules, name=name)
150-
151-
def shift(self: ScheduleComponent, time: int, name: Optional[str] = None) -> 'Schedule':
152-
"""Return a new schedule shifted forward by `time`.
153-
154-
Args:
155-
time: Time to shift by
156-
name: Name of the new schedule. Defaults to name of self
157-
"""
158-
if name is None:
159-
name = self.name
160-
return Schedule((time, self), name=name)
161-
162-
def insert(self, start_time: int, schedule: ScheduleComponent,
163-
name: Optional[str] = None) -> 'Schedule':
164-
"""Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted within
165-
``self`` at ``start_time``.
166-
167-
Args:
168-
start_time: Time to insert the schedule schedule
169-
schedule: Schedule to insert
170-
name: Name of the new schedule. Defaults to name of self
171-
"""
172-
return self.union((start_time, schedule), name=name)
173-
174-
def append(self, schedule: ScheduleComponent,
175-
name: Optional[str] = None) -> 'Schedule':
176-
"""Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted at the
177-
maximum time over all channels shared between ``self`` and ``schedule``.
178-
179-
Args:
180-
schedule: schedule to be appended
181-
name: Name of the new schedule. Defaults to name of self
182-
"""
183-
common_channels = set(self.channels) & set(schedule.channels)
184-
time = self.ch_stop_time(*common_channels)
185-
return self.insert(time, schedule, name=name)
186-
187-
def draw(self, dt: float = 1, style: Optional['SchedStyle'] = None,
188-
filename: Optional[str] = None, interp_method: Optional[Callable] = None,
189-
scale: float = 1, channels_to_plot: Optional[List[Channel]] = None,
190-
plot_all: bool = False, plot_range: Optional[Tuple[float]] = None,
191-
interactive: bool = False, table: bool = True,
192-
label: bool = False, framechange: bool = True,
193-
scaling: float = None,
194-
channels: Optional[List[Channel]] = None):
195-
"""Plot the instruction.
196-
197-
Args:
198-
dt: Time interval of samples
199-
style: A style sheet to configure plot appearance
200-
filename: Name required to save pulse image
201-
interp_method: A function for interpolation
202-
scale: Relative visual scaling of waveform amplitudes
203-
channels_to_plot: Deprecated, see `channels`
204-
plot_all: Plot empty channels
205-
plot_range: A tuple of time range to plot
206-
interactive: When set true show the circuit in a new window
207-
(this depends on the matplotlib backend being used supporting this)
208-
table: Draw event table for supported commands
209-
label: Label individual instructions
210-
framechange: Add framechange indicators
211-
scaling: Deprecated, see `scale`
212-
channels: A list of channel names to plot
213-
214-
Returns:
215-
matplotlib.figure: A matplotlib figure object of the pulse schedule
216-
"""
217-
# pylint: disable=invalid-name, cyclic-import
218-
if scaling is not None:
219-
warnings.warn('The parameter "scaling" is being replaced by "scale"',
220-
DeprecationWarning, 3)
221-
scale = scaling
222-
223-
from qiskit import visualization
224-
225-
if channels_to_plot:
226-
warnings.warn('The parameter "channels_to_plot" is being replaced by "channels"',
227-
DeprecationWarning, 3)
228-
channels = channels_to_plot
229-
230-
return visualization.pulse_drawer(self, dt=dt, style=style,
231-
filename=filename, interp_method=interp_method,
232-
scale=scale,
233-
plot_all=plot_all, plot_range=plot_range,
234-
interactive=interactive, table=table,
235-
label=label, framechange=framechange,
236-
channels=channels)
237-
238-
def __eq__(self, other: 'Instruction'):
239-
"""Check if this Instruction is equal to the `other` instruction.
240-
241-
Equality is determined by the instruction sharing the same command and channels.
242-
"""
243-
return (self.command == other.command) and (set(self.channels) == set(other.channels))
244-
245-
def __hash__(self):
246-
return hash((self.command.__hash__(), self.channels.__hash__()))
247-
248-
def __add__(self, other: ScheduleComponent) -> 'Schedule':
249-
"""Return a new schedule with `other` inserted within `self` at `start_time`."""
250-
return self.append(other)
251-
252-
def __or__(self, other: ScheduleComponent) -> 'Schedule':
253-
"""Return a new schedule which is the union of `self` and `other`."""
254-
return self.union(other)
255-
256-
def __lshift__(self, time: int) -> 'Schedule':
257-
"""Return a new schedule which is shifted forward by `time`."""
258-
return self.shift(time)
259-
260-
def __repr__(self):
261-
return "%s(%s, %s)" % (self.__class__.__name__,
262-
self._command,
263-
', '.join(str(ch) for ch in self.channels))
18+
from qiskit.pulse.instructions import Instruction

qiskit/pulse/instructions/__init__.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This code is part of Qiskit.
4+
#
5+
# (C) Copyright IBM 2020.
6+
#
7+
# This code is licensed under the Apache License, Version 2.0. You may
8+
# obtain a copy of this license in the LICENSE.txt file in the root directory
9+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
10+
#
11+
# Any modifications or derivative works of this code must retain this
12+
# copyright notice, and modified files need to carry a notice indicating
13+
# that they have been altered from the originals.
14+
15+
"""The ``instruction`` module holds the various ``Instruction`` s which are supported by
16+
Qiskit Pulse. Instructions accept a list of operands unique to instructions of that type.
17+
Instructions typically include at least one :py:class:`~qiskit.pulse.channels.Channel` as an
18+
operand specifying where the instruction will be applied, and every instruction has a duration,
19+
whether implicitly or explicitly defined.
20+
21+
An instruction can be added to a :py:class:`~qiskit.pulse.Schedule`, which is a
22+
sequence of scheduled Pulse ``Instruction`` s over many channels.
23+
"""
24+
from .instruction import Instruction

0 commit comments

Comments
 (0)