Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #7078: Change prefix of MemorySlot channel in Qasm Instructions #8166

Merged
merged 15 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions qiskit/pulse/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ class PulseChannel(Channel, metaclass=ABCMeta):
pass


class ClassicalIOChannel(Channel, metaclass=ABCMeta):
"""Base class of classical IO channels. These cannot have instructions scheduled on them."""

pass


class DriveChannel(PulseChannel):
"""Drive channels transmit signals to qubits which enact gate operations."""

Expand Down Expand Up @@ -192,7 +198,7 @@ class AcquireChannel(Channel):
prefix = "a"


class SnapshotChannel(Channel):
class SnapshotChannel(ClassicalIOChannel):
"""Snapshot channels are used to specify instructions for simulators."""

prefix = "s"
Expand All @@ -202,13 +208,13 @@ def __init__(self):
super().__init__(0)


class MemorySlot(Channel):
class MemorySlot(ClassicalIOChannel):
"""Memory slot channels represent classical memory storage."""

prefix = "m"


class RegisterSlot(Channel):
class RegisterSlot(ClassicalIOChannel):
"""Classical resister slot channels represent classical registers (low-latency classical
memory).
"""
Expand Down
14 changes: 13 additions & 1 deletion qiskit/pulse/instructions/frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import Optional, Union, Tuple

from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.pulse.channels import PulseChannel
from qiskit.pulse.channels import PulseChannel, PulseError
from qiskit.pulse.instructions.instruction import Instruction


