Skip to content

Commit

Permalink
Brought pulse command naming in line with circuit Registers (#2593). (#…
Browse files Browse the repository at this point in the history
…2623)

* Braught pulse command naming in line with circuit Registers (#2593).

* Added create_name(...) to command.py that can be used to set pulse command names.

* Added an instance counter to class Command and its children.

If a pulse command is not given a name it will have the name 'prefix%d' where
%d counts the number of class instances. The default prefixes are
* Command: c
* SamplePulse: p
* FrameChange: fc
* PersistentValue: pv
* Acquire: acq
* Snapshot: snap

* Brought pulse command naming in line with circuit Registers (#2593)

* Changed create_method to classmethod from staticmethod
* SubClass prefixes are now class variables

* Brought pulse command naming in line with circuit Registers (#2593).

* Implementer the class counter using a metaclass instead of itertools.

* Brought pulse command naming in line with circuit Registers (#2593).

* Added a test case to ensure class counter is properly incremented.

* * Added pylint: disable=E1101 for the instances counter.

* Removed method increment_counter() in command.py. Incrementing is now done in create_name(name).

* * Removed **kwargs from class MetaCount.
  • Loading branch information
eggerdj authored and taalexander committed Jun 17, 2019
1 parent f5dd2e2 commit 508feaf
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 19 deletions.
6 changes: 5 additions & 1 deletion qiskit/pulse/commands/acquire.py
Expand Up @@ -28,8 +28,9 @@ class Acquire(Command):
"""Acquire."""

ALIAS = 'acquire'
prefix = 'acq'

def __init__(self, duration, discriminator=None, kernel=None):
def __init__(self, duration, discriminator=None, kernel=None, name=None):
"""Create new acquire command.
Args:
Expand All @@ -39,12 +40,15 @@ def __init__(self, duration, discriminator=None, kernel=None):
kernel (Kernel): The data structures defining the measurement kernels
to be used (from the list of available kernels) and set of parameters
(if applicable) if the measurement level is 1 or 2.
name (str): Name of this command.
Raises:
PulseError: when invalid discriminator or kernel object is input.
"""
super().__init__(duration=duration)

self._name = Acquire.create_name(name)

if discriminator:
if isinstance(discriminator, Discriminator):
self._discriminator = discriminator
Expand Down
44 changes: 36 additions & 8 deletions qiskit/pulse/commands/command.py
Expand Up @@ -15,25 +15,35 @@
"""
Base command.
"""
import re

from abc import ABCMeta, abstractmethod

from qiskit.pulse.exceptions import PulseError

from .instruction import Instruction


class Command(metaclass=ABCMeta):
class MetaCount(ABCMeta):
"""Meta class to count class instances."""
def __new__(mcs, name, bases, namespace):
new_cls = super(MetaCount, mcs).__new__(mcs, name, bases, namespace)
new_cls.instances_counter = 0
return new_cls


class Command(metaclass=MetaCount):
"""Super abstract class of command group."""

pulseIndex = 0
# Counter for the number of instances in this class
prefix = 'c'

@abstractmethod
def __init__(self, duration: int = None, name: str = None):
def __init__(self, duration: int = None):
"""Create a new command.
Args:
duration (int): Duration of this command.
name (str): Name of this command.
Raises:
PulseError: when duration is not number of points.
"""
Expand All @@ -42,11 +52,29 @@ def __init__(self, duration: int = None, name: str = None):
else:
raise PulseError('Pulse duration should be integer.')

if name:
self._name = name
self._name = Command.create_name()

@classmethod
def create_name(cls, name: str = None) -> str:
"""Method to create names for pulse commands."""
if name is None:
try:
name = '%s%i' % (cls.prefix, cls.instances_counter) # pylint: disable=E1101
except TypeError:
raise PulseError("prefix and counter must be non-None when name is None.")
else:
self._name = 'p%d' % Command.pulseIndex
Command.pulseIndex += 1
try:
name = str(name)
except Exception:
raise PulseError("The pulse command name should be castable to a string "
"(or None for autogenerate a name).")
name_format = re.compile('[a-z][a-zA-Z0-9_]*')
if name_format.match(name) is None:
raise PulseError("%s is an invalid OpenPulse command name." % name)

cls.instances_counter += 1 # pylint: disable=E1101

return name

@property
def duration(self) -> int:
Expand Down
6 changes: 5 additions & 1 deletion qiskit/pulse/commands/frame_change.py
Expand Up @@ -24,15 +24,19 @@
class FrameChange(Command):
"""Frame change pulse."""

def __init__(self, phase):
prefix = 'fc'

def __init__(self, phase, name=None):
"""Create new frame change pulse.
Args:
phase (float): Frame change phase in radians.
The allowable precision is device specific.
name (str): Name of this command.
"""
super().__init__(duration=0)
self._phase = float(phase)
self._name = FrameChange.create_name(name)

@property
def phase(self):
Expand Down
6 changes: 5 additions & 1 deletion qiskit/pulse/commands/persistent_value.py
Expand Up @@ -25,12 +25,15 @@
class PersistentValue(Command):
"""Persistent value."""

def __init__(self, value):
prefix = 'pv'

def __init__(self, value, name=None):
"""create new persistent value command.
Args:
value (complex): Complex value to apply, bounded by an absolute value of 1.
The allowable precision is device specific.
name (str): Name of this command.
Raises:
PulseError: when input value exceed 1.
"""
Expand All @@ -40,6 +43,7 @@ def __init__(self, value):
raise PulseError("Absolute value of PV amplitude exceeds 1.")

self._value = complex(value)
self._name = PersistentValue.create_name(name)

@property
def value(self):
Expand Down
5 changes: 4 additions & 1 deletion qiskit/pulse/commands/sample_pulse.py
Expand Up @@ -28,6 +28,8 @@
class SamplePulse(Command):
"""Container for functional pulse."""

prefix = 'p'

def __init__(self, samples, name=None):
"""Create new sample pulse command.
Expand All @@ -37,12 +39,13 @@ def __init__(self, samples, name=None):
Raises:
PulseError: when pulse envelope amplitude exceeds 1.
"""
super().__init__(duration=len(samples), name=name)
super().__init__(duration=len(samples))

if np.any(np.abs(samples) > 1):
raise PulseError('Absolute value of pulse envelope amplitude exceeds 1.')

self._samples = np.asarray(samples, dtype=np.complex_)
self._name = SamplePulse.create_name(name)

@property
def samples(self):
Expand Down
8 changes: 5 additions & 3 deletions qiskit/pulse/commands/snapshot.py
Expand Up @@ -15,7 +15,6 @@
"""
Snapshot.
"""

from qiskit.pulse.channels import SnapshotChannel
from .instruction import Instruction
from .command import Command
Expand All @@ -24,6 +23,8 @@
class Snapshot(Command, Instruction):
"""Snapshot."""

prefix = 'snap'

def __init__(self, name: str, snap_type: str):
"""Create new snapshot command.
Expand All @@ -35,8 +36,9 @@ def __init__(self, name: str, snap_type: str):
"""
self._type = snap_type
self._channel = SnapshotChannel()
Command.__init__(self, duration=0, name=name)
Instruction.__init__(self, self, self._channel, name=name)
Command.__init__(self, duration=0)
self._name = Snapshot.create_name(name)
Instruction.__init__(self, self, self._channel, name=self.name)
self._buffer = 0

@property
Expand Down
15 changes: 11 additions & 4 deletions test/python/pulse/test_commands.py
Expand Up @@ -50,15 +50,20 @@ def test_can_construct_valid_acquire_command(self):
self.assertEqual(acq_command.discriminator.params, discriminator_opts)
self.assertEqual(acq_command.kernel.name, 'boxcar')
self.assertEqual(acq_command.kernel.params, kernel_opts)
self.assertTrue(acq_command.name.startswith('acq'))

def test_can_construct_acquire_command_with_default_values(self):
"""Test if an acquire command can be constructed with default discriminator and kernel.
"""
acq_command = Acquire(duration=10)
acq_command_a = Acquire(duration=10)
acq_command_b = Acquire(duration=10)

self.assertEqual(acq_command.duration, 10)
self.assertEqual(acq_command.discriminator, None)
self.assertEqual(acq_command.kernel, None)
self.assertEqual(acq_command_a.duration, 10)
self.assertEqual(acq_command_a.discriminator, None)
self.assertEqual(acq_command_a.kernel, None)
self.assertTrue(acq_command_a.name.startswith('acq'))
self.assertNotEqual(acq_command_a.name, acq_command_b.name)
self.assertEqual(acq_command_b.name, 'acq' + str(int(acq_command_a.name[3:]) + 1))


class TestFrameChange(QiskitTestCase):
Expand All @@ -71,6 +76,7 @@ def test_default(self):

self.assertEqual(fc_command.phase, 1.57)
self.assertEqual(fc_command.duration, 0)
self.assertTrue(fc_command.name.startswith('fc'))


class TestFunctionalPulse(QiskitTestCase):
Expand Down Expand Up @@ -107,6 +113,7 @@ def test_default(self):

self.assertEqual(pv_command.value, 0.5-0.5j)
self.assertEqual(pv_command.duration, 0)
self.assertTrue(pv_command.name.startswith('pv'))


class TestSnapshot(QiskitTestCase):
Expand Down

0 comments on commit 508feaf

Please sign in to comment.