Expand Down Expand Up @@ -46,7 +46,13 @@ def __init__(
frequency: New frequency of the channel in Hz.
channel: The channel this instruction operates on.
name: Name of this set channel frequency instruction.
Raises:
PulseError: If channel is not a PulseChannel.
"""
if not isinstance(channel, PulseChannel):
raise PulseError(
"The `channel` argument to `SetFrequency` must be of type `channels.PulseChannel`."
)
if not isinstance(frequency, ParameterExpression):
frequency = float(frequency)
super().__init__(operands=(frequency, channel), name=name)
Expand Down Expand Up @@ -93,9 +99,15 @@ def __init__(
frequency: Frequency shift of the channel in Hz.
channel: The channel this instruction operates on.
name: Name of this set channel frequency instruction.
Raises:
PulseError: If channel is not a PulseChannel.
"""
if not isinstance(frequency, ParameterExpression):
frequency = float(frequency)
if not isinstance(channel, PulseChannel):
raise PulseError(
"The `channel` argument to `ShiftFrequency` must be of type `channels.PulseChannel`."
)
super().__init__(operands=(frequency, channel), name=name)

@property
Expand Down
15 changes: 14 additions & 1 deletion qiskit/pulse/instructions/phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from typing import Optional, Union, Tuple

from qiskit.circuit import ParameterExpression
from qiskit.pulse.channels import PulseChannel
from qiskit.pulse.channels import PulseChannel, PulseError
from qiskit.pulse.instructions.instruction import Instruction


Expand Down Expand Up @@ -52,7 +52,14 @@ def __init__(
phase: The rotation angle in radians.
channel: The channel this instruction operates on.
name: Display name for this instruction.

Raises:
PulseError: If channel is not a PulseChannel.
"""
if not isinstance(channel, PulseChannel):
raise PulseError(
"The `channel` argument to `ShiftPhase` must be of type `channels.PulseChannel`."
)
super().__init__(operands=(phase, channel), name=name)

@property
Expand Down Expand Up @@ -108,7 +115,13 @@ def __init__(
phase: The rotation angle in radians.
channel: The channel this instruction operates on.
name: Display name for this instruction.
Raises:
PulseError: If channel is not a PulseChannel.
"""
if not isinstance(channel, PulseChannel):
raise PulseError(
"The `channel` argument to `SetPhase` must be of type `channels.PulseChannel`."
)
super().__init__(operands=(phase, channel), name=name)

@property
Expand Down
2 changes: 1 addition & 1 deletion qiskit/pulse/instructions/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, pulse: Pulse, channel: PulseChannel, name: Optional[str] = No
name: Name of the instruction for display purposes. Defaults to ``pulse.name``.

Raises:
PulseError: If pulse is not a Pulse type.
PulseError: If pulse is not a Pulse type, or channel is not a PulseChannel.
"""
if not isinstance(pulse, Pulse):
raise PulseError("The `pulse` argument to `Play` must be of type `library.Pulse`.")
Expand Down
3 changes: 3 additions & 0 deletions qiskit/pulse/transforms/canonicalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import numpy as np

from qiskit.pulse import channels as chans, exceptions, instructions
from qiskit.pulse.channels import ClassicalIOChannel
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.exceptions import UnassignedDurationError
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap
Expand Down Expand Up @@ -475,6 +476,8 @@ def pad(
channels = channels or schedule.channels

for channel in channels:
if isinstance(channel, ClassicalIOChannel):
continue
if channel not in schedule.channels:
schedule |= instructions.Delay(until, channel)
continue
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features:
- |
In the qiskit.pulse.channels package, ClassicalIOChannel class has been added
as an abstract base class of MemorySlot, RegisterSlot, and SnapshotChannel.

The qiskit.pulse.transforms.canonicalization.pad method does not introduce
delays to any channels which are instances of ClassicalIOChannel.

In qiskit.pulse.instructions, the constructors to SetPhase, ShiftPhase,
SetFrequency and ShiftFrequency now throw a PulseError if the channel parameter
is not of type PulseChannel.

17 changes: 15 additions & 2 deletions test/python/pulse/test_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
from qiskit.pulse.channels import (
AcquireChannel,
Channel,
DriveChannel,
ClassicalIOChannel,
ControlChannel,
DriveChannel,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this list in alphabetical order.

MeasureChannel,
MemorySlot,
PulseChannel,
Expand Down Expand Up @@ -67,15 +68,25 @@ def test_channel_hash(self):
self.assertEqual(hash_1, hash_2)


class TestClassicalIOChannel(QiskitTestCase):
"""Test base classical IO channel."""

def test_cannot_be_instantiated(self):
"""Test base classical IO channel cannot be instantiated."""
with self.assertRaises(NotImplementedError):
ClassicalIOChannel(0)


class TestMemorySlot(QiskitTestCase):
"""AcquireChannel tests."""
"""MemorySlot tests."""

def test_default(self):
"""Test default memory slot."""
memory_slot = MemorySlot(123)

self.assertEqual(memory_slot.index, 123)
self.assertEqual(memory_slot.name, "m123")
self.assertTrue(isinstance(memory_slot, ClassicalIOChannel))


class TestRegisterSlot(QiskitTestCase):
Expand All @@ -87,6 +98,7 @@ def test_default(self):

self.assertEqual(register_slot.index, 123)
self.assertEqual(register_slot.name, "c123")
self.assertTrue(isinstance(register_slot, ClassicalIOChannel))


class TestSnapshotChannel(QiskitTestCase):
Expand All @@ -98,6 +110,7 @@ def test_default(self):

self.assertEqual(snapshot_channel.index, 0)
self.assertEqual(snapshot_channel.name, "s0")
self.assertTrue(isinstance(snapshot_channel, ClassicalIOChannel))


class TestDriveChannel(QiskitTestCase):
Expand Down
63 changes: 63 additions & 0 deletions test/python/pulse/test_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,64 @@ def test_freq(self):
)
self.assertEqual(repr(set_freq), "SetFrequency(4500000000.0, DriveChannel(1), name='test')")

def test_freq_non_pulse_channel(self):
"""Test set frequency constructor with illegal channel"""
with self.assertRaises(exceptions.PulseError):
instructions.SetFrequency(4.5e9, channels.RegisterSlot(1), name="test")


class TestShiftFrequency(QiskitTestCase):
"""Shift frequency tests."""

def test_shift_freq(self):
"""Test shift frequency basic functionality."""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't see a test for happy-path constructor for ShiftFrequency or SetPhase so I put them in.

shift_freq = instructions.ShiftFrequency(4.5e9, channels.DriveChannel(1), name="test")

self.assertIsInstance(shift_freq.id, int)
self.assertEqual(shift_freq.duration, 0)
self.assertEqual(shift_freq.frequency, 4.5e9)
self.assertEqual(shift_freq.operands, (4.5e9, channels.DriveChannel(1)))
self.assertEqual(
shift_freq, instructions.ShiftFrequency(4.5e9, channels.DriveChannel(1), name="test")
)
self.assertNotEqual(
shift_freq, instructions.ShiftFrequency(4.5e8, channels.DriveChannel(1), name="test")
)
self.assertEqual(
repr(shift_freq), "ShiftFrequency(4500000000.0, DriveChannel(1), name='test')"
)

def test_freq_non_pulse_channel(self):
"""Test shift frequency constructor with illegal channel"""
with self.assertRaises(exceptions.PulseError):
instructions.ShiftFrequency(4.5e9, channels.RegisterSlot(1), name="test")


class TestSetPhase(QiskitTestCase):
"""Test the instruction construction."""

def test_default(self):
"""Test basic SetPhase."""
set_phase = instructions.SetPhase(1.57, channels.DriveChannel(0))

self.assertIsInstance(set_phase.id, int)
self.assertEqual(set_phase.name, None)
self.assertEqual(set_phase.duration, 0)
self.assertEqual(set_phase.phase, 1.57)
self.assertEqual(set_phase.operands, (1.57, channels.DriveChannel(0)))
self.assertEqual(
set_phase, instructions.SetPhase(1.57, channels.DriveChannel(0), name="test")
)
self.assertNotEqual(
set_phase, instructions.SetPhase(1.57j, channels.DriveChannel(0), name="test")
)
self.assertEqual(repr(set_phase), "SetPhase(1.57, DriveChannel(0))")

def test_set_phase_non_pulse_channel(self):
"""Test shift phase constructor with illegal channel"""
with self.assertRaises(exceptions.PulseError):
instructions.SetPhase(1.57, channels.RegisterSlot(1), name="test")


class TestShiftPhase(QiskitTestCase):
"""Test the instruction construction."""
Expand All @@ -177,6 +235,11 @@ def test_default(self):
)
self.assertEqual(repr(shift_phase), "ShiftPhase(1.57, DriveChannel(0))")

def test_shift_phase_non_pulse_channel(self):
"""Test shift phase constructor with illegal channel"""
with self.assertRaises(exceptions.PulseError):
instructions.ShiftPhase(1.57, channels.RegisterSlot(1), name="test")


class TestSnapshot(QiskitTestCase):
"""Snapshot tests."""
Expand Down
25 changes: 24 additions & 1 deletion test/python/pulse/test_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@
Constant,
)
from qiskit.pulse import transforms, instructions
from qiskit.pulse.channels import MemorySlot, DriveChannel, AcquireChannel
from qiskit.pulse.channels import (
MemorySlot,
DriveChannel,
AcquireChannel,
RegisterSlot,
SnapshotChannel,
)
from qiskit.pulse.instructions import directives
from qiskit.test import QiskitTestCase
from qiskit.providers.fake_provider import FakeOpenPulse2Q
Expand Down Expand Up @@ -349,6 +355,23 @@ def test_padding_prepended_delay(self):

self.assertEqual(transforms.pad(sched, until=30, inplace=True), ref_sched)

def test_pad_no_delay_on_classical_io_channels(self):
"""Test padding does not apply to classical IO channels."""
delay = 10
sched = (
Delay(delay, MemorySlot(0)).shift(20)
+ Delay(delay, RegisterSlot(0)).shift(10)
+ Delay(delay, SnapshotChannel())
)

ref_sched = (
Delay(delay, MemorySlot(0)).shift(20)
+ Delay(delay, RegisterSlot(0)).shift(10)
+ Delay(delay, SnapshotChannel())
)

self.assertEqual(transforms.pad(sched, until=15), ref_sched)


def get_pulse_ids(schedules: List[Schedule]) -> Set[int]:
"""Returns ids of pulses used in Schedules."""
Expand Down