From 19d149a4d55f66295ba76cedc511db20510c35f8 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Tue, 18 Feb 2020 12:14:05 -0500 Subject: [PATCH 01/12] Docathon: Module level docs for Pulse (#3849) * Update the pulse module init file for documentation. This includes adding autosummary references to important parts of the module that were previously unreachable, removing references to pieces that are mostly internal usage, and providing introductary summaries where appropriate * Apply suggestions from code review Helpful feedback from Dan, including more API cross referencing :tada: Co-Authored-By: Daniel Puzzuoli * Small fixups * Another attempt to make linking work Co-authored-by: Daniel Puzzuoli Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit/pulse/__init__.py | 91 +++++++++++++++++++++++++++------------- qiskit/pulse/channels.py | 4 +- 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 02d4b9169fb4..925e9db900e8 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -13,56 +13,78 @@ # that they have been altered from the originals. """ -=============================== -OpenPulse (:mod:`qiskit.pulse`) -=============================== - .. currentmodule:: qiskit.pulse -Channels -======== +=========================== +Pulse (:mod:`qiskit.pulse`) +=========================== -.. autosummary:: - :toctree: ../stubs/ +Qiskit-Pulse is a pulse-level quantum programming kit. This lower level of programming offers the +user more control than programming with :py:class:`~qiskit.circuit.QuantumCircuit` s. - DriveChannel - MeasureChannel - AcquireChannel - ControlChannel - RegisterSlot - MemorySlot +Extracting the greatest performance from quantum hardware requires real-time pulse-level +instructions. Pulse answers that need: it enables the quantum physicist *user* to specify the +exact time dynamics of an experiment. It is especially powerful for error mitigation techniques. + +The input is given as arbitrary, time-ordered signals (see: :ref:`pulse-commands`) scheduled in +parallel over multiple virtual hardware or simulator resources (see: :ref:`pulse-channels`). The +system also allows the user to recover the time dynamics of the measured output. + +This is sufficient to allow the quantum physicist to explore and correct for noise in a quantum +system. -Commands -======== +.. _pulse-commands: + +Commands (:mod:`~qiskit.pulse.commands`) +================================================ .. autosummary:: :toctree: ../stubs/ - Instruction - Acquire - AcquireInstruction - FrameChange - PersistentValue SamplePulse - Snapshot - Kernel - Discriminator Delay - ParametricPulse - ParametricInstruction + FrameChange Gaussian GaussianSquare Drag ConstantPulse + Acquire + Snapshot + +.. _pulse-channels: + +Channels (:mod:`~qiskit.pulse.channels`) +======================================== + +Pulse is meant to be agnostic to the underlying hardware implementation, while still allowing +low-level control. Therefore, our signal channels are *virtual* hardware channels. The backend +which executes our programs is responsible for mapping these virtual channels to the proper +physical channel within the quantum control hardware. + +Channels are characterized by their type and their index. See each channel type below to learn more. + +.. autosummary:: + :toctree: ../stubs/ + + DriveChannel + MeasureChannel + AcquireChannel + ControlChannel + RegisterSlot + MemorySlot Schedules ========= +Schedules are Pulse programs. They describe instruction sequences for the control hardware. +An :class:`~qiskit.pulse.Instruction` is a :py:class:`~qiskit.pulse.commands.Command` which has +been assigned to its :class:`~qiskit.pulse.channels.Channel` (s). + .. autosummary:: :toctree: ../stubs/ Schedule - ScheduleComponent + Instruction Configuration ============= @@ -71,8 +93,18 @@ :toctree: ../stubs/ InstructionScheduleMap - LoConfig - LoRange + +Rescheduling Utilities +====================== + +These utilities return modified :class:`~qiskit.pulse.Schedule` s. + +.. autosummary:: + :toctree: ../stubs/ + + ~reschedule.align_measures + ~reschedule.add_implicit_acquires + ~reschedule.pad Pulse Library ============= @@ -89,6 +121,7 @@ :toctree: ../stubs/ PulseError + """ from .channels import (DriveChannel, MeasureChannel, AcquireChannel, diff --git a/qiskit/pulse/channels.py b/qiskit/pulse/channels.py index 18381ab3bc79..63d5355991ef 100644 --- a/qiskit/pulse/channels.py +++ b/qiskit/pulse/channels.py @@ -15,7 +15,7 @@ """ This module defines Pulse Channels. Channels include: - - transmit channels, which should subclass``PulseChannel`` + - transmit channels, which should subclass ``PulseChannel`` - receive channels, such as ``AcquireChannel`` - non-signal "channels" such as ``SnapshotChannel``, ``MemorySlot`` and ``RegisterChannel``. @@ -31,7 +31,7 @@ class Channel(metaclass=ABCMeta): """Base class of channels. Channels provide a Qiskit-side label for typical quantum control hardware signal channels. The final label -> physical channel mapping is the responsibility - of the hardware backend. For instance,``DriveChannel(0)`` holds instructions which the backend + of the hardware backend. For instance, ``DriveChannel(0)`` holds instructions which the backend should map to the signal line driving gate operations on the qubit labeled (indexed) 0. """ From 5111e24391516bcc7ec8630915dfda9c3d8ae53d Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Tue, 18 Feb 2020 13:21:53 -0500 Subject: [PATCH 02/12] Docathon: schedule.py and remove deprecated buffer arg (#3836) * Remove the buffer options in Schedule, Instruction and ScheduleComponent which has been deprecated since 0.11.0. Update Schedule docstrings with minor formatting improvements, rst formatting directives, minor wording improvements. Update the pulse __init__ file to show Instruction near Schedule * style * style * Update other release note that already mentions buffer * Improve the formatting of the math/psuedocode in Schedule.append * Apply suggestions from code review Incorporate feedback from Dan Co-Authored-By: Daniel Puzzuoli * Add a few more verbatim instances and fixup some spacing in docstrings. * Release note improvement Co-authored-by: Daniel Puzzuoli --- qiskit/pulse/commands/instruction.py | 17 +- qiskit/pulse/interfaces.py | 13 +- qiskit/pulse/schedule.py | 191 +++++++++--------- ...eprecations-in-pulse-ffc384b325b077f1.yaml | 7 +- 4 files changed, 108 insertions(+), 120 deletions(-) diff --git a/qiskit/pulse/commands/instruction.py b/qiskit/pulse/commands/instruction.py index 114ccf85ef20..1f4e05da1bdd 100644 --- a/qiskit/pulse/commands/instruction.py +++ b/qiskit/pulse/commands/instruction.py @@ -159,32 +159,27 @@ def shift(self: ScheduleComponent, time: int, name: Optional[str] = None) -> 'Sc name = self.name return Schedule((time, self), name=name) - def insert(self, start_time: int, schedule: ScheduleComponent, buffer: bool = False, + def insert(self, start_time: int, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted within `self` at `start_time`. + """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted within + ``self`` at ``start_time``. Args: start_time: Time to insert the schedule schedule schedule: Schedule to insert - buffer: Whether to obey buffer when inserting name: Name of the new schedule. Defaults to name of self """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") return self.union((start_time, schedule), name=name) - def append(self, schedule: ScheduleComponent, buffer: bool = False, + def append(self, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted at the maximum time over - all channels shared between `self` and `schedule`. + """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted at the + maximum time over all channels shared between ``self`` and ``schedule``. Args: schedule: schedule to be appended - buffer: Whether to obey buffer when appending name: Name of the new schedule. Defaults to name of self """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") common_channels = set(self.channels) & set(schedule.channels) time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name) diff --git a/qiskit/pulse/interfaces.py b/qiskit/pulse/interfaces.py index 31fef1ef5dd9..0210ca14b0da 100644 --- a/qiskit/pulse/interfaces.py +++ b/qiskit/pulse/interfaces.py @@ -15,8 +15,6 @@ """ ScheduleComponent, a common interface for components of schedule (Instruction and Schedule). """ -import warnings - from abc import ABCMeta, abstractmethod from typing import Tuple, List, Union, Optional @@ -54,12 +52,6 @@ def start_time(self) -> int: """Starting time of this schedule component.""" pass - @property - def buffer(self) -> int: - """Buffer for schedule. To be used when appending""" - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") - return 0 - @property @abstractmethod def stop_time(self) -> int: @@ -128,27 +120,24 @@ def shift(self: 'ScheduleComponent', time: int, @abstractmethod def insert(self, start_time: int, schedule: 'ScheduleComponent', - buffer: bool = False, 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 - buffer: Obey buffer when appending name: Name of the new schedule. Defaults to name of parent """ pass @abstractmethod - def append(self, schedule: 'ScheduleComponent', buffer: bool = False, + 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 - buffer: Obey buffer when appending name: Name of the new schedule. Defaults to name of parent """ pass diff --git a/qiskit/pulse/schedule.py b/qiskit/pulse/schedule.py index b0118dddccc8..104415eb3e6c 100644 --- a/qiskit/pulse/schedule.py +++ b/qiskit/pulse/schedule.py @@ -12,7 +12,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Schedule.""" +"""The Schedule is one of the most fundamental objects to this pulse-level programming module. +A ``Schedule`` is a representation of a *program* in Pulse. Each schedule tracks the time of each +instruction occuring in parallel over multiple signal *channels*. +""" import abc from typing import List, Tuple, Iterable, Union, Dict, Callable, Set, Optional, Type @@ -28,19 +31,21 @@ class Schedule(ScheduleComponent): - """Schedule of `ScheduleComponent`s. The composite node of a schedule tree.""" - # pylint: disable=missing-type-doc + """A quantum program *schedule* with exact time constraints for its instructions, operating + over all input signal *channels* and supporting special syntaxes for building. + """ + def __init__(self, *schedules: List[Union[ScheduleComponent, Tuple[int, ScheduleComponent]]], name: Optional[str] = None): - """Create empty schedule. + """Create an empty schedule. Args: *schedules: Child Schedules of this parent Schedule. May either be passed as - the list of schedules, or a list of (start_time, schedule) pairs - name: Name of this schedule + the list of schedules, or a list of ``(start_time, schedule)`` pairs. + name: Name of this schedule. Raises: - PulseError: If timeslots intercept. + PulseError: If the input schedules have instructions which overlap. """ self._name = name @@ -105,7 +110,7 @@ def _children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: @property def instructions(self) -> Tuple[Tuple[int, 'Instruction'], ...]: - """Get time-ordered instructions from Schedule tree.""" + """Get the time-ordered instructions from self.""" def key(time_inst_pair): inst = time_inst_pair[1] @@ -115,18 +120,18 @@ def key(time_inst_pair): return tuple(sorted(self._instructions(), key=key)) def ch_duration(self, *channels: List[Channel]) -> int: - """Return duration of schedule over supplied channels. + """Return the time of the end of the last instruction over the supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_duration(*channels) def ch_start_time(self, *channels: List[Channel]) -> int: - """Return minimum start time over supplied channels. + """Return the time of the start of the first instruction over the supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_start_time(*channels) @@ -134,7 +139,7 @@ def ch_stop_time(self, *channels: List[Channel]) -> int: """Return maximum start time over supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_stop_time(*channels) @@ -142,22 +147,22 @@ def _instructions(self, time: int = 0) -> Iterable[Tuple[int, 'Instruction']]: """Iterable for flattening Schedule tree. Args: - time: Shifted time due to parent + time: Shifted time due to parent. Yields: - Tuple[int, Instruction]: Tuple containing time `Instruction` starts - at and the flattened `Instruction`. + Tuple containing the time each :class:`~qiskit.pulse.Instruction` + starts at and the flattened :class:`~qiskit.pulse.Instruction` s. """ for insert_time, child_sched in self._children: yield from child_sched._instructions(time + insert_time) def union(self, *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponent]], name: Optional[str] = None) -> 'Schedule': - """Return a new schedule which is the union of both `self` and `schedules`. + """Return a new schedule which is the union of both ``self`` and ``schedules``. Args: - *schedules: Schedules to be take the union with this `Schedule`. - name: Name of the new schedule. Defaults to name of self + *schedules: Schedules to be take the union with this ``Schedule``. + name: Name of the new schedule. Defaults to the name of self. """ if name is None: name = self.name @@ -170,10 +175,10 @@ def union(self, *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponen return new_sched def _union(self, other: Tuple[int, ScheduleComponent]) -> 'Schedule': - """Mutably union `self` and `other` Schedule with shift time. + """Mutably union ``self`` and ``other`` with shift time. Args: - other: Schedule with shift time to be take the union with this `Schedule`. + other: Schedule with shift time to be take the union with this ``Schedule``. """ shift_time, sched = other if isinstance(sched, Schedule): @@ -188,50 +193,47 @@ def _union(self, other: Tuple[int, ScheduleComponent]) -> 'Schedule': self._timeslots = self.timeslots.merge(sched_timeslots) def shift(self, time: int, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule shifted forward by `time`. + """Return a new schedule shifted forward by ``time``. Args: - time: Time to shift by - name: Name of the new schedule. Defaults to name of self + time: Time to shift by. + name: Name of the new schedule. Defaults to the name of self. """ if name is None: name = self.name return Schedule((time, self), name=name) - def insert(self, start_time: int, schedule: ScheduleComponent, buffer: bool = False, + def insert(self, start_time: int, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted within `self` at `start_time`. + """Return a new schedule with ``schedule`` inserted into ``self`` at ``start_time``. Args: - start_time: Time to insert the schedule - schedule: Schedule to insert - buffer: Whether to obey buffer when inserting - name: Name of the new schedule. Defaults to name of self + start_time: Time to insert the schedule. + schedule: Schedule to insert. + name: Name of the new schedule. Defaults to the name of self. """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") return self.union((start_time, schedule), name=name) - def append(self, schedule: ScheduleComponent, buffer: bool = False, + def append(self, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - r"""Return a new schedule with `schedule` inserted at the maximum time over - all channels shared between `self` and `schedule`. + r"""Return a new schedule with ``schedule`` inserted at the maximum time over + all channels shared between ``self`` and ``schedule``. + + .. math:: - $t = \textrm{max}({x.stop\_time |x \in self.channels \cap schedule.channels})$ + t = \textrm{max}(\texttt{x.stop_time} |\texttt{x} \in + \texttt{self.channels} \cap \texttt{schedule.channels}) Args: - schedule: schedule to be appended - buffer: Whether to obey buffer when appending - name: Name of the new schedule. Defaults to name of self + schedule: Schedule to be appended. + name: Name of the new ``Schedule``. Defaults to name of ``self``. """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") common_channels = set(self.channels) & set(schedule.channels) time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name) def flatten(self) -> 'Schedule': - """Return a new schedule which is the flattened schedule contained all `instructions`.""" + """Return a new schedule which is the flattened schedule contained all ``instructions``.""" return Schedule(*self.instructions, name=self.name) def filter(self, *filter_funcs: List[Callable], @@ -239,22 +241,22 @@ def filter(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> 'Schedule': - """ - Return a new Schedule with only the instructions from this Schedule which pass though the - provided filters; i.e. an instruction will be retained iff every function in filter_funcs - returns True, the instruction occurs on a channel type contained in channels, - the instruction type is contained in instruction_types, and the period over which the - instruction operates is fully contained in one specified in time_ranges or intervals. + """Return a new ``Schedule`` with only the instructions from this ``Schedule`` which pass + though the provided filters; i.e. an instruction will be retained iff every function in + ``filter_funcs`` returns ``True``, the instruction occurs on a channel type contained in + ``channels``, the instruction type is contained in ``instruction_types``, and the period + over which the instruction operates is *fully* contained in one specified in + ``time_ranges`` or ``intervals``. - If no arguments are provided, this schedule is returned. + If no arguments are provided, ``self`` is returned. Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ composed_filter = self._construct_filter(*filter_funcs, channels=channels, @@ -269,18 +271,18 @@ def exclude(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> 'Schedule': - """ - Return a Schedule with only the instructions from this Schedule *failing* at least one of - the provided filters. This method is the complement of `self.filter`, so that: + """Return a Schedule with only the instructions from this Schedule *failing* at least one + of the provided filters. This method is the complement of ``self.filter``, so that:: + self.filter(args) | self.exclude(args) == self Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ composed_filter = self._construct_filter(*filter_funcs, channels=channels, @@ -291,13 +293,12 @@ def exclude(self, *filter_funcs: List[Callable], new_sched_name="{name}-excluded".format(name=self.name)) def _apply_filter(self, filter_func: Callable, new_sched_name: str) -> 'Schedule': - """ - Return a Schedule containing only the instructions from this Schedule for which - filter_func returns True. + """Return a Schedule containing only the instructions from this Schedule for which + ``filter_func`` returns ``True``. Args: - filter_func: function of the form (int, ScheduleComponent) -> bool - new_sched_name: name of the returned Schedule + filter_func: Function of the form (int, ScheduleComponent) -> bool. + new_sched_name: Name of the returned ``Schedule``. """ subschedules = self.flatten()._children valid_subschedules = [sched for sched in subschedules if filter_func(sched)] @@ -308,21 +309,20 @@ def _construct_filter(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> Callable: - """ - Returns a boolean-valued function with input type (int, ScheduleComponent) that returns True - iff the input satisfies all of the criteria specified by the arguments; i.e. iff every - function in filter_funcs returns True, the instruction occurs on a channel type contained - in channels, the instruction type is contained in instruction_types, and the period over - which the instruction operates is fully contained in one specified in time_ranges or - intervals. + """Returns a boolean-valued function with input type ``(int, ScheduleComponent)`` that + returns ``True`` iff the input satisfies all of the criteria specified by the arguments; + i.e. iff every function in ``filter_funcs`` returns ``True``, the instruction occurs on a + channel type contained in ``channels``, the instruction type is contained in + ``instruction_types``, and the period over which the instruction operates is fully + contained in one specified in ``time_ranges`` or ``intervals``. Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ def only_channels(channels: Set[Channel]) -> Callable: def channel_filter(time_inst: Tuple[int, 'Instruction']) -> bool: @@ -376,29 +376,29 @@ def draw(self, dt: float = 1, style: Optional['SchedStyle'] = None, filename: Name required to save pulse image interp_method: A function for interpolation scale: Relative visual scaling of waveform amplitudes, see Additional Information. - channel_scales: Channel independent scaling as a dictionary of `Channel` object. - channels_to_plot: Deprecated, see `channels` + channel_scales: Channel independent scaling as a dictionary of ``Channel`` object. + channels_to_plot: Deprecated, see ``channels`` plot_all: Plot empty channels plot_range: A tuple of time range to plot interactive: When set true show the circuit in a new window - (this depends on the matplotlib backend being used supporting this) + (this depends on the matplotlib backend being used supporting this) table: Draw event table for supported commands label: Label individual instructions framechange: Add framechange indicators - scaling: Deprecated, see `scale` + scaling: Deprecated, see ``scale`` channels: A list of channel names to plot show_framechange_channels: Plot channels with only framechanges Additional Information: If you want to manually rescale the waveform amplitude of channels one by one, - you can set `channel_scales` argument instead of `scale`. - The `channel_scales` should be given as a python dictionary:: + you can set ``channel_scales`` argument instead of ``scale``. + The ``channel_scales`` should be given as a python dictionary:: channel_scales = {pulse.DriveChannels(0): 10.0, pulse.MeasureChannels(0): 5.0} - When the channel to plot is not included in the `channel_scales` dictionary, - scaling factor of that channel is overwritten by the value of `scale` argument. + When the channel to plot is not included in the ``channel_scales`` dictionary, + scaling factor of that channel is overwritten by the value of ``scale` argument. In default, waveform amplitude is normalized by the maximum amplitude of the channel. The scaling factor is displayed under the channel name alias. @@ -430,9 +430,12 @@ def __eq__(self, other: ScheduleComponent) -> bool: """Test if two ScheduleComponents are equal. Equality is checked by verifying there is an equal instruction at every time - in `other` for every instruction in this Schedule. + in ``other`` for every instruction in this ``Schedule``. + + .. warning:: + + This does not check for logical equivalency. Ie., - Warning: This does not check for logical equivalency. Ie., ```python >>> (Delay(10)(DriveChannel(0)) + Delay(10)(DriveChannel(0)) == Delay(20)(DriveChannel(0))) @@ -461,15 +464,15 @@ def __eq__(self, other: ScheduleComponent) -> bool: return True def __add__(self, other: ScheduleComponent) -> 'Schedule': - """Return a new schedule with `other` inserted within `self` at `start_time`.""" + """Return a new schedule with ``other`` inserted within ``self`` at ``start_time``.""" return self.append(other) def __or__(self, other: ScheduleComponent) -> 'Schedule': - """Return a new schedule which is the union of `self` and `other`.""" + """Return a new schedule which is the union of ``self`` and ``other``.""" return self.union(other) def __lshift__(self, time: int) -> 'Schedule': - """Return a new schedule which is shifted forward by `time`.""" + """Return a new schedule which is shifted forward by ``time``.""" return self.shift(time) def __repr__(self): @@ -486,10 +489,10 @@ class ParameterizedSchedule: This should not be returned to users as it is currently only a helper class. This class is takes an input command definition that accepts - a set of parameters. Calling `bind` on the class will return a `Schedule`. + a set of parameters. Calling ``bind`` on the class will return a ``Schedule``. # TODO: In the near future this will be replaced with proper incorporation of parameters - into the `Schedule` class. + into the ``Schedule`` class. """ def __init__(self, *schedules, parameters: Optional[Dict[str, Union[float, complex]]] = None, diff --git a/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml b/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml index eae8962c6f0e..a63c9abdfd54 100644 --- a/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml +++ b/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml @@ -1,9 +1,10 @@ --- upgrade: - | - Channel ``buffer`` option was deprecated in Terra 0.11.0 and has now been - removed. To add a delay on a channel, specify it explicitly in your - Schedule with a Delay:: + The pulse ``buffer`` option found in :class:`qiskit.pulse.Channel` and + :class:`qiskit.pulse.Schedule` was deprecated in Terra 0.11.0 and has now + been removed. To add a delay on a channel or in a schedule, specify it + explicitly in your Schedule with a Delay:: sched = Schedule() sched += Delay(5)(DriveChannel(0)) From 6cfd47d7970198647962e2b00bd15ba010617c4a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 19 Feb 2020 07:15:29 -0500 Subject: [PATCH 03/12] Improve jsonschema validation error messages (#3432) Right now the jsonschema validation errors are next to useless because of how the schemas files our constructed. The schemas are built in away that tries to constrict too much of the structure that uses oneOf in multiple levels of the schema. These oneOf's make the schema much more difficult to debug because any rule that fails in the subschema below those will not actually be what fails validation, but instead it will be the oneOf lines because none of the elements in the list of valid subschemas passed validation. This becomes very problematic for debugging things that get jsonschema validated, especially with our current error message structure. The current error message prints the entire error message from jsonschema to either the exception message if it's less than 1000 characters (which it rarely is) or as a debug log message to python logging if it's more than 1000 characters. These messsages are not very useful since the contain most of the schema as the rule that failed (which is why it's often over 1000 characters) because of the use of oneOf checks. This commit changes our error reporting to try and be a bit more useful. It leverages the best_match [1] feature from jsonschema to use a heuristic to try and figure out what actually caused the validation failure instead of just blindly saying it was the whole schema. In local testing this often gets the right spot on the input dictionary while not necessarily the exact rule. For example, if you passed an instruction for a qasm experiment missing a qubits field it says the name field was invalid for that instruction. While not exact this is a much closer hint for debugging. The error messages are adjusted to use the best match path to point to the element (not the specific field) that failed validation, and then print the error message from best match and saying it's possibly the error. This should make it easier to find errors in qobj dictionary that isn't passing schema validation. It's not always perfect, but something more targetted should hopefully be better than everything, which is the status quo. A follow-up here will be to try and rewrite the schema to not rely on so many high level oneOf rules. This will likely result in relaxing the schema slightly so validation won't catch high level errors (like sending both a qasm configuration and a pulse configuration at the same time). But, those errors should be next to non-existent and are not really the class of errors we should be trying to validate with schema anyway. Especially for qiskit where the qobj dictionaries are being generated and we don't have to worry about such cases. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../jsonschema/schema_validation.py | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/qiskit/validation/jsonschema/schema_validation.py b/qiskit/validation/jsonschema/schema_validation.py index 35024d9403fc..c0d5cb5bf6ec 100644 --- a/qiskit/validation/jsonschema/schema_validation.py +++ b/qiskit/validation/jsonschema/schema_validation.py @@ -131,21 +131,38 @@ def validate_json_against_schema(json_dict, schema, SchemaValidationError: Raised if validation fails. """ - try: - if isinstance(schema, str): - schema_name = schema - schema = _SCHEMAS[schema_name] - validator = _get_validator(schema_name) - validator.validate(json_dict) - else: + if isinstance(schema, str): + schema_name = schema + schema = _SCHEMAS[schema_name] + validator = _get_validator(schema_name) + errors = list(validator.iter_errors(json_dict)) + if errors: + best_match_error = jsonschema.exceptions.best_match(errors) + failure_path = list(best_match_error.absolute_path) + if len(failure_path) > 1: + failure_path = failure_path[:-1] + error_path = "" + for component in failure_path: + if isinstance(component, int): + error_path += "[%s]" % component + else: + error_path += "['%s']" % component + if failure_path: + err_message = "Validation failed. Possibly at %s" % error_path + err_message += " because of %s" % best_match_error.message + else: + err_message = "Validation failed. " + err_message += "Possibly because %s" % best_match_error.message + raise SchemaValidationError(err_message) + else: + try: jsonschema.validate(json_dict, schema) - except jsonschema.ValidationError as err: - if err_msg is None: - err_msg = "JSON failed validation. Set Qiskit log level to DEBUG " \ - "for further information." - newerr = SchemaValidationError(err_msg) - newerr.__cause__ = _SummaryValidationError(err) - logger.debug('%s', _format_causes(err)) + except jsonschema.ValidationError as err: + if err_msg is None: + err_msg = ("JSON failed validation. Set Qiskit log level to " + "DEBUG for further information.") + newerr = SchemaValidationError(err_msg) + newerr.__cause__ = _SummaryValidationError(err) raise newerr From 813219fffcca1cee89b7b0efd385cc624f56dd55 Mon Sep 17 00:00:00 2001 From: itoko Date: Wed, 19 Feb 2020 21:39:02 +0900 Subject: [PATCH 04/12] speed up schedulers (#3795) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit/scheduler/methods/basic.py | 62 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/qiskit/scheduler/methods/basic.py b/qiskit/scheduler/methods/basic.py index fa5f1317984f..50cc99e9b089 100644 --- a/qiskit/scheduler/methods/basic.py +++ b/qiskit/scheduler/methods/basic.py @@ -55,8 +55,6 @@ def as_soon_as_possible(circuit: QuantumCircuit, A schedule corresponding to the input ``circuit`` with pulses occurring as early as possible. """ - sched = Schedule(name=circuit.name) - qubit_time_available = defaultdict(int) def update_times(inst_qubits: List[int], time: int = 0) -> None: @@ -64,15 +62,20 @@ def update_times(inst_qubits: List[int], time: int = 0) -> None: for q in inst_qubits: qubit_time_available[q] = time + start_times = [] circ_pulse_defs = translate_gates_to_pulse_defs(circuit, schedule_config) for circ_pulse_def in circ_pulse_defs: - time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) - if isinstance(circ_pulse_def.schedule, Barrier): - update_times(circ_pulse_def.qubits, time) - else: - sched = sched.insert(time, circ_pulse_def.schedule) - update_times(circ_pulse_def.qubits, time + circ_pulse_def.schedule.duration) - return sched + start_time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) + stop_time = start_time + if not isinstance(circ_pulse_def.schedule, Barrier): + stop_time += circ_pulse_def.schedule.duration + + start_times.append(start_time) + update_times(circ_pulse_def.qubits, stop_time) + + timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier)] + return Schedule(*timed_schedules, name=circuit.name) def as_late_as_possible(circuit: QuantumCircuit, @@ -97,36 +100,29 @@ def as_late_as_possible(circuit: QuantumCircuit, A schedule corresponding to the input ``circuit`` with pulses occurring as late as possible. """ - sched = Schedule(name=circuit.name) - # Align channel end times. - circuit.barrier() - # We schedule in reverse order to get ALAP behaviour. We need to know how far out from t=0 any - # qubit will become occupied. We add positive shifts to these times as we go along. - # The time is initialized to 0 because all qubits are involved in the final barrier. - qubit_available_until = defaultdict(lambda: 0) - - def update_times(inst_qubits: List[int], shift: int = 0, inst_start_time: int = 0) -> None: + qubit_time_available = defaultdict(int) + + def update_times(inst_qubits: List[int], time: int = 0) -> None: """Update the time tracker for all inst_qubits to the given time.""" for q in inst_qubits: - qubit_available_until[q] = inst_start_time - for q in qubit_available_until.keys(): - if q not in inst_qubits: - # Uninvolved qubits might be free for the duration of the new instruction - qubit_available_until[q] += shift + qubit_time_available[q] = time + rev_stop_times = [] circ_pulse_defs = translate_gates_to_pulse_defs(circuit, schedule_config) for circ_pulse_def in reversed(circ_pulse_defs): - inst_sched = circ_pulse_def.schedule - # The new instruction should end when one of its qubits becomes occupied - inst_start_time = (min([qubit_available_until[q] for q in circ_pulse_def.qubits]) - - getattr(inst_sched, 'duration', 0)) # Barrier has no duration - # We have to translate qubit times forward when the inst_start_time is negative - shift_amount = max(0, -inst_start_time) - inst_start_time = max(inst_start_time, 0) + start_time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) + stop_time = start_time if not isinstance(circ_pulse_def.schedule, Barrier): - sched = inst_sched.shift(inst_start_time).insert(shift_amount, sched, name=sched.name) - update_times(circ_pulse_def.qubits, shift_amount, inst_start_time) - return sched + stop_time += circ_pulse_def.schedule.duration + + rev_stop_times.append(stop_time) + update_times(circ_pulse_def.qubits, stop_time) + + last_stop = max(t for t in qubit_time_available.values()) + start_times = [last_stop - t for t in reversed(rev_stop_times)] + timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier)] + return Schedule(*timed_schedules, name=circuit.name) def translate_gates_to_pulse_defs(circuit: QuantumCircuit, From 016f7a167ccba3de2145f63eddb52b695660624c Mon Sep 17 00:00:00 2001 From: ewinston Date: Wed, 19 Feb 2020 09:30:30 -0500 Subject: [PATCH 05/12] add ability to specify open controls. (#3739) * add ability specify open controls. This PR adds the ability to control on '0' qubit states. * add release notes * expected blank space * allow string control state specification. * linting * linting * remove f-string * add ctrl_state keyword to standard extensions * fix comments. add ctrl_state attribute to ControlledGate. * add ctrl_state and test of open controlled unitary matrix generation * minor bug fixes * linting * linting Co-authored-by: Ali Javadi-Abhari --- qiskit/circuit/__init__.py | 1 + qiskit/circuit/add_control.py | 72 +++++++---- qiskit/circuit/controlledgate.py | 81 +++++++++++- qiskit/circuit/gate.py | 14 +-- qiskit/extensions/standard/h.py | 12 +- qiskit/extensions/standard/rx.py | 12 +- qiskit/extensions/standard/ry.py | 12 +- qiskit/extensions/standard/rz.py | 12 +- qiskit/extensions/standard/swap.py | 12 +- qiskit/extensions/standard/u1.py | 12 +- qiskit/extensions/standard/u3.py | 12 +- qiskit/extensions/standard/x.py | 28 +++-- qiskit/extensions/standard/y.py | 12 +- qiskit/extensions/standard/z.py | 12 +- qiskit/extensions/unitary.py | 79 ++++++++---- .../add-open-controls-bb21111b866ada43.yaml | 19 +++ test/python/circuit/test_controlled_gate.py | 118 ++++++++++++++---- .../pickles/TestsBasicSwap_a_cx_to_map.pickle | Bin 1518 -> 1538 bytes .../TestsBasicSwap_handle_measurement.pickle | Bin 1902 -> 1930 bytes .../TestsBasicSwap_initial_layout.pickle | Bin 1759 -> 1779 bytes .../TestsLookaheadSwap_a_cx_to_map.pickle | Bin 2143 -> 2163 bytes ...stsLookaheadSwap_handle_measurement.pickle | Bin 3336 -> 3367 bytes .../TestsLookaheadSwap_initial_layout.pickle | Bin 2843 -> 2863 bytes .../TestsStochasticSwap_a_cx_to_map.pickle | Bin 1348 -> 1368 bytes ...tsStochasticSwap_handle_measurement.pickle | Bin 1745 -> 1773 bytes .../TestsStochasticSwap_initial_layout.pickle | Bin 1549 -> 1569 bytes 26 files changed, 383 insertions(+), 137 deletions(-) create mode 100644 releasenotes/notes/add-open-controls-bb21111b866ada43.yaml diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index 6003ffbd20dd..f23a7dfed9b3 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -59,6 +59,7 @@ from .classicalregister import ClassicalRegister, Clbit from .quantumregister import QuantumRegister, Qubit from .gate import Gate +# pylint: disable=cyclic-import from .controlledgate import ControlledGate from .instruction import Instruction from .instructionset import InstructionSet diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index ae85a063b62e..df74771de7da 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -14,22 +14,39 @@ """ Add control to operation if supported. """ -from qiskit import QiskitError +from typing import Union, Optional + +from qiskit.circuit.exceptions import CircuitError from qiskit.extensions import UnitaryGate +from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit + + +def add_control(operation: Union[Gate, ControlledGate], + num_ctrl_qubits: int, + label: Union[str, None], + ctrl_state: Union[int, str, None]) -> ControlledGate: + """For standard gates, if the controlled version already exists in the + library, it will be returned (e.g. XGate.control() = CnotGate(). + For more generic gates, this method implements the controlled + version by first decomposing into the ['u1', 'u3', 'cx'] basis, then + controlling each gate in the decomposition. -def add_control(operation, num_ctrl_qubits, label): - """Add num_ctrl_qubits controls to operation + Open controls are implemented by conjugating the control line with + X gates. Adds num_ctrl_qubits controls to operation. Args: - operation (Gate or ControlledGate): operation to add control to. - num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + operation: Operation for which control will be added. + num_ctrl_qubits: The number of controls to add to gate (default=1). + label: Optional gate label. + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + Controlled version of gate. + """ import qiskit.extensions.standard as standard if isinstance(operation, standard.RZGate) or operation.name == 'rz': @@ -42,29 +59,34 @@ def add_control(operation, num_ctrl_qubits, label): if isinstance(operation, UnitaryGate): # attempt decomposition operation._define() - return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label) + return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) -def control(operation, num_ctrl_qubits=1, label=None): +def control(operation: Union[Gate, ControlledGate], + num_ctrl_qubits: Optional[int] = 1, + label: Optional[Union[None, str]] = None, + ctrl_state: Optional[Union[None, int, str]] = None) -> ControlledGate: """Return controlled version of gate using controlled rotations Args: - operation (Gate or Controlledgate): gate to create ControlledGate from - num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + operation: gate to create ControlledGate from + num_ctrl_qubits: number of controls to add to gate (default=1) + label: optional gate label + ctrl_state: The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. + Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + Controlled version of gate. Raises: - QiskitError: gate contains non-gate in definitionl + CircuitError: gate contains non-gate in definition """ from math import pi # pylint: disable=cyclic-import import qiskit.circuit.controlledgate as controlledgate - from qiskit.circuit.quantumregister import QuantumRegister - from qiskit.circuit.quantumcircuit import QuantumCircuit # pylint: disable=unused-import import qiskit.extensions.standard.multi_control_rotation_gates import qiskit.extensions.standard.multi_control_toffoli_gate @@ -73,7 +95,6 @@ def control(operation, num_ctrl_qubits=1, label=None): q_control = QuantumRegister(num_ctrl_qubits, name='control') q_target = QuantumRegister(operation.num_qubits, name='target') q_ancillae = None # TODO: add - qc = QuantumCircuit(q_control, q_target) if operation.name == 'x' or ( @@ -122,7 +143,7 @@ def control(operation, num_ctrl_qubits=1, label=None): None, mode='noancilla') else: - raise QiskitError('gate contains non-controllable instructions') + raise CircuitError('gate contains non-controllable instructions') instr = qc.to_instruction() if isinstance(operation, controlledgate.ControlledGate): new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits @@ -146,20 +167,19 @@ def control(operation, num_ctrl_qubits=1, label=None): operation.params, label=label, num_ctrl_qubits=new_num_ctrl_qubits, - definition=instr.definition) + definition=instr.definition, + ctrl_state=ctrl_state) cgate.base_gate = base_gate return cgate def _gate_to_circuit(operation): - from qiskit.circuit.quantumcircuit import QuantumCircuit - from qiskit.circuit.quantumregister import QuantumRegister qr = QuantumRegister(operation.num_qubits) qc = QuantumCircuit(qr, name=operation.name) if hasattr(operation, 'definition') and operation.definition: for rule in operation.definition: if rule[0].name in {'id', 'barrier', 'measure', 'snapshot'}: - raise QiskitError('Cannot make controlled gate with {} instruction'.format( + raise CircuitError('Cannot make controlled gate with {} instruction'.format( rule[0].name)) qc.append(rule[0], qargs=[qr[bit.index] for bit in rule[1]], cargs=[]) else: diff --git a/qiskit/circuit/controlledgate.py b/qiskit/circuit/controlledgate.py index 5d67261d1b6d..6f97eef1870b 100644 --- a/qiskit/circuit/controlledgate.py +++ b/qiskit/circuit/controlledgate.py @@ -15,17 +15,21 @@ """ Controlled unitary gate. """ - from qiskit.circuit.exceptions import CircuitError from .gate import Gate +from . import QuantumRegister class ControlledGate(Gate): """Controlled unitary gate.""" def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, - definition=None): - """Create a new gate. + definition=None, ctrl_state=None): + """Create a controlled gate. + + Attributes: + num_ctrl_qubits (int): The number of control qubits. + ctrl_state (int): The control state in decimal notation. Args: name (str): The Qobj name of the gate. @@ -34,8 +38,13 @@ def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, label (str or None): An optional label for the gate [Default: None] num_ctrl_qubits (int): Number of control qubits. definition (list): list of gate rules for implementing this gate. + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. Raises: CircuitError: num_ctrl_qubits >= num_qubits + CircuitError: ctrl_state < 0 or ctrl_state > 2**num_ctrl_qubits. """ super().__init__(name, num_qubits, params, label=label) if num_ctrl_qubits < num_qubits: @@ -50,14 +59,76 @@ def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, self.base_gate = base_gate.base_gate else: self.base_gate = base_gate + self._ctrl_state = None + self.ctrl_state = ctrl_state + + @property + def definition(self): + """Return definition in terms of other basic gates. If the gate has + open controls, as determined from `self.ctrl_state`, the returned + definition is conjugated with X.""" + if not self._definition: + self._define() + # pylint: disable=cyclic-import + from qiskit.extensions.standard import XGate, CnotGate + bit_ctrl_state = bin(self.ctrl_state)[2:].zfill(self.num_ctrl_qubits) + # hacky way to get register assuming single register + if self._definition: + qreg = self._definition[0][1][0].register + elif isinstance(self, CnotGate): + qreg = QuantumRegister(self.num_qubits, 'q') + self._definition = [(self, [qreg[0], qreg[1]], [])] + open_rules = [] + for qind, val in enumerate(bit_ctrl_state[::-1]): + if val == '0': + open_rules.append([XGate(), [qreg[qind]], []]) + return open_rules + self._definition + open_rules + + @definition.setter + def definition(self, excited_def): + """Set controlled gate definition with closed controls.""" + super(Gate, self.__class__).definition.fset(self, excited_def) + + @property + def ctrl_state(self): + """Return the control state of the gate as a decimal integer.""" + return self._ctrl_state + + @ctrl_state.setter + def ctrl_state(self, ctrl_state): + """Set the control state of this gate. + + Args: + ctrl_state (int or str or None): The control state of the gate. + + Raises: + CircuitError: ctrl_state is invalid. + """ + if isinstance(ctrl_state, str): + try: + assert len(ctrl_state) == self.num_ctrl_qubits + ctrl_state = int(ctrl_state, 2) + except ValueError: + raise CircuitError('invalid control bit string: ' + ctrl_state) + except AssertionError: + raise CircuitError('invalid control bit string: length != ' + 'num_ctrl_qubits') + if isinstance(ctrl_state, int): + if 0 <= ctrl_state < 2**self.num_ctrl_qubits: + self._ctrl_state = ctrl_state + else: + raise CircuitError('invalid control state specification') + elif ctrl_state is None: + self._ctrl_state = 2**self.num_ctrl_qubits - 1 + else: + raise CircuitError('invalid control state specification') def __eq__(self, other): if not isinstance(other, ControlledGate): return False else: return (other.num_ctrl_qubits == self.num_ctrl_qubits and - self.base_gate == other.base_gate and - super().__eq__(other)) + self.base_gate == other.base_gate) def inverse(self): """Invert this gate by calling inverse on the base gate.""" diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index a8095fded48c..618435474f23 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -106,24 +106,24 @@ def label(self, name): else: raise TypeError('label expects a string or None') - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return controlled version of gate Args: num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + label (str or None): optional gate label + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If None, use 2**num_ctrl_qubits-1. Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + ControlledGate: controlled version of gate. Raises: - QiskitError: unrecognized mode + QiskitError: unrecognized mode or invalid ctrl_state """ # pylint: disable=cyclic-import from .add_control import add_control - return add_control(self, num_ctrl_qubits, label) + return add_control(self, num_ctrl_qubits, label, ctrl_state) @staticmethod def _broadcast_single_argument(qarg): diff --git a/qiskit/extensions/standard/h.py b/qiskit/extensions/standard/h.py index 002bca37b813..5e3d1629235f 100644 --- a/qiskit/extensions/standard/h.py +++ b/qiskit/extensions/standard/h.py @@ -46,19 +46,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CHGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CHGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/standard/rx.py b/qiskit/extensions/standard/rx.py index c14335e654a8..49003f14c85f 100644 --- a/qiskit/extensions/standard/rx.py +++ b/qiskit/extensions/standard/rx.py @@ -46,19 +46,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CrxGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CrxGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. diff --git a/qiskit/extensions/standard/ry.py b/qiskit/extensions/standard/ry.py index f9d86892e47c..071ff385a0ea 100644 --- a/qiskit/extensions/standard/ry.py +++ b/qiskit/extensions/standard/ry.py @@ -46,19 +46,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CryGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CryGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. diff --git a/qiskit/extensions/standard/rz.py b/qiskit/extensions/standard/rz.py index f215c276a9a5..83571433811b 100644 --- a/qiskit/extensions/standard/rz.py +++ b/qiskit/extensions/standard/rz.py @@ -43,19 +43,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CrzGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CrzGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. diff --git a/qiskit/extensions/standard/swap.py b/qiskit/extensions/standard/swap.py index 1cfce87a4840..43aee0c1c4ec 100644 --- a/qiskit/extensions/standard/swap.py +++ b/qiskit/extensions/standard/swap.py @@ -46,19 +46,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return FredkinGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return FredkinGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/standard/u1.py b/qiskit/extensions/standard/u1.py index 014d24d90718..c8a506110299 100644 --- a/qiskit/extensions/standard/u1.py +++ b/qiskit/extensions/standard/u1.py @@ -42,19 +42,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return Cu1Gate(*self.params) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return Cu1Gate(*self.params) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/standard/u3.py b/qiskit/extensions/standard/u3.py index 3a4449a69b28..832b8c04d680 100644 --- a/qiskit/extensions/standard/u3.py +++ b/qiskit/extensions/standard/u3.py @@ -39,19 +39,23 @@ def inverse(self): """ return U3Gate(-self.params[0], -self.params[2], -self.params[1]) - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return Cu3Gate(*self.params) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return Cu3Gate(*self.params) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def to_matrix(self): """Return a Numpy.array for the U3 gate.""" diff --git a/qiskit/extensions/standard/x.py b/qiskit/extensions/standard/x.py index 93457b083fb3..83f7de4642d0 100644 --- a/qiskit/extensions/standard/x.py +++ b/qiskit/extensions/standard/x.py @@ -47,21 +47,25 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CnotGate() - elif num_ctrl_qubits == 2: - return ToffoliGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CnotGate() + elif num_ctrl_qubits == 2: + return ToffoliGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" @@ -114,19 +118,23 @@ def __init__(self): super().__init__("cx", 2, [], num_ctrl_qubits=1) self.base_gate = XGate() - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return ToffoliGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return ToffoliGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/standard/y.py b/qiskit/extensions/standard/y.py index 25f85bd34586..8a66c1957808 100644 --- a/qiskit/extensions/standard/y.py +++ b/qiskit/extensions/standard/y.py @@ -42,19 +42,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CyGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CyGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/standard/z.py b/qiskit/extensions/standard/z.py index 2c970b8781f5..ff1dbb3d0cde 100644 --- a/qiskit/extensions/standard/z.py +++ b/qiskit/extensions/standard/z.py @@ -42,19 +42,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CzGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CzGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" diff --git a/qiskit/extensions/unitary.py b/qiskit/extensions/unitary.py index a5247c57a6ed..c2b0c6f88586 100644 --- a/qiskit/extensions/unitary.py +++ b/qiskit/extensions/unitary.py @@ -22,6 +22,7 @@ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister +from qiskit.exceptions import QiskitError from qiskit.extensions.standard import U3Gate from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.quantum_info.operators.predicates import is_unitary_matrix @@ -111,44 +112,24 @@ def _define(self): raise NotImplementedError("Not able to generate a subcircuit for " "a {}-qubit unitary".format(self.num_qubits)) - def control(self, num_ctrl_qubits=1, label=None): - """Return controlled version of gate + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + r"""Return controlled version of gate Args: num_ctrl_qubits (int): number of controls to add to gate (default=1) label (str): optional gate label + ctrl_state (int or str or None): The control state in decimal or as a + bit string (e.g. '1011'). If None, use 2**num_ctrl_qubits-1. Returns: UnitaryGate: controlled version of gate. Raises: - QiskitError: unrecognized mode + QiskitError: invalid ctrl_state """ - cmat = self._compute_control_matrix(self.to_matrix(), num_ctrl_qubits) + cmat = _compute_control_matrix(self.to_matrix(), num_ctrl_qubits) return UnitaryGate(cmat, label=label) - def _compute_control_matrix(self, base_mat, num_ctrl_qubits): - """ - Compute the controlled version of the input matrix with qiskit ordering. - - Args: - base_mat (ndarray): unitary to be controlled - num_ctrl_qubits (int): number of controls for new unitary - - Returns: - ndarray: controlled version of base matrix. - """ - num_target = int(numpy.log2(base_mat.shape[0])) - ctrl_dim = 2**num_ctrl_qubits - ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim-1]) - full_mat_dim = ctrl_dim * base_mat.shape[0] - full_mat = numpy.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) - ctrl_proj = numpy.diag(numpy.roll(ctrl_grnd, ctrl_dim - 1)) - full_mat = (numpy.kron(numpy.eye(2**num_target), - numpy.eye(ctrl_dim) - ctrl_proj) - + numpy.kron(base_mat, ctrl_proj)) - return full_mat - def qasm(self): """ The qasm for a custom unitary gate This is achieved by adding a custom gate that corresponds to the definition @@ -199,6 +180,52 @@ def qasm(self): return self._qasm_definition + self._qasmif(self._qasm_name) +def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None): + r""" + Compute the controlled version of the input matrix with qiskit ordering. + This function computes the controlled unitary with :math:`n` control qubits + and :math:`m` target qubits, + + .. math:: + + V_n^j(U_{2^m}) = (U_{2^m} \otimes |j\rangle\!\langle j|) + + (I_{2^m} \otimes (I_{2^n} - |j\rangle\!\langle j|)). + + where :math:`|j\rangle \in \mathcal{H}^{2^n}` is the control state. + + Args: + base_mat (ndarray): unitary to be controlled + num_ctrl_qubits (int): number of controls for new unitary + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If None, use 2**num_ctrl_qubits-1. + + Returns: + ndarray: controlled version of base matrix. + + Raises: + QiskitError: unrecognized mode or invalid ctrl_state + """ + num_target = int(numpy.log2(base_mat.shape[0])) + ctrl_dim = 2**num_ctrl_qubits + ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim-1]) + if ctrl_state is None: + ctrl_state = ctrl_dim - 1 + elif isinstance(ctrl_state, str): + ctrl_state = int(ctrl_state, 2) + if isinstance(ctrl_state, int): + if not 0 <= ctrl_state < ctrl_dim: + raise QiskitError('Invalid control state value specified.') + else: + raise QiskitError('Invalid control state type specified.') + full_mat_dim = ctrl_dim * base_mat.shape[0] + full_mat = numpy.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) + ctrl_proj = numpy.diag(numpy.roll(ctrl_grnd, ctrl_state)) + full_mat = (numpy.kron(numpy.eye(2**num_target), + numpy.eye(ctrl_dim) - ctrl_proj) + + numpy.kron(base_mat, ctrl_proj)) + return full_mat + + def unitary(self, obj, qubits, label=None): """Apply u2 to q.""" if isinstance(qubits, QuantumRegister): diff --git a/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml b/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml new file mode 100644 index 000000000000..8efb4bde8f38 --- /dev/null +++ b/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml @@ -0,0 +1,19 @@ +--- +features: + - | + Add ability to specify control conditioned on a qubit being in the + ground state. The state of the control qubits is represented by a + decimal integer. For example: + + from qiskit import QuantumCircuit + from qiskit.extensions.standard import XGate + + qc = QuantumCircuit(4) + cgate = XGate().control(3, ctrl_state=6) + qc.append(cgate, [0, 1, 2, 3]) + + Creates a four qubit gate where the fourth qubit gets flipped if + the first qubit is in the ground state and the second and third + qubits are in the excited state. If ctrl_state is None, the + default, control is conditioned on all control qubits being + excited. diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index d0ad34219a07..c1bfb7d63e83 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -19,13 +19,15 @@ from inspect import signature import numpy as np from numpy import pi -import scipy from ddt import ddt, data from qiskit import QuantumRegister, QuantumCircuit, execute, BasicAer, QiskitError from qiskit.test import QiskitTestCase from qiskit.circuit import ControlledGate +from qiskit.circuit.exceptions import CircuitError from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix +from qiskit.quantum_info.random import random_unitary +from qiskit.quantum_info.states import Statevector import qiskit.circuit.add_control as ac from qiskit.transpiler.passes import Unroller from qiskit.converters.circuit_to_dag import circuit_to_dag @@ -37,7 +39,7 @@ RYGate, CryGate, CrxGate, FredkinGate, U3Gate, CHGate, CrzGate, Cu3Gate, MSGate, Barrier, RCCXGate, RCCCXGate) -from qiskit.extensions.unitary import UnitaryGate +from qiskit.extensions.unitary import _compute_control_matrix import qiskit.extensions.standard as allGates @@ -156,8 +158,8 @@ def test_single_controlled_composite_gate(self): num_target = cgate.width() gate = cgate.to_gate() cont_gate = gate.control(num_ctrl_qubits=num_ctrl) - control = QuantumRegister(num_ctrl) - target = QuantumRegister(num_target) + control = QuantumRegister(num_ctrl, 'control') + target = QuantumRegister(num_target, 'target') qc = QuantumCircuit(control, target) qc.append(cont_gate, control[:]+target[:]) simulator = BasicAer.get_backend('unitary_simulator') @@ -400,13 +402,52 @@ def test_controlled_unitary(self, num_ctrl_qubits): def test_controlled_random_unitary(self, num_ctrl_qubits): """test controlled unitary""" num_target = 2 - base_gate = UnitaryGate(scipy.stats.unitary_group.rvs(num_target)) + base_gate = random_unitary(2**num_target).to_instruction() base_mat = base_gate.to_matrix() cgate = base_gate.control(num_ctrl_qubits) test_op = Operator(cgate) cop_mat = _compute_control_matrix(base_mat, num_ctrl_qubits) self.assertTrue(matrix_equal(cop_mat, test_op.data, ignore_phase=True)) + @data(1, 2, 3) + def test_open_controlled_unitary_matrix(self, num_ctrl_qubits): + """test open controlled unitary matrix""" + # verify truth table + num_target_qubits = 2 + num_qubits = num_ctrl_qubits + num_target_qubits + target_op = Operator(XGate()) + for i in range(num_target_qubits - 1): + target_op = target_op.tensor(XGate()) + print('') + for i in range(2**num_qubits): + input_bitstring = bin(i)[2:].zfill(num_qubits) + input_target = input_bitstring[0:num_target_qubits] + input_ctrl = input_bitstring[-num_ctrl_qubits:] + phi = Statevector.from_label(input_bitstring) + cop = Operator(_compute_control_matrix(target_op.data, + num_ctrl_qubits, + ctrl_state=input_ctrl)) + for j in range(2**num_qubits): + output_bitstring = bin(j)[2:].zfill(num_qubits) + output_target = output_bitstring[0:num_target_qubits] + output_ctrl = output_bitstring[-num_ctrl_qubits:] + psi = Statevector.from_label(output_bitstring) + cxout = np.dot(phi.data, psi.evolve(cop).data) + if input_ctrl == output_ctrl: + # flip the target bits + cond_output = ''.join([str(int(not int(a))) for a in input_target]) + else: + cond_output = input_target + if cxout == 1: + self.assertTrue( + (output_ctrl == input_ctrl) and + (output_target == cond_output)) + else: + self.assertTrue(( + (output_ctrl == input_ctrl) and + (output_target != cond_output)) or + output_ctrl != input_ctrl) + def test_base_gate_setting(self): """ Test all gates in standard extensions which are of type ControlledGate @@ -516,28 +557,51 @@ def test_relative_phase_toffoli_gates(self, num_ctrl_qubits): # compare simulated matrix with the matrix representation provided by the class self.assertTrue(matrix_equal(simulated_mat, repr_mat)) - -def _compute_control_matrix(base_mat, num_ctrl_qubits): - """ - Compute the controlled version of the input matrix with qiskit ordering. - - Args: - base_mat (ndarray): unitary to be controlled - num_ctrl_qubits (int): number of controls for new unitary - - Returns: - ndarray: controlled version of base matrix. - """ - num_target = int(np.log2(base_mat.shape[0])) - ctrl_dim = 2**num_ctrl_qubits - ctrl_grnd = np.repeat([[1], [0]], [1, ctrl_dim-1]) - full_mat_dim = ctrl_dim * base_mat.shape[0] - full_mat = np.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) - ctrl_proj = np.diag(np.roll(ctrl_grnd, ctrl_dim - 1)) - full_mat = (np.kron(np.eye(2**num_target), - np.eye(ctrl_dim) - ctrl_proj) - + np.kron(base_mat, ctrl_proj)) - return full_mat + def test_open_controlled_gate(self): + """ + Test controlled gates with control on '0' + """ + base_gate = XGate() + base_mat = base_gate.to_matrix() + num_ctrl_qubits = 3 + + ctrl_state = 5 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = None + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = 0 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = 7 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = '110' + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + def test_open_controlled_gate_raises(self): + """ + Test controlled gates with open controls raises if ctrl_state isn't allowed. + """ + base_gate = XGate() + num_ctrl_qubits = 3 + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state=-1) + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state=2**num_ctrl_qubits) + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state='201') if __name__ == '__main__': diff --git a/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle b/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle index 2dc1af8cf87721d5df256e54ba6b750397c03828..bf9d7480c7c84845d2713b757dd75742b51788ea 100644 GIT binary patch delta 660 zcmZWnTW<_u7@pb5qSi5lgr@0+i;)NyN{c!xWoi^H<1ohet>r`40~R!cM?tp>}qAd;t*H$vDYz7H$9tfc@}j97@^z6Sj8&9QdBhc&#V`<_F+0cKp9AY=9zvo3X0fpVT5t5lm~6q{O0thC5E&x%!eh9%Q-}$^nSXhBg@A4flLEn{K*= z3S-OIHyT{Ct!>6mQ)_PP2Lq{={LbX;YVtSpXdn`AjS>6TWR6lTR27Y%j9-yTFIuXE MyE}0Wb-CZeQ|eKo(M<|JS8RV&MRmPFmcLBle$?7jX~ zzIW1DaRUADi^jWq-}%0G=VwAh>FFqIh0!p~`Hfs8S>7*yonyuCVzKI=)?7thq2YR) zYPDJ-`VuqVUj8JJ^kUiVOOfV7*_TO%P2Of+==MY=u-V5J*ZPv9OeSeGNYYlCF;5J8 zt&y{s46;KalMc4BlWhvyU2lsv5Q{*@a5~tX+Iq>ZI6c%Bq)q4Z9VTc_4IKMEeId?j&a|` zY^P_$igSV=g@^mu!vhKrRvxtY^(H*TP8zmp50_3JacO~?oWrAIJVsj0k1ISeVbhMa zq$im~tS@3c(LU1ZcuHZh+C8O+OcINYf}3u@%0vsE6Lcn{2a0|8Pnx z)iem^2MVE`h7djCAt08%bg4_){*uD77P8i_7QDttA+nejyv9x`oVM6n!HY3s(uL?N zBtw#Whyg_*3Dp SG$^j2SezZ?8UAKnxBdY#8E-KF literal 1902 zcma)7=bzg|5Vd`&x`Z_P5ki3DlmkrfodXgOF=>D(ido5<8xdK)XGz2fEL>=&h2GoW z%Ir#OoiC6dUu0``-Y#DpJemqE z*Wb`+G*Z!*nDKY>UlM6Ak=?!&SuvD-ndaEwZ{&q;Pvim{18j0_E;-6&n#F@OYiBv* z#IV;MIZNpvKPYnPU^6G#qOjHVH)(=IgffBC@n;R4JQb&L&S=bbXvS}GFdAW-@9>|r z=yy?sah_qj*P4W)#Dp&M8vbU@u_A_|pJ7LUo&HvhAEdpKa#zRSQSn6iFtKF2y{6e1 zicEHwStRycx){H#Hko~dt7yB|B-TT$;cD8|DZ(NQaLuGe>&7vZ z%W;;=A*rLL%Ha1g)lEVvL zgP-#ySh)av)at)7;@fd06KCbY@1Wnu4f}Ug$v6pBM zX=gm8@N~U%$`eZyPdww2Ynt@z7|)TGcTnL_4VzN+e5L9I3-ZQz(WpAC(5XRFs$QB_ zbwuH4y>m*{v5BhVtcm8h>=R?WY};#HS6u?m;qo$Y@CvJbRl(OT(+9BHfY++AHT5xg zUCWr|lMvtyn&Zj6xt=#lM3%*oNUY(VC4I0R1k8U@q00vQKbVT8J_T%L+W4GNz5LW3j^9Hn!I2VvN(I8<8mVIT1}2pVq|> z$mQZ!NHPKjF;vJdLgY1~Fh+lemKkxzAVvyjFG8HF5%1DG=Xj4|Mkr|xF}SH2-Vg8r z=}Tc%;k;(pZyWKUYtHsZ3Loq8R;%NyZ-Y-1KD9A>2aFXyvoU)Ie6H|?jg_qtCUs;| ze;ME_(v!HL@U>1onZY-%>Fl=(-)Z=TDz)zR_X*DmPU~4HszK9HI#u7Y(YA3a&CP8MKXNY0}gpFJW_x%PGsQ zv3bUol~btl~mc+7}q5c4NC1vtv3d>T2z!;Z&Fnxw-U*1#+`p9qOb>w zyHpZ|Lvy0Imnd9@_n)FRP~4}oD14e1#e<%2t&^_N@#_KVHi*Wg&IaS5ve=Q1;*mjH zg*O=ss=>x;UkQ86Xr)XIN5!=nNSPXrVjV`1ven_ZlDa1oPjZJgB2vrfGM=d1f7h^R zBm-Y!JXQMcT5h$NXN>15OBch87b!~@(__3$S(}I`1CCO^jv>>-r!I*!%-R-%}m=IjweqiUwgOChYliAi-;M^>fOI9cxtA@USX^%HtxKu zmhz18JkzqJykKyqWlMRD?4TAbr3Qm>t+b#{u@J2*@m@^z0k zI&dx8BoiK^WYK2YBIhB7Z&8M}(l$FoeGZOTGFHzr&KY$nUiRQN za6T@0fe~H&uVG`k;dIPC!?>h5y6joT<&K6|l0W1sx>&l)Ai3J1qDS5 zPCK|s1CjWej9co{hAqbJj)r%VhIbkF)YEF`YwP=r2S(HU=5l@m5B2XHMfHQ1>>dBG+#TtV!Sq5vQsy{rxf4w#=+aT#ysO)M~(MM zjRnRB9iKNf_DSYP#-b7R9_{vt@!4qRo}7MGRk!Aj_@Q4bhOSrf@uitvGr0S!jC^B! cFRWHi^2-m#&%i5mt5R_%M(ZJd$yc=JAFMVyk^lez delta 875 zcmZXTNpI6o6ou^u2n0}r5QCtLERZk=!G=G;iiM>tSXqEc?P79)KzcGjT6!^Maw$`( zTV~2U6ezUJ|ArsIf-Ubp$891u)_X_i9ewA%=Rf)1`T6N(y{inv@V$CaDtnCp2jjV( zzOa>Z_N!eEhjxtOFvGQIZ;|pv$~dO7w{V13+a|5C4O;7>M7_nPPwQOtTeM!C@EE>D z8)=hT4=|2el%XxO)y`0#i@+jJyN)r+;u@fWZL0Hfk{HJ=3KOxX=?6+L*oOL&Ec;!Y zh;Wj!%35KZlHgn$#T1m&3Pm^-OH~IBGLWLw81*ichDC$g)nuFzS1myawRo1%O2tkT zPk~fj4AC}4G0Zq8rtNAy!eGhYnN~e-X-~+wAfAzSm0_)pGA^d-V!WDz7a_c)8(wBy z`LE&CrG}Bl9AjLQ92xdF<9b)a8_9*-WZV+Zc$+{*eVZ|nYBK(@WY!AB3)MDqaYu7b zGGg&b!zspeSHrtW!+VVT;+bma%VZuf9;TY)PcG$0coaCx)k8upDA)1W#S@J`!+0w3 z`xYO|vxNUS*Afpo@Ko6!v2t8e`L&weYOMp z6XSELNvu?vXDnzf{+lhBU*gRx_w4$r^lyys&I;9|)bRXDfFF#Xb>GQ~Qt^hW%?5s{ HKV$blxp^mY diff --git a/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle b/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle index 8b3dc7f56807c521e3a55e78ae128997fef9f9c6..b4a70c461dfb677a1cdfbea85571144352acbd32 100644 GIT binary patch literal 3367 zcma)9cbwEz5WYeUAibBPDU0Q-Aog+!=*meG7B!yPy>MCB+v7`iC<%v2_lZX(Nnk+ss9 zY<+NRjPY-){HTGF!E;bbD2v3Jl$4aDt$G{fu@3yxvC_3EyQbc@ysT^2+vyCN#F}!T zrq;?>XlkNaMCY;_Gj`fbHl)2uFT?9Bx3;p;s7g0vMpzl!Kyyygg3vM&YbG0{thk+m zQ5|cgwzDTE)5#34MyqP4JXXeFq7kiQ2LDKB>uZpWCo>+}M9cC}BBh}1q9w8Bvd1*b zwdy^zHPJ5CQr0)5YyBylo^ zcDY;Z(?BoI(3{XF5^KRb>nJL;y#HRVdOucghrYZ~KSKWq*I7gY161F=eKbJ%rD`N6Ciqx_jOup8&zov??={(HR4Tg{S60f-1s$UF3?=L> zxy9@-!als(8Q`!xhdu2ZQJnn<`wM4SAWK{xKsZoi;%%s69z-};qj@K6(_+RD6NhYR zB9XE@FPX4Xx=luv46A`dIYkxWFy8F{tO<50^zLw8Kb$Z^_b!2v+SCydM|MAw5EZGz z15<(>MHrV zThokjeDUqnvL>4_xnM`0hC)IeJ1Wzs*ion0MADZ#g)miHN)hTqmeM|_fiO)tsUk}t zxEd2nQ&fRGLPn#-O>sQiL^g+$9C?mTCp6||I<1S<&5(NYlg;5LnbXOU=h95V$pv=k ziFAtcBK;v>=`6yj+RbdjoRFK-e9q~FGlVm{$jw~BnHm!}vy_`0;Vg}Y+{~3;k%_Z& zILDC_elFoW$rU=)=ZhErRL@iU%_m%-)h!@g7*cnUukK>PCBj({sGF}=UP`!3qx$)D zIpGS8<<+rx8J~G3u2iRFA>k?^$;n$pxLQ)?&)hZsnY)&7op2TfDHp1h*As5gsHBvW zcq8E^jfGO?aI+%=cnjgyLa#z|cUv%bxBEU96Yh|$wn%o2PuvO2X5^SrNEduG~CBcvhp@&2xn3H5PKSQs!=%i5GHsQO(^;gqI~( zXzpGq%-t%LV>RJbt!@qBwUD~kBhG5YdV}z$u-33XIRo-Od5iG2#|?FbN6AyS*utd5k3~yx*(;@*e8TfHKyn8Gs5Q@3#H8A z3sr|N314khhp!8DSg+h{Abca+$jogdd>g96cYc??C;T9sjX{?-sFgnwe$uF}!_S0Y zG?o_?oWJ!ZepL=P5q=Yr)L}E>_fQ@F@Xz0$gujHdIY=p0_?z4k{<_hy^n+oylreJ4 ziXx(BQ{mN-bIUns`yXli`u5x=d~do<`6|BMX8eQP=6o%7@3v3?x7=;12!^_^m?HE* zmIPa=VC!%&7#6pU3T_t;2Gw%gs$jct&@Y>%)FzbL_DnF{4t$l8xgFK2PHI(QfZWas zkb-nkgs$qoVv5iUMRdEVVE1rPm$HWn_6!GufpdGQVDE5Ht8K^cRsVC{<`3MOj1^DW zZXf3Ax3&J3RiE_2Q50jeeBsOOcv)e%6}*Hk^i{d~@zwv=!r!mILI?2Gi~Ii|q^ubY N*^E1oA^r@n{0HSWJQDx_ literal 3336 zcma)9cbpSd5WYfPdhbV543@Kj*b66uMozl1tmh`T+y-(vzTHF)WCRqA4aBa9y(1Rv z4MedQ5W6Dw3Sw8pKJ#{8l11g`Kku?L-<$cqnR&bK=9DLp^rj{=m5HRA$nsxgt#l?^ zA3Pdk{l_ZbYoKKCT$B>ZBC#eVB_(OA-bQ(>BmZ=)bZyG6skbdJ>)Q2pI)f&$rtGMx zwK5i(nrIf$x$MS_o%WIqX|K}D@IK3}t!y-^(hZrBR>n5aoRhR5w2Z`>Nr03Uw^J~x zW35y;dvY?J%j_Wo%3|qIJyRAL(p;4YKiM#zUKES>BXLDO1~MNvyfVm}a?F zy@z%t+Q(YT{)Tj|AF@MrtX&W%k#SQxSjT9o0>&-RuBl^^=(KGyF<(h%R?>ygHR2f6 zj-p%2(HqgtX%j7Vn2*C8x;q`JvvE}{SZ=dAG0LC$F&ugF=;}BV5SyHnnr-OiKL_|kW28i zwYVvcYn#aAkagspc@klIo~F~fm|dgPo8MwiCr92%GYB&a$k5y9WTiuTGs|a5gj2MV z*@QVEC3Ahusf5!6GrLI1Ji_VPCQ3*tIfIbX){v5U(x+J_&dlK~M^5+IgmWZU=tQ3@ zI{XtoU&UKMI8U=%NH{;l?gF3Pg@lU)voK({K<&JkaEZ3+chjYW%d{=ej>*gD%r|kl zIw6Y)R|rT>-D1L(k}`kZuJX^@)r4yVvp7h(NbS6qaGkbFN;!qs6K>G9P|6%`bY%E$ zBHUc)RcPjJ31;qAU*{6SZ4ye3(NejbPU^71#Lr6M zX2LH5k~(Z5{2HpmZ~oc)o$!ZXwgf4q3V#y*(l(AMvD^~A=8#*;tM&+$&4uqp&Mjl7 z?cdD!o9(&f{A{{ScoorZQ~p72GhU0OyUmq>`|Y++fR^gHVv4{6PU19_vsKtBJ7S**)ykrR<@cJ;Tmm+T32s**omiY}@n4)W470e5<`){KU1#vRBezI#{x4Q38A AQvd(} diff --git a/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle b/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle index 90be0799bc1062ea7c0c34a7f0100a4ed5a5b47c..ce58363424ba968e8359fd86195e2d784e4dcfdb 100644 GIT binary patch literal 2863 zcma)8XP6UJ6x~7{5D*a&8&(F?Rj~_VSrs&}R5g|{PLg3?Ae-HjnSlko0=9^~g4h+Y zR}i}*_J+M;@4X<3i28lr`z9}&!1tklQqH^Y-gC~ougt7XA>&WV1a+xQJ{9seLN^zL zO~t>~nCNePG-{!0z${b~YV3Hss;a7-+vK4(-i?1|a+x6GW)oR=YI7K%UA#SmiL@KI zXz!qdy>^`@&-KH+XN@d=qa$bTMCfd*+IUBvTMU2|x1>_olt7rw1U|aNs!LERtD&w8 z$(%%sn|GUhY~Y|KtI(%uA3m7yLDnk3wOqmK<8^jw=`(Tlv- znDGNI&t`Bn3mY-hUWAS9cqh(iM$!Afr)z7+nsnHN6ZIi%Dw3-?70_4P>Sv~T*eq7` zP%GTr!4|garKI+jgsp7VTlLZOGr6?aihi-`FsVnkCTyceRlitG5!;rqoxy6uq{nc- zSXG#G`A1I&+po<$m34hTlXA00oK-JITiAii_b1q#?ElOuW@%GAfaeb+>}V9Hu#;k; ziskKWtNu#z*AaFRvICi;h!Sj9!fpl=QGyL3>~63MQ3dQ_$a$@Sm-Bi3{W?Ezb7?o9 zu2a_VVQ%1A*prj&McCVpcaYA=x=AmKeHv7chT?iC?5nujRM%LQ;+EP^b#GAICxf-m zLkRmzd0E7vgacTT>TARtXzQ4R2nS2d(8z8zM5FbDLkwzG=upC81}lkS^|C^P9SkdA z`1&;YQ`{D79DhX04rfkB5Ms76a%8tk5Js?~8lz|=D+-ei8X~%*2&0XpF@&)dlE&F; zl%_wPaHK?yDXx#SiQI)%_GwX7n$YU$#s(#723OmCX5JDqTnVMm0ME7;Aj)pQ*-lQ2u7 zsEpkygi{S>*quf=-C*VHq>-mNI74sU0^v*vlH*`D;VdaxItb3Tb<8=0b0ubWaqko~ zdLH3?gGxy`2reL8Xs}XA**gVXqX<12oFlk;xZo|B0Ov`Atl~CLU`0*vRsRztB2W%4+|YUrkO7xJT774!&1T%6+S!} z`S29sX^B}{=EF0DXALGL#hd2{&l^liE@>-Szzbe<<@3Gf<3$HAX_dl6d|fnTjnJ<{3qNqC z9|=E2&&Wi}pvm}|@QckCt(doh@T(}07FQEY2Fj zUj`G0R+RdIZ}wGu7p{)gsx_g0cqJweYQf)xe{8jasjT9N)f}-VRvU)NFj@C6R*BCV literal 2843 zcma)8XP6UJ6x~7{5D*a&8&(F?Rj~_VSrs&}R5g|{PLg3?B%9rnnSlkl3KoLBg4h+Y zR}i}*_J+M;@4X<3xatq@eUq0>;QP=&DQE87d(OG{m6^3EWcsE)lhea zK_Q(%vLZm7!h@nk3wOqmK<8^j@1|(Tlv- znDGNI&t`Bn3mY-hK7@_!co)uSo}%x6zpkwvZ_;5CPSlUEsYtHoR6u`itDl+fVY67# zL#=Rg2V2;xkCNJ361K8cU)4|3&*aixD+a`>!=!$?HDMe5R1JvL6tQgy+Zn7jOnM9t zh*gD2mw)tju>IQ1Q(4#dGbuM~#98%Xw1pkG{6K=u$^Oqf#VqZL2l4xZ2|F6aDeRJ*3wv^sy$E~T@lG-sSvToru}_2Q)llq*!oG^9O?8h|DITf)RL=&*b23!> zJdCivl$RzBCmg_%RDUDpKwHNgL^xPthDUa*VH&L`9AZ$@p+gCW8LT9R)k}wlIv7#F z$n|ORr@AfH1pbJW9nPGNAjE8C3G(>dA5XKrw;|Sv`Bu%i@ z7)^g7;Yf)ZSL~0BX<12oFlk zk}@A2B0Ov`Atl~CLU`0*(yqnPF~jV{heZw^)6AC=9+xojVHx3x3Ll<~e0YlRw8Shc z^Who7vj&rr;>~k}=M5$$m$sKI-~}%_-uVpk@uGv5w94g#mn&4h5~+NZ@S3DrUZ(PO z!W#xND&Hi$WiTlz%HJluW3Z?ht}dO@rQdb%o>sep@P38b41ZG629}3(M_v(&->n_4f1ZzZ^>l6d|mX&8l`W77JlGNKN5b5u8fJ6 zAye=(;TM|^RWWZR;a5>0Be;t2TSaqzkD9ZZ@Q1{#Dr?Rf!k-2+&1ob2WiWAQWoaJx q6kpBf-kMmgY76xxD=}qA3;rhjW2==+Wi>~v;fS_aZ5SrQWZl2yRme2} diff --git a/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle b/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle index 2cfd35193b9a3a8d9cc14859733ddd6864d49ba0..8ae086d5c19c542d2146bfd559b0dc646f3df4c2 100644 GIT binary patch delta 305 zcmZ9|O-jQ+7{>7osY+8+-03DzM2H}|5wB2fG=A&&F~*rV=3NBK(#@=Qd88vRgm7{)-&& z*~8xYD~1)qfV_x$5xG+E`tltv*6$fv$lIGo~$))*gGI0?w5 z;1`GnoHF3o8gWLxRye0^J;g;UE-T1j_@7+;k!$KRxuG2g*fU3^hR8S zi_pagzC=NAR@}PhaX-TM@a;2bO?jSYsIXa|N{i0uK8RjaN8TYemtL{uupM)l@^s39 z@}>NQgl7_;XA_TG8aq6nR;Dr-Hwi@XWd%P%k-{BzUfa71_*WgfqT&u+qN2GDg z>omkkBTgO8;@*Fv{>VAEgh%_%o^`~>D`dqD69kjaF(N^fV z{+3p&m5PzXf`5QtNu4n|UB#YB!|nq!Nkqc}Mu+6o(7W@teqbYX#4!@+u zXn-P&^9*~u&MXuqCUlqA^0#Y_4KWd;40{9Y^LJ|eI2~4$`}_XhnkUL9i6uMWwX5tV z$VKN8cXLdGLy^hBx{e(D?*sbP>F(jv-K%h)OHQB65!%c$#r?F$YZKQ)|H0HOjZDWFzoM{eWs2|~u{y+>yzi1`Kdx{>_jjdktB$QHd|+c* zo1aGJZ;vF<)ot8vz) zCF-)+=SrL>js95SlNp-kK%dqiUmH$4s9hmTazU zq>whd=PDb|D%;RW^zl(*LfWL6Le3_jIb#-@zB1;ww8X}|M8{dpsX<&;xUvcHU4!`E z+!94=jQGJIt}0yHgt*=yel+I4K_?mUlQa_mZ1@Z}1N=;j!bBnyDFggME5hH$)~F`2 z&dM@&+F|De&h51uJA|oPf>CFsqj}8R+Byvm&Zkp Jzxjst{sE;6G*z~s^6m7da1ISAdMO_i>5-$ z_182Sja2j{X8axeN+RtgvfGy;D~7T!(;RF3wY<>niCkc9fOW3jOOA4xX7M1++F8yx zG3>QR&QdzamqjietmjQOC~S27b($a%p-kX({7r^VUW?N>XEZi-XvS}GFdAXA@9;}n z^t&j+IM1-fYfVB?VnR=O4S&7nSQSIj&#*PXHh-hW57J&qxxM3Wt$3n*m{_tMUehFo zB9q+}7KxqzU5sB=@-$bnOJTQ5PKS)#CbN(53~lk6#CnJ|?4j+QA}qoH&rVwOh#f<@ z5@)#_vL9TDgXfrcufp@Lzk&JeD)w1X{|-0qp0#v%fr(}nUUa#WS{m?@aW;!@NxbY; z7Mkw865v&r_LAOvPT@6|_R*}dI8J+Vgn6%7gl5%M*l$*8-fLB{1%=mbY^De$hv&Tp zKj&nCH^w+%r9Wvn$T?B=v(0-SRCsf`pM*mX_w%snXMiKLAOgHK#@m+Lwxya<=TteZ z4&GtjcNN~#ZO{`^CE)#vx2b*lfO*+OZPbV4GWAD~@0AI<*!0M$k$4io4db(|x*f>yAQm@_6_r(L z3;%&X(Y+|rzVAJIv-?s0Du0`1t)yvM&`Nqy5GOnl=(;*9QWo-91Z|bM-YOQ1S5)7Q8e}Lm!VEsDdqgJRac*tP(zzc;wiF3;G6(WQ-VR2`*;COKElgfTuHnhi=p}>C;o)ofK4QTEh4u$;S$3!`4_E;xPuxA z0ox?*1+!=c?1^@aZ65)ac%HQKBXmB>Kj?~SXRz-ixcC5M+(K62xU)Ypt5*|hZ zI}*Eb$fi{i@wZNzAZDM|ir1sZvBbn-WAcMb`j3{q6gWF1|_} SIMiy?>eLp%c8ye>b>j!#XM(!` delta 394 zcmZY5zfQtX6bA6tQjLfb6B9`!B*vN%}%z@dU; zQP!auhYVNe@ji}d*4Amx)+q1cnC6|xk9-d&7L}+hJ)914Mk|cZ6)r3?Xzd^93Akjy z+Y)g_CE>xPZ85}kDyj;Dn=x`bA|92Q+|dq`T1xH}#F+3$qD~bS4ccWPrEBsD=*nh* z2M Date: Thu, 20 Feb 2020 09:37:55 -0600 Subject: [PATCH 06/12] Fixes 3684 - MPL drawer renders cU1 gate correctly (#3709) * Fixed issue 3640 by adding logic for complex imaginary numbers Co-Author: Sumit Puri * Added test for display of 'pi'. * Added test for displaying 'pi' for both real and imaginary parts of complex number. * Release notes added for Fixes #3640 * release notes * Fixes #3400 Examples have been provided for the following methods- ccx, ch, crz, cswap, cu1, cu3, cx, cy, cz, h, iden, rx, ry, rz, s, sdg, swap, t, tdg, u1, u2, u3, x, y, z * Revert "Fixes #3400" This reverts commit 36b788195976d6d2014d9b0fa805edd186d420d0. * Fixes #3400 Examples have been provided for the following methods- ccx, ch, crz, cswap, cu1, cu3, cx, cy, cz, h, iden, rx, ry, rz, s, sdg, swap, t, tdg, u1, u2, u3, x, y, z * Added release notes for Fixes #3400 * Fixes #3684 Text drawer displays cU1 correctly. * Revert "Fixes #3684 Text drawer displays cU1 correctly." This reverts commit b8b010eaf191bade36dd857a2ba3b2913b9f81ff. * Fixes #3400 by adding jupyter-execute statement in the examples. * Fixes #3400 by adding jupyter-execute statement in the examples and correcting by performing 'make style'. * Update qiskit/extensions/standard/crz.py Co-Authored-By: Kevin Krsulich * Revert "Update qiskit/extensions/standard/crz.py" This reverts commit 2abc82461bd04de069eec5efc529afd3a62d376a. * Fixes #3400 Added import numpy * #3400 added import numpy statements * Fixes #3684 * Release notes for #3684 fixes * Fixes #3400 incorporated comments from @nonhermitian * Fixes #3400 added more text. * Update qiskit/extensions/standard/rx.py Co-Authored-By: Kevin Krsulich * Added params for matrix generation. * Removed Matrix representation from rz.py * Update qiskit/extensions/standard/z.py Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/y.py Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/y.py Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/u2.py Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/cx.py Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/u3.py Co-Authored-By: Kevin Krsulich * Fixes #3400 incorporated suggestions * Fixes #3400 H gate description. * Added matrix for cswap * Consistency with removing hyphen. * Fixes #3400 consistency * Fixing style * Rearranged the import order * Fixes matrix representation * Incorporating suggestions * Incorporated comments (#3400) * Update qiskit/extensions/standard/swap.py Fixed (#3400) Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/h.py Fixed (#3400) Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/swap.py Fixed (#3400) Co-Authored-By: Kevin Krsulich * Update qiskit/extensions/standard/x.py Fixed (#3400) Co-Authored-By: Kevin Krsulich * Fix lint failures following merge resolution. * Delete fix3400_quantum_methods-e822a1247329f927.yaml This reno belongs to a different PR Co-authored-by: Luciano Bello Co-authored-by: Kevin Krsulich --- qiskit/visualization/matplotlib.py | 12 +++++++++++- releasenotes/notes/fix3684-69e230f98546deb6.yaml | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix3684-69e230f98546deb6.yaml diff --git a/qiskit/visualization/matplotlib.py b/qiskit/visualization/matplotlib.py index ab482a7cd774..a53475029369 100644 --- a/qiskit/visualization/matplotlib.py +++ b/qiskit/visualization/matplotlib.py @@ -910,7 +910,7 @@ def _draw_ops(self, verbose=False): else: self._line(qreg_b, qreg_t, zorder=PORDER_LINE + 1) # control gate - elif op.name in ['cy', 'ch', 'cu3', 'cu1', 'crz']: + elif op.name in ['cy', 'ch', 'cu3', 'crz']: disp = op.name.replace('c', '') color = None @@ -937,6 +937,16 @@ def _draw_ops(self, verbose=False): # add qubit-qubit wiring self._line(qreg_b, qreg_t) + + # cu1 gate + elif op.name == 'cu1': + self._ctrl_qubit(q_xy[0]) + self._ctrl_qubit(q_xy[1]) + self._sidetext(qreg_b, text='U1 ({})'.format(param)) + + # add qubit-qubit wiring + self._line(qreg_b, qreg_t) + # swap gate elif op.name == 'swap': self._swap(q_xy[0]) diff --git a/releasenotes/notes/fix3684-69e230f98546deb6.yaml b/releasenotes/notes/fix3684-69e230f98546deb6.yaml new file mode 100644 index 000000000000..1b89c54fe16a --- /dev/null +++ b/releasenotes/notes/fix3684-69e230f98546deb6.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The MPL drawer has been fixed and renders cU1 gate correctly. + Fixes `issue #3684 ` \ No newline at end of file From 025f6f9e73572a4aa608ac6e90bb1d7bc557e51f Mon Sep 17 00:00:00 2001 From: Ali Javadi-Abhari Date: Thu, 20 Feb 2020 12:37:56 -0500 Subject: [PATCH 07/12] start of a circuit library (#3841) * start of boolean logic circuit library * add tests * release notes * tweaks * make the library elements into QuantumCircuit types * lint * pylint * review comments and rename shift to xor Co-authored-by: Luciano Bello --- qiskit/circuit/library/__init__.py | 17 +++ qiskit/circuit/library/boolean_logic.py | 124 ++++++++++++++++++ ...ogic-circuit-library-0f913cc04063210b.yaml | 11 ++ test/python/circuit/test_library.py | 52 ++++++++ 4 files changed, 204 insertions(+) create mode 100644 qiskit/circuit/library/__init__.py create mode 100644 qiskit/circuit/library/boolean_logic.py create mode 100644 releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml create mode 100644 test/python/circuit/test_library.py diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py new file mode 100644 index 000000000000..701956717ebb --- /dev/null +++ b/qiskit/circuit/library/__init__.py @@ -0,0 +1,17 @@ +# -*- 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. + +"""Module for builtin library of circuits.""" + +from .boolean_logic import Permutation, XOR, InnerProduct diff --git a/qiskit/circuit/library/boolean_logic.py b/qiskit/circuit/library/boolean_logic.py new file mode 100644 index 000000000000..d6377d438304 --- /dev/null +++ b/qiskit/circuit/library/boolean_logic.py @@ -0,0 +1,124 @@ +# -*- 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=no-member + +"""Implementations of boolean logic quantum circuits.""" + +from typing import List, Optional + +import numpy as np +from qiskit.circuit import QuantumRegister, QuantumCircuit +from qiskit.circuit.exceptions import CircuitError + + +class Permutation(QuantumCircuit): + """An n_qubit circuit that permutes qubits.""" + + def __init__(self, + n_qubits: int, + pattern: Optional[List[int]] = None, + seed: Optional[int] = None) -> QuantumCircuit: + """Return an n_qubit permutation circuit implemented using SWAPs. + + Args: + n_qubits: circuit width. + pattern: permutation pattern. If None, permute randomly. + seed: random seed in case a random permutation is requested. + + Returns: + A permutation circuit. + + Raises: + CircuitError: if permutation pattern is malformed. + """ + super().__init__(n_qubits, name="permutation") + + if pattern is not None: + if sorted(pattern) != list(range(n_qubits)): + raise CircuitError("Permutation pattern must be some " + "ordering of 0..n_qubits-1 in a list.") + pattern = np.array(pattern) + else: + rng = np.random.RandomState(seed) + pattern = np.arange(n_qubits) + rng.shuffle(pattern) + + for i in range(n_qubits): + if (pattern[i] != -1) and (pattern[i] != i): + self.swap(i, int(pattern[i])) + pattern[pattern[i]] = -1 + + +class XOR(QuantumCircuit): + """An n_qubit circuit for bitwise xor-ing the input with some integer ``amount``. + + The ``amount`` is xor-ed in bitstring form with the input. + + This circuit can also represent addition by ``amount`` over the finite field GF(2). + """ + + def __init__(self, + n_qubits: int, + amount: Optional[int] = None, + seed: Optional[int] = None) -> QuantumCircuit: + """Return a circuit implementing bitwise xor. + + Args: + n_qubits: the width of circuit. + amount: the xor amount in decimal form. + seed: random seed in case a random xor is requested. + + Returns: + A circuit for bitwise XOR. + + Raises: + CircuitError: if the xor bitstring exceeds available qubits. + """ + super().__init__(n_qubits, name="xor") + + if amount is not None: + if len(bin(amount)[2:]) > n_qubits: + raise CircuitError("Bits in 'amount' exceed circuit width") + else: + rng = np.random.RandomState(seed) + amount = rng.randint(0, 2**n_qubits) + + for i in range(n_qubits): + bit = amount & 1 + amount = amount >> 1 + if bit == 1: + self.x(i) + + +class InnerProduct(QuantumCircuit): + """An n_qubit circuit that computes the inner product of two registers.""" + + def __init__(self, n_qubits: int) -> QuantumCircuit: + """Return a circuit to compute the inner product of 2 n-qubit registers. + + This implementation uses CZ gates. + + Args: + n_qubits: width of top and bottom registers (half total circuit width) + + Returns: + A circuit computing inner product of two registers. + """ + qr_a = QuantumRegister(n_qubits) + qr_b = QuantumRegister(n_qubits) + super().__init__(qr_a, qr_b, name="inner_product") + + for i in range(n_qubits): + self.cz(qr_a[i], qr_b[i]) diff --git a/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml b/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml new file mode 100644 index 000000000000..8246ef980151 --- /dev/null +++ b/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml @@ -0,0 +1,11 @@ +--- +prelude: > + A library of quantum circuits is introduced in ``qiskit.circuit.library``. + This library contains useful circuits which can be used for experiments or + be used as subcircuits in building up other circuits. The contents of this + library will grow over time and better implementations will be introduced. +features: + - | + A few simple circuits for basic boolean logic operations such as ``shift``, + ``permutation``, and ``inner_product`` are added to + ``qiskit.circuit.library``. diff --git a/test/python/circuit/test_library.py b/test/python/circuit/test_library.py new file mode 100644 index 000000000000..225c2801dc83 --- /dev/null +++ b/test/python/circuit/test_library.py @@ -0,0 +1,52 @@ +# -*- 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. + +"""Test library of quantum circuits.""" + +from qiskit.test import QiskitTestCase +from qiskit.circuit import QuantumCircuit +from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit.library import Permutation, XOR, InnerProduct + + +class TestBooleanLogicLibrary(QiskitTestCase): + """Test library of boolean logic quantum circuits.""" + + def test_permutation(self): + """Test permutation circuit.""" + circuit = Permutation(n_qubits=4, pattern=[1, 0, 3, 2]) + expected = QuantumCircuit(4) + expected.swap(0, 1) + expected.swap(2, 3) + self.assertEqual(circuit, expected) + + def test_permutation_bad(self): + """Test that [0,..,n-1] permutation is required (no -1 for last element)""" + self.assertRaises(CircuitError, Permutation, 4, [1, 0, -1, 2]) + + def test_xor(self): + """Test xor circuit.""" + circuit = XOR(n_qubits=3, amount=4) + expected = QuantumCircuit(3) + expected.x(2) + self.assertEqual(circuit, expected) + + def test_inner_product(self): + """Test inner product circuit.""" + circuit = InnerProduct(n_qubits=3) + expected = QuantumCircuit(*circuit.qregs) + expected.cz(0, 3) + expected.cz(1, 4) + expected.cz(2, 5) + self.assertEqual(circuit, expected) From fcace96d088e75ce20d2d58d7f921eed06fe0ee8 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Feb 2020 11:56:55 -0500 Subject: [PATCH 08/12] Fix the broken hyperlink in Pulse API docs (#3871) * Fix the broken hyperlink of :mod: * Revert "Fix the broken hyperlink of :mod:" This reverts commit 5d99305b69059ea5d56dc99047a9a10ae44944fc. * Fix the broken hyperlink of :mod: * change hyperlink to commands module and add docstrings to commands * Fix bug in hidden autosummary --- qiskit/pulse/__init__.py | 9 ++++++++- qiskit/pulse/commands/__init__.py | 26 +++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 925e9db900e8..a8f6e8676c28 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -36,7 +36,7 @@ .. _pulse-commands: Commands (:mod:`~qiskit.pulse.commands`) -================================================ +======================================== .. autosummary:: :toctree: ../stubs/ @@ -86,6 +86,13 @@ Schedule Instruction +.. autosummary:: + :hidden: + :toctree: ../stubs/ + + qiskit.pulse.commands + qiskit.pulse.channels + Configuration ============= diff --git a/qiskit/pulse/commands/__init__.py b/qiskit/pulse/commands/__init__.py index 6ce0900b59da..edc750b23875 100644 --- a/qiskit/pulse/commands/__init__.py +++ b/qiskit/pulse/commands/__init__.py @@ -12,7 +12,31 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Command classes for pulse.""" +""" +Supported command types in Pulse. + +.. autosummary:: + :toctree: ../stubs/ + + Acquire + FrameChange + SamplePulse + Snapshot + Delay + Gaussian + GaussianSquare + Drag + ConstantPulse + +Abstract Classes +---------------- +.. autosummary:: + :toctree: ../stubs/ + + ParametricPulse + Command + + """ from .instruction import Instruction from .acquire import Acquire, AcquireInstruction from .frame_change import FrameChange, FrameChangeInstruction From 1713bbd7462d47ff67dd42bdaa5172a9bb31ad05 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Fri, 21 Feb 2020 12:44:38 -0500 Subject: [PATCH 09/12] Refactor/assemble schedules (#3794) * Refactor the assemble_schedules.py file to break execution into two main parts: assembling the 'experiments' and assembling the 'config' for the Qobj. Break out instruction assembly from experiment (schedule) assembly into another private function. No API changes, no new public functions. Add type hints. * Complete documentation * Respond to feedback from Kevin and update some docstrings following from docathon ideas * Check hasattr measmap rather than catching AttrError to avoid masking errors in function call --- qiskit/assembler/assemble_schedules.py | 398 ++++++++++++++----------- 1 file changed, 232 insertions(+), 166 deletions(-) diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index d5ced498a03b..0c9cf634d81a 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -13,213 +13,221 @@ # that they have been altered from the originals. """Assemble function for converting a list of circuits into a qobj.""" +from typing import Any, Dict, List, Tuple from collections import defaultdict from qiskit.exceptions import QiskitError -from qiskit.pulse.commands import (PulseInstruction, AcquireInstruction, +from qiskit.pulse import Schedule +from qiskit.pulse.commands import (Command, PulseInstruction, Acquire, AcquireInstruction, DelayInstruction, SamplePulse, ParametricInstruction) -from qiskit.qobj import (PulseQobj, QobjExperimentHeader, +from qiskit.qobj import (PulseQobj, QobjHeader, QobjExperimentHeader, PulseQobjInstruction, PulseQobjExperimentConfig, PulseQobjExperiment, PulseQobjConfig, PulseLibraryItem) from qiskit.qobj.converters import InstructionToQobjConverter, LoConfigConverter from qiskit.qobj.converters.pulse_instruction import ParametricPulseShapes from qiskit.qobj.utils import MeasLevel, MeasReturnType +from .run_config import RunConfig -def assemble_schedules(schedules, qobj_id, qobj_header, run_config): + +def assemble_schedules(schedules: List[Schedule], + qobj_id: int, + qobj_header: QobjHeader, + run_config: RunConfig) -> PulseQobj: """Assembles a list of schedules into a qobj that can be run on the backend. Args: - schedules (list[Schedule]): schedules to assemble - qobj_id (int): identifier for the generated qobj - qobj_header (QobjHeader): header to pass to the results - run_config (RunConfig): configuration of the runtime environment + schedules: Schedules to assemble. + qobj_id: Identifier for the generated qobj. + qobj_header: Header to pass to the results. + run_config: Configuration of the runtime environment. + Returns: - PulseQobj: the Qobj to be run on the backends + The Qobj to be run on the backends. + Raises: - QiskitError: when invalid schedules or configs are provided + QiskitError: when frequency settings are not supplied. """ - if hasattr(run_config, 'instruction_converter'): - instruction_converter = run_config.instruction_converter - else: - instruction_converter = InstructionToQobjConverter - - qobj_config = run_config.to_dict() - - qubit_lo_freq = qobj_config.get('qubit_lo_freq', None) - if qubit_lo_freq is None: + if not hasattr(run_config, 'qubit_lo_freq'): raise QiskitError('qubit_lo_freq must be supplied.') - - meas_lo_freq = qobj_config.get('meas_lo_freq', None) - if meas_lo_freq is None: + if not hasattr(run_config, 'meas_lo_freq'): raise QiskitError('meas_lo_freq must be supplied.') - meas_map = qobj_config.pop('meas_map', None) + lo_converter = LoConfigConverter(PulseQobjExperimentConfig, + **run_config.to_dict()) + experiments, experiment_config = _assemble_experiments(schedules, + lo_converter, + run_config) + qobj_config = _assemble_config(lo_converter, experiment_config, run_config) + + return PulseQobj(experiments=experiments, + qobj_id=qobj_id, + header=qobj_header, + config=qobj_config) + + +def _assemble_experiments( + schedules: List[Schedule], + lo_converter: LoConfigConverter, + run_config: RunConfig +) -> Tuple[List[PulseQobjExperiment], Dict[str, Any]]: + """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that + will be assembled into the Qobj configuration. - # convert enums to serialized values - meas_return = qobj_config.get('meas_return', 'avg') - if isinstance(meas_return, MeasReturnType): - qobj_config['meas_return'] = meas_return.value + Args: + schedules: Schedules to assemble. + lo_converter: The configured frequency converter and validator. + run_config: Configuration of the runtime environment. - meas_level = qobj_config.get('meas_return', 2) - if isinstance(meas_level, MeasLevel): - qobj_config['meas_level'] = meas_level.value + Returns: + The list of assembled experiments, and the dictionary of related experiment config. - instruction_converter = instruction_converter(PulseQobjInstruction, **qobj_config) + Raises: + QiskitError: when frequency settings are not compatible with the experiments. + """ + freq_configs = [lo_converter(lo_dict) for lo_dict in getattr(run_config, 'schedule_los', [])] - qubit_lo_range = qobj_config.pop('qubit_lo_range', None) - meas_lo_range = qobj_config.pop('meas_lo_range', None) - lo_converter = LoConfigConverter(PulseQobjExperimentConfig, - qubit_lo_range=qubit_lo_range, - meas_lo_range=meas_lo_range, - **qobj_config) + if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]: + raise QiskitError('Invalid frequency setting is specified. If the frequency is specified, ' + 'it should be configured the same for all schedules, configured for each ' + 'schedule, or a list of frequencies should be provided for a single ' + 'frequency sweep schedule.') - memory_slot_size = 0 + instruction_converter = getattr(run_config, 'instruction_converter', InstructionToQobjConverter) + instruction_converter = instruction_converter(PulseQobjInstruction, **run_config.to_dict()) - # Pack everything into the Qobj - qobj_schedules = [] user_pulselib = {} + experiments = [] for idx, schedule in enumerate(schedules): - # instructions - max_memory_slot = 0 - qobj_instructions = [] - acquire_instruction_map = defaultdict(list) - - # Instructions are returned as tuple of shifted time and instruction - for shift, instruction in schedule.instructions: - # TODO: support conditional gate - - if isinstance(instruction, ParametricInstruction): - pulse_shape = ParametricPulseShapes(type(instruction.command)).name - if pulse_shape not in run_config.parametric_pulses: - # Convert to SamplePulse if the backend does not support it - instruction = PulseInstruction(instruction.command.get_sample_pulse(), - instruction.channels[0], - name=instruction.name) - - if isinstance(instruction, PulseInstruction): - name = instruction.command.name - if name in user_pulselib and instruction.command != user_pulselib[name]: - name = "{0}-{1:x}".format(name, hash(instruction.command.samples.tostring())) - instruction = PulseInstruction( - command=SamplePulse(name=name, samples=instruction.command.samples), - name=instruction.name, - channel=instruction.channels[0]) - # add samples to pulse library - user_pulselib[name] = instruction.command - - if isinstance(instruction, AcquireInstruction): - max_memory_slot = max(max_memory_slot, - *[slot.index for slot in instruction.mem_slots]) - # Acquires have a single AcquireChannel per inst, but we have to bundle them - # together into the Qobj as one instruction with many channels - acquire_instruction_map[(shift, instruction.command)].append(instruction) - continue - - if isinstance(instruction, DelayInstruction): - # delay instructions are ignored as timing is explicit within qobj - continue - - qobj_instructions.append(instruction_converter(shift, instruction)) - - if acquire_instruction_map: - if meas_map: - _validate_meas_map(acquire_instruction_map, meas_map) - for (shift, _), instructions in acquire_instruction_map.items(): - qubits, mem_slots, reg_slots = _bundle_channel_indices(instructions) - qobj_instructions.append( - instruction_converter.convert_single_acquires( - shift, instructions[0], - qubits=qubits, memory_slot=mem_slots, register_slot=reg_slots)) - - # memory slot size is memory slot index + 1 because index starts from zero - exp_memory_slot_size = max_memory_slot + 1 - memory_slot_size = max(memory_slot_size, exp_memory_slot_size) - - # experiment header + qobj_instructions, user_pulses, max_memory_slot = _assemble_instructions( + schedule, + instruction_converter, + run_config) + user_pulselib.update(user_pulses) + # TODO: add other experimental header items (see circuit assembler) qobj_experiment_header = QobjExperimentHeader( - memory_slots=exp_memory_slot_size, - name=schedule.name or 'Experiment-%d' % idx - ) + memory_slots=max_memory_slot + 1, # Memory slots are 0 indexed + name=schedule.name or 'Experiment-%d' % idx) + + experiment = PulseQobjExperiment( + header=qobj_experiment_header, + instructions=qobj_instructions) + if freq_configs: + # This handles the cases where one frequency setting applies to all experiments and + # where each experiment has a different frequency + freq_idx = idx if len(freq_configs) != 1 else 0 + experiment.config = freq_configs[freq_idx] + + experiments.append(experiment) + + # Frequency sweep + if freq_configs and len(experiments) == 1: + experiment = experiments[0] + experiments = [] + for freq_config in freq_configs: + experiments.append(PulseQobjExperiment( + header=experiment.header, + instructions=experiment.instructions, + config=freq_config)) - qobj_schedules.append({ - 'header': qobj_experiment_header, - 'instructions': qobj_instructions - }) + # Top level Qobj configuration + experiment_config = { + 'pulse_library': [PulseLibraryItem(name=pulse.name, samples=pulse.samples) + for pulse in user_pulselib.values()], + 'memory_slots': max([exp.header.memory_slots for exp in experiments]) + } - # set number of memoryslots - qobj_config['memory_slots'] = memory_slot_size + return experiments, experiment_config - # setup pulse_library - qobj_config['pulse_library'] = [PulseLibraryItem(name=pulse.name, samples=pulse.samples) - for pulse in user_pulselib.values()] - # convert lo frequencies to GHz - qobj_config['qubit_lo_freq'] = [freq/1e9 for freq in qubit_lo_freq] - qobj_config['meas_lo_freq'] = [freq/1e9 for freq in meas_lo_freq] +def _assemble_instructions( + schedule: Schedule, + instruction_converter: InstructionToQobjConverter, + run_config: RunConfig +) -> Tuple[List[PulseQobjInstruction], Dict[str, Command], int]: + """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns + related metadata that will be assembled into the Qobj configuration. - # create qobj experiment field - experiments = [] - schedule_los = qobj_config.pop('schedule_los', []) - - if len(schedule_los) == 1: - lo_dict = schedule_los[0] - # update global config - q_los = lo_converter.get_qubit_los(lo_dict) - if q_los: - qobj_config['qubit_lo_freq'] = [freq/1e9 for freq in q_los] - m_los = lo_converter.get_meas_los(lo_dict) - if m_los: - qobj_config['meas_lo_freq'] = [freq/1e9 for freq in m_los] - - if schedule_los: - # multiple frequency setups - if len(qobj_schedules) == 1: - # frequency sweep - for lo_dict in schedule_los: - experiments.append(PulseQobjExperiment( - instructions=qobj_schedules[0]['instructions'], - header=qobj_schedules[0]['header'], - config=lo_converter(lo_dict) - )) - elif len(qobj_schedules) == len(schedule_los): - # n:n setup - for lo_dict, schedule in zip(schedule_los, qobj_schedules): - experiments.append(PulseQobjExperiment( - instructions=schedule['instructions'], - header=schedule['header'], - config=lo_converter(lo_dict) - )) - else: - raise QiskitError('Invalid LO setting is specified. ' - 'The LO should be configured for each schedule, or ' - 'single setup for all schedules (unique), or ' - 'multiple setups for a single schedule (frequency sweep),' - 'or no LO configured at all.') - else: - # unique frequency setup - for schedule in qobj_schedules: - experiments.append(PulseQobjExperiment( - instructions=schedule['instructions'], - header=schedule['header'], - )) + Args: + schedule: Schedule to assemble. + instruction_converter: A converter instance which can convert PulseInstructions to + PulseQobjInstructions. + run_config: Configuration of the runtime environment. - qobj_config = PulseQobjConfig(**qobj_config) + Returns: + A list of converted instructions, the user pulse library dictionary (from pulse name to + pulse command), and the maximum number of readout memory slots used by this Schedule. + """ + max_memory_slot = 0 + qobj_instructions = [] + user_pulselib = {} - return PulseQobj(qobj_id=qobj_id, - config=qobj_config, - experiments=experiments, - header=qobj_header) + acquire_instruction_map = defaultdict(list) + for time, instruction in schedule.instructions: + + if isinstance(instruction, ParametricInstruction): + pulse_shape = ParametricPulseShapes(type(instruction.command)).name + if pulse_shape not in run_config.parametric_pulses: + # Convert to SamplePulse if the backend does not support it + instruction = PulseInstruction(instruction.command.get_sample_pulse(), + instruction.channels[0], + name=instruction.name) + + if isinstance(instruction, PulseInstruction): + name = instruction.command.name + if name in user_pulselib and instruction.command != user_pulselib[name]: + name = "{0}-{1:x}".format(name, hash(instruction.command.samples.tostring())) + instruction = PulseInstruction( + command=SamplePulse(name=name, samples=instruction.command.samples), + name=instruction.name, + channel=instruction.channels[0]) + # add samples to pulse library + user_pulselib[name] = instruction.command + + if isinstance(instruction, AcquireInstruction): + max_memory_slot = max(max_memory_slot, + *[slot.index for slot in instruction.mem_slots]) + # Acquires have a single AcquireChannel per inst, but we have to bundle them + # together into the Qobj as one instruction with many channels + acquire_instruction_map[(time, instruction.command)].append(instruction) + continue + + if isinstance(instruction, DelayInstruction): + # delay instructions are ignored as timing is explicit within qobj + continue + + qobj_instructions.append(instruction_converter(time, instruction)) + + if acquire_instruction_map: + if hasattr(run_config, 'meas_map'): + _validate_meas_map(acquire_instruction_map, run_config.meas_map) + for (time, _), instructions in acquire_instruction_map.items(): + qubits, mem_slots, reg_slots = _bundle_channel_indices(instructions) + qobj_instructions.append( + instruction_converter.convert_single_acquires( + time, instructions[0], + qubits=qubits, memory_slot=mem_slots, register_slot=reg_slots)) + + return qobj_instructions, user_pulselib, max_memory_slot + + +def _validate_meas_map(instruction_map: Dict[Tuple[int, Acquire], List[AcquireInstruction]], + meas_map: List[List[int]]) -> None: + """Validate all qubits tied in ``meas_map`` are to be acquired. + Args: + instruction_map: A dictionary grouping AcquireInstructions according to their start time + and the command features (notably, their duration). + meas_map: List of groups of qubits that must be acquired together. -def _validate_meas_map(instruction_map, meas_map): - """Validate all qubits tied in meas_map are to be acquired.""" + Raises: + QiskitError: If the instructions do not satisfy the measurement map. + """ meas_map_sets = [set(m) for m in meas_map] # Check each acquisition time individually for _, instructions in instruction_map.items(): - measured_qubits = set() for inst in instructions: measured_qubits.update([acq.index for acq in inst.acquires]) @@ -231,9 +239,18 @@ def _validate_meas_map(instruction_map, meas_map): 'in measurement map: {1}'.format(measured_qubits, meas_set)) -def _bundle_channel_indices(instructions): +def _bundle_channel_indices( + instructions: List[AcquireInstruction] +) -> Tuple[List[int], List[int], List[int]]: """From the list of AcquireInstructions, bundle the indices of the acquire channels, - memory slots, and register slots into a 3-tuple of lists.""" + memory slots, and register slots into a 3-tuple of lists. + + Args: + instructions: A list of AcquireInstructions to be bundled. + + Returns: + The qubit indices, the memory slot indices, and register slot indices from instructions. + """ qubits = [] mem_slots = [] reg_slots = [] @@ -242,3 +259,52 @@ def _bundle_channel_indices(instructions): mem_slots.extend(mem_slot.index for mem_slot in inst.mem_slots) reg_slots.extend(reg.index for reg in inst.reg_slots) return qubits, mem_slots, reg_slots + + +def _assemble_config(lo_converter: LoConfigConverter, + experiment_config: Dict[str, Any], + run_config: RunConfig) -> PulseQobjConfig: + """Assembles the QobjConfiguration from experimental config and runtime config. + + Args: + lo_converter: The configured frequency converter and validator. + experiment_config: Schedules to assemble. + run_config: Configuration of the runtime environment. + + Returns: + The assembled PulseQobjConfig. + """ + qobj_config = run_config.to_dict() + qobj_config.update(experiment_config) + + # Run config not needed in qobj config + qobj_config.pop('meas_map', None) + qobj_config.pop('qubit_lo_range', None) + qobj_config.pop('meas_lo_range', None) + + # convert enums to serialized values + meas_return = qobj_config.get('meas_return', 'avg') + if isinstance(meas_return, MeasReturnType): + qobj_config['meas_return'] = meas_return.value + + meas_level = qobj_config.get('meas_level', 2) + if isinstance(meas_level, MeasLevel): + qobj_config['meas_level'] = meas_level.value + + # convert lo frequencies to Hz + qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in qobj_config['qubit_lo_freq']] + qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in qobj_config['meas_lo_freq']] + + # frequency sweep config + schedule_los = qobj_config.pop('schedule_los', []) + if len(schedule_los) == 1: + lo_dict = schedule_los[0] + q_los = lo_converter.get_qubit_los(lo_dict) + # Hz -> GHz + if q_los: + qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in q_los] + m_los = lo_converter.get_meas_los(lo_dict) + if m_los: + qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in m_los] + + return PulseQobjConfig(**qobj_config) From f1814d743b53425cac2cbecf4bae07aeef6f835b Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Fri, 21 Feb 2020 23:10:12 +0100 Subject: [PATCH 10/12] Consistent naming of the standard gates (#3695) * rename the gates according to terra issue #3633 * fix typos, quotes * replace gate names in qiskit and test dirs * replace qc.iden by qc.i * fix lint, and some leftover renamings * add matrix representation of CY * UCG -> UCGate and .ucg -> uc * UC(X,Y,Z) -> UCR(X,Y,Z)Gate and uc(x,y,z) -> ucr(x,y,z) * fix line length * fix lint * add deprecation warnings for old classes * fix missing theta arg * fix test, test type via subclass not "== type" * add reno changelog for the gate names * rename UCRot -> UCPauliRotGate * rename ucrot file, fix line len * rename Sdg -> Sinv, sdg -> sinv * fix metaclass instancecheck using isinstance() as required by pylint caused unterminated recursive call of instancecheck * fix parser test sinv is one char longer than sdg, thus the parsed commands are longer (3 appearances, therefore 3 chars longer) * rename Tdg -> Tinv, tdg -> tinv * fix text drawer test * update to non-deprecated qc methods * test.python.test_qasm_parser.TestParser.test_parser * support both *dg/*inv in qelib1.inc * *dg must remain standard extensions in AstInterpreter * name: ucrot(X,Y,Z) -> ucr(x,y,z), fix (doc)strings along the way * add test on all qelib1.inc gates * fix *inv -> *Inv naming * fix missing renamings, use all_gates.qasm instead of a str * correct gate label for sinv/tinv * start renaming "id" -> "i" * rename all id -> i * reno: fix typos, update gate list * add i gate to qasm * deprecation warnings adhere to contribution guidelines * fix id json keyword * fix modified png file * remove obsolete if case * set deprecation version to 0.12.0 * revert changes on backend configs * keep sdg,tdg,id as valid aliases * revert names to id/sdg/tdg * update error message error message on "i" changed back to "id" * avoid cyclic imports * rename the method diag_gate to diag, update reno * add comment why Initialize is not unitary * change deprecation version to 0.14.0 * fix lints, rename diag -> diagonal * fix deprecation on import * fix Cnot/CX naming * fix deprecation version * fix return of multiplexer pauli rotations * name swap uppercase throughout * rename iso -> isometry (keep alias) * Update deprecation warning Co-Authored-By: Luciano Bello * Update deprecation warning of ucx Co-Authored-By: Luciano Bello * Update deprecation warning of ucz Co-Authored-By: Luciano Bello * Update deprecation warning of ucy Co-Authored-By: Luciano Bello * fix redefine outer scope * attempt fixing jupyter cells * move matrix repr from Fredkin to CSwap * add reno * Revert "move matrix repr from Fredkin to CSwap" This reverts commit cd13878ad30cfee9e8501b2dd26e60538c07789d. * Revert "add reno" This reverts commit f1be570122c46c81bf03e20d71159f07d6272e99. * unrevert revert * fix leftover Cnot * keep old quantum initializer classes but deprecate * fix lint & test * add test for the deprecated classes Co-authored-by: Luciano Bello Co-authored-by: ewinston Co-authored-by: Ali Javadi-Abhari --- qiskit/assembler/disassemble.py | 2 - qiskit/circuit/add_control.py | 4 +- qiskit/circuit/random/utils.py | 24 +- qiskit/converters/ast_to_dag.py | 52 +-- qiskit/converters/dag_to_circuit.py | 2 +- qiskit/extensions/__init__.py | 22 +- .../quantum_initializer/__init__.py | 10 +- qiskit/extensions/quantum_initializer/diag.py | 134 +------ .../quantum_initializer/diagonal.py | 176 +++++++++ .../quantum_initializer/initializer.py | 8 +- .../quantum_initializer/isometry.py | 38 +- .../quantum_initializer/mcg_up_to_diagonal.py | 8 +- qiskit/extensions/quantum_initializer/squ.py | 2 +- qiskit/extensions/quantum_initializer/uc.py | 347 ++++++++++++++++++ .../quantum_initializer/uc_pauli_rot.py | 195 ++++++++++ qiskit/extensions/quantum_initializer/ucg.py | 302 +-------------- .../extensions/quantum_initializer/ucrot.py | 159 +------- qiskit/extensions/quantum_initializer/ucrx.py | 128 +++++++ qiskit/extensions/quantum_initializer/ucry.py | 125 +++++++ qiskit/extensions/quantum_initializer/ucrz.py | 126 +++++++ qiskit/extensions/quantum_initializer/ucx.py | 86 +---- qiskit/extensions/quantum_initializer/ucy.py | 85 +---- qiskit/extensions/quantum_initializer/ucz.py | 86 +---- qiskit/extensions/standard/__init__.py | 33 +- qiskit/extensions/standard/ccx.py | 2 +- qiskit/extensions/standard/ch.py | 4 +- qiskit/extensions/standard/crx.py | 2 +- qiskit/extensions/standard/cry.py | 2 +- qiskit/extensions/standard/crz.py | 2 +- qiskit/extensions/standard/cswap.py | 2 +- qiskit/extensions/standard/cu1.py | 2 +- qiskit/extensions/standard/cu3.py | 2 +- qiskit/extensions/standard/cx.py | 2 +- qiskit/extensions/standard/cy.py | 2 +- qiskit/extensions/standard/cz.py | 2 +- qiskit/extensions/standard/h.py | 25 +- qiskit/extensions/standard/i.py | 111 ++++++ qiskit/extensions/standard/iden.py | 62 +--- qiskit/extensions/standard/ms.py | 6 +- qiskit/extensions/standard/r.py | 6 +- qiskit/extensions/standard/rcccx.py | 14 +- qiskit/extensions/standard/rccx.py | 8 +- qiskit/extensions/standard/rx.py | 48 ++- qiskit/extensions/standard/rxx.py | 11 +- qiskit/extensions/standard/ry.py | 54 ++- qiskit/extensions/standard/rz.py | 50 ++- qiskit/extensions/standard/rzz.py | 14 +- qiskit/extensions/standard/s.py | 20 +- qiskit/extensions/standard/swap.py | 72 ++-- qiskit/extensions/standard/t.py | 26 +- qiskit/extensions/standard/u1.py | 48 ++- qiskit/extensions/standard/u2.py | 20 +- qiskit/extensions/standard/u3.py | 60 ++- qiskit/extensions/standard/x.py | 114 ++++-- qiskit/extensions/standard/y.py | 57 ++- qiskit/extensions/standard/z.py | 52 ++- qiskit/quantum_info/operators/operator.py | 4 +- qiskit/quantum_info/operators/pauli.py | 4 +- .../synthesis/two_qubit_decompose.py | 4 +- .../passes/basis/ms_basis_decomposer.py | 6 +- .../passes/optimization/consolidate_blocks.py | 4 +- .../crosstalk_adaptive_schedule.py | 6 +- .../remove_diagonal_gates_before_measure.py | 4 +- .../passes/utils/check_cx_direction.py | 4 +- .../transpiler/passes/utils/cx_direction.py | 4 +- qiskit/visualization/qcstyle.py | 4 +- qiskit/visualization/text.py | 5 + ...nsistent-gate-naming-efa669c7a8d3a654.yaml | 31 ++ test/python/circuit/test_circuit_data.py | 22 +- .../circuit/test_circuit_load_from_qasm.py | 78 +++- test/python/circuit/test_controlled_gate.py | 98 +++-- .../{test_diag.py => test_diagonal_gate.py} | 5 +- .../circuit/test_extensions_standard.py | 48 +-- test/python/circuit/test_gate_power.py | 4 +- .../python/circuit/test_instruction_repeat.py | 16 +- test/python/circuit/test_instructions.py | 18 +- test/python/circuit/test_isometry.py | 2 +- .../circuit/{test_ucg.py => test_uc.py} | 10 +- test/python/circuit/test_ucx_y_z.py | 10 +- test/python/compiler/test_transpiler.py | 6 +- .../pickles/TestsBasicSwap_a_cx_to_map.pickle | Bin 1538 -> 1536 bytes .../TestsBasicSwap_handle_measurement.pickle | Bin 1930 -> 1928 bytes .../TestsBasicSwap_initial_layout.pickle | Bin 1779 -> 1777 bytes .../TestsLookaheadSwap_a_cx_to_map.pickle | Bin 2163 -> 2161 bytes ...stsLookaheadSwap_handle_measurement.pickle | Bin 3367 -> 3365 bytes .../TestsLookaheadSwap_initial_layout.pickle | Bin 2863 -> 2861 bytes .../TestsStochasticSwap_a_cx_to_map.pickle | Bin 1368 -> 1366 bytes ...tsStochasticSwap_handle_measurement.pickle | Bin 1773 -> 1771 bytes .../TestsStochasticSwap_initial_layout.pickle | Bin 1569 -> 1567 bytes test/python/qasm/all_gates.qasm | 55 +++ .../quantum_info/operators/test_operator.py | 4 +- test/python/quantum_info/test_synthesis.py | 12 +- test/python/test_dagcircuit.py | 74 ++-- test/python/test_qasm_parser.py | 19 +- .../transpiler/test_collect_2q_blocks.py | 22 +- .../transpiler/test_consolidate_blocks.py | 16 +- test/python/transpiler/test_decompose.py | 4 +- .../transpiler/test_optimize_1q_gates.py | 4 +- .../python/transpiler/test_passmanager_run.py | 6 +- ...st_remove_diagonal_gates_before_measure.py | 8 +- test/python/transpiler/test_unroller.py | 4 +- .../references/circuit_text_ref.txt | 26 +- .../references/circuit_text_ref_original.txt | 12 + .../visualization/test_circuit_text_drawer.py | 6 +- .../test_circuit_visualization_output.py | 4 +- .../randomized/test_transpiler_equivalence.py | 10 +- 106 files changed, 2304 insertions(+), 1435 deletions(-) create mode 100644 qiskit/extensions/quantum_initializer/diagonal.py create mode 100644 qiskit/extensions/quantum_initializer/uc.py create mode 100644 qiskit/extensions/quantum_initializer/uc_pauli_rot.py create mode 100644 qiskit/extensions/quantum_initializer/ucrx.py create mode 100644 qiskit/extensions/quantum_initializer/ucry.py create mode 100644 qiskit/extensions/quantum_initializer/ucrz.py create mode 100644 qiskit/extensions/standard/i.py create mode 100644 releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml rename test/python/circuit/{test_diag.py => test_diagonal_gate.py} (95%) rename test/python/circuit/{test_ucg.py => test_uc.py} (90%) create mode 100644 test/python/qasm/all_gates.qasm create mode 100644 test/python/visualization/references/circuit_text_ref_original.txt diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py index 3b66c559a1eb..132ebd30a216 100644 --- a/qiskit/assembler/disassemble.py +++ b/qiskit/assembler/disassemble.py @@ -50,8 +50,6 @@ def _experiments_to_circuits(qobj): conditional = {} for i in x.instructions: name = i.name - if i.name == 'id': - name = 'iden' qubits = [] params = getattr(i, 'params', []) try: diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index df74771de7da..e3a47731febe 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -54,7 +54,7 @@ def add_control(operation: Union[Gate, ControlledGate], # the condition matching 'name' above is to catch a test case, # 'TestControlledGate.test_rotation_gates', where the rz gate # gets converted to a circuit before becoming a generic Gate object. - cgate = standard.CrzGate(*operation.params) + cgate = standard.CRZGate(*operation.params) return cgate.control(num_ctrl_qubits - 1) if isinstance(operation, UnitaryGate): # attempt decomposition @@ -119,7 +119,7 @@ def control(operation: Union[Gate, ControlledGate], for rule in bgate.definition: if rule[0].name == 'u3': theta, phi, lamb = rule[0].params - if phi == -pi/2 and lamb == pi/2: + if phi == -pi / 2 and lamb == pi / 2: qc.mcrx(theta, q_control, q_target[rule[1][0].index], use_basis_gates=True) elif phi == 0 and lamb == 0: diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py index 71cad691a88b..31306c52c878 100644 --- a/qiskit/circuit/random/utils.py +++ b/qiskit/circuit/random/utils.py @@ -18,12 +18,12 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Reset -from qiskit.extensions import (IdGate, U1Gate, U2Gate, U3Gate, XGate, +from qiskit.extensions import (IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, HGate, SGate, SdgGate, TGate, - TdgGate, RXGate, RYGate, RZGate, CnotGate, - CyGate, CzGate, CHGate, CrzGate, Cu1Gate, - Cu3Gate, SwapGate, RZZGate, - ToffoliGate, FredkinGate) + TdgGate, RXGate, RYGate, RZGate, CXGate, + CYGate, CZGate, CHGate, CRZGate, CU1Gate, + CU3Gate, SwapGate, RZZGate, + CCXGate, CSwapGate) from qiskit.circuit.exceptions import CircuitError @@ -49,14 +49,14 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") - one_q_ops = [IdGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, + one_q_ops = [IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, HGate, SGate, SdgGate, TGate, TdgGate, RXGate, RYGate, RZGate] - one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, Cu1Gate, CrzGate] + one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, CU1Gate, CRZGate] two_param = [U2Gate] - three_param = [U3Gate, Cu3Gate] - two_q_ops = [CnotGate, CyGate, CzGate, CHGate, CrzGate, - Cu1Gate, Cu3Gate, SwapGate, RZZGate] - three_q_ops = [ToffoliGate, FredkinGate] + three_param = [U3Gate, CU3Gate] + two_q_ops = [CXGate, CYGate, CZGate, CHGate, CRZGate, + CU1Gate, CU3Gate, SwapGate, RZZGate] + three_q_ops = [CCXGate, CSwapGate] qr = QuantumRegister(n_qubits, 'q') qc = QuantumCircuit(n_qubits) @@ -96,7 +96,7 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, num_angles = 3 else: num_angles = 0 - angles = [rng.uniform(0, 2*np.pi) for x in range(num_angles)] + angles = [rng.uniform(0, 2 * np.pi) for x in range(num_angles)] register_operands = [qr[i] for i in operands] op = operation(*angles) diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index 3e07aec68f7c..a8dc17ee96fd 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -25,14 +25,14 @@ from qiskit.circuit.measure import Measure from qiskit.circuit.reset import Reset from qiskit.extensions.standard.barrier import Barrier -from qiskit.extensions.standard.x import ToffoliGate -from qiskit.extensions.standard.swap import FredkinGate -from qiskit.extensions.standard.x import CnotGate -from qiskit.extensions.standard.y import CyGate -from qiskit.extensions.standard.z import CzGate +from qiskit.extensions.standard.x import CCXGate +from qiskit.extensions.standard.swap import CSwapGate +from qiskit.extensions.standard.x import CXGate +from qiskit.extensions.standard.y import CYGate +from qiskit.extensions.standard.z import CZGate from qiskit.extensions.standard.swap import SwapGate from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.iden import IdGate +from qiskit.extensions.standard.i import IGate from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.s import SdgGate from qiskit.extensions.standard.t import TGate @@ -46,14 +46,14 @@ from qiskit.extensions.standard.rx import RXGate from qiskit.extensions.standard.ry import RYGate from qiskit.extensions.standard.rz import RZGate -from qiskit.extensions.standard.u1 import Cu1Gate -from qiskit.extensions.standard.h import CHGate -from qiskit.extensions.standard.rx import CrxGate -from qiskit.extensions.standard.ry import CryGate -from qiskit.extensions.standard.rz import CrzGate -from qiskit.extensions.standard.u3 import Cu3Gate from qiskit.extensions.standard.rxx import RXXGate from qiskit.extensions.standard.rzz import RZZGate +from qiskit.extensions.standard.u1 import CU1Gate +from qiskit.extensions.standard.u3 import CU3Gate +from qiskit.extensions.standard.h import CHGate +from qiskit.extensions.standard.rx import CRXGate +from qiskit.extensions.standard.ry import CRYGate +from qiskit.extensions.standard.rz import CRZGate def ast_to_dag(ast): @@ -113,19 +113,19 @@ class AstInterpreter: "ry": RYGate, "rz": RZGate, "rzz": RZZGate, - "id": IdGate, + "id": IGate, "h": HGate, - "cx": CnotGate, - "cy": CyGate, - "cz": CzGate, + "cx": CXGate, + "cy": CYGate, + "cz": CZGate, "ch": CHGate, - "crx": CrxGate, - "cry": CryGate, - "crz": CrzGate, - "cu1": Cu1Gate, - "cu3": Cu3Gate, - "ccx": ToffoliGate, - "cswap": FredkinGate} + "crx": CRXGate, + "cry": CRYGate, + "crz": CRZGate, + "cu1": CU1Gate, + "cu3": CU3Gate, + "ccx": CCXGate, + "cswap": CSwapGate} def __init__(self, dag): """Initialize interpreter's data.""" @@ -239,11 +239,11 @@ def _process_cnot(self, node): maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): if len(id0) > 1 and len(id1) > 1: - self.dag.apply_operation_back(CnotGate(), [id0[idx], id1[idx]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[idx], id1[idx]], [], self.condition) elif len(id0) > 1: - self.dag.apply_operation_back(CnotGate(), [id0[idx], id1[0]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[idx], id1[0]], [], self.condition) else: - self.dag.apply_operation_back(CnotGate(), [id0[0], id1[idx]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[0], id1[idx]], [], self.condition) def _process_measure(self, node): """Process a measurement node.""" diff --git a/qiskit/converters/dag_to_circuit.py b/qiskit/converters/dag_to_circuit.py index 7bd1e161d018..f6cf4e90460c 100644 --- a/qiskit/converters/dag_to_circuit.py +++ b/qiskit/converters/dag_to_circuit.py @@ -31,7 +31,7 @@ def dag_to_circuit(dag): from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.dagcircuit import DAGCircuit from qiskit.converters import circuit_to_dag - from qiskit.extensions.standard import CHGate, U2Gate, CnotGate + from qiskit.extensions.standard import CHGate, U2Gate, CXGate from qiskit.converters import dag_to_circuit %matplotlib inline diff --git a/qiskit/extensions/__init__.py b/qiskit/extensions/__init__.py index 1eedc7b96840..e77596231d0b 100644 --- a/qiskit/extensions/__init__.py +++ b/qiskit/extensions/__init__.py @@ -26,19 +26,19 @@ :toctree: ../stubs/ Barrier - ToffoliGate + CCXGate CHGate - CrxGate - CryGate - CrzGate - FredkinGate - Cu1Gate - Cu3Gate - CnotGate - CyGate - CzGate + CRXGate + CRYGate + CRZGate + CSwapGate + CU1Gate + CU3Gate + CXGate + CYGate + CZGate HGate - IdGate + IGate MSGate RXGate RXXGate diff --git a/qiskit/extensions/quantum_initializer/__init__.py b/qiskit/extensions/quantum_initializer/__init__.py index 5aab84a27772..8b1b3f2b71d6 100644 --- a/qiskit/extensions/quantum_initializer/__init__.py +++ b/qiskit/extensions/quantum_initializer/__init__.py @@ -15,9 +15,9 @@ """Initialize qubit registers to desired arbitrary state.""" from .squ import SingleQubitUnitary -from .ucz import UCZ -from .ucy import UCY -from .ucx import UCX -from .diag import DiagGate -from .ucg import UCG +from .ucrz import UCRZGate +from .ucry import UCRYGate +from .ucrx import UCRXGate +from .diagonal import DiagonalGate +from .uc import UCGate from .isometry import Isometry diff --git a/qiskit/extensions/quantum_initializer/diag.py b/qiskit/extensions/quantum_initializer/diag.py index 296349974673..f8cae74dad7f 100644 --- a/qiskit/extensions/quantum_initializer/diag.py +++ b/qiskit/extensions/quantum_initializer/diag.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,132 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, -# which was supervised by Raban Iten and Prof. Renato Renner. +"""The diagonal gate. -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc - -""" -Decomposes a diagonal matrix into elementary gates using the method described in Theorem 7 in -"Synthesis of Quantum Logic Circuits" by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). +This module is deprecated, see diagonal.py """ -import cmath -import math - -import numpy as np - -from qiskit.circuit import Gate -from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister -from qiskit.exceptions import QiskitError - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class DiagGate(Gate): - """ - diag = list of the 2^k diagonal entries (for a diagonal gate on k qubits). Must contain at - least two entries. - """ - - def __init__(self, diag): - """Check types""" - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - # Check if the right number of diagonal entries is provided and if the diagonal entries - # have absolute value one. - num_action_qubits = math.log2(len(diag)) - if num_action_qubits < 1 or not num_action_qubits.is_integer(): - raise QiskitError("The number of diagonal entries is not a positive power of 2.") - for z in diag: - try: - complex(z) - except TypeError: - raise QiskitError("Not all of the diagonal entries can be converted to " - "complex numbers.") - if not np.abs(z) - 1 < _EPS: - raise QiskitError("A diagonal entry has not absolute value one.") - # Create new gate. - super().__init__("diag", int(num_action_qubits), diag) - - def _define(self): - diag_circuit = self._dec_diag() - gate = diag_circuit.to_instruction() - q = QuantumRegister(self.num_qubits) - diag_circuit = QuantumCircuit(q) - diag_circuit.append(gate, q[:]) - self.definition = diag_circuit.data - - def _dec_diag(self): - """ - Call to create a circuit implementing the diagonal gate. - """ - q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q) - # Since the diagonal is a unitary, all its entries have absolute value one and the diagonal - # is fully specified by the phases of its entries - diag_phases = [cmath.phase(z) for z in self.params] - n = len(self.params) - while n >= 2: - angles_rz = [] - for i in range(0, n, 2): - diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) - angles_rz.append(rz_angle) - num_act_qubits = int(np.log2(n)) - contr_qubits = q[self.num_qubits - num_act_qubits + 1:self.num_qubits] - target_qubit = q[self.num_qubits - num_act_qubits] - circuit.ucz(angles_rz, contr_qubits, target_qubit) - n //= 2 - return circuit - - -# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) -# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) -def _extract_rz(phi1, phi2): - phase = (phi1 + phi2) / 2.0 - z_angle = phi2 - phi1 - return phase, z_angle - - -def diag_gate(self, diag, qubit): - """Attach a diagonal gate to a circuit. - - The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by - Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). - - Args: - diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). - Must contain at least two entries - qubit (QuantumRegister|list): list of k qubits the diagonal is - acting on (the order of the qubits specifies the computational basis in which the - diagonal gate is provided: the first element in diag acts on the state where all - the qubits in q are in the state 0, the second entry acts on the state where all - the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, - and so on) - - Returns: - QuantumCircuit: the diagonal gate which was attached to the circuit. - - Raises: - QiskitError: if the list of the diagonal entries or the qubit list is in bad format; - if the number of diagonal entries is not 2^k, where k denotes the number of qubits - """ - - if isinstance(qubit, QuantumRegister): - qubit = qubit[:] - # Check if q has type "list" - if not isinstance(qubit, list): - raise QiskitError("The qubits must be provided as a list " - "(also if there is only one qubit).") - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - num_action_qubits = math.log2(len(diag)) - if not len(qubit) == num_action_qubits: - raise QiskitError("The number of diagonal entries does not correspond to" - " the number of qubits.") - return self.append(DiagGate(diag), qubit) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.diagonal import DiagGate, diag_gate -QuantumCircuit.diag_gate = diag_gate +warnings.warn('This module is deprecated. The DiagonalGate/DiagGate is now in diagonal.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py new file mode 100644 index 000000000000..1083ffd36946 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/diagonal.py @@ -0,0 +1,176 @@ +# -*- 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. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, +# which was supervised by Raban Iten and Prof. Renato Renner. + +# pylint: disable=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Decomposes a diagonal matrix into elementary gates using the method described in Theorem 7 in +"Synthesis of Quantum Logic Circuits" by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). +""" +import cmath +import math + +import numpy as np + +from qiskit.circuit import Gate +from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister +from qiskit.exceptions import QiskitError + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class DiagonalMeta(type): + """A metaclass to ensure that DiagonalGate and DiagGate are of the same type. + + Can be removed when DiagGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {DiagonalGate, DiagGate} # pylint: disable=unidiomatic-typecheck + + +class DiagonalGate(Gate, metaclass=DiagonalMeta): + """ + diag = list of the 2^k diagonal entries (for a diagonal gate on k qubits). Must contain at + least two entries. + """ + + def __init__(self, diag): + """Check types""" + # Check if diag has type "list" + if not isinstance(diag, list): + raise QiskitError("The diagonal entries are not provided in a list.") + # Check if the right number of diagonal entries is provided and if the diagonal entries + # have absolute value one. + num_action_qubits = math.log2(len(diag)) + if num_action_qubits < 1 or not num_action_qubits.is_integer(): + raise QiskitError("The number of diagonal entries is not a positive power of 2.") + for z in diag: + try: + complex(z) + except TypeError: + raise QiskitError("Not all of the diagonal entries can be converted to " + "complex numbers.") + if not np.abs(z) - 1 < _EPS: + raise QiskitError("A diagonal entry has not absolute value one.") + # Create new gate. + super().__init__("diagonal", int(num_action_qubits), diag) + + def _define(self): + diag_circuit = self._dec_diag() + gate = diag_circuit.to_instruction() + q = QuantumRegister(self.num_qubits) + diag_circuit = QuantumCircuit(q) + diag_circuit.append(gate, q[:]) + self.definition = diag_circuit.data + + def _dec_diag(self): + """ + Call to create a circuit implementing the diagonal gate. + """ + q = QuantumRegister(self.num_qubits) + circuit = QuantumCircuit(q) + # Since the diagonal is a unitary, all its entries have absolute value one and the diagonal + # is fully specified by the phases of its entries + diag_phases = [cmath.phase(z) for z in self.params] + n = len(self.params) + while n >= 2: + angles_rz = [] + for i in range(0, n, 2): + diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) + angles_rz.append(rz_angle) + num_act_qubits = int(np.log2(n)) + contr_qubits = q[self.num_qubits - num_act_qubits + 1:self.num_qubits] + target_qubit = q[self.num_qubits - num_act_qubits] + circuit.ucrz(angles_rz, contr_qubits, target_qubit) + n //= 2 + return circuit + + +# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) +# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) +def _extract_rz(phi1, phi2): + phase = (phi1 + phi2) / 2.0 + z_angle = phi2 - phi1 + return phase, z_angle + + +def diagonal(self, diag, qubit): + """Attach a diagonal gate to a circuit. + + The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by + Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). + + Args: + diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). + Must contain at least two entries + qubit (QuantumRegister|list): list of k qubits the diagonal is + acting on (the order of the qubits specifies the computational basis in which the + diagonal gate is provided: the first element in diag acts on the state where all + the qubits in q are in the state 0, the second entry acts on the state where all + the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, + and so on) + + Returns: + QuantumCircuit: the diagonal gate which was attached to the circuit. + + Raises: + QiskitError: if the list of the diagonal entries or the qubit list is in bad format; + if the number of diagonal entries is not 2^k, where k denotes the number of qubits + """ + + if isinstance(qubit, QuantumRegister): + qubit = qubit[:] + # Check if q has type "list" + if not isinstance(qubit, list): + raise QiskitError("The qubits must be provided as a list " + "(also if there is only one qubit).") + # Check if diag has type "list" + if not isinstance(diag, list): + raise QiskitError("The diagonal entries are not provided in a list.") + num_action_qubits = math.log2(len(diag)) + if not len(qubit) == num_action_qubits: + raise QiskitError("The number of diagonal entries does not correspond to" + " the number of qubits.") + return self.append(DiagonalGate(diag), qubit) + + +class DiagGate(DiagonalGate, metaclass=DiagonalMeta): + """The deprecated DiagonalGate class.""" + + def __init__(self, diag): + import warnings + warnings.warn('The class DiagGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class DiagonalGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(diag) + + +def diag_gate(self, diag, qubit): + """Deprecated version of QuantumCircuit.diagonal.""" + import warnings + warnings.warn('The QuantumCircuit.diag_gate() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.diagonal() method instead.', + DeprecationWarning, stacklevel=2) + return diagonal(self, diag, qubit) + + +QuantumCircuit.diagonal = diagonal +QuantumCircuit.diag_gate = diag_gate # deprecated diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index b728ecd50ce9..6047121457bf 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -23,7 +23,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.circuit import Instruction -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.ry import RYGate from qiskit.extensions.standard.rz import RZGate from qiskit.circuit.reset import Reset @@ -37,6 +37,8 @@ class Initialize(Instruction): Class that implements the (complex amplitude) initialization of some flexible collection of qubit registers (assuming the qubits are in the zero state). + Note that Initialize is an Instruction and not a Gate since it contains a reset instruction, + which is not unitary. """ def __init__(self, params): @@ -221,7 +223,7 @@ def _multiplex(self, target_gate, list_of_angles): circuit.append(multiplex_1.to_instruction(), q[0:-1]) # attach CNOT as follows, thereby flipping the LSB qubit - circuit.append(CnotGate(), [msb, lsb]) + circuit.append(CXGate(), [msb, lsb]) # implement extra efficiency from the paper of cancelling adjacent # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the @@ -233,7 +235,7 @@ def _multiplex(self, target_gate, list_of_angles): circuit.append(multiplex_2.to_instruction(), q[0:-1]) # attach a final CNOT - circuit.append(CnotGate(), [msb, lsb]) + circuit.append(CXGate(), [msb, lsb]) return circuit diff --git a/qiskit/extensions/quantum_initializer/isometry.py b/qiskit/extensions/quantum_initializer/isometry.py index 89336dbf1629..08052daa9eac 100644 --- a/qiskit/extensions/quantum_initializer/isometry.py +++ b/qiskit/extensions/quantum_initializer/isometry.py @@ -30,7 +30,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_isometry -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate from qiskit.extensions.quantum_initializer.mcg_up_to_diagonal import MCGupDiag _EPS = 1e-10 # global variable used to chop very small numbers to zero @@ -93,7 +93,7 @@ def __init__(self, isometry, num_ancillas_zero, num_ancillas_dirty): num_qubits = int(n) + num_ancillas_zero + num_ancillas_dirty - super().__init__("iso", num_qubits, 0, [isometry]) + super().__init__("isometry", num_qubits, 0, [isometry]) def _define(self): # call to generate the circuit that takes the isometry to the first 2^m columns @@ -109,7 +109,7 @@ def _define(self): if num_gates == 0: # ToDo: improve handling of empty circuit, such that the following line # ToDo: is not required. - iso_circuit.iden(q[0]) + iso_circuit.i(q[0]) else: iso_circuit.append(gate, q[:]) self.definition = iso_circuit.data @@ -139,7 +139,7 @@ def _gates_to_uncompute(self): # remove first column (which is now stored in diag) remaining_isometry = remaining_isometry[:, 1:] if len(diag) > 1 and not _diag_is_identity_up_to_global_phase(diag): - circuit.diag_gate(np.conj(diag).tolist(), q_input) + circuit.diagonal(np.conj(diag).tolist(), q_input) return circuit def _decompose_column(self, circuit, q, diag, remaining_isometry, column_index): @@ -163,7 +163,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): v = remaining_isometry n = int(np.log2(self.params[0].shape[0])) - # MCG to set one entry to zero (preparation for disentangling with UCG): + # MCG to set one entry to zero (preparation for disentangling with UCGate): index1 = 2 * _a(k, s + 1) * 2 ** s + _b(k, s + 1) index2 = (2 * _a(k, s + 1) + 1) * 2 ** s + _b(k, s + 1) target_label = n - s - 1 @@ -183,17 +183,17 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # update the diag according to the applied diagonal gate _apply_diagonal_gate_to_diag(diag, control_labels + [target_label], diag_mcg_inverse, n) - # UCG to disentangle a qubit: - # Find the UCG, decompose it and apply it to the remaining isometry + # UCGate to disentangle a qubit: + # Find the UCGate, decompose it and apply it to the remaining isometry single_qubit_gates = self._find_squs_for_disentangling(v, k, s) if not _ucg_is_identity_up_to_global_phase(single_qubit_gates): control_labels = list(range(target_label)) diagonal_ucg = self._append_ucg_up_to_diagonal(circuit, q, single_qubit_gates, control_labels, target_label) - # merge the diagonal into the UCG for efficient application of both together + # merge the diagonal into the UCGate for efficient application of both together diagonal_ucg_inverse = np.conj(diagonal_ucg).tolist() - single_qubit_gates = _merge_UCG_and_diag(single_qubit_gates, diagonal_ucg_inverse) - # apply the UCG (with the merged diagonal gate) to the remaining isometry + single_qubit_gates = _merge_UCGate_and_diag(single_qubit_gates, diagonal_ucg_inverse) + # apply the UCGate (with the merged diagonal gate) to the remaining isometry _apply_ucg(v, len(control_labels), single_qubit_gates) # update the diag according to the applied diagonal gate _apply_diagonal_gate_to_diag(diag, control_labels + [target_label], @@ -202,7 +202,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # diag_inv = np.conj(diag).tolist() # _apply_diagonal_gate(v, control_labels + [target_label], diag_inv) - # This method finds the single-qubit gates for a UCG to disentangle a qubit: + # This method finds the single-qubit gates for a UCGate to disentangle a qubit: # we consider the n-qubit state v[:,0] starting with k zeros (in the computational basis). # The qubit with label n-s-1 is disentangled into the basis state k_s(k,s). def _find_squs_for_disentangling(self, v, k, s): @@ -218,17 +218,17 @@ def _find_squs_for_disentangling(self, v, k, s): for l in range(i_start, 2 ** (n - s - 1))] return id_list + squs - # Append a UCG up to diagonal to the circuit circ. + # Append a UCGate up to diagonal to the circuit circ. def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels, target_label): (q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty) = \ self._define_qubit_role(q) n = int(np.log2(self.params[0].shape[0])) qubits = q_input + q_ancillas_for_output # Note that we have to reverse the control labels, since controls are provided by - # increasing qubit number toa UCG by convention + # increasing qubit number toa UCGate by convention control_qubits = _reverse_qubit_oder(_get_qubits_by_label(control_labels, qubits, n)) target_qubit = _get_qubits_by_label([target_label], qubits, n)[0] - ucg = UCG(single_qubit_gates, up_to_diagonal=True) + ucg = UCGate(single_qubit_gates, up_to_diagonal=True) circ.append(ucg, [target_qubit] + control_qubits) return ucg._get_diagonal() @@ -283,8 +283,8 @@ def _reverse_qubit_state(state, basis_state): # Methods for applying gates to matrices (should be moved to Qiskit AER) # Input: matrix m with 2^n rows (and arbitrary many columns). Think of the columns as states -# on n qubits. The method applies a uniformly controlled gate (UCG) to all the columns, where -# the UCG is specified by the inputs k and single_qubit_gates: +# on n qubits. The method applies a uniformly controlled gate (UCGate) to all the columns, where +# the UCGate is specified by the inputs k and single_qubit_gates: # k = number of controls. We assume that the controls are on the k most significant qubits # (and the target is on the (k+1)th significant qubit) @@ -440,10 +440,10 @@ def _get_binary_rep_as_list(n, num_digits): return binary[-num_digits:] -# absorb a diagonal gate into a UCG +# absorb a diagonal gate into a UCGate -def _merge_UCG_and_diag(single_qubit_gates, diag): +def _merge_UCGate_and_diag(single_qubit_gates, diag): for (i, gate) in enumerate(single_qubit_gates): single_qubit_gates[i] = \ np.array([[diag[2 * i], 0.], [0., diag[2 * i + 1]]]).dot(gate) @@ -553,4 +553,6 @@ def iso(self, isometry, q_input, q_ancillas_for_output, q_ancillas_zero=None, q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty) +# support both QuantumCircuit.iso and QuantumCircuit.isometry QuantumCircuit.iso = iso +QuantumCircuit.isometry = iso diff --git a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py index 46524d2414f4..01f71829eacf 100644 --- a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py +++ b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py @@ -29,7 +29,7 @@ from qiskit.circuit.quantumcircuit import QuantumRegister, QuantumCircuit from qiskit.quantum_info.operators.predicates import is_isometry from qiskit.exceptions import QiskitError -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate _EPS = 1e-10 # global variable used to chop very small numbers to zero @@ -94,14 +94,14 @@ def _dec_mcg_up_diag(self): circuit = QuantumCircuit(q) (q_target, q_controls, q_ancillas_zero, q_ancillas_dirty) = self._define_qubit_role(q) # ToDo: Keep this threshold updated such that the lowest gate count is achieved: - # ToDo: we implement the MCG with a UCG up to diagonal if the number of controls is + # ToDo: we implement the MCG with a UCGate up to diagonal if the number of controls is # ToDo: smaller than the threshold. threshold = float("inf") if self.num_controls < threshold: - # Implement the MCG as a UCG (up to diagonal) + # Implement the MCG as a UCGate (up to diagonal) gate_list = [np.eye(2, 2) for i in range(2 ** self.num_controls)] gate_list[-1] = self.params[0] - ucg = UCG(gate_list, up_to_diagonal=True) + ucg = UCGate(gate_list, up_to_diagonal=True) circuit.append(ucg, [q_target] + q_controls) diag = ucg._get_diagonal() # else: diff --git a/qiskit/extensions/quantum_initializer/squ.py b/qiskit/extensions/quantum_initializer/squ.py index 820e34192d4e..e5cc7901c7c6 100644 --- a/qiskit/extensions/quantum_initializer/squ.py +++ b/qiskit/extensions/quantum_initializer/squ.py @@ -99,7 +99,7 @@ def _dec_single_qubit_unitary(self): circuit.rz(c, q[0]) is_identity = False if is_identity: - circuit.iden(q[0]) + circuit.i(q[0]) return circuit, diag def _zyz_dec(self): diff --git a/qiskit/extensions/quantum_initializer/uc.py b/qiskit/extensions/quantum_initializer/uc.py new file mode 100644 index 000000000000..f4501d673f37 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/uc.py @@ -0,0 +1,347 @@ +# -*- 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. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at +# ETH in 2018, which was supervised by Raban Iten and Prof. Renato Renner. + +# pylint: disable=invalid-name +# pylint: disable=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Uniformly controlled gates (also called multiplexed gates). + +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state |i> (in the computational basis), +a single-qubit unitary U_i is applied to the target qubit. + +This gate is represented by a block-diagonal matrix, where each block is a +2x2 unitary: + + [[U_0, 0, ...., 0], + [0, U_1, ...., 0], + . + . + [0, 0, ...., U_(2^k-1)]] +""" + +import cmath +import math + +import numpy as np + +from qiskit.circuit.gate import Gate +from qiskit.extensions.standard.h import HGate +from qiskit.quantum_info.operators.predicates import is_unitary_matrix +from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.exceptions import QiskitError +from qiskit.quantum_info.synthesis import euler_angles_1q + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class UCMeta(type): + """A metaclass to ensure that UCGate and UCG are of the same type. + + Can be removed when UCGG gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCGate, UCG} # pylint: disable=unidiomatic-typecheck + + +class UCGate(Gate, metaclass=UCMeta): + """Uniformly controlled gate (also called multiplexed gate). + The decomposition is based on: https://arxiv.org/pdf/quant-ph/0410066.pdf. + """ + + def __init__(self, gate_list, up_to_diagonal=False): + """UCGate Gate initializer. + + Args: + gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], + where each single-qubit unitary U_i is given as a 2*2 numpy array. + + up_to_diagonal (bool): determines if the gate is implemented up to a diagonal. + or if it is decomposed completely (default: False). + If the UCGate u is decomposed up to a diagonal d, this means that the circuit + implements a unitary u' such that d.u'=u. + + Raises: + QiskitError: in case of bad input to the constructor + """ + # check input format + if not isinstance(gate_list, list): + raise QiskitError("The single-qubit unitaries are not provided in a list.") + for gate in gate_list: + if not gate.shape == (2, 2): + raise QiskitError("The dimension of a controlled gate is not equal to (2,2).") + if not gate_list: + raise QiskitError("The gate list cannot be empty.") + + # Check if number of gates in gate_list is a positive power of two + num_contr = math.log2(len(gate_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled single-qubit gates is not a " + "non-negative power of 2.") + + # Check if the single-qubit gates are unitaries + for gate in gate_list: + if not is_unitary_matrix(gate, _EPS): + raise QiskitError("A controlled gate is not unitary.") + + # Create new gate. + super().__init__("multiplexer", int(num_contr) + 1, gate_list) + self.up_to_diagonal = up_to_diagonal + + def _get_diagonal(self): + # Important: for a control list q_controls = [q[0],...,q_[k-1]] the + # diagonal gate is provided in the computational basis of the qubits + # q[k-1],...,q[0],q_target, decreasingly ordered with respect to the + # significance of the qubit in the computational basis + _, diag = self._dec_ucg() + return diag + + def _define(self): + ucg_circuit, _ = self._dec_ucg() + self.definition = ucg_circuit.data + + def _dec_ucg(self): + """ + Call to create a circuit that implements the uniformly controlled gate. If + up_to_diagonal=True, the circuit implements the gate up to a diagonal gate and + the diagonal gate is also returned. + """ + diag = np.ones(2 ** self.num_qubits).tolist() + q = QuantumRegister(self.num_qubits) + q_controls = q[1:] + q_target = q[0] + circuit = QuantumCircuit(q) + # If there is no control, we use the ZYZ decomposition + if not q_controls: + theta, phi, lamb = euler_angles_1q(self.params[0]) + circuit.u3(theta, phi, lamb, q) + return circuit, diag + # If there is at least one control, first, + # we find the single qubit gates of the decomposition. + (single_qubit_gates, diag) = self._dec_ucg_help() + # Now, it is easy to place the C-NOT gates and some Hadamards and Rz(pi/2) gates + # (which are absorbed into the single-qubit unitaries) to get back the full decomposition. + for i, gate in enumerate(single_qubit_gates): + # Absorb Hadamards and Rz(pi/2) gates + if i == 0: + squ = HGate().to_matrix().dot(gate) + elif i == len(single_qubit_gates) - 1: + squ = gate.dot(UCGate._rz(np.pi / 2)).dot(HGate().to_matrix()) + else: + squ = HGate().to_matrix().dot(gate.dot(UCGate._rz(np.pi / 2))).dot( + HGate().to_matrix()) + # Add single-qubit gate + circuit.squ(squ, q_target) + # The number of the control qubit is given by the number of zeros at the end + # of the binary representation of (i+1) + binary_rep = np.binary_repr(i + 1) + num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) + q_contr_index = num_trailing_zeros + # Add C-NOT gate + if not i == len(single_qubit_gates) - 1: + circuit.cx(q_controls[q_contr_index], q_target) + if not self.up_to_diagonal: + # Important: the diagonal gate is given in the computational basis of the qubits + # q[k-1],...,q[0],q_target (ordered with decreasing significance), + # where q[i] are the control qubits and t denotes the target qubit. + circuit.diagonal(diag.tolist(), q) + return circuit, diag + + def _dec_ucg_help(self): + """ + This method finds the single qubit gate arising in the decomposition of UCGates given in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + """ + single_qubit_gates = [gate.astype(complex) for gate in self.params] + diag = np.ones(2 ** self.num_qubits, dtype=complex) + num_contr = self.num_qubits - 1 + for dec_step in range(num_contr): + num_ucgs = 2 ** dec_step + # The decomposition works recursively and the following loop goes over the different + # UCGates that arise in the decomposition + for ucg_index in range(num_ucgs): + len_ucg = 2 ** (num_contr - dec_step) + for i in range(int(len_ucg / 2)): + shift = ucg_index * len_ucg + a = single_qubit_gates[shift + i] + b = single_qubit_gates[shift + len_ucg // 2 + i] + # Apply the decomposition for UCGates given in equation (3) in + # https://arxiv.org/pdf/quant-ph/0410066.pdf + # to demultiplex one control of all the num_ucgs uniformly-controlled gates + # with log2(len_ucg) uniform controls + v, u, r = self._demultiplex_single_uc(a, b) + # replace the single-qubit gates with v,u (the already existing ones + # are not needed any more) + single_qubit_gates[shift + i] = v + single_qubit_gates[shift + len_ucg // 2 + i] = u + # Now we decompose the gates D as described in Figure 4 in + # https://arxiv.org/pdf/quant-ph/0410066.pdf and merge some of the gates + # into the UCGates and the diagonal at the end of the circuit + + # Remark: The Rz(pi/2) rotation acting on the target qubit and the Hadamard + # gates arising in the decomposition of D are ignored for the moment (they will + # be added together with the C-NOT gates at the end of the decomposition + # (in the method dec_ucg())) + if ucg_index < num_ucgs - 1: + # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and + # merge the UC-Rz rotation with the following UCGate, + # which hasn't been decomposed yet. + k = shift + len_ucg + i + single_qubit_gates[k] = \ + single_qubit_gates[k].dot(UCGate._ct(r)) * \ + UCGate._rz(np.pi / 2).item((0, 0)) + k = k + len_ucg // 2 + single_qubit_gates[k] = \ + single_qubit_gates[k].dot(r) * UCGate._rz(np.pi / 2).item((1, 1)) + else: + # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge + # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit + for ucg_index_2 in range(num_ucgs): + shift_2 = ucg_index_2 * len_ucg + k = 2 * (i + shift_2) + diag[k] = diag[k] * UCGate._ct(r).item((0, 0)) * \ + UCGate._rz(np.pi / 2).item((0, 0)) + diag[k + 1] = diag[k + 1] * UCGate._ct(r).item((1, 1)) * UCGate._rz( + np.pi / 2).item((0, 0)) + k = len_ucg + k + diag[k] *= r.item((0, 0)) * UCGate._rz(np.pi / 2).item((1, 1)) + diag[k + 1] *= r.item((1, 1)) * UCGate._rz(np.pi / 2).item((1, 1)) + return single_qubit_gates, diag + + def _demultiplex_single_uc(self, a, b): + """ + This method implements the decomposition given in equation (3) in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + The decomposition is used recursively to decompose uniformly controlled gates. + a,b = single qubit unitaries + v,u,r = outcome of the decomposition given in the reference mentioned above + (see there for the details). + """ + # The notation is chosen as in https://arxiv.org/pdf/quant-ph/0410066.pdf. + x = a.dot(UCGate._ct(b)) + det_x = np.linalg.det(x) + x11 = x.item((0, 0)) / cmath.sqrt(det_x) + phi = cmath.phase(det_x) + r1 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 - cmath.phase(x11))) + r2 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 + cmath.phase(x11) + np.pi)) + r = np.array([[r1, 0], [0, r2]], dtype=complex) + d, u = np.linalg.eig(r.dot(x).dot(r)) + # If d is not equal to diag(i,-i), then we put it into this "standard" form + # (see eq. (13) in https://arxiv.org/pdf/quant-ph/0410066.pdf) by interchanging + # the eigenvalues and eigenvectors. + if abs(d[0] + 1j) < _EPS: + d = np.flip(d, 0) + u = np.flip(u, 1) + d = np.diag(np.sqrt(d)) + v = d.dot(UCGate._ct(u)).dot(UCGate._ct(r)).dot(b) + return v, u, r + + @staticmethod + def _ct(m): + return np.transpose(np.conjugate(m)) + + @staticmethod + def _rz(alpha): + return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) + + +def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): + """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. + + The decomposition was introduced by Bergholm et al. in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + + Args: + gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], + where each single-qubit unitary U_i is a given as a 2*2 array + q_controls (QuantumRegister|list[(QuantumRegister,int)]): list of k control qubits. + The qubits are ordered according to their significance in the computational basis. + For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), + the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is + performed if q[2] is in the state zero and q[1] is in the state one, and so on + q_target (QuantumRegister|(QuantumRegister,int)): target qubit, where we act on with + the single-qubit gates. + up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up + to a diagonal gate, i.e. a unitary u' is implemented such that there exists a + diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly + controlled gate + + Returns: + QuantumCircuit: the uniformly controlled gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing more than" + " one qubit.") + # Check if q_controls has type "list" + if not isinstance(q_controls, list): + raise QiskitError("The control qubits must be provided as a list" + " (also if there is only one control qubit).") + # Check if gate_list has type "list" + if not isinstance(gate_list, list): + raise QiskitError("The single-qubit unitaries are not provided in a list.") + # Check if number of gates in gate_list is a positive power of two + num_contr = math.log2(len(gate_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled single-qubit gates is not a non negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of single-qubit rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled gates does not correspond to the number of" + " control qubits.") + return self.append(UCGate(gate_list, up_to_diagonal), [q_target] + q_controls) + + +class UCG(UCGate, metaclass=UCMeta): + """The deprecated UCGate class.""" + + def __init__(self, gate_list, up_to_diagonal=False): + import warnings + warnings.warn('The class UCG is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(gate_list, up_to_diagonal) + + +def ucg(self, angle_list, q_controls, q_target, up_to_diagonal=False): + """Deprecated version of uc.""" + + import warnings + warnings.warn('The QuantumCircuit.ucg() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.uc() method instead.', + DeprecationWarning, stacklevel=2) + return uc(self, angle_list, q_controls, q_target, up_to_diagonal) + + +QuantumCircuit.uc = uc +QuantumCircuit.ucg = ucg # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py new file mode 100644 index 000000000000..83cb1f923338 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py @@ -0,0 +1,195 @@ +# -*- 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. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, +# which was supervised by Raban Iten and Prof. Renato Renner. + +""" +(Abstract) base class for uniformly controlled (also called multiplexed) single-qubit rotations R_t. +This class provides a basis for the decomposition of uniformly controlled R_x,R_y and R_z gates +(i.e., for t=x,y,z). These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_t(a_i) is applied to the target qubit for a (real) angle a_i. +""" + +import math + +import numpy as np + +from qiskit.circuit import Gate, QuantumCircuit +from qiskit.circuit.quantumcircuit import QuantumRegister +from qiskit.exceptions import QiskitError + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class UCPauliRotMeta(type): + """A metaclass to ensure that UCPauliRotGate and UCRot are of the same type. + + Can be removed when UCRot gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCPauliRotGate, UCRot} # pylint: disable=unidiomatic-typecheck + + +class UCPauliRotGate(Gate, metaclass=UCPauliRotMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on 'Synthesis of Quantum Logic Circuits' + by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}]. Must have at least one entry. + + rot_axis = rotation axis for the single qubit rotations + (currently, 'X', 'Y' and 'Z' are supported) + """ + + def __init__(self, angle_list, rot_axis): + self.rot_axes = rot_axis + # Check if angle_list has type "list" + if not isinstance(angle_list, list): + raise QiskitError('The angles are not provided in a list.') + # Check if the angles in angle_list are real numbers + for angle in angle_list: + try: + float(angle) + except TypeError: + raise QiskitError( + 'An angle cannot be converted to type float (real angles are expected).') + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + 'The number of controlled rotation gates is not a non-negative power of 2.') + if rot_axis not in ('X', 'Y', 'Z'): + raise QiskitError('Rotation axis is not supported.') + # Create new gate. + num_qubits = int(num_contr) + 1 + super().__init__('ucr' + rot_axis.lower(), num_qubits, angle_list) + + def _define(self): + ucr_circuit = self._dec_ucrot() + gate_num = len(ucr_circuit.data) + gate = ucr_circuit.to_instruction() + q = QuantumRegister(self.num_qubits) + ucr_circuit = QuantumCircuit(q) + if gate_num == 0: + # ToDo: if we would not add the identity here, this would lead to troubles + # ToDo: simulating the circuit afterwards. + # this should probably be fixed in the behavior of QuantumCircuit. + ucr_circuit.i(q[0]) + else: + ucr_circuit.append(gate, q[:]) + self.definition = ucr_circuit.data + + def _dec_ucrot(self): + """ + Finds a decomposition of a UC rotation gate into elementary gates + (C-NOTs and single-qubit rotations). + """ + q = QuantumRegister(self.num_qubits) + circuit = QuantumCircuit(q) + q_target = q[0] + q_controls = q[1:] + if not q_controls: # equivalent to: if len(q_controls) == 0 + if self.rot_axes == 'X': + if np.abs(self.params[0]) > _EPS: + circuit.rx(self.params[0], q_target) + if self.rot_axes == 'Y': + if np.abs(self.params[0]) > _EPS: + circuit.ry(self.params[0], q_target) + if self.rot_axes == 'Z': + if np.abs(self.params[0]) > _EPS: + circuit.rz(self.params[0], q_target) + else: + # First, we find the rotation angles of the single-qubit rotations acting + # on the target qubit + angles = self.params.copy() + UCPauliRotGate._dec_uc_rotations(angles, 0, len(angles), False) + # Now, it is easy to place the C-NOT gates to get back the full decomposition. + for (i, angle) in enumerate(angles): + if self.rot_axes == 'X': + if np.abs(angle) > _EPS: + circuit.rx(angle, q_target) + if self.rot_axes == 'Y': + if np.abs(angle) > _EPS: + circuit.ry(angle, q_target) + if self.rot_axes == 'Z': + if np.abs(angle) > _EPS: + circuit.rz(angle, q_target) + # Determine the index of the qubit we want to control the C-NOT gate. + # Note that it corresponds + # to the number of trailing zeros in the binary representation of i+1 + if not i == len(angles) - 1: + binary_rep = np.binary_repr(i + 1) + q_contr_index = len(binary_rep) - \ + len(binary_rep.rstrip('0')) + else: + # Handle special case: + q_contr_index = len(q_controls) - 1 + # For X rotations, we have to additionally place some Ry gates around the + # C-NOT gates. They change the basis of the NOT operation, such that the + # decomposition of for uniformly controlled X rotations works correctly by symmetry + # with the decomposition of uniformly controlled Z or Y rotations + if self.rot_axes == 'X': + circuit.ry(np.pi / 2, q_target) + circuit.cx(q_controls[q_contr_index], q_target) + if self.rot_axes == 'X': + circuit.ry(-np.pi / 2, q_target) + return circuit + + @staticmethod + def _dec_uc_rotations(angles, start_index, end_index, reversed_dec): + """ + Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at + the end of the circuit. The rotation angles of the gate R_t are stored in + angles[start_index:end_index]. If reversed_dec == True, it decomposes the gate such that + there is a C-NOT gate at the start of the circuit (in fact, the circuit topology for + the reversed decomposition is the reversed one of the original decomposition) + """ + interval_len_half = (end_index - start_index) // 2 + for i in range(start_index, start_index + interval_len_half): + if not reversed_dec: + angles[i], angles[i + interval_len_half] = \ + UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half]) + else: + angles[i + interval_len_half], angles[i] = \ + UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half]) + if interval_len_half <= 1: + return + else: + UCPauliRotGate._dec_uc_rotations(angles, start_index, start_index + interval_len_half, + False) + UCPauliRotGate._dec_uc_rotations(angles, start_index + interval_len_half, end_index, + True) + + @staticmethod + def _update_angles(angle1, angle2): + """Calculate the new rotation angles according to Shende's decomposition.""" + return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0 + + +class UCRot(UCPauliRotGate, metaclass=UCPauliRotMeta): + """The deprecated DiagonalGate class.""" + + def __init__(self, angle_list, rot_axis): + import warnings + warnings.warn('The class UCRot is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCPauliRotGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list, rot_axis) diff --git a/qiskit/extensions/quantum_initializer/ucg.py b/qiskit/extensions/quantum_initializer/ucg.py index 80f0737aa227..980d54013b5e 100644 --- a/qiskit/extensions/quantum_initializer/ucg.py +++ b/qiskit/extensions/quantum_initializer/ucg.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,300 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at -# ETH in 2018, which was supervised by Raban Iten and Prof. Renato Renner. +"""The uniformly controlled gate. -# pylint: disable=invalid-name -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc - -""" -Uniformly controlled gates (also called multiplexed gates). - -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state |i> (in the computational basis), -a single-qubit unitary U_i is applied to the target qubit. - -This gate is represented by a block-diagonal matrix, where each block is a -2x2 unitary: - - [[U_0, 0, ...., 0], - [0, U_1, ...., 0], - . - . - [0, 0, ...., U_(2^k-1)]] +This module is deprecated, see uc.py. """ -import cmath -import math - -import numpy as np - -from qiskit.circuit.gate import Gate -from qiskit.extensions.standard.h import HGate -from qiskit.quantum_info.operators.predicates import is_unitary_matrix -from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.quantum_info.synthesis import euler_angles_1q - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class UCG(Gate): - """Uniformly controlled gate (also called multiplexed gate). - The decomposition is based on: https://arxiv.org/pdf/quant-ph/0410066.pdf. - """ - - def __init__(self, gate_list, up_to_diagonal=False): - """UCG Gate initializer. - - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is given as a 2*2 numpy array. - - up_to_diagonal (bool): determines if the gate is implemented up to a diagonal. - or if it is decomposed completely (default: False). - If the UCG u is decomposed up to a diagonal d, this means that the circuit - implements a unitary u' such that d.u'=u. - - Raises: - QiskitError: in case of bad input to the constructor - """ - # check input format - if not isinstance(gate_list, list): - raise QiskitError("The single-qubit unitaries are not provided in a list.") - for gate in gate_list: - if not gate.shape == (2, 2): - raise QiskitError("The dimension of a controlled gate is not equal to (2,2).") - if not gate_list: - raise QiskitError("The gate list cannot be empty.") - - # Check if number of gates in gate_list is a positive power of two - num_contr = math.log2(len(gate_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a " - "non-negative power of 2.") - - # Check if the single-qubit gates are unitaries - for gate in gate_list: - if not is_unitary_matrix(gate, _EPS): - raise QiskitError("A controlled gate is not unitary.") - - # Create new gate. - super().__init__("multiplexer", int(num_contr) + 1, gate_list) - self.up_to_diagonal = up_to_diagonal - - def _get_diagonal(self): - # Important: for a control list q_controls = [q[0],...,q_[k-1]] the - # diagonal gate is provided in the computational basis of the qubits - # q[k-1],...,q[0],q_target, decreasingly ordered with respect to the - # significance of the qubit in the computational basis - _, diag = self._dec_ucg() - return diag - - def _define(self): - ucg_circuit, _ = self._dec_ucg() - self.definition = ucg_circuit.data - - def _dec_ucg(self): - """ - Call to create a circuit that implements the uniformly controlled gate. If - up_to_diagonal=True, the circuit implements the gate up to a diagonal gate and - the diagonal gate is also returned. - """ - diag = np.ones(2 ** self.num_qubits).tolist() - q = QuantumRegister(self.num_qubits) - q_controls = q[1:] - q_target = q[0] - circuit = QuantumCircuit(q) - # If there is no control, we use the ZYZ decomposition - if not q_controls: - theta, phi, lamb = euler_angles_1q(self.params[0]) - circuit.u3(theta, phi, lamb, q) - return circuit, diag - # If there is at least one control, first, - # we find the single qubit gates of the decomposition. - (single_qubit_gates, diag) = self._dec_ucg_help() - # Now, it is easy to place the C-NOT gates and some Hadamards and Rz(pi/2) gates - # (which are absorbed into the single-qubit unitaries) to get back the full decomposition. - for i, gate in enumerate(single_qubit_gates): - # Absorb Hadamards and Rz(pi/2) gates - if i == 0: - squ = HGate().to_matrix().dot(gate) - elif i == len(single_qubit_gates) - 1: - squ = gate.dot(UCG._rz(np.pi / 2)).dot(HGate().to_matrix()) - else: - squ = HGate().to_matrix().dot(gate.dot(UCG._rz(np.pi / 2))).dot(HGate().to_matrix()) - # Add single-qubit gate - circuit.squ(squ, q_target) - # The number of the control qubit is given by the number of zeros at the end - # of the binary representation of (i+1) - binary_rep = np.binary_repr(i + 1) - num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) - q_contr_index = num_trailing_zeros - # Add C-NOT gate - if not i == len(single_qubit_gates) - 1: - circuit.cx(q_controls[q_contr_index], q_target) - if not self.up_to_diagonal: - # Important: the diagonal gate is given in the computational basis of the qubits - # q[k-1],...,q[0],q_target (ordered with decreasing significance), - # where q[i] are the control qubits and t denotes the target qubit. - circuit.diag_gate(diag.tolist(), q) - return circuit, diag - - def _dec_ucg_help(self): - """ - This method finds the single qubit gate arising in the decomposition of UCGs given in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - """ - single_qubit_gates = [gate.astype(complex) for gate in self.params] - diag = np.ones(2 ** self.num_qubits, dtype=complex) - num_contr = self.num_qubits - 1 - for dec_step in range(num_contr): - num_ucgs = 2 ** dec_step - # The decomposition works recursively and the following loop goes over the different - # UCGs that arise in the decomposition - for ucg_index in range(num_ucgs): - len_ucg = 2 ** (num_contr - dec_step) - for i in range(int(len_ucg / 2)): - shift = ucg_index * len_ucg - a = single_qubit_gates[shift + i] - b = single_qubit_gates[shift + len_ucg // 2 + i] - # Apply the decomposition for UCGs given in equation (3) in - # https://arxiv.org/pdf/quant-ph/0410066.pdf - # to demultiplex one control of all the num_ucgs uniformly-controlled gates - # with log2(len_ucg) uniform controls - v, u, r = self._demultiplex_single_uc(a, b) - # replace the single-qubit gates with v,u (the already existing ones - # are not needed any more) - single_qubit_gates[shift + i] = v - single_qubit_gates[shift + len_ucg // 2 + i] = u - # Now we decompose the gates D as described in Figure 4 in - # https://arxiv.org/pdf/quant-ph/0410066.pdf and merge some of the gates - # into the UCGs and the diagonal at the end of the circuit - - # Remark: The Rz(pi/2) rotation acting on the target qubit and the Hadamard - # gates arising in the decomposition of D are ignored for the moment (they will - # be added together with the C-NOT gates at the end of the decomposition - # (in the method dec_ucg())) - if ucg_index < num_ucgs - 1: - # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and - # merge the UC-Rz rotation with the following UCG, - # which hasn't been decomposed yet. - k = shift + len_ucg + i - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(UCG._ct(r)) * UCG._rz(np.pi / 2).item((0, 0)) - k = k + len_ucg // 2 - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(r) * UCG._rz(np.pi / 2).item((1, 1)) - else: - # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge - # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit - for ucg_index_2 in range(num_ucgs): - shift_2 = ucg_index_2 * len_ucg - k = 2 * (i + shift_2) - diag[k] = diag[k] * UCG._ct(r).item((0, 0)) * UCG._rz(np.pi / 2).item( - (0, 0)) - diag[k + 1] = diag[k + 1] * UCG._ct(r).item((1, 1)) * UCG._rz( - np.pi / 2).item((0, 0)) - k = len_ucg + k - diag[k] *= r.item((0, 0)) * UCG._rz(np.pi / 2).item((1, 1)) - diag[k + 1] *= r.item((1, 1)) * UCG._rz(np.pi / 2).item((1, 1)) - return single_qubit_gates, diag - - def _demultiplex_single_uc(self, a, b): - """ - This method implements the decomposition given in equation (3) in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - The decomposition is used recursively to decompose uniformly controlled gates. - a,b = single qubit unitaries - v,u,r = outcome of the decomposition given in the reference mentioned above - (see there for the details). - """ - # The notation is chosen as in https://arxiv.org/pdf/quant-ph/0410066.pdf. - x = a.dot(UCG._ct(b)) - det_x = np.linalg.det(x) - x11 = x.item((0, 0)) / cmath.sqrt(det_x) - phi = cmath.phase(det_x) - r1 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 - cmath.phase(x11))) - r2 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 + cmath.phase(x11) + np.pi)) - r = np.array([[r1, 0], [0, r2]], dtype=complex) - d, u = np.linalg.eig(r.dot(x).dot(r)) - # If d is not equal to diag(i,-i), then we put it into this "standard" form - # (see eq. (13) in https://arxiv.org/pdf/quant-ph/0410066.pdf) by interchanging - # the eigenvalues and eigenvectors. - if abs(d[0] + 1j) < _EPS: - d = np.flip(d, 0) - u = np.flip(u, 1) - d = np.diag(np.sqrt(d)) - v = d.dot(UCG._ct(u)).dot(UCG._ct(r)).dot(b) - return v, u, r - - @staticmethod - def _ct(m): - return np.transpose(np.conjugate(m)) - - @staticmethod - def _rz(alpha): - return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) - - -def ucg(self, gate_list, q_controls, q_target, up_to_diagonal=False): - """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. - - The decomposition was introduced by Bergholm et al. in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is a given as a 2*2 array - q_controls (QuantumRegister|list[(QuantumRegister,int)]): list of k control qubits. - The qubits are ordered according to their significance in the computational basis. - For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), - the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is - performed if q[2] is in the state zero and q[1] is in the state one, and so on - q_target (QuantumRegister|(QuantumRegister,int)): target qubit, where we act on with - the single-qubit gates. - up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up - to a diagonal gate, i.e. a unitary u' is implemented such that there exists a - diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly - controlled gate - - Returns: - QuantumCircuit: the uniformly controlled gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing more than" - " one qubit.") - # Check if q_controls has type "list" - if not isinstance(q_controls, list): - raise QiskitError("The control qubits must be provided as a list" - " (also if there is only one control qubit).") - # Check if gate_list has type "list" - if not isinstance(gate_list, list): - raise QiskitError("The single-qubit unitaries are not provided in a list.") - # Check if number of gates in gate_list is a positive power of two - num_contr = math.log2(len(gate_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a non negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of single-qubit rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled gates does not correspond to the number of" - " control qubits.") - return self.append(UCG(gate_list, up_to_diagonal), [q_target] + q_controls) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.uc import UCG, ucg -QuantumCircuit.ucg = ucg +warnings.warn('This module is deprecated. The UCGate/UCG can now be found in uc.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucrot.py b/qiskit/extensions/quantum_initializer/ucrot.py index 81cff4c4bfe9..514a86f7d471 100644 --- a/qiskit/extensions/quantum_initializer/ucrot.py +++ b/qiskit/extensions/quantum_initializer/ucrot.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,157 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, -# which was supervised by Raban Iten and Prof. Renato Renner. +"""The uniformly controlled Pauli rotation gate. +This module is deprecated, see uc_pauli_rot.py """ -(Abstract) base class for uniformly controlled (also called multiplexed) single-qubit rotations R_t. -This class provides a basis for the decomposition of uniformly controlled R_x,R_y and R_z gates -(i.e., for t=x,y,z). These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_t(a_i) is applied to the target qubit for a (real) angle a_i. -""" - -import math - -import numpy as np - -from qiskit.circuit import Gate, QuantumCircuit -from qiskit.circuit.quantumcircuit import QuantumRegister -from qiskit.exceptions import QiskitError - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class UCRot(Gate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on 'Synthesis of Quantum Logic Circuits' - by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}]. Must have at least one entry. - - rot_axis = rotation axis for the single qubit rotations - (currently, "X","Y" and "Z" are supported) - """ - - def __init__(self, angle_list, rot_axis): - self.rot_axes = rot_axis - # Check if angle_list has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles are not provided in a list.") - # Check if the angles in angle_list are real numbers - for angle in angle_list: - try: - float(angle) - except TypeError: - raise QiskitError( - "An angle cannot be converted to type float (real angles are expected).") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled rotation gates is not a non-negative power of 2.") - if rot_axis not in ("X", "Y", "Z"): - raise QiskitError("Rotation axis is not supported.") - # Create new gate. - num_qubits = int(num_contr) + 1 - super().__init__("ucrot" + rot_axis, num_qubits, angle_list) - - def _define(self): - ucr_circuit = self._dec_ucrot() - gate_num = len(ucr_circuit.data) - gate = ucr_circuit.to_instruction() - q = QuantumRegister(self.num_qubits) - ucr_circuit = QuantumCircuit(q) - if gate_num == 0: - # ToDo: if we would not add the identity here, this would lead to troubles - # ToDo: simulating the circuit afterwards. - # this should probably be fixed in the behaviour of QuantumCircuit. - ucr_circuit.iden(q[0]) - else: - ucr_circuit.append(gate, q[:]) - self.definition = ucr_circuit.data - - def _dec_ucrot(self): - """ - finds a decomposition of a UC rotation gate into elementary gates - (C-NOTs and single-qubit rotations). - """ - q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q) - q_target = q[0] - q_controls = q[1:] - if not q_controls: # equivalent to: if len(q_controls) == 0 - if self.rot_axes == "X": - if np.abs(self.params[0]) > _EPS: - circuit.rx(self.params[0], q_target) - if self.rot_axes == "Y": - if np.abs(self.params[0]) > _EPS: - circuit.ry(self.params[0], q_target) - if self.rot_axes == "Z": - if np.abs(self.params[0]) > _EPS: - circuit.rz(self.params[0], q_target) - else: - # First, we find the rotation angles of the single-qubit rotations acting - # on the target qubit - angles = self.params.copy() - UCRot._dec_uc_rotations(angles, 0, len(angles), False) - # Now, it is easy to place the C-NOT gates to get back the full decomposition.s - for (i, angle) in enumerate(angles): - if self.rot_axes == "X": - if np.abs(angle) > _EPS: - circuit.rx(angle, q_target) - if self.rot_axes == "Y": - if np.abs(angle) > _EPS: - circuit.ry(angle, q_target) - if self.rot_axes == "Z": - if np.abs(angle) > _EPS: - circuit.rz(angle, q_target) - # Determine the index of the qubit we want to control the C-NOT gate. - # Note that it corresponds - # to the number of trailing zeros in the binary representation of i+1 - if not i == len(angles) - 1: - binary_rep = np.binary_repr(i + 1) - q_contr_index = len(binary_rep) - len(binary_rep.rstrip('0')) - else: - # Handle special case: - q_contr_index = len(q_controls) - 1 - # For X rotations, we have to additionally place some Ry gates around the - # C-NOT gates. They change the basis of the NOT operation, such that the - # decomposition of for uniformly controlled X rotations works correctly by symmetry - # with the decomposition of uniformly controlled Z or Y rotations - if self.rot_axes == "X": - circuit.ry(np.pi / 2, q_target) - circuit.cx(q_controls[q_contr_index], q_target) - if self.rot_axes == "X": - circuit.ry(-np.pi / 2, q_target) - return circuit - @staticmethod - def _dec_uc_rotations(angles, start_index, end_index, reversedDec): - """ - Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at - the end of the circuit. The rotation angles of the gate R_t are stored in - angles[start_index:end_index]. If reversed == True, it decomposes the gate such that - there is a C-NOT gate at the start of the circuit (in fact, the circuit topology for - the reversed decomposition is the reversed one of the original decomposition) - """ - interval_len_half = (end_index - start_index) // 2 - for i in range(start_index, start_index + interval_len_half): - if not reversedDec: - angles[i], angles[i + interval_len_half] = UCRot._update_angles(angles[i], angles[ - i + interval_len_half]) - else: - angles[i + interval_len_half], angles[i] = UCRot._update_angles(angles[i], angles[ - i + interval_len_half]) - if interval_len_half <= 1: - return - else: - UCRot._dec_uc_rotations(angles, start_index, start_index + interval_len_half, False) - UCRot._dec_uc_rotations(angles, start_index + interval_len_half, end_index, True) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCRot - @staticmethod - def _update_angles(angle1, angle2): - """Calculate the new rotation angles according to Shende's decomposition""" - return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0 +warnings.warn('This module is deprecated. The UCPauliRotGate/UCRot is now in uc_pauli_rot.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucrx.py b/qiskit/extensions/quantum_initializer/ucrx.py new file mode 100644 index 000000000000..f6893db4d2fe --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucrx.py @@ -0,0 +1,128 @@ +# -*- 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=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations around the X-axes +(i.e., uniformly controlled R_x rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_x(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRXMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRXGate and UCX are of the same type. + + Can be removed when UCX gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRXGate, UCX} # pylint: disable=unidiomatic-typecheck + + +class UCRXGate(UCPauliRotGate, metaclass=UCRXMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "X") + + +def ucrx(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Rx(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Rx(a_1) is performed if q[1] is in the state + one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing more" + " than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not a non-negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to the number of" + " control-qubits.") + return self.append(UCRXGate(angle_list), [q_target] + q_controls, []) + + +class UCX(UCRXGate, metaclass=UCRXMeta): + """The deprecated UCRXGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCX is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucx(self, angle_list, q_controls, q_target): + """Deprecated version of ucrx.""" + import warnings + warnings.warn('The QuantumCircuit. ucx() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit. ucrx() method instead.', + DeprecationWarning, stacklevel=2) + return ucrx(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucrx = ucrx +QuantumCircuit.ucx = ucx # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucry.py b/qiskit/extensions/quantum_initializer/ucry.py new file mode 100644 index 000000000000..280bbad20dd6 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucry.py @@ -0,0 +1,125 @@ +# -*- 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. + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations +around the Y-axes (i.e., uniformly controlled R_y rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_y(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRYMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRYGate and UCY are of the same type. + + Can be removed when UCY gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRYGate, UCY} # pylint: disable=unidiomatic-typecheck + + +class UCRYGate(UCPauliRotGate, metaclass=UCRYMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "Y") + + +def ucry(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list[Qubit]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Ry(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Ry(a_1) is performed if q[1] is in the state + one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing" + " more than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not" + " a non-negative power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to" + " the number of control-qubits.") + return self.append(UCRYGate(angle_list), [q_target] + q_controls, []) + + +class UCY(UCRYGate, metaclass=UCRYMeta): + """The deprecated UCRYGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCY is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucy(self, angle_list, q_controls, q_target): + """Deprecated version of ucry.""" + import warnings + warnings.warn('The QuantumCircuit ucy() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit ucry() method instead.', + DeprecationWarning, stacklevel=2) + return ucry(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucry = ucry +QuantumCircuit.ucy = ucy # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucrz.py b/qiskit/extensions/quantum_initializer/ucrz.py new file mode 100644 index 000000000000..cfdd7e684629 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucrz.py @@ -0,0 +1,126 @@ +# -*- 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. + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations +around the Z-axes (i.e., uniformly controlled R_z rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_z(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRZMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRZGate and UCZ are of the same type. + + Can be removed when UCZ gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRZGate, UCZ} # pylint: disable=unidiomatic-typecheck + + +class UCRZGate(UCPauliRotGate, metaclass=UCRZMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "Z") + + +def ucrz(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed gates) Rz rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list[Qubit]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Rz(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Rz(a_1) is performed if q[1] is in + the state one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to + the provided number of single-qubit unitaries; if an input is of + the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing" + " more than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not a non-negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to the number" + " of control-qubits.") + return self.append(UCRZGate(angle_list), [q_target] + q_controls, []) + + +class UCZ(UCRZGate, metaclass=UCRZMeta): + """The deprecated UCRZGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCZ is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucz(self, angle_list, q_controls, q_target): + """Deprecated version of ucrz.""" + import warnings + warnings.warn('The QuantumCircuit.ucz() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.ucrz() method instead.', + DeprecationWarning, stacklevel=2) + return ucrz(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucrz = ucrz +QuantumCircuit.ucz = ucz # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucx.py b/qiskit/extensions/quantum_initializer/ucx.py index 3dbc3a12eae3..9da13eda5d28 100644 --- a/qiskit/extensions/quantum_initializer/ucx.py +++ b/qiskit/extensions/quantum_initializer/ucx.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,84 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc +"""The uniformly controlled RX gate. +This module is deprecated, see ucrx.py """ -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations around the X-axes -(i.e., uniformly controlled R_x rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_x(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCX(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "X") - - -def ucx(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Rx(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Rx(a_1) is performed if q[1] is in the state - one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing more" - " than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number of" - " control-qubits.") - return self.append(UCX(angle_list), [q_target] + q_controls, []) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucrx import UCX, ucx -QuantumCircuit.ucx = ucx +warnings.warn('This module is deprecated. The UCRXGate/UCX can now be found in ucrx.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucy.py b/qiskit/extensions/quantum_initializer/ucy.py index 1c3546cd3675..f993508bcd46 100644 --- a/qiskit/extensions/quantum_initializer/ucy.py +++ b/qiskit/extensions/quantum_initializer/ucy.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,81 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations -around the Y-axes (i.e., uniformly controlled R_y rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_y(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCY(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Y") +"""The uniformly controlled RY gate. +This module is deprecated, see ucry.py +""" -def ucy(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list[Qubit]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Ry(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Ry(a_1) is performed if q[1] is in the state - one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not" - " a non-negative power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to" - " the number of control-qubits.") - return self.append(UCY(angle_list), [q_target] + q_controls, []) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucry import UCY, ucy -QuantumCircuit.ucy = ucy +warnings.warn('This module is deprecated. The UCRYGate/UCY can now be found in ucry.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucz.py b/qiskit/extensions/quantum_initializer/ucz.py index 132ef2967f43..b3ca60da5ee1 100644 --- a/qiskit/extensions/quantum_initializer/ucz.py +++ b/qiskit/extensions/quantum_initializer/ucz.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (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 @@ -12,82 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations -around the Z-axes (i.e., uniformly controlled R_z rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_z(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCZ(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Z") +"""The uniformly controlled RZ gate. +This module is deprecated, see ucrz.py +""" -def ucz(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed gates) Rz rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list[Qubit]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Rz(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Rz(a_1) is performed if q[1] is in - the state one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to - the provided number of single-qubit unitaries; if an input is of - the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number" - " of control-qubits.") - return self.append(UCZ(angle_list), [q_target] + q_controls, []) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucrz import UCZ, ucz -QuantumCircuit.ucz = ucz +warnings.warn('This module is deprecated. The UCRZGate/UCZ can now be found in ucrz.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/__init__.py b/qiskit/extensions/standard/__init__.py index 863d3821ad04..c5231f02a2e7 100644 --- a/qiskit/extensions/standard/__init__.py +++ b/qiskit/extensions/standard/__init__.py @@ -15,22 +15,35 @@ """Standard gates.""" from .barrier import Barrier from .h import HGate, CHGate -from .iden import IdGate +from .i import IGate from .ms import MSGate from .r import RGate from .rccx import RCCXGate from .rcccx import RCCCXGate -from .rx import RXGate, CrxGate +from .rx import RXGate, CRXGate from .rxx import RXXGate -from .ry import RYGate, CryGate -from .rz import RZGate, CrzGate +from .ry import RYGate, CRYGate +from .rz import RZGate, CRZGate from .rzz import RZZGate from .s import SGate, SdgGate -from .swap import SwapGate, FredkinGate +from .swap import SwapGate, CSwapGate from .t import TGate, TdgGate -from .u1 import U1Gate, Cu1Gate +from .u1 import U1Gate, CU1Gate from .u2 import U2Gate -from .u3 import U3Gate, Cu3Gate -from .x import XGate, CnotGate, ToffoliGate -from .y import YGate, CyGate -from .z import ZGate, CzGate +from .u3 import U3Gate, CU3Gate +from .x import XGate, CXGate, CCXGate +from .y import YGate, CYGate +from .z import ZGate, CZGate + +# deprecated gates, to be removed +from .i import IdGate +from .x import ToffoliGate +from .swap import FredkinGate +from .x import CnotGate +from .y import CyGate +from .z import CzGate +from .u1 import Cu1Gate +from .u3 import Cu3Gate +from .rx import CrxGate +from .ry import CryGate +from .rz import CrzGate diff --git a/qiskit/extensions/standard/ccx.py b/qiskit/extensions/standard/ccx.py index 685d996265af..816c11f36339 100644 --- a/qiskit/extensions/standard/ccx.py +++ b/qiskit/extensions/standard/ccx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -Toffoli gate. Controlled-Controlled-X. +Controlled-Controlled-X (or Toffoli) Gate. """ import warnings diff --git a/qiskit/extensions/standard/ch.py b/qiskit/extensions/standard/ch.py index c6223843cac0..19bd446ef99e 100644 --- a/qiskit/extensions/standard/ch.py +++ b/qiskit/extensions/standard/ch.py @@ -13,11 +13,11 @@ # that they have been altered from the originals. """ -controlled-H gate. +Controlled-H gate. """ import warnings # pylint: disable=unused-import -from qiskit.extensions.standard.h import HGate, ch +from qiskit.extensions.standard.h import CHGate, ch warnings.warn('This module is deprecated. The CHGate can now be found in h.py', category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/crx.py b/qiskit/extensions/standard/crx.py index cc8b4f373e87..5155fa62e524 100644 --- a/qiskit/extensions/standard/crx.py +++ b/qiskit/extensions/standard/crx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-rx gate. +Controlled-rx gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cry.py b/qiskit/extensions/standard/cry.py index 91d0ad400934..610fdee92010 100644 --- a/qiskit/extensions/standard/cry.py +++ b/qiskit/extensions/standard/cry.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-ry gate. +Controlled-ry gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/crz.py b/qiskit/extensions/standard/crz.py index 4b582257ffc5..001304f21802 100644 --- a/qiskit/extensions/standard/crz.py +++ b/qiskit/extensions/standard/crz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-rz gate. +Controlled-rz gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cswap.py b/qiskit/extensions/standard/cswap.py index 48e4d9ee7cd4..9a39550ee305 100644 --- a/qiskit/extensions/standard/cswap.py +++ b/qiskit/extensions/standard/cswap.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -Fredkin gate. Controlled-SWAP. +Controlled-Swap gate or Fredkin gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cu1.py b/qiskit/extensions/standard/cu1.py index 0f671409ee82..68acbec0eb01 100644 --- a/qiskit/extensions/standard/cu1.py +++ b/qiskit/extensions/standard/cu1.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-u1 gate. +Controlled-u1 gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cu3.py b/qiskit/extensions/standard/cu3.py index ecc5504b18a3..4b7d9fd1a08f 100644 --- a/qiskit/extensions/standard/cu3.py +++ b/qiskit/extensions/standard/cu3.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-u3 gate. +Controlled-u3 gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cx.py b/qiskit/extensions/standard/cx.py index 0562ad745fb6..121647049828 100644 --- a/qiskit/extensions/standard/cx.py +++ b/qiskit/extensions/standard/cx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-NOT gate. +Controlled-not gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cy.py b/qiskit/extensions/standard/cy.py index 98157d4b7032..c2c2574990db 100644 --- a/qiskit/extensions/standard/cy.py +++ b/qiskit/extensions/standard/cy.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-Y gate. +Controlled-Y gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cz.py b/qiskit/extensions/standard/cz.py index 37be8f528c2f..12ce11bbaee1 100644 --- a/qiskit/extensions/standard/cz.py +++ b/qiskit/extensions/standard/cz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-Phase gate. +Controlled-Phase gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/h.py b/qiskit/extensions/standard/h.py index 5e3d1629235f..b6fc2460ee7b 100644 --- a/qiskit/extensions/standard/h.py +++ b/qiskit/extensions/standard/h.py @@ -20,6 +20,8 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.circuit.controlledgate import ControlledGate +from qiskit.extensions.standard.t import TGate, TdgGate +from qiskit.extensions.standard.s import SGate, SdgGate from qiskit.qasm import pi from qiskit.util import deprecate_arguments @@ -30,7 +32,7 @@ class HGate(Gate): def __init__(self, label=None): """Create new Hadamard gate.""" - super().__init__("h", 1, [], label=label) + super().__init__('h', 1, [], label=label) def _define(self): """ @@ -38,7 +40,7 @@ def _define(self): """ from qiskit.extensions.standard.u2 import U2Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U2Gate(0, pi), [q[0]], []) ] @@ -106,11 +108,11 @@ def h(self, qubit, *, q=None): # pylint: disable=invalid-name,unused-argument class CHGate(ControlledGate): - """controlled-H gate.""" + """The controlled-H gate.""" def __init__(self): """Create new CH gate.""" - super().__init__("ch", 2, [], num_ctrl_qubits=1) + super().__init__('ch', 2, [], num_ctrl_qubits=1) self.base_gate = HGate() def _define(self): @@ -125,16 +127,14 @@ def _define(self): sdg b; } """ - from qiskit.extensions.standard.s import SGate, SdgGate - from qiskit.extensions.standard.t import TGate, TdgGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (SGate(), [q[1]], []), (HGate(), [q[1]], []), (TGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (TdgGate(), [q[1]], []), (HGate(), [q[1]], []), (SdgGate(), [q[1]], []) @@ -148,11 +148,12 @@ def inverse(self): return CHGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Ch gate.""" + """Return a numpy.array for the CH gate.""" return numpy.array([[1, 0, 0, 0], - [0, 1/numpy.sqrt(2), 0, 1/numpy.sqrt(2)], + [0, 1 / numpy.sqrt(2), 0, 1 / numpy.sqrt(2)], [0, 0, 1, 0], - [0, 1/numpy.sqrt(2), 0, -1/numpy.sqrt(2)]], dtype=complex) + [0, 1 / numpy.sqrt(2), 0, -1 / numpy.sqrt(2)]], + dtype=complex) @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) diff --git a/qiskit/extensions/standard/i.py b/qiskit/extensions/standard/i.py new file mode 100644 index 000000000000..77b079f384c3 --- /dev/null +++ b/qiskit/extensions/standard/i.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017. +# +# 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. + +""" +Identity gate. +""" +import warnings +import numpy +from qiskit.circuit import Gate +from qiskit.circuit import QuantumCircuit +from qiskit.util import deprecate_arguments + + +class IMeta(type): + """A metaclass to ensure that Id and I are of the same type. + + Can be removed when IdGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {IGate, IdGate} # pylint: disable=unidiomatic-typecheck + + +class IGate(Gate, metaclass=IMeta): + """Identity gate. + + Identity gate corresponds to a single-qubit gate wait cycle, + and should not be optimized or unrolled (it is an opaque gate). + """ + + def __init__(self, label=None): + """Create new Identity gate.""" + super().__init__('id', 1, [], label=label) + + def inverse(self): + """Invert this gate.""" + return IGate() # self-inverse + + def to_matrix(self): + """Return a numpy.array for the identity gate.""" + return numpy.array([[1, 0], + [0, 1]], dtype=complex) + + +class IdGate(IGate, metaclass=IMeta): + """The deprecated IGate class.""" + + def __init__(self): + warnings.warn('The class IdGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class IGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + +@deprecate_arguments({'q': 'qubit'}) +def i(self, qubit, *, q=None): # pylint: disable=unused-argument + """Apply Identity to to a specified qubit (qubit). + + The Identity gate ensures that nothing is applied to a qubit for one unit + of gate time. It leaves the quantum states |0> and |1> unchanged. + The Identity gate should not be optimized or unrolled (it is an opaque gate). + + Examples: + + Circuit Representation: + + .. jupyter-execute:: + + from qiskit import QuantumCircuit + + circuit = QuantumCircuit(1) + circuit.id(0) # or circuit.i(0) + circuit.draw() + + Matrix Representation: + + .. jupyter-execute:: + + from qiskit.extensions.standard.i import IGate + IGate().to_matrix() + """ + return self.append(IGate(), [qubit], []) + + +@deprecate_arguments({'q': 'qubit'}) +def iden(self, qubit, *, q=None): # pylint: disable=unused-argument + """Deprecated identity gate.""" + warnings.warn('The QuantumCircuit.iden() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.i() method instead.', + DeprecationWarning, stacklevel=2) + return self.append(IGate(), [q], []) + + +# support both i and id as methods of QuantumCircuit +QuantumCircuit.i = i +QuantumCircuit.id = i + +QuantumCircuit.iden = iden # deprecated, remove once IdGate is removed diff --git a/qiskit/extensions/standard/iden.py b/qiskit/extensions/standard/iden.py index bfcafe17d473..d40485931bd6 100644 --- a/qiskit/extensions/standard/iden.py +++ b/qiskit/extensions/standard/iden.py @@ -15,61 +15,9 @@ """ Identity gate. """ -import numpy -from qiskit.circuit import Gate -from qiskit.circuit import QuantumCircuit -from qiskit.util import deprecate_arguments +import warnings +# pylint: disable=unused-import +from qiskit.extensions.standard.i import IdGate, iden - -class IdGate(Gate): - """Identity gate. - - Identity gate corresponds to a single-qubit gate wait cycle, - and should not be optimized or unrolled (it is an opaque gate). - """ - - def __init__(self, label=None): - """Create new Identity gate.""" - super().__init__("id", 1, [], label=label) - - def inverse(self): - """Invert this gate.""" - return IdGate() # self-inverse - - def to_matrix(self): - """Return a Numpy.array for the Id gate.""" - return numpy.array([[1, 0], - [0, 1]], dtype=complex) - - -@deprecate_arguments({'q': 'qubit'}) -def iden(self, qubit, *, q=None): # pylint: disable=unused-argument - """Apply Identity to to a specified qubit (qubit). - - The Identity gate ensures that nothing is applied to a qubit for one unit - of gate time. It leaves the quantum states |0> and |1> unchanged. - The Identity gate should not be optimized or unrolled (it is an opaque gate). - - Examples: - - Circuit Representation: - - .. jupyter-execute:: - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.iden(0) - circuit.draw() - - Matrix Representation: - - .. jupyter-execute:: - - from qiskit.extensions.standard.iden import IdGate - IdGate().to_matrix() - """ - return self.append(IdGate(), [qubit], []) - - -QuantumCircuit.iden = iden +warnings.warn('This module is deprecated. The IdGate can now be found in i.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/ms.py b/qiskit/extensions/standard/ms.py index 161704ce1f45..8c626dfc75b3 100755 --- a/qiskit/extensions/standard/ms.py +++ b/qiskit/extensions/standard/ms.py @@ -35,15 +35,15 @@ class MSGate(Gate): def __init__(self, n_qubits, theta): """Create new MS gate.""" - super().__init__("ms", n_qubits, [theta]) + super().__init__('ms', n_qubits, [theta]) def _define(self): from qiskit.extensions.standard.rxx import RXXGate definition = [] - q = QuantumRegister(self.num_qubits, "q") + q = QuantumRegister(self.num_qubits, 'q') rule = [] for i in range(self.num_qubits): - for j in range(i+1, self.num_qubits): + for j in range(i + 1, self.num_qubits): rule += [(RXXGate(self.params[0]), [q[i], q[j]], [])] for inst in rule: diff --git a/qiskit/extensions/standard/r.py b/qiskit/extensions/standard/r.py index 4e189d23b43b..191444f7c390 100644 --- a/qiskit/extensions/standard/r.py +++ b/qiskit/extensions/standard/r.py @@ -29,7 +29,7 @@ class RGate(Gate): def __init__(self, theta, phi): """Create new r single-qubit gate.""" - super().__init__("r", 1, [theta, phi]) + super().__init__('r', 1, [theta, phi]) def _define(self): """ @@ -37,7 +37,7 @@ def _define(self): """ from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') theta = self.params[0] phi = self.params[1] rule = [ @@ -55,7 +55,7 @@ def inverse(self): return RGate(-self.params[0], self.params[1]) def to_matrix(self): - """Return a Numpy.array for the R gate.""" + """Return a numpy.array for the R gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) exp_m = numpy.exp(-1j * self.params[1]) diff --git a/qiskit/extensions/standard/rcccx.py b/qiskit/extensions/standard/rcccx.py index b91917a86639..1fb481e07167 100644 --- a/qiskit/extensions/standard/rcccx.py +++ b/qiskit/extensions/standard/rcccx.py @@ -19,7 +19,7 @@ from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.qasm import pi @@ -67,20 +67,20 @@ def _define(self): rule = [ (U2Gate(0, pi), [q[3]], []), # H gate (U1Gate(pi / 4), [q[3]], []), # T gate - (CnotGate(), [q[2], q[3]], []), + (CXGate(), [q[2], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), # inverse T gate (U2Gate(0, pi), [q[3]], []), - (CnotGate(), [q[0], q[3]], []), + (CXGate(), [q[0], q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[1], q[3]], []), + (CXGate(), [q[1], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), - (CnotGate(), [q[0], q[3]], []), + (CXGate(), [q[0], q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[1], q[3]], []), + (CXGate(), [q[1], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), (U2Gate(0, pi), [q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[2], q[3]], []), + (CXGate(), [q[2], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), (U2Gate(0, pi), [q[3]], []), ] diff --git a/qiskit/extensions/standard/rccx.py b/qiskit/extensions/standard/rccx.py index 1ee5f58c7364..7835b80c138a 100644 --- a/qiskit/extensions/standard/rccx.py +++ b/qiskit/extensions/standard/rccx.py @@ -19,7 +19,7 @@ from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.qasm import pi @@ -59,11 +59,11 @@ def _define(self): rule = [ (U2Gate(0, pi), [q[2]], []), # H gate (U1Gate(pi / 4), [q[2]], []), # T gate - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (U1Gate(-pi / 4), [q[2]], []), # inverse T gate - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (U1Gate(pi / 4), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (U1Gate(-pi / 4), [q[2]], []), # inverse T gate (U2Gate(0, pi), [q[2]], []), # H gate ] diff --git a/qiskit/extensions/standard/rx.py b/qiskit/extensions/standard/rx.py index 49003f14c85f..801bc27eef75 100644 --- a/qiskit/extensions/standard/rx.py +++ b/qiskit/extensions/standard/rx.py @@ -17,8 +17,8 @@ """ import math import numpy -from qiskit.circuit import ControlledGate from qiskit.circuit import Gate +from qiskit.circuit import ControlledGate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.qasm import pi @@ -26,11 +26,11 @@ class RXGate(Gate): - """rotation around the x-axis.""" + """The rotation around the x-axis.""" def __init__(self, theta): """Create new rx single qubit gate.""" - super().__init__("rx", 1, [theta]) + super().__init__('rx', 1, [theta]) def _define(self): """ @@ -38,7 +38,7 @@ def _define(self): """ from qiskit.extensions.standard.r import RGate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (RGate(self.params[0], 0), [q[0]], []) ] @@ -60,7 +60,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CrxGate(self.params[0]) + return CRXGate(self.params[0]) return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -72,7 +72,7 @@ def inverse(self): return RXGate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the RX gate.""" + """Return a numpy.array for the RX gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -1j * sin], @@ -112,8 +112,18 @@ def rx(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.rx = rx -class CrxGate(ControlledGate): - """controlled-rx gate.""" +class CRXMeta(type): + """A metaclass to ensure that CrxGate and CRXGate are of the same type. + + Can be removed when CrxGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRXGate, CrxGate} # pylint: disable=unidiomatic-typecheck + + +class CRXGate(ControlledGate, metaclass=CRXMeta): + """The controlled-rx gate.""" def __init__(self, theta): """Create new crx gate.""" @@ -132,14 +142,14 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u3 import U3Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] q = QuantumRegister(2, 'q') rule = [ (U1Gate(pi / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(self.params[0] / 2, -pi / 2, 0), [q[1]], []) ] for inst in rule: @@ -148,7 +158,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CrxGate(-self.params[0]) + return CRXGate(-self.params[0]) + + +class CrxGate(CRXGate, metaclass=CRXMeta): + """The deprecated CRXGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CrxGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -156,7 +178,7 @@ def inverse(self): def crx(self, theta, control_qubit, target_qubit, *, ctl=None, tgt=None): # pylint: disable=unused-argument """Apply crx from ctl to tgt with angle theta.""" - return self.append(CrxGate(theta), [control_qubit, target_qubit], []) + return self.append(CRXGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.crx = crx diff --git a/qiskit/extensions/standard/rxx.py b/qiskit/extensions/standard/rxx.py index 5c5dcf726956..853eada75cb3 100644 --- a/qiskit/extensions/standard/rxx.py +++ b/qiskit/extensions/standard/rxx.py @@ -29,24 +29,24 @@ class RXXGate(Gate): def __init__(self, theta): """Create new rxx gate.""" - super().__init__("rxx", 2, [theta]) + super().__init__('rxx', 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate from qiskit.extensions.standard.u3 import U3Gate from qiskit.extensions.standard.h import HGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') theta = self.params[0] rule = [ (U3Gate(np.pi / 2, theta, 0), [q[0]], []), (HGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-theta), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []), (U2Gate(-np.pi, np.pi - theta), [q[0]], []), ] @@ -76,5 +76,4 @@ def rxx(self, theta, qubit1, qubit2): return self.append(RXXGate(theta), [qubit1, qubit2], []) -# Add to QuantumCircuit class QuantumCircuit.rxx = rxx diff --git a/qiskit/extensions/standard/ry.py b/qiskit/extensions/standard/ry.py index 071ff385a0ea..a76668f132f1 100644 --- a/qiskit/extensions/standard/ry.py +++ b/qiskit/extensions/standard/ry.py @@ -26,11 +26,11 @@ class RYGate(Gate): - """rotation around the y-axis.""" + """The rotation around the y-axis.""" def __init__(self, theta): - """Create new ry single qubit gate.""" - super().__init__("ry", 1, [theta]) + """Create new RY single qubit gate.""" + super().__init__('ry', 1, [theta]) def _define(self): """ @@ -38,9 +38,9 @@ def _define(self): """ from qiskit.extensions.standard.r import RGate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (RGate(self.params[0], pi/2), [q[0]], []) + (RGate(self.params[0], pi / 2), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -60,7 +60,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CryGate(self.params[0]) + return CRYGate(self.params[0]) return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -72,7 +72,7 @@ def inverse(self): return RYGate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the RY gate.""" + """Return a numpy.array for the RY gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -sin], @@ -112,12 +112,22 @@ def ry(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.ry = ry -class CryGate(ControlledGate): - """controlled-ry gate.""" +class CRYMeta(type): + """A metaclass to ensure that CryGate and CRYGate are of the same type. + + Can be removed when CryGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRYGate, CryGate} # pylint: disable=unidiomatic-typecheck + + +class CRYGate(ControlledGate, metaclass=CRYMeta): + """The controlled-ry gate.""" def __init__(self, theta): """Create new cry gate.""" - super().__init__("cry", 2, [theta], num_ctrl_qubits=1) + super().__init__('cry', 2, [theta], num_ctrl_qubits=1) self.base_gate = RYGate(theta) def _define(self): @@ -128,15 +138,15 @@ def _define(self): } """ - from qiskit.extensions.standard.x import CnotGate from qiskit.extensions.standard.u3 import U3Gate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U3Gate(self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -144,7 +154,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CryGate(-self.params[0]) + return CRYGate(-self.params[0]) + + +class CryGate(CRYGate, metaclass=CRYMeta): + """The deprecated CRYGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CryGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -152,7 +174,7 @@ def inverse(self): def cry(self, theta, control_qubit, target_qubit, *, ctl=None, tgt=None): # pylint: disable=unused-argument """Apply cry from ctl to tgt with angle theta.""" - return self.append(CryGate(theta), [control_qubit, target_qubit], []) + return self.append(CRYGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.cry = cry diff --git a/qiskit/extensions/standard/rz.py b/qiskit/extensions/standard/rz.py index 83571433811b..74bdb120cab6 100644 --- a/qiskit/extensions/standard/rz.py +++ b/qiskit/extensions/standard/rz.py @@ -23,11 +23,11 @@ class RZGate(Gate): - """rotation around the z-axis.""" + """The rotation around the z-axis.""" def __init__(self, phi): - """Create new rz single qubit gate.""" - super().__init__("rz", 1, [phi]) + """Create new RZ single qubit gate.""" + super().__init__('rz', 1, [phi]) def _define(self): """ @@ -35,7 +35,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(self.params[0]), [q[0]], []) ] @@ -57,7 +57,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CrzGate(self.params[0]) + return CRZGate(self.params[0]) return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -94,12 +94,22 @@ def rz(self, phi, qubit, *, q=None): # pylint: disable=invalid-name,unused-argu QuantumCircuit.rz = rz -class CrzGate(ControlledGate): - """controlled-rz gate.""" +class CRZMeta(type): + """A metaclass to ensure that CrzGate and CRZGate are of the same type. + + Can be removed when CrzGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRZGate, CrzGate} # pylint: disable=unidiomatic-typecheck + + +class CRZGate(ControlledGate, metaclass=CRZMeta): + """The controlled-rz gate.""" def __init__(self, theta): """Create new crz gate.""" - super().__init__("crz", 2, [theta], num_ctrl_qubits=1) + super().__init__('crz', 2, [theta], num_ctrl_qubits=1) self.base_gate = RZGate(theta) def _define(self): @@ -109,15 +119,15 @@ def _define(self): u1(-lambda/2) b; cx a,b; } """ - from qiskit.extensions.standard.x import CnotGate from qiskit.extensions.standard.u1 import U1Gate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate(self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -125,7 +135,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CrzGate(-self.params[0]) + return CRZGate(-self.params[0]) + + +class CrzGate(CRZGate, metaclass=CRZMeta): + """The deprecated CRZGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CrzGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) @@ -148,7 +170,7 @@ def crz(self, theta, control_qubit, target_qubit, circuit.crz(theta,0,1) circuit.draw() """ - return self.append(CrzGate(theta), [control_qubit, target_qubit], []) + return self.append(CRZGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.crz = crz diff --git a/qiskit/extensions/standard/rzz.py b/qiskit/extensions/standard/rzz.py index 717fb264dc96..00f7e322754a 100644 --- a/qiskit/extensions/standard/rzz.py +++ b/qiskit/extensions/standard/rzz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -two-qubit ZZ-rotation gate. +Two-qubit ZZ-rotation gate. """ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit @@ -21,24 +21,24 @@ class RZZGate(Gate): - """Two-qubit ZZ-rotation gate.""" + """The two-qubit ZZ-rotation gate.""" def __init__(self, theta): """Create new rzz gate.""" - super().__init__("rzz", 2, [theta]) + super().__init__('rzz', 2, [theta]) def _define(self): """ gate rzz(theta) a, b { cx a, b; u1(theta) b; cx a, b; } """ from qiskit.extensions.standard.u1 import U1Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(self.params[0]), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) diff --git a/qiskit/extensions/standard/s.py b/qiskit/extensions/standard/s.py index cea199c41a22..7dc61269fe37 100644 --- a/qiskit/extensions/standard/s.py +++ b/qiskit/extensions/standard/s.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -S=diag(1,i) Clifford phase gate or its inverse. +The S gate (Clifford phase gate) and its inverse. """ import numpy from qiskit.circuit import Gate @@ -24,11 +24,11 @@ class SGate(Gate): - """S=diag(1,i) Clifford phase gate.""" + """The S gate, also called Clifford phase gate.""" def __init__(self, label=None): - """Create new S gate.""" - super().__init__("s", 1, [], label=label) + """Create a new S gate.""" + super().__init__('s', 1, [], label=label) def _define(self): """ @@ -36,7 +36,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(pi / 2), [q[0]], []) ] @@ -49,7 +49,7 @@ def inverse(self): return SdgGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the S gate.""" return numpy.array([[1, 0], [0, 1j]], dtype=complex) @@ -58,8 +58,8 @@ class SdgGate(Gate): """Sdg=diag(1,-i) Clifford adjoint phase gate.""" def __init__(self, label=None): - """Create new Sdg gate.""" - super().__init__("sdg", 1, [], label=label) + """Create a new Sdg gate.""" + super().__init__('sdg', 1, [], label=label) def _define(self): """ @@ -67,7 +67,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(-pi / 2), [q[0]], []) ] @@ -80,7 +80,7 @@ def inverse(self): return SGate() def to_matrix(self): - """Return a Numpy.array for the Sdg gate.""" + """Return a numpy.array for the Sdg gate.""" return numpy.array([[1, 0], [0, -1j]], dtype=complex) diff --git a/qiskit/extensions/standard/swap.py b/qiskit/extensions/standard/swap.py index 43aee0c1c4ec..5512eb4e82a8 100644 --- a/qiskit/extensions/standard/swap.py +++ b/qiskit/extensions/standard/swap.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -SWAP gate. +Swap gate. """ import numpy from qiskit.circuit import ControlledGate @@ -28,19 +28,19 @@ class SwapGate(Gate): def __init__(self): """Create new SWAP gate.""" - super().__init__("swap", 2, []) + super().__init__('swap', 2, []) def _define(self): """ gate swap a,b { cx a,b; cx b,a; cx a,b; } """ - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ - (CnotGate(), [q[0], q[1]], []), - (CnotGate(), [q[1], q[0]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), + (CXGate(), [q[1], q[0]], []), + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -60,7 +60,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return FredkinGate() + return CSwapGate() return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -69,7 +69,7 @@ def inverse(self): return SwapGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Swap gate.""" + """Return a numpy.array for the SWAP gate.""" return numpy.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], @@ -105,12 +105,22 @@ def swap(self, qubit1, qubit2): QuantumCircuit.swap = swap -class FredkinGate(ControlledGate): - """Fredkin gate.""" +class CSwapMeta(type): + """A Metaclass to ensure that CSwapGate and FredkinGate are of the same type. + + Can be removed when FredkinGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CSwapGate, FredkinGate} # pylint: disable=unidiomatic-typecheck + + +class CSwapGate(ControlledGate, metaclass=CSwapMeta): + """The controlled-swap gate, also called Fredkin gate.""" def __init__(self): - """Create new Fredkin gate.""" - super().__init__("cswap", 3, [], num_ctrl_qubits=1) + """Create new CSWAP gate.""" + super().__init__('cswap', 3, [], num_ctrl_qubits=1) self.base_gate = SwapGate() def _define(self): @@ -121,13 +131,14 @@ def _define(self): cx c,b; } """ - from qiskit.extensions.standard.x import CnotGate, ToffoliGate + from qiskit.extensions.standard.x import CXGate + from qiskit.extensions.standard.x import CCXGate definition = [] - q = QuantumRegister(3, "q") + q = QuantumRegister(3, 'q') rule = [ - (CnotGate(), [q[2], q[1]], []), - (ToffoliGate(), [q[0], q[1], q[2]], []), - (CnotGate(), [q[2], q[1]], []) + (CXGate(), [q[2], q[1]], []), + (CCXGate(), [q[0], q[1], q[2]], []), + (CXGate(), [q[2], q[1]], []) ] for inst in rule: definition.append(inst) @@ -135,10 +146,10 @@ def _define(self): def inverse(self): """Invert this gate.""" - return FredkinGate() # self-inverse + return CSwapGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Fredkin (CSWAP) gate.""" + """Return a numpy.array for the Fredkin (CSWAP) gate.""" return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], @@ -149,6 +160,18 @@ def to_matrix(self): [0, 0, 0, 0, 0, 0, 0, 1]], dtype=complex) +class FredkinGate(CSwapGate, metaclass=CSwapMeta): + """The deprecated CSwapGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class FredkinGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CSwapGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt1': 'target_qubit1', 'tgt2': 'target_qubit2'}) @@ -175,13 +198,12 @@ def cswap(self, control_qubit, target_qubit1, target_qubit2, .. jupyter-execute:: - from qiskit.extensions.standard.swap import FredkinGate - FredkinGate().to_matrix() + from qiskit.extensions.standard.swap import CSwapGate + CSwapGate().to_matrix() """ - return self.append(FredkinGate(), - [control_qubit, target_qubit1, target_qubit2], - []) + return self.append(CSwapGate(), [control_qubit, target_qubit1, target_qubit2], []) +# support both cswap and fredkin as methods of QuantumCircuit QuantumCircuit.cswap = cswap QuantumCircuit.fredkin = cswap diff --git a/qiskit/extensions/standard/t.py b/qiskit/extensions/standard/t.py index dd05b7ca5d92..ff49cb7ddcae 100644 --- a/qiskit/extensions/standard/t.py +++ b/qiskit/extensions/standard/t.py @@ -28,7 +28,7 @@ class TGate(Gate): def __init__(self, label=None): """Create new T gate.""" - super().__init__("t", 1, [], label=label) + super().__init__('t', 1, [], label=label) def _define(self): """ @@ -36,9 +36,9 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U1Gate(pi/4), [q[0]], []) + (U1Gate(pi / 4), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -49,27 +49,27 @@ def inverse(self): return TdgGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the T gate.""" return numpy.array([[1, 0], - [0, (1+1j) / numpy.sqrt(2)]], dtype=complex) + [0, (1 + 1j) / numpy.sqrt(2)]], dtype=complex) class TdgGate(Gate): - """T Gate: -pi/4 rotation around Z axis.""" + """Tdg Gate: -pi/4 rotation around Z axis.""" def __init__(self, label=None): - """Create new Tdg gate.""" - super().__init__("tdg", 1, [], label=label) + """Create a new Tdg gate.""" + super().__init__('tdg', 1, [], label=label) def _define(self): """ - gate t a { u1(pi/4) a; } + gate tdg a { u1(pi/4) a; } """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U1Gate(-pi/4), [q[0]], []) + (U1Gate(-pi / 4), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -80,9 +80,9 @@ def inverse(self): return TGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the inverse T gate.""" return numpy.array([[1, 0], - [0, (1-1j) / numpy.sqrt(2)]], dtype=complex) + [0, (1 - 1j) / numpy.sqrt(2)]], dtype=complex) @deprecate_arguments({'q': 'qubit'}) diff --git a/qiskit/extensions/standard/u1.py b/qiskit/extensions/standard/u1.py index c8a506110299..c1eb41574139 100644 --- a/qiskit/extensions/standard/u1.py +++ b/qiskit/extensions/standard/u1.py @@ -29,12 +29,12 @@ class U1Gate(Gate): def __init__(self, theta, label=None): """Create new diagonal single-qubit gate.""" - super().__init__("u1", 1, [theta], label=label) + super().__init__('u1', 1, [theta], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U3Gate(0, 0, self.params[0]), [q[0]], []) ] @@ -56,7 +56,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return Cu1Gate(*self.params) + return CU1Gate(*self.params) return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -65,7 +65,7 @@ def inverse(self): return U1Gate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the U1 gate.""" + """Return a numpy.array for the U1 gate.""" lam = self.params[0] lam = float(lam) return numpy.array([[1, 0], [0, numpy.exp(1j * lam)]], dtype=complex) @@ -103,12 +103,22 @@ def u1(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.u1 = u1 -class Cu1Gate(ControlledGate): - """controlled-u1 gate.""" +class CU1Meta(type): + """A metaclass to ensure that Cu1Gate and CU1Gate are of the same type. + + Can be removed when Cu1Gate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CU1Gate, Cu1Gate} # pylint: disable=unidiomatic-typecheck + + +class CU1Gate(ControlledGate, metaclass=CU1Meta): + """The controlled-u1 gate.""" def __init__(self, theta): """Create new cu1 gate.""" - super().__init__("cu1", 2, [theta], num_ctrl_qubits=1) + super().__init__('cu1', 2, [theta], num_ctrl_qubits=1) self.base_gate = U1Gate(theta) def _define(self): @@ -119,14 +129,14 @@ def _define(self): u1(lambda/2) b; } """ - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate(self.params[0] / 2), [q[0]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(self.params[0] / 2), [q[1]], []) ] for inst in rule: @@ -135,7 +145,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return Cu1Gate(-self.params[0]) + return CU1Gate(-self.params[0]) + + +class Cu1Gate(CU1Gate, metaclass=CU1Meta): + """The deprecated CU1Gate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class Cu1Gate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CU1Gate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -159,7 +181,7 @@ def cu1(self, theta, control_qubit, target_qubit, circuit.cu1(theta,0,1) circuit.draw() """ - return self.append(Cu1Gate(theta), [control_qubit, target_qubit], []) + return self.append(CU1Gate(theta), [control_qubit, target_qubit], []) QuantumCircuit.cu1 = cu1 diff --git a/qiskit/extensions/standard/u2.py b/qiskit/extensions/standard/u2.py index 50fcf4b35c13..f73c0bae41c8 100644 --- a/qiskit/extensions/standard/u2.py +++ b/qiskit/extensions/standard/u2.py @@ -28,12 +28,12 @@ class U2Gate(Gate): def __init__(self, phi, lam, label=None): """Create new one-pulse single-qubit gate.""" - super().__init__("u2", 1, [phi, lam], label=label) + super().__init__('u2', 1, [phi, lam], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [(U3Gate(pi / 2, self.params[0], self.params[1]), [q[0]], [])] for inst in rule: definition.append(inst) @@ -51,12 +51,16 @@ def to_matrix(self): isqrt2 = 1 / numpy.sqrt(2) phi, lam = self.params phi, lam = float(phi), float(lam) - return numpy.array([[isqrt2, -numpy.exp(1j * lam) * isqrt2], - [ - numpy.exp(1j * phi) * isqrt2, - numpy.exp(1j * (phi + lam)) * isqrt2 - ]], - dtype=complex) + return numpy.array([ + [ + isqrt2, + -numpy.exp(1j * lam) * isqrt2 + ], + [ + numpy.exp(1j * phi) * isqrt2, + numpy.exp(1j * (phi + lam)) * isqrt2 + ] + ], dtype=complex) @deprecate_arguments({'q': 'qubit'}) diff --git a/qiskit/extensions/standard/u3.py b/qiskit/extensions/standard/u3.py index 832b8c04d680..ccba7e83f817 100644 --- a/qiskit/extensions/standard/u3.py +++ b/qiskit/extensions/standard/u3.py @@ -30,7 +30,7 @@ class U3Gate(Gate): def __init__(self, theta, phi, lam, label=None): """Create new two-pulse single qubit gate.""" - super().__init__("u3", 1, [theta, phi, lam], label=label) + super().__init__('u3', 1, [theta, phi, lam], label=label) def inverse(self): """Invert this gate. @@ -53,7 +53,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return Cu3Gate(*self.params) + return CU3Gate(*self.params) return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -61,16 +61,16 @@ def to_matrix(self): """Return a Numpy.array for the U3 gate.""" theta, phi, lam = self.params theta, phi, lam = float(theta), float(phi), float(lam) - return numpy.array( - [[ + return numpy.array([ + [ numpy.cos(theta / 2), -numpy.exp(1j * lam) * numpy.sin(theta / 2) ], - [ - numpy.exp(1j * phi) * numpy.sin(theta / 2), - numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2) - ]], - dtype=complex) + [ + numpy.exp(1j * phi) * numpy.sin(theta / 2), + numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2) + ] + ], dtype=complex) @deprecate_arguments({'q': 'qubit'}) @@ -107,12 +107,22 @@ def u3(self, theta, phi, lam, qubit, *, q=None): # pylint: disable=invalid-name QuantumCircuit.u3 = u3 -class Cu3Gate(ControlledGate): - """controlled-u3 gate.""" +class CU3Meta(type): + """A metaclass to ensure that Cu3Gate and CU3Gate are of the same type. + + Can be removed when Cu3Gate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CU3Gate, Cu3Gate} # pylint: disable=unidiomatic-typecheck + + +class CU3Gate(ControlledGate, metaclass=CU3Meta): + """The controlled-u3 gate.""" def __init__(self, theta, phi, lam): """Create new cu3 gate.""" - super().__init__("cu3", 2, [theta, phi, lam], num_ctrl_qubits=1) + super().__init__('cu3', 2, [theta, phi, lam], num_ctrl_qubits=1) self.base_gate = U3Gate(theta, phi, lam) def _define(self): @@ -127,15 +137,15 @@ def _define(self): } """ from qiskit.extensions.standard.u1 import U1Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate((self.params[2] + self.params[1]) / 2), [q[0]], []), (U1Gate((self.params[2] - self.params[1]) / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, -(self.params[1] + self.params[2]) / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(self.params[0] / 2, self.params[1], 0), [q[1]], []) ] for inst in rule: @@ -144,7 +154,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return Cu3Gate(-self.params[0], -self.params[2], -self.params[1]) + return CU3Gate(-self.params[0], -self.params[2], -self.params[1]) + + +class Cu3Gate(CU3Gate, metaclass=CU3Meta): + """The deprecated CU3Gate class.""" + + def __init__(self, theta, phi, lam): + import warnings + warnings.warn('The class Cu3Gate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CU3Gate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta, phi, lam) @deprecate_arguments({'ctl': 'control_qubit', @@ -171,9 +193,7 @@ def cu3(self, theta, phi, lam, control_qubit, target_qubit, circuit.cu3(theta,phi,lam,0,1) circuit.draw() """ - return self.append(Cu3Gate(theta, phi, lam), - [control_qubit, target_qubit], - []) + return self.append(CU3Gate(theta, phi, lam), [control_qubit, target_qubit], []) QuantumCircuit.cu3 = cu3 diff --git a/qiskit/extensions/standard/x.py b/qiskit/extensions/standard/x.py index 83f7de4642d0..0f67bcc61ee7 100644 --- a/qiskit/extensions/standard/x.py +++ b/qiskit/extensions/standard/x.py @@ -20,6 +20,9 @@ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister +from qiskit.extensions.standard.h import HGate +from qiskit.extensions.standard.t import TGate +from qiskit.extensions.standard.t import TdgGate from qiskit.qasm import pi from qiskit.util import deprecate_arguments @@ -29,7 +32,7 @@ class XGate(Gate): def __init__(self, label=None): """Create new X gate.""" - super().__init__("x", 1, [], label=label) + super().__init__('x', 1, [], label=label) def _define(self): """ @@ -39,7 +42,7 @@ def _define(self): """ from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U3Gate(pi, 0, pi), [q[0]], []) ] @@ -61,9 +64,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CnotGate() + return CXGate() elif num_ctrl_qubits == 2: - return ToffoliGate() + return CCXGate() return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -72,7 +75,7 @@ def inverse(self): return XGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the X gate.""" + """Return a numpy.array for the X gate.""" return numpy.array([[0, 1], [1, 0]], dtype=complex) @@ -110,12 +113,22 @@ def x(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.x = x -class CnotGate(ControlledGate): - """controlled-NOT gate.""" +class CXMeta(type): + """A metaclass to ensure that CnotGate and CXGate are of the same type. + + Can be removed when CnotGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CnotGate, CXGate} # pylint: disable=unidiomatic-typecheck + + +class CXGate(ControlledGate, metaclass=CXMeta): + """The controlled-X gate.""" def __init__(self): - """Create new CNOT gate.""" - super().__init__("cx", 2, [], num_ctrl_qubits=1) + """Create new cx gate.""" + super().__init__('cx', 2, [], num_ctrl_qubits=1) self.base_gate = XGate() def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): @@ -132,22 +145,34 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return ToffoliGate() + return CCXGate() return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" - return CnotGate() # self-inverse + return CXGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Cx gate.""" + """Return a numpy.array for the CX gate.""" return numpy.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex) +class CnotGate(CXGate, metaclass=CXMeta): + """The deprecated CXGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CnotGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) def cx(self, control_qubit, target_qubit, # pylint: disable=invalid-name @@ -174,22 +199,33 @@ def cx(self, control_qubit, target_qubit, # pylint: disable=invalid-name .. jupyter-execute:: - from qiskit.extensions.standard.cx import CnotGate - CnotGate().to_matrix() + from qiskit.extensions.standard.x import CXGate + CXGate().to_matrix() """ - return self.append(CnotGate(), [control_qubit, target_qubit], []) + return self.append(CXGate(), [control_qubit, target_qubit], []) +# support both cx and cnot in QuantumCircuits QuantumCircuit.cx = cx QuantumCircuit.cnot = cx -class ToffoliGate(ControlledGate): - """Toffoli gate.""" +class CCXMeta(type): + """A metaclass to ensure that CCXGate and ToffoliGate are of the same type. + + Can be removed when ToffoliGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CCXGate, ToffoliGate} # pylint: disable=unidiomatic-typecheck + + +class CCXGate(ControlledGate, metaclass=CCXMeta): + """The double-controlled-not gate, also called Toffoli gate.""" def __init__(self): - """Create new Toffoli gate.""" - super().__init__("ccx", 3, [], num_ctrl_qubits=2) + """Create new CCX gate.""" + super().__init__('ccx', 3, [], num_ctrl_qubits=2) self.base_gate = XGate() def _define(self): @@ -201,27 +237,24 @@ def _define(self): t b; t c; h c; cx a,b; t a; tdg b; cx a,b;} """ - from qiskit.extensions.standard.h import HGate - from qiskit.extensions.standard.t import TGate - from qiskit.extensions.standard.t import TdgGate definition = [] - q = QuantumRegister(3, "q") + q = QuantumRegister(3, 'q') rule = [ (HGate(), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (TdgGate(), [q[2]], []), - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (TGate(), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (TdgGate(), [q[2]], []), - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (TGate(), [q[1]], []), (TGate(), [q[2]], []), (HGate(), [q[2]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (TGate(), [q[0]], []), (TdgGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -229,10 +262,10 @@ def _define(self): def inverse(self): """Invert this gate.""" - return ToffoliGate() # self-inverse + return CCXGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Toffoli gate.""" + """Return a numpy.array for the CCX gate.""" return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], @@ -243,6 +276,18 @@ def to_matrix(self): [0, 0, 0, 1, 0, 0, 0, 0]], dtype=complex) +class ToffoliGate(CCXGate, metaclass=CCXMeta): + """The deprecated CCXGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class ToffoliGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CCXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl1': 'control_qubit1', 'ctl2': 'control_qubit2', 'tgt': 'target_qubit'}) @@ -268,13 +313,14 @@ def ccx(self, control_qubit1, control_qubit2, target_qubit, .. jupyter-execute:: - from qiskit.extensions.standard.x import ToffoliGate - ToffoliGate().to_matrix() + from qiskit.extensions.standard.x import CCXGate + CCXGate().to_matrix() """ - return self.append(ToffoliGate(), + return self.append(CCXGate(), [control_qubit1, control_qubit2, target_qubit], []) +# support both ccx and toffoli as methods of QuantumCircuit QuantumCircuit.ccx = ccx QuantumCircuit.toffoli = ccx diff --git a/qiskit/extensions/standard/y.py b/qiskit/extensions/standard/y.py index 8a66c1957808..20cda181068b 100644 --- a/qiskit/extensions/standard/y.py +++ b/qiskit/extensions/standard/y.py @@ -29,14 +29,14 @@ class YGate(Gate): def __init__(self, label=None): """Create new Y gate.""" - super().__init__("y", 1, [], label=label) + super().__init__('y', 1, [], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U3Gate(pi, pi/2, pi/2), [q[0]], []) + (U3Gate(pi, pi / 2, pi / 2), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -56,7 +56,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CyGate() + return CYGate() return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -65,7 +65,7 @@ def inverse(self): return YGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Y gate.""" + """Return a numpy.array for the Y gate.""" return numpy.array([[0, -1j], [1j, 0]], dtype=complex) @@ -103,12 +103,22 @@ def y(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.y = y -class CyGate(ControlledGate): - """controlled-Y gate.""" +class CYMeta(type): + """A metaclass to ensure that CyGate and CYGate are of the same type. + + Can be removed when CyGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CYGate, CyGate} # pylint: disable=unidiomatic-typecheck + + +class CYGate(ControlledGate, metaclass=CYMeta): + """The controlled-Y gate.""" def __init__(self): - """Create new CY gate.""" - super().__init__("cy", 2, [], num_ctrl_qubits=1) + """Create a new CY gate.""" + super().__init__('cy', 2, [], num_ctrl_qubits=1) self.base_gate = YGate() def _define(self): @@ -117,12 +127,12 @@ def _define(self): """ from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.s import SdgGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (SdgGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (SGate(), [q[1]], []) ] for inst in rule: @@ -131,7 +141,26 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CyGate() # self-inverse + return CYGate() # self-inverse + + def to_matrix(self): + """Return a numpy.array for the CY gate.""" + return numpy.array([[1, 0, 0, 0], + [0, 0, 0, -1j], + [0, 0, 1, 0], + [0, 1j, 0, 0]], dtype=complex) + + +class CyGate(CYGate, metaclass=CYMeta): + """A deprecated CYGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CyGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() @deprecate_arguments({'ctl': 'control_qubit', @@ -156,7 +185,7 @@ def cy(self, control_qubit, target_qubit, # pylint: disable=invalid-name circuit.cy(0,1) circuit.draw() """ - return self.append(CyGate(), [control_qubit, target_qubit], []) + return self.append(CYGate(), [control_qubit, target_qubit], []) QuantumCircuit.cy = cy diff --git a/qiskit/extensions/standard/z.py b/qiskit/extensions/standard/z.py index ff1dbb3d0cde..91a5f76e4cf7 100644 --- a/qiskit/extensions/standard/z.py +++ b/qiskit/extensions/standard/z.py @@ -29,12 +29,12 @@ class ZGate(Gate): def __init__(self, label=None): """Create new Z gate.""" - super().__init__("z", 1, [], label=label) + super().__init__('z', 1, [], label=label) def _define(self): from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(pi), [q[0]], []) ] @@ -56,7 +56,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: if num_ctrl_qubits == 1: - return CzGate() + return CZGate() return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -65,7 +65,7 @@ def inverse(self): return ZGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the X gate.""" + """Return a numpy.array for the Z gate.""" return numpy.array([[1, 0], [0, -1]], dtype=complex) @@ -103,12 +103,22 @@ def z(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.z = z -class CzGate(ControlledGate): - """controlled-Z gate.""" +class CZMeta(type): + """A metaclass to ensure that CzGate and CZGate are of the same type. + + Can be removed when CzGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CZGate, CzGate} # pylint: disable=unidiomatic-typecheck + + +class CZGate(ControlledGate, metaclass=CZMeta): + """The controlled-Z gate.""" def __init__(self, label=None): """Create new CZ gate.""" - super().__init__("cz", 2, [], label=label, num_ctrl_qubits=1) + super().__init__('cz', 2, [], label=label, num_ctrl_qubits=1) self.base_gate = ZGate() def _define(self): @@ -116,12 +126,12 @@ def _define(self): gate cz a,b { h b; cx a,b; h b; } """ from qiskit.extensions.standard.h import HGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (HGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []) ] for inst in rule: @@ -130,16 +140,28 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CzGate() # self-inverse + return CZGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Cz gate.""" + """Return a numpy.array for the CZ gate.""" return numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex) +class CzGate(CZGate, metaclass=CZMeta): + """The deprecated CZGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CzGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) def cz(self, control_qubit, target_qubit, # pylint: disable=invalid-name @@ -167,10 +189,10 @@ def cz(self, control_qubit, target_qubit, # pylint: disable=invalid-name .. jupyter-execute:: - from qiskit.extensions.standard.cz import CzGate - CzGate().to_matrix() + from qiskit.extensions.standard.z import CZGate + CZGate().to_matrix() """ - return self.append(CzGate(), [control_qubit, target_qubit], []) + return self.append(CZGate(), [control_qubit, target_qubit], []) QuantumCircuit.cz = cz diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index bb492f33863e..d2054101609d 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -23,7 +23,7 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.instruction import Instruction -from qiskit.extensions.standard import IdGate, XGate, YGate, ZGate, HGate, SGate, TGate +from qiskit.extensions.standard import IGate, XGate, YGate, ZGate, HGate, SGate, TGate from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix, matrix_equal from qiskit.quantum_info.operators.base_operator import BaseOperator @@ -140,7 +140,7 @@ def from_label(cls, label): """ # Check label is valid label_mats = { - 'I': IdGate().to_matrix(), + 'I': IGate().to_matrix(), 'X': XGate().to_matrix(), 'Y': YGate().to_matrix(), 'Z': ZGate().to_matrix(), diff --git a/qiskit/quantum_info/operators/pauli.py b/qiskit/quantum_info/operators/pauli.py index c66d66f84039..d71ddc785e67 100644 --- a/qiskit/quantum_info/operators/pauli.py +++ b/qiskit/quantum_info/operators/pauli.py @@ -312,8 +312,8 @@ def to_operator(self): def to_instruction(self): """Convert to Pauli circuit instruction.""" from qiskit.circuit import QuantumCircuit, QuantumRegister - from qiskit.extensions.standard import IdGate, XGate, YGate, ZGate - gates = {'I': IdGate(), 'X': XGate(), 'Y': YGate(), 'Z': ZGate()} + from qiskit.extensions.standard import IGate, XGate, YGate, ZGate + gates = {'I': IGate(), 'X': XGate(), 'Y': YGate(), 'Z': ZGate()} label = self.to_label() n_qubits = self.numberofqubits qreg = QuantumRegister(n_qubits) diff --git a/qiskit/quantum_info/synthesis/two_qubit_decompose.py b/qiskit/quantum_info/synthesis/two_qubit_decompose.py index a921d9addad3..227c3cbf31a1 100644 --- a/qiskit/quantum_info/synthesis/two_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/two_qubit_decompose.py @@ -34,7 +34,7 @@ from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.extensions.standard.u3 import U3Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix from qiskit.quantum_info.synthesis.weyl import weyl_coordinates @@ -477,4 +477,4 @@ def num_basis_gates(self, unitary): return np.argmax([trace_to_fid(traces[i]) * self.basis_fidelity**i for i in range(4)]) -two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CnotGate()) +two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CXGate()) diff --git a/qiskit/transpiler/passes/basis/ms_basis_decomposer.py b/qiskit/transpiler/passes/basis/ms_basis_decomposer.py index 7f0f9be9d8e1..21902baa53c6 100644 --- a/qiskit/transpiler/passes/basis/ms_basis_decomposer.py +++ b/qiskit/transpiler/passes/basis/ms_basis_decomposer.py @@ -18,7 +18,7 @@ from qiskit.exceptions import QiskitError from qiskit.converters import circuit_to_dag -from qiskit.extensions.standard import U3Gate, CnotGate +from qiskit.extensions.standard import U3Gate, CXGate from qiskit.transpiler.passes import Unroller from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer @@ -28,7 +28,7 @@ class MSBasisDecomposer(TransformationPass): """Convert a circuit in ``U3, CX`` to ``Rx, Ry, Rxx`` without unrolling or simplification.""" - supported_input_gates = (U3Gate, CnotGate) + supported_input_gates = (U3Gate, CXGate) def __init__(self, basis_gates): """MSBasisDecomposer initializer. @@ -79,7 +79,7 @@ def run(self, dag): if isinstance(node.op, U3Gate): replacement_circuit = one_q_decomposer(node.op) - elif isinstance(node.op, CnotGate): + elif isinstance(node.op, CXGate): # N.B. We can't circuit_to_dag once outside the loop because # substitute_node_with_dag will modify the input DAG if the # node to be replaced is conditional. diff --git a/qiskit/transpiler/passes/optimization/consolidate_blocks.py b/qiskit/transpiler/passes/optimization/consolidate_blocks.py index 9b752acfd378..a256be84326f 100644 --- a/qiskit/transpiler/passes/optimization/consolidate_blocks.py +++ b/qiskit/transpiler/passes/optimization/consolidate_blocks.py @@ -20,7 +20,7 @@ from qiskit.dagcircuit import DAGCircuit from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.synthesis import TwoQubitBasisDecomposer -from qiskit.extensions import UnitaryGate, CnotGate +from qiskit.extensions import UnitaryGate, CXGate from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError @@ -37,7 +37,7 @@ class ConsolidateBlocks(TransformationPass): given such that blocks are in topological order. The blocks are collected by a previous pass, such as `Collect2qBlocks`. """ - def __init__(self, kak_basis_gate=CnotGate(), force_consolidate=False): + def __init__(self, kak_basis_gate=CXGate(), force_consolidate=False): """ConsolidateBlocks initializer. Args: diff --git a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py index 52ee82152391..f22803a65dc4 100644 --- a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py +++ b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py @@ -40,7 +40,7 @@ HAS_Z3 = False from qiskit.transpiler.basepasses import TransformationPass from qiskit.dagcircuit import DAGCircuit -from qiskit.extensions.standard import U1Gate, U2Gate, U3Gate, CnotGate +from qiskit.extensions.standard import U1Gate, U2Gate, U3Gate, CXGate from qiskit.circuit import Measure from qiskit.extensions.standard.barrier import Barrier from qiskit.transpiler.exceptions import TranspilerError @@ -327,7 +327,7 @@ def basic_bounds(self): dur = self.bp_u2_dur[q_0] elif isinstance(gate.op, U3Gate): dur = self.bp_u3_dur[q_0] - elif isinstance(gate.op, CnotGate): + elif isinstance(gate.op, CXGate): dur = self.bp_cx_dur[self.cx_tuple(gate)] self.opt.add(self.gate_duration[gate] == dur) @@ -383,7 +383,7 @@ def fidelity_constraints(self): fid = math.log(1.0 - self.bp_u2_err[q_0]) elif isinstance(gate.op, U3Gate): fid = math.log(1.0 - self.bp_u3_err[q_0]) - elif isinstance(gate.op, CnotGate): + elif isinstance(gate.op, CXGate): fid = math.log(1.0 - self.bp_cx_err[self.cx_tuple(gate)]) self.opt.add(self.gate_fidelity[gate] == round(fid, NUM_PREC)) else: diff --git a/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py b/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py index dd7b7f673e76..fa3674dc53c8 100644 --- a/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py +++ b/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py @@ -16,7 +16,7 @@ from qiskit.circuit import Measure from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate,\ - CzGate, CrzGate, Cu1Gate, RZZGate + CZGate, CRZGate, CU1Gate, RZZGate from qiskit.transpiler.basepasses import TransformationPass @@ -37,7 +37,7 @@ def run(self, dag): DAGCircuit: the optimized DAG. """ diagonal_1q_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) - diagonal_2q_gates = (CzGate, CrzGate, Cu1Gate, RZZGate) + diagonal_2q_gates = (CZGate, CRZGate, CU1Gate, RZZGate) nodes_to_remove = set() for measure in dag.op_nodes(Measure): diff --git a/qiskit/transpiler/passes/utils/check_cx_direction.py b/qiskit/transpiler/passes/utils/check_cx_direction.py index edcfbf31649b..958d82c90634 100644 --- a/qiskit/transpiler/passes/utils/check_cx_direction.py +++ b/qiskit/transpiler/passes/utils/check_cx_direction.py @@ -15,7 +15,7 @@ """Check if the CNOTs follow the right direction with respect to the coupling map.""" from qiskit.transpiler.basepasses import AnalysisPass -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate class CheckCXDirection(AnalysisPass): @@ -53,7 +53,7 @@ def run(self, dag): physical_q0 = gate.qargs[0].index physical_q1 = gate.qargs[1].index - if isinstance(gate.op, CnotGate) and ( + if isinstance(gate.op, CXGate) and ( physical_q0, physical_q1) not in edges: self.property_set['is_direction_mapped'] = False return diff --git a/qiskit/transpiler/passes/utils/cx_direction.py b/qiskit/transpiler/passes/utils/cx_direction.py index 9fb988a138f3..c45de6e7f352 100644 --- a/qiskit/transpiler/passes/utils/cx_direction.py +++ b/qiskit/transpiler/passes/utils/cx_direction.py @@ -21,7 +21,7 @@ from qiskit.circuit import QuantumRegister from qiskit.dagcircuit import DAGCircuit -from qiskit.extensions.standard import U2Gate, CnotGate +from qiskit.extensions.standard import U2Gate, CXGate class CXDirection(TransformationPass): @@ -91,7 +91,7 @@ def run(self, dag): sub_dag.apply_operation_back(U2Gate(0, pi), [sub_qr[1]], []) # Flips the cx - sub_dag.apply_operation_back(CnotGate(), [sub_qr[1], sub_qr[0]], []) + sub_dag.apply_operation_back(CXGate(), [sub_qr[1], sub_qr[0]], []) # Add H gates after sub_dag.apply_operation_back(U2Gate(0, pi), [sub_qr[0]], []) diff --git a/qiskit/visualization/qcstyle.py b/qiskit/visualization/qcstyle.py index 94371cb6d5b2..02b80c6811bf 100644 --- a/qiskit/visualization/qcstyle.py +++ b/qiskit/visualization/qcstyle.py @@ -47,7 +47,7 @@ def __init__(self): self.sfs = 8 self.colored_add_width = 0.2 self.disptex = { - 'id': 'Id', + 'id': 'I', 'u0': 'U_0', 'u1': 'U_1', 'u2': 'U_2', @@ -151,7 +151,7 @@ def __init__(self): self.colored_add_width = 0.2 self.sfs = 8 self.disptex = { - 'id': 'Id', + 'id': 'I', 'u0': 'U_0', 'u1': 'U_1', 'u2': 'U_2', diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py index 731b0a12c284..d6c46378b189 100644 --- a/qiskit/visualization/text.py +++ b/qiskit/visualization/text.py @@ -707,7 +707,12 @@ def label_for_box(instruction, controlled=False): else: label = instruction.name params = TextDrawing.params_for_label(instruction) + + # generate correct label for the box + if label == 'id': + label = 'i' label = label.capitalize() + if params: label += "(%s)" % ','.join(params) return label diff --git a/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml b/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml new file mode 100644 index 000000000000..d87636c94538 --- /dev/null +++ b/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml @@ -0,0 +1,31 @@ +--- +deprecations: + - | + The gates are named more consistently. + * the Pauli gates all have one uppercase letter only (I, X, Y, Z) + * the parameterized Pauli gates (i.e. rotations) prepend the uppercase letter R (RX, RY, RZ) + * a controlled version prepends the uppercase letter C (CX, CRX, CCX) + * gates are named according to their action, not their alternative names (CCX, not Toffoli) + + This is a list of the changed names in the format `new_name <- old_name`: + + class | name | qc._ + -------------------------+------------------+---------------------- + CCXGate <- ToffoliGate | ccx | ccx, toffoli + CRXGate <- CrxGate | crx | crx + CRYGate <- CryGate | cry | cry + CRZGate <- CrzGate | crz | crz + CSwapGate <- FredkinGate | cswap | cswap, fredkin + CU1Gate <- Cu1Gate | cu1 | cu1 + CU3Gate <- Cu3Gate | cu3 | cu3 + CXGate <- CnotGate | cx | cx, cnot + CYGate <- CyGate | cy | cy + CZGate <- CzGate | cz | cz + DiagonalGate <- DiagGate | diagonal <- diag | diagonal <- diag_gate + IGate <- IdGate | id | i,id <- iden + Isometry | isometry <- iso | isometry,iso <- iso + UCGate <- UCG | multiplexer | uc <- ucg + UCPauliRotGate <- UCRot | | + UCRXGate <- UCX | ucrx <- ucrotX | ucrx <- ucx + UCRYGate <- UCY | ucry <- ucrotY | ucry <- ucy + UCRZGate <- UCZ | ucrz <- ucrotZ | ucrz <- ucz diff --git a/test/python/circuit/test_circuit_data.py b/test/python/circuit/test_circuit_data.py index 421d89433bdb..d3ee18bc4f35 100644 --- a/test/python/circuit/test_circuit_data.py +++ b/test/python/circuit/test_circuit_data.py @@ -15,7 +15,7 @@ """Test operations on circuit.data.""" from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.extensions.standard import HGate, XGate, CnotGate +from qiskit.extensions.standard import HGate, XGate, CXGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError @@ -39,7 +39,7 @@ def test_getitem_by_insertion_order(self): data = qc.data self.assertEqual(data[0], (HGate(), [qr[0]], [])) - self.assertEqual(data[1], (CnotGate(), [qr[0], qr[1]], [])) + self.assertEqual(data[1], (CXGate(), [qr[0], qr[1]], [])) self.assertEqual(data[2], (HGate(), [qr[1]], [])) def test_count_gates(self): @@ -88,7 +88,7 @@ def test_index_gates(self): qc.h(0) self.assertEqual(qc.data.index((HGate(), [qr[0]], [])), 0) - self.assertEqual(qc.data.index((CnotGate(), [qr[0], qr[1]], [])), 1) + self.assertEqual(qc.data.index((CXGate(), [qr[0], qr[1]], [])), 1) self.assertEqual(qc.data.index((HGate(), [qr[1]], [])), 2) def test_iter(self): @@ -102,7 +102,7 @@ def test_iter(self): iter_ = iter(qc.data) self.assertEqual(next(iter_), (HGate(), [qr[0]], [])) - self.assertEqual(next(iter_), (CnotGate(), [qr[0], qr[1]], [])) + self.assertEqual(next(iter_), (CXGate(), [qr[0], qr[1]], [])) self.assertEqual(next(iter_), (HGate(), [qr[1]], [])) self.assertRaises(StopIteration, next, iter_) @@ -129,9 +129,9 @@ def test_slice(self): (HGate(), [qr[0]], []), ]) self.assertEqual(cx_slice, [ - (CnotGate(), [qr[0], qr[1]], []), - (CnotGate(), [qr[1], qr[0]], []), - (CnotGate(), [qr[0], qr[1]], []), + (CXGate(), [qr[0], qr[1]], []), + (CXGate(), [qr[1], qr[0]], []), + (CXGate(), [qr[0], qr[1]], []), ]) def test_copy(self): @@ -323,7 +323,7 @@ def test_append_is_validated(self): qc = QuantumCircuit(qr) qc.data.append((HGate(), [qr[0]], [])) - qc.data.append((CnotGate(), [0, 1], [])) + qc.data.append((CXGate(), [0, 1], [])) qc.data.append((HGate(), [qr[1]], [])) expected_qc = QuantumCircuit(qr) @@ -343,7 +343,7 @@ def test_insert_is_validated(self): qc = QuantumCircuit(qr) qc.data.insert(0, (HGate(), [qr[0]], [])) - qc.data.insert(1, (CnotGate(), [0, 1], [])) + qc.data.insert(1, (CXGate(), [0, 1], [])) qc.data.insert(2, (HGate(), [qr[1]], [])) expected_qc = QuantumCircuit(qr) @@ -363,7 +363,7 @@ def test_extend_is_validated(self): qc = QuantumCircuit(qr) qc.data.extend([(HGate(), [qr[0]], []), - (CnotGate(), [0, 1], []), + (CXGate(), [0, 1], []), (HGate(), [qr[1]], [])]) expected_qc = QuantumCircuit(qr) @@ -383,7 +383,7 @@ def test_setting_data_is_validated(self): qc = QuantumCircuit(qr) qc.data = [(HGate(), [qr[0]], []), - (CnotGate(), [0, 1], []), + (CXGate(), [0, 1], []), (HGate(), [qr[1]], [])] expected_qc = QuantumCircuit(qr) diff --git a/test/python/circuit/test_circuit_load_from_qasm.py b/test/python/circuit/test_circuit_load_from_qasm.py index 973b2f2dd53c..e1e08fce3e2c 100644 --- a/test/python/circuit/test_circuit_load_from_qasm.py +++ b/test/python/circuit/test_circuit_load_from_qasm.py @@ -26,11 +26,11 @@ class LoadFromQasmTest(QiskitTestCase): def setUp(self): self.qasm_file_name = 'entangled_registers.qasm' - self.qasm_file_path = self._get_resource_path( - 'qasm/' + self.qasm_file_name, Path.EXAMPLES) + self.qasm_file_path = self._get_resource_path('qasm/' + self.qasm_file_name, Path.EXAMPLES) def test_qasm_file(self): - """Test qasm_file and get_circuit. + """ + Test qasm_file and get_circuit. If all is correct we should get the qasm file loaded in _qasm_file_path """ @@ -48,8 +48,54 @@ def test_qasm_file(self): q_circuit_2.measure(qr_b, cr_d) self.assertEqual(q_circuit, q_circuit_2) + def test_loading_all_qelib1_gates(self): + """Test setting up a circuit with all gates defined in qiskit/qasm/libs/qelib1.inc.""" + all_gates_qasm = self._get_resource_path('all_gates.qasm', Path.QASMS) + qasm_circuit = QuantumCircuit.from_qasm_file(all_gates_qasm) + + # the hardware primitives + ref_circuit = QuantumCircuit(3, 3) + ref_circuit.u3(0.2, 0.1, 0.6, 0) + ref_circuit.u2(0.1, 0.6, 0) + ref_circuit.u1(0.6, 0) + ref_circuit.id(0) + ref_circuit.cx(0, 1) + # the standard single qubit gates + ref_circuit.x(0) + ref_circuit.y(0) + ref_circuit.z(0) + ref_circuit.h(0) + ref_circuit.s(0) + ref_circuit.t(0) + ref_circuit.sdg(0) + ref_circuit.tdg(0) + # the standard rotations + ref_circuit.rx(0.1, 0) + ref_circuit.ry(0.1, 0) + ref_circuit.rz(0.1, 0) + # the barrier + ref_circuit.barrier() + # the standard user-defined gates + ref_circuit.swap(0, 1) + ref_circuit.cswap(0, 1, 2) + ref_circuit.cy(0, 1) + ref_circuit.cz(0, 1) + ref_circuit.ch(0, 1) + ref_circuit.cu1(0.6, 0, 1) + ref_circuit.cu3(0.2, 0.1, 0.6, 0, 1) + ref_circuit.ccx(0, 1, 2) + ref_circuit.crx(0.6, 0, 1) + ref_circuit.cry(0.6, 0, 1) + ref_circuit.crz(0.6, 0, 1) + ref_circuit.rxx(0.2, 0, 1) + ref_circuit.rzz(0.2, 0, 1) + ref_circuit.measure([0, 1, 2], [0, 1, 2]) + + self.assertEqual(qasm_circuit, ref_circuit) + def test_fail_qasm_file(self): - """Test fail_qasm_file. + """ + Test fail_qasm_file. If all is correct we should get a QiskitError """ @@ -57,7 +103,8 @@ def test_fail_qasm_file(self): QuantumCircuit.from_qasm_file, "") def test_fail_qasm_string(self): - """Test fail_qasm_string. + """ + Test fail_qasm_string. If all is correct we should get a QiskitError """ @@ -65,7 +112,8 @@ def test_fail_qasm_string(self): QuantumCircuit.from_qasm_str, "") def test_qasm_text(self): - """Test qasm_text and get_circuit. + """ + Test qasm_text and get_circuit. If all is correct we should get the qasm file loaded from the string """ @@ -102,7 +150,8 @@ def test_qasm_text(self): self.assertEqual(q_circuit, ref) def test_qasm_text_conditional(self): - """Test qasm_text and get_circuit when conditionals are present. + """ + Test qasm_text and get_circuit when conditionals are present. """ qasm_string = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -125,8 +174,11 @@ def test_qasm_text_conditional(self): self.assertEqual(q_circuit, ref) def test_opaque_gate(self): - """Test parse an opaque gate - See https://github.com/Qiskit/qiskit-terra/issues/1566""" + """ + Test parse an opaque gate + + See https://github.com/Qiskit/qiskit-terra/issues/1566. + """ qasm_string = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -137,13 +189,13 @@ def test_opaque_gate(self): qr = QuantumRegister(3, 'q') expected = QuantumCircuit(qr) - expected.append(Gate(name='my_gate', num_qubits=2, params=[1, 2, 3]), [qr[1], qr[2]]) + expected.append(Gate(name='my_gate', num_qubits=2, + params=[1, 2, 3]), [qr[1], qr[2]]) self.assertEqual(circuit, expected) def test_qasm_example_file(self): - """Loads qasm/example.qasm. - """ + """Loads qasm/example.qasm.""" qasm_filename = self._get_resource_path('example.qasm', Path.QASMS) expected_circuit = QuantumCircuit.from_qasm_str('\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -172,7 +224,7 @@ def test_qasm_example_file(self): self.assertEqual(len(q_circuit.qregs), 2) def test_qasm_qas_string_order(self): - """ Test that gates are returned in qasm in ascending order""" + """Test that gates are returned in qasm in ascending order.""" expected_qasm = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", "qreg q[3];", diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index c1bfb7d63e83..68ee5d520d62 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -33,11 +33,11 @@ from qiskit.converters.circuit_to_dag import circuit_to_dag from qiskit.converters.dag_to_circuit import dag_to_circuit from qiskit.quantum_info import Operator -from qiskit.extensions.standard import (CnotGate, XGate, YGate, ZGate, U1Gate, - CyGate, CzGate, Cu1Gate, SwapGate, - ToffoliGate, HGate, RZGate, RXGate, - RYGate, CryGate, CrxGate, FredkinGate, - U3Gate, CHGate, CrzGate, Cu3Gate, +from qiskit.extensions.standard import (CXGate, XGate, YGate, ZGate, U1Gate, + CYGate, CZGate, CU1Gate, SwapGate, + CCXGate, HGate, RZGate, RXGate, + RYGate, CRYGate, CRXGate, CSwapGate, + U3Gate, CHGate, CRZGate, CU3Gate, MSGate, Barrier, RCCXGate, RCCCXGate) from qiskit.extensions.unitary import _compute_control_matrix import qiskit.extensions.standard as allGates @@ -49,15 +49,15 @@ class TestControlledGate(QiskitTestCase): def test_controlled_x(self): """Test creation of controlled x gate""" - self.assertEqual(XGate().control(), CnotGate()) + self.assertEqual(XGate().control(), CXGate()) def test_controlled_y(self): """Test creation of controlled y gate""" - self.assertEqual(YGate().control(), CyGate()) + self.assertEqual(YGate().control(), CYGate()) def test_controlled_z(self): """Test creation of controlled z gate""" - self.assertEqual(ZGate().control(), CzGate()) + self.assertEqual(ZGate().control(), CZGate()) def test_controlled_h(self): """Test creation of controlled h gate""" @@ -66,41 +66,41 @@ def test_controlled_h(self): def test_controlled_u1(self): """Test creation of controlled u1 gate""" theta = 0.5 - self.assertEqual(U1Gate(theta).control(), Cu1Gate(theta)) + self.assertEqual(U1Gate(theta).control(), CU1Gate(theta)) def test_controlled_rz(self): """Test creation of controlled rz gate""" theta = 0.5 - self.assertEqual(RZGate(theta).control(), CrzGate(theta)) + self.assertEqual(RZGate(theta).control(), CRZGate(theta)) def test_controlled_ry(self): """Test creation of controlled ry gate""" theta = 0.5 - self.assertEqual(RYGate(theta).control(), CryGate(theta)) + self.assertEqual(RYGate(theta).control(), CRYGate(theta)) def test_controlled_rx(self): """Test creation of controlled rx gate""" theta = 0.5 - self.assertEqual(RXGate(theta).control(), CrxGate(theta)) + self.assertEqual(RXGate(theta).control(), CRXGate(theta)) def test_controlled_u3(self): """Test creation of controlled u3 gate""" theta, phi, lamb = 0.1, 0.2, 0.3 self.assertEqual(U3Gate(theta, phi, lamb).control(), - Cu3Gate(theta, phi, lamb)) + CU3Gate(theta, phi, lamb)) def test_controlled_cx(self): """Test creation of controlled cx gate""" - self.assertEqual(CnotGate().control(), ToffoliGate()) + self.assertEqual(CXGate().control(), CCXGate()) def test_controlled_swap(self): """Test creation of controlled swap gate""" - self.assertEqual(SwapGate().control(), FredkinGate()) + self.assertEqual(SwapGate().control(), CSwapGate()) def test_circuit_append(self): """Test appending controlled gate to quantum circuit""" circ = QuantumCircuit(5) - inst = CnotGate() + inst = CXGate() circ.append(inst.control(), qargs=[0, 2, 1]) circ.append(inst.control(2), qargs=[0, 3, 1, 2]) circ.append(inst.control().control(), qargs=[0, 3, 1, 2]) # should be same as above @@ -196,7 +196,7 @@ def test_multi_control_u3(self): width = 3 qr = QuantumRegister(width) qc_cu3 = QuantumCircuit(qr) - cu3gate = u3.Cu3Gate(alpha, beta, gamma) + cu3gate = u3.CU3Gate(alpha, beta, gamma) c_cu3 = cu3gate.control(1) qc_cu3.append(c_cu3, qr, []) @@ -257,7 +257,7 @@ def test_multi_control_u1(self): width = 3 qr = QuantumRegister(width) qc_cu1 = QuantumCircuit(qr) - cu1gate = u1.Cu1Gate(theta) + cu1gate = u1.CU1Gate(theta) c_cu1 = cu1gate.control(1) qc_cu1.append(c_cu1, qr, []) @@ -443,10 +443,10 @@ def test_open_controlled_unitary_matrix(self, num_ctrl_qubits): (output_ctrl == input_ctrl) and (output_target == cond_output)) else: - self.assertTrue(( - (output_ctrl == input_ctrl) and - (output_target != cond_output)) or - output_ctrl != input_ctrl) + self.assertTrue( + ((output_ctrl == input_ctrl) and + (output_target != cond_output)) or + output_ctrl != input_ctrl) def test_base_gate_setting(self): """ @@ -472,7 +472,7 @@ def test_all_inverses(self): # only verify basic gates right now, as already controlled ones # will generate differing definitions with self.subTest(i=cls): - if issubclass(cls, ControlledGate) or cls == allGates.IdGate: + if issubclass(cls, ControlledGate) or issubclass(cls, allGates.IGate): continue try: sig = signature(cls) @@ -603,6 +603,58 @@ def test_open_controlled_gate_raises(self): with self.assertRaises(CircuitError): base_gate.control(num_ctrl_qubits, ctrl_state='201') + def test_deprecated_gates(self): + """Test types of the deprecated gate classes.""" + + import qiskit.extensions.standard.i as i + import qiskit.extensions.standard.rx as rx + import qiskit.extensions.standard.ry as ry + import qiskit.extensions.standard.rz as rz + import qiskit.extensions.standard.swap as swap + import qiskit.extensions.standard.u1 as u1 + import qiskit.extensions.standard.u3 as u3 + import qiskit.extensions.standard.x as x + import qiskit.extensions.standard.y as y + import qiskit.extensions.standard.z as z + + import qiskit.extensions.quantum_initializer.diagonal as diagonal + import qiskit.extensions.quantum_initializer.uc as uc + import qiskit.extensions.quantum_initializer.uc_pauli_rot as uc_pauli_rot + import qiskit.extensions.quantum_initializer.ucrx as ucrx + import qiskit.extensions.quantum_initializer.ucry as ucry + import qiskit.extensions.quantum_initializer.ucrz as ucrz + + theta, phi, lam = 0.1, 0.2, 0.3 + diag = [1, 1] + unitary = np.array([[1, 0], [0, 1]]) + renamend_gates = [ + (diagonal.DiagonalGate, diagonal.DiagGate, [diag]), + (i.IGate, i.IdGate, []), + (rx.CRXGate, rx.CrxGate, [theta]), + (ry.CRYGate, ry.CryGate, [theta]), + (rz.CRZGate, rz.CrzGate, [theta]), + (swap.CSwapGate, swap.FredkinGate, []), + (u1.CU1Gate, u1.Cu1Gate, [theta]), + (u3.CU3Gate, u3.Cu3Gate, [theta, phi, lam]), + (uc.UCGate, uc.UCG, [[unitary]]), + (uc_pauli_rot.UCPauliRotGate, uc_pauli_rot.UCRot, [[theta], 'X']), + (ucrx.UCRXGate, ucrx.UCX, [[theta]]), + (ucry.UCRYGate, ucry.UCY, [[theta]]), + (ucrz.UCRZGate, ucrz.UCZ, [[theta]]), + (x.CXGate, x.CnotGate, []), + (x.CCXGate, x.ToffoliGate, []), + (y.CYGate, y.CyGate, []), + (z.CZGate, z.CzGate, []), + ] + for new, old, params in renamend_gates: + with self.subTest(msg='comparing {} and {}'.format(new, old)): + # assert old gate class derives from new + self.assertTrue(issubclass(old, new)) + + # assert both are representatives of one another + self.assertTrue(isinstance(new(*params), old)) + self.assertTrue(isinstance(old(*params), new)) + if __name__ == '__main__': unittest.main() diff --git a/test/python/circuit/test_diag.py b/test/python/circuit/test_diagonal_gate.py similarity index 95% rename from test/python/circuit/test_diag.py rename to test/python/circuit/test_diagonal_gate.py index 4b0bfbefcb3e..6be0aebb9f7b 100644 --- a/test/python/circuit/test_diag.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -25,10 +25,11 @@ from qiskit.quantum_info.operators.predicates import matrix_equal -class TestDiagGate(QiskitTestCase): +class TestDiagonalGate(QiskitTestCase): """ Diagonal gate tests. """ + def test_diag_gate(self): """Test diagonal gates.""" for phases in [[0, 0], [0, 0.8], [0, 0, 1, 1], [0, 1, 0.5, 1], @@ -40,7 +41,7 @@ def test_diag_gate(self): num_qubits = int(np.log2(len(diag))) q = QuantumRegister(num_qubits) qc = QuantumCircuit(q) - qc.diag_gate(diag, q[0:num_qubits]) + qc.diagonal(diag, q[0:num_qubits]) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py index ecf2020d3c69..bb60bc667078 100644 --- a/test/python/circuit/test_extensions_standard.py +++ b/test/python/circuit/test_extensions_standard.py @@ -29,10 +29,10 @@ from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix from qiskit.extensions.standard import ( - HGate, CHGate, IdGate, RGate, RXGate, CrxGate, RYGate, CryGate, RZGate, - CrzGate, SGate, SdgGate, FredkinGate, TGate, TdgGate, U1Gate, Cu1Gate, - U2Gate, U3Gate, Cu3Gate, XGate, CnotGate, ToffoliGate, YGate, CyGate, - ZGate, CzGate + HGate, CHGate, IGate, RGate, RXGate, CRXGate, RYGate, CRYGate, RZGate, + CRZGate, SGate, SdgGate, CSwapGate, TGate, TdgGate, U1Gate, CU1Gate, + U2Gate, U3Gate, CU3Gate, XGate, CXGate, CCXGate, YGate, CYGate, + ZGate, CZGate ) @@ -383,33 +383,33 @@ def test_h_reg_inv(self): self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) def test_iden(self): - self.circuit.iden(self.qr[1]) + self.circuit.i(self.qr[1]) op, _, _ = self.circuit[0] self.assertEqual(op.name, 'id') self.assertEqual(op.params, []) def test_iden_wires(self): - self.circuit.iden(1) + self.circuit.i(1) op, _, _ = self.circuit[0] self.assertEqual(op.name, 'id') self.assertEqual(op.params, []) def test_iden_invalid(self): qc = self.circuit - self.assertRaises(CircuitError, qc.iden, self.cr[0]) - self.assertRaises(CircuitError, qc.iden, self.cr) - self.assertRaises(CircuitError, qc.iden, (self.qr, 3)) - self.assertRaises(CircuitError, qc.iden, (self.qr, 'a')) - self.assertRaises(CircuitError, qc.iden, .0) + self.assertRaises(CircuitError, qc.i, self.cr[0]) + self.assertRaises(CircuitError, qc.i, self.cr) + self.assertRaises(CircuitError, qc.i, (self.qr, 3)) + self.assertRaises(CircuitError, qc.i, (self.qr, 'a')) + self.assertRaises(CircuitError, qc.i, .0) def test_iden_reg(self): - instruction_set = self.circuit.iden(self.qr) + instruction_set = self.circuit.i(self.qr) self.assertEqual(len(instruction_set.instructions), 3) self.assertEqual(instruction_set.instructions[0].name, 'id') self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) def test_iden_reg_inv(self): - instruction_set = self.circuit.iden(self.qr).inverse() + instruction_set = self.circuit.i(self.qr).inverse() self.assertEqual(len(instruction_set.instructions), 3) self.assertEqual(instruction_set.instructions[0].name, 'id') self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) @@ -1390,18 +1390,18 @@ class TestQubitKeywordArgRenaming(QiskitTestCase): @data( ('h', HGate, 0, [('q', 'qubit')]), ('ch', CHGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), - ('iden', IdGate, 0, [('q', 'qubit')]), + ('id', IGate, 0, [('q', 'qubit')]), ('r', RGate, 2, [('q', 'qubit')]), ('rx', RXGate, 1, [('q', 'qubit')]), - ('crx', CrxGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('crx', CRXGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('ry', RYGate, 1, [('q', 'qubit')]), - ('cry', CryGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cry', CRYGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('rz', RZGate, 1, [('q', 'qubit')]), - ('crz', CrzGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('crz', CRZGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('s', SGate, 0, [('q', 'qubit')]), ('sdg', SdgGate, 0, [('q', 'qubit')]), ('cswap', - FredkinGate, + CSwapGate, 0, [('ctl', 'control_qubit'), ('tgt1', 'target_qubit1'), @@ -1409,22 +1409,22 @@ class TestQubitKeywordArgRenaming(QiskitTestCase): ('t', TGate, 0, [('q', 'qubit')]), ('tdg', TdgGate, 0, [('q', 'qubit')]), ('u1', U1Gate, 1, [('q', 'qubit')]), - ('cu1', Cu1Gate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cu1', CU1Gate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('u2', U2Gate, 2, [('q', 'qubit')]), ('u3', U3Gate, 3, [('q', 'qubit')]), - ('cu3', Cu3Gate, 3, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cu3', CU3Gate, 3, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('x', XGate, 0, [('q', 'qubit')]), - ('cx', CnotGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cx', CXGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('ccx', - ToffoliGate, + CCXGate, 0, [('ctl1', 'control_qubit1'), ('ctl2', 'control_qubit2'), ('tgt', 'target_qubit')]), ('y', YGate, 0, [('q', 'qubit')]), - ('cy', CyGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cy', CYGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('z', ZGate, 0, [('q', 'qubit')]), - ('cz', CzGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cz', CZGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ) # pylint: enable=bad-whitespace def test_kwarg_deprecation(self, instr_name, inst_class, n_params, kwarg_map): diff --git a/test/python/circuit/test_gate_power.py b/test/python/circuit/test_gate_power.py index 442f28c18b4c..3a1f1aff175f 100644 --- a/test/python/circuit/test_gate_power.py +++ b/test/python/circuit/test_gate_power.py @@ -22,7 +22,7 @@ from numpy.linalg import matrix_power from qiskit.test import QiskitTestCase -from qiskit.extensions import SGate, UnitaryGate, CnotGate +from qiskit.extensions import SGate, UnitaryGate, CXGate from qiskit.circuit import Gate from qiskit.quantum_info.operators import Operator @@ -76,7 +76,7 @@ class TestPowerIntCX(QiskitTestCase): def test_cx_int(self, n): """Test CX.power() method. """ - result = CnotGate().power(n) + result = CXGate().power(n) self.assertEqual(result.label, 'cx^' + str(n)) self.assertIsInstance(result, UnitaryGate) diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index 57595110c6a9..97fa9413c8be 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -21,7 +21,7 @@ from qiskit.transpiler import PassManager from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.test import QiskitTestCase -from qiskit.extensions import SGate, U3Gate, UnitaryGate, CnotGate +from qiskit.extensions import SGate, U3Gate, UnitaryGate, CXGate from qiskit.circuit import Instruction, Measure, Gate from qiskit.transpiler.passes import Unroller from qiskit.circuit.exceptions import CircuitError @@ -68,11 +68,11 @@ def test_standard_2Q_two(self): """ qr = QuantumRegister(2, 'qr') expected_circ = QuantumCircuit(qr) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) expected = expected_circ.to_instruction() - result = CnotGate().repeat(2) + result = CXGate().repeat(2) self.assertEqual(result.name, 'cx*2') self.assertEqual(result.definition, expected.definition) @@ -83,10 +83,10 @@ def test_standard_2Q_one(self): """ qr = QuantumRegister(2, 'qr') expected_circ = QuantumCircuit(qr) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) expected = expected_circ.to_instruction() - result = CnotGate().repeat(1) + result = CXGate().repeat(1) self.assertEqual(result.name, 'cx*1') self.assertEqual(result.definition, expected.definition) @@ -205,7 +205,7 @@ def test_standard_2Q_minus_one(self): """Test standard 2Q gate.repeat(-1) method. Raises, since n<1. """ with self.assertRaises(CircuitError) as context: - _ = CnotGate().repeat(-1) + _ = CXGate().repeat(-1) self.assertIn('strictly positive integer', str(context.exception)) def test_measure_minus_one(self): @@ -219,7 +219,7 @@ def test_standard_2Q_zero(self): """Test standard 2Q gate.repeat(0) method. Raises, since n<1. """ with self.assertRaises(CircuitError) as context: - _ = CnotGate().repeat(0) + _ = CXGate().repeat(0) self.assertIn('strictly positive integer', str(context.exception)) diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index 4be158ef2de2..93195cc40e39 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -24,7 +24,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister, ClassicalRegister from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.t import TGate from qiskit.test import QiskitTestCase @@ -53,7 +53,7 @@ def test_instructions_equal(self): self.assertFalse(uop1 == uop3) self.assertTrue(HGate() == HGate()) - self.assertFalse(HGate() == CnotGate()) + self.assertFalse(HGate() == CXGate()) self.assertFalse(hop1 == HGate()) eop1 = Instruction('kraus', 1, 0, [np.array([[1, 0], [0, 1]])]) @@ -117,7 +117,7 @@ def circuit_instruction_circuit_roundtrip(self): circ1 = QuantumCircuit(q, c, name='circuit1') circ1.h(q[0]) circ1.crz(0.1, q[0], q[1]) - circ1.iden(q[1]) + circ1.i(q[1]) circ1.u3(0.1, 0.2, -0.2, q[0]) circ1.barrier() circ1.measure(q, c) @@ -165,13 +165,13 @@ def test_mirror_gate(self): circ = QuantumCircuit(q, c, name='circ') circ.h(q[0]) circ.crz(0.1, q[0], q[1]) - circ.iden(q[1]) + circ.i(q[1]) circ.u3(0.1, 0.2, -0.2, q[0]) gate = circ.to_instruction() circ = QuantumCircuit(q, c, name='circ') circ.u3(0.1, 0.2, -0.2, q[0]) - circ.iden(q[1]) + circ.i(q[1]) circ.crz(0.1, q[0], q[1]) circ.h(q[0]) gate_mirror = circ.to_instruction() @@ -229,12 +229,12 @@ def test_inverse_composite_gate(self): circ = QuantumCircuit(q, name='circ') circ.h(q[0]) circ.crz(0.1, q[0], q[1]) - circ.iden(q[1]) + circ.i(q[1]) circ.u3(0.1, 0.2, -0.2, q[0]) gate = circ.to_instruction() circ = QuantumCircuit(q, name='circ') circ.u3(-0.1, 0.2, -0.2, q[0]) - circ.iden(q[1]) + circ.i(q[1]) circ.crz(-0.1, q[0], q[1]) circ.h(q[0]) gate_inverse = circ.to_instruction() @@ -252,12 +252,12 @@ def test_inverse_recursive(self): qr1 = QuantumRegister(4) circ1 = QuantumCircuit(qr1, name='circuit1') circ1.cu1(-0.1, qr1[0], qr1[2]) - circ1.iden(qr1[1]) + circ1.i(qr1[1]) circ1.append(little_gate, [qr1[2], qr1[3]]) circ_inv = QuantumCircuit(qr1, name='circ1_dg') circ_inv.append(little_gate.inverse(), [qr1[2], qr1[3]]) - circ_inv.iden(qr1[1]) + circ_inv.i(qr1[1]) circ_inv.cu1(0.1, qr1[0], qr1[2]) self.assertEqual(circ1.inverse(), circ_inv) diff --git a/test/python/circuit/test_isometry.py b/test/python/circuit/test_isometry.py index 50476ba7ec8e..ace679e093fd 100644 --- a/test/python/circuit/test_isometry.py +++ b/test/python/circuit/test_isometry.py @@ -48,7 +48,7 @@ def test_isometry(self): n = num_q_input + num_q_ancilla_for_output q = QuantumRegister(n) qc = QuantumCircuit(q) - qc.iso(iso, q[:num_q_input], q[num_q_input:]) + qc.isometry(iso, q[:num_q_input], q[num_q_input:]) # Verify the circuit can be decomposed self.assertIsInstance(qc.decompose(), QuantumCircuit) diff --git a/test/python/circuit/test_ucg.py b/test/python/circuit/test_uc.py similarity index 90% rename from test/python/circuit/test_ucg.py rename to test/python/circuit/test_uc.py index 240c8009c167..46748513e276 100644 --- a/test/python/circuit/test_ucg.py +++ b/test/python/circuit/test_uc.py @@ -26,7 +26,7 @@ import numpy as np from scipy.linalg import block_diag -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute from qiskit.test import QiskitTestCase @@ -45,8 +45,8 @@ up_to_diagonal_list = [True, False] -class TestUCG(QiskitTestCase): - """Qiskit UCG tests.""" +class TestUCGate(QiskitTestCase): + """Qiskit UCGate tests.""" def test_ucg(self): """Test uniformly controlled gates.""" @@ -55,7 +55,7 @@ def test_ucg(self): num_con = int(np.log2(len(squs))) q = QuantumRegister(num_con + 1) qc = QuantumCircuit(q) - qc.ucg(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) + qc.uc(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate @@ -63,7 +63,7 @@ def test_ucg(self): result = execute(qc, simulator).result() unitary = result.get_unitary(qc) if up_to_diagonal: - ucg = UCG(squs, up_to_diagonal=up_to_diagonal) + ucg = UCGate(squs, up_to_diagonal=up_to_diagonal) unitary = np.dot(np.diagflat(ucg._get_diagonal()), unitary) unitary_desired = _get_ucg_matrix(squs) self.assertTrue(matrix_equal(unitary_desired, unitary, ignore_phase=True)) diff --git a/test/python/circuit/test_ucx_y_z.py b/test/python/circuit/test_ucx_y_z.py index 801ad0fbf017..a5c3e3c8a111 100644 --- a/test/python/circuit/test_ucx_y_z.py +++ b/test/python/circuit/test_ucx_y_z.py @@ -36,8 +36,8 @@ rot_axis_list = ["X", "Y", "Z"] -class TestUCXYZ(QiskitTestCase): - """Qiskit tests for UCX, UCY and UCZ rotations gates.""" +class TestUCRXYZ(QiskitTestCase): + """Qiskit tests for UCRXGate, UCRYGate and UCRZGate rotations gates.""" def test_ucy(self): """ Test the decomposition of uniformly controlled rotations.""" @@ -47,11 +47,11 @@ def test_ucy(self): q = QuantumRegister(num_contr + 1) qc = QuantumCircuit(q) if rot_axis == "X": - qc.ucx(angles, q[1:num_contr + 1], q[0]) + qc.ucrx(angles, q[1:num_contr + 1], q[0]) elif rot_axis == "Y": - qc.ucy(angles, q[1:num_contr + 1], q[0]) + qc.ucry(angles, q[1:num_contr + 1], q[0]) else: - qc.ucz(angles, q[1:num_contr + 1], q[0]) + qc.ucrz(angles, q[1:num_contr + 1], q[0]) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 11db9b58a534..d050ec94cca0 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -25,7 +25,7 @@ from qiskit.compiler import transpile from qiskit.converters import circuit_to_dag from qiskit.dagcircuit.exceptions import DAGCircuitError -from qiskit.extensions.standard import CnotGate +from qiskit.extensions.standard import CXGate from qiskit.test import QiskitTestCase, Path from qiskit.test.mock import FakeMelbourne, FakeRueschlikon from qiskit.transpiler import Layout, CouplingMap @@ -118,7 +118,7 @@ def test_transpile_non_adjacent_layout(self): initial_layout=initial_layout) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_transpile_qft_grid(self): @@ -138,7 +138,7 @@ def test_transpile_qft_grid(self): coupling_map=coupling_map) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_already_mapped_1(self): diff --git a/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle b/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle index bf9d7480c7c84845d2713b757dd75742b51788ea..21adf001e29c4ef5a634d83e5bb6576638b5bdc3 100644 GIT binary patch delta 14 VcmZqTY2evV!NeG`xtgh+82};+1Xut7 delta 16 XcmZqRY2w*X!Ni=GU$VK9sht@BExrX& diff --git a/test/python/pickles/TestsBasicSwap_handle_measurement.pickle b/test/python/pickles/TestsBasicSwap_handle_measurement.pickle index f4fdaab4e45ff0ecf7cfacbe108ce1f9970f5af1..ea6f4c1939be0480ccb94054dc60721e0bbf3683 100644 GIT binary patch delta 12 TcmeC;@8F-%%NVh7!Y?)e9ft(y delta 14 VcmeC+@8X}(%bb^Ava#pby diff --git a/test/python/pickles/TestsBasicSwap_initial_layout.pickle b/test/python/pickles/TestsBasicSwap_initial_layout.pickle index acade011a9e0eadc5055bd628735464227e65ba7..ed250fc70d8d2cb6e716a5531f18606863e9e4fd 100644 GIT binary patch delta 14 Vcmey&`;m7;KNDlb=1EM6EC4Qj1w{Y= delta 16 Xcmey!`c; + + + diff --git a/test/python/quantum_info/operators/test_operator.py b/test/python/quantum_info/operators/test_operator.py index db40a34af5c7..ba29b9a1b32a 100644 --- a/test/python/quantum_info/operators/test_operator.py +++ b/test/python/quantum_info/operators/test_operator.py @@ -24,7 +24,7 @@ from qiskit import QiskitError from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit.extensions.standard import HGate, CHGate, CnotGate +from qiskit.extensions.standard import HGate, CHGate, CXGate from qiskit.test import QiskitTestCase from qiskit.quantum_info.operators.operator import Operator from qiskit.quantum_info.operators.predicates import matrix_equal @@ -173,7 +173,7 @@ def test_circuit_init(self): def test_instruction_init(self): """Test initialization from a circuit.""" - gate = CnotGate() + gate = CXGate() op = Operator(gate).data target = gate.to_matrix() global_phase_equivalent = matrix_equal(op, target, ignore_phase=True) diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 8e729630e9a6..c857a8cbeef6 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -21,8 +21,8 @@ from qiskit import execute from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.extensions import UnitaryGate -from qiskit.extensions.standard import (HGate, IdGate, SdgGate, SGate, U3Gate, - XGate, YGate, ZGate, CnotGate) +from qiskit.extensions.standard import (HGate, IGate, SdgGate, SGate, U3Gate, + XGate, YGate, ZGate, CXGate) from qiskit.providers.basicaer import UnitarySimulatorPy from qiskit.quantum_info.operators import Operator, Pauli from qiskit.quantum_info.random import random_unitary @@ -38,9 +38,9 @@ def make_oneq_cliffords(): """Make as list of 1q Cliffords""" - ixyz_list = [g().to_matrix() for g in (IdGate, XGate, YGate, ZGate)] - ih_list = [g().to_matrix() for g in (IdGate, HGate)] - irs_list = [IdGate().to_matrix(), + ixyz_list = [g().to_matrix() for g in (IGate, XGate, YGate, ZGate)] + ih_list = [g().to_matrix() for g in (IGate, HGate)] + irs_list = [IGate().to_matrix(), SdgGate().to_matrix() @ HGate().to_matrix(), HGate().to_matrix() @ SGate().to_matrix()] oneq_cliffords = [Operator(ixyz @ ih @ irs) for ixyz in ixyz_list @@ -401,7 +401,7 @@ def check_exact_decomposition(self, target_unitary, decomposer, tolerance=1.e-7) def test_cnot_rxx_decompose(self): """Verify CNOT decomposition into RXX gate is correct""" - cnot = Operator(CnotGate()) + cnot = Operator(CXGate()) decomps = [cnot_rxx_decompose(), cnot_rxx_decompose(plus_ry=True, plus_rxx=True), cnot_rxx_decompose(plus_ry=True, plus_rxx=False), diff --git a/test/python/test_dagcircuit.py b/test/python/test_dagcircuit.py index 76fb01ac7f0c..e86ec690a5dc 100644 --- a/test/python/test_dagcircuit.py +++ b/test/python/test_dagcircuit.py @@ -27,10 +27,10 @@ from qiskit.circuit import Measure from qiskit.circuit import Reset from qiskit.circuit import Gate, Instruction -from qiskit.extensions.standard.iden import IdGate +from qiskit.extensions.standard.i import IGate from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.x import CnotGate -from qiskit.extensions.standard.z import CzGate +from qiskit.extensions.standard.x import CXGate +from qiskit.extensions.standard.z import CZGate from qiskit.extensions.standard.x import XGate from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.barrier import Barrier @@ -196,7 +196,7 @@ def setUp(self): def test_apply_operation_back(self): """The apply_operation_back() method.""" self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], [], condition=None) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition) self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None) @@ -335,7 +335,7 @@ def test_apply_operation_front(self): def test_get_op_nodes_all(self): """The method dag.op_nodes() returns all op nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) op_nodes = self.dag.op_nodes() @@ -350,7 +350,7 @@ def test_get_op_nodes_particular(self): self.dag.apply_operation_back(HGate(), [self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) op_nodes = self.dag.op_nodes(op=HGate) self.assertEqual(len(op_nodes), 2) @@ -364,7 +364,7 @@ def test_get_op_nodes_particular(self): def test_quantum_successors(self): """The method dag.quantum_successors() returns successors connected by quantum edges""" self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) successor_measure = self.dag.quantum_successors( @@ -373,7 +373,7 @@ def test_quantum_successors(self): with self.assertRaises(StopIteration): next(successor_measure) - self.assertIsInstance(cnot_node.op, CnotGate) + self.assertIsInstance(cnot_node.op, CXGate) successor_cnot = self.dag.quantum_successors(cnot_node) self.assertEqual(next(successor_cnot).type, 'out') @@ -384,7 +384,7 @@ def test_quantum_successors(self): def test_quantum_predecessors(self): """The method dag.quantum_predecessors() returns predecessors connected by quantum edges""" self.dag.apply_operation_back(Reset(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) predecessor_measure = self.dag.quantum_predecessors( @@ -393,7 +393,7 @@ def test_quantum_predecessors(self): with self.assertRaises(StopIteration): next(predecessor_measure) - self.assertIsInstance(cnot_node.op, CnotGate) + self.assertIsInstance(cnot_node.op, CXGate) predecessor_cnot = self.dag.quantum_predecessors(cnot_node) self.assertIsInstance(next(predecessor_cnot).op, Reset) @@ -404,7 +404,7 @@ def test_quantum_predecessors(self): def test_get_gates_nodes(self): """The method dag.gate_nodes() returns all gate nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) op_nodes = self.dag.gate_nodes() @@ -419,7 +419,7 @@ def test_get_gates_nodes(self): def test_two_q_gates(self): """The method dag.twoQ_gates() returns all 2Q gate nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Barrier(2), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) @@ -432,10 +432,10 @@ def test_two_q_gates(self): def test_get_named_nodes(self): """The get_named_nodes(AName) method returns all the nodes with name AName""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) # The ordering is not assured, so we only compare the output (unordered) sets. @@ -453,10 +453,10 @@ def test_get_named_nodes(self): def test_topological_nodes(self): """The topological_nodes() method""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) named_nodes = self.dag.topological_nodes() @@ -480,10 +480,10 @@ def test_topological_nodes(self): def test_topological_op_nodes(self): """The topological_op_nodes() method""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) named_nodes = self.dag.topological_op_nodes() @@ -497,7 +497,7 @@ def test_topological_op_nodes(self): def test_dag_nodes_on_wire(self): """Test that listing the gates on a qubit/classical bit gets the correct gates""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) qbit = self.dag.qubits()[0] @@ -524,9 +524,9 @@ def test_dag_nodes_on_wire_multiple_successors(self): Both the 2nd CX gate and the H gate follow the first CX gate in the DAG, so they both must be returned but in the correct order. """ - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) nodes = self.dag.nodes_on_wire(self.dag.qubits()[1], only_ops=True) node_names = [nd.name for nd in nodes] @@ -545,10 +545,10 @@ def test_remove_op_node(self): def test_remove_op_node_longer(self): """Test remove_op_node method in a "longer" dag""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1]) self.dag.apply_operation_back(HGate(), [self.qubit0]) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1]) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2]) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2]) self.dag.apply_operation_back(HGate(), [self.qubit2]) op_nodes = list(self.dag.topological_op_nodes()) @@ -573,8 +573,8 @@ def test_dag_collect_runs(self): self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1]) - self.dag.apply_operation_back(CnotGate(), [self.qubit1, self.qubit2]) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2]) self.dag.apply_operation_back(HGate(), [self.qubit2]) collected_runs = self.dag.collect_runs(['u1', 'cx', 'h']) self.assertEqual(len(collected_runs), 3) @@ -643,7 +643,7 @@ def test_layers_basic(self): dag.add_qreg(qreg) dag.add_creg(creg) dag.apply_operation_back(HGate(), [qubit0], []) - dag.apply_operation_back(CnotGate(), [qubit0, qubit1], [], condition=None) + dag.apply_operation_back(CXGate(), [qubit0, qubit1], [], condition=None) dag.apply_operation_back(Measure(), [qubit1, clbit1], [], condition=None) dag.apply_operation_back(XGate(), [qubit1], [], condition=condition) dag.apply_operation_back(Measure(), [qubit0, clbit0], [], condition=None) @@ -680,7 +680,7 @@ def test_layers_maintains_order(self): qc.x(0) dag = circuit_to_dag(qc) dag1 = list(dag.layers())[0]['graph'] - dag1.apply_operation_back(IdGate(), [qr[0]], []) + dag1.apply_operation_back(IGate(), [qr[0]], []) comp = [(nd.type, nd.name, nd._node_id) for nd in dag1.topological_nodes()] self.assertEqual(comp, truth) @@ -833,19 +833,19 @@ def setUp(self): self.condition = (creg, 3) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(XGate(), [self.qubit1], []) def test_substitute_circuit_one_middle(self): """The method substitute_node_with_dag() replaces a in-the-middle node with a DAG.""" - cx_node = self.dag.op_nodes(op=CnotGate).pop() + cx_node = self.dag.op_nodes(op=CXGate).pop() flipped_cx_circuit = DAGCircuit() v = QuantumRegister(2, "v") flipped_cx_circuit.add_qreg(v) flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], []) - flipped_cx_circuit.apply_operation_back(CnotGate(), [v[1], v[0]], []) + flipped_cx_circuit.apply_operation_back(CXGate(), [v[1], v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], []) @@ -897,7 +897,7 @@ def test_substituting_node_with_wrong_width_node_raises(self): dag = DAGCircuit() qr = QuantumRegister(2) dag.add_qreg(qr) - node_to_be_replaced = dag.apply_operation_back(CnotGate(), [qr[0], qr[1]]) + node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[0], qr[1]]) with self.assertRaises(DAGCircuitError) as _: dag.substitute_node(node_to_be_replaced, Measure()) @@ -923,12 +923,12 @@ def test_substituting_node_preserves_args_condition(self, inplace): dag.add_qreg(qr) dag.add_creg(cr) dag.apply_operation_back(HGate(), [qr[1]]) - node_to_be_replaced = dag.apply_operation_back(CnotGate(), [qr[1], qr[0]], + node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[1], qr[0]], condition=(cr, 1)) dag.apply_operation_back(HGate(), [qr[1]]) - replacement_node = dag.substitute_node(node_to_be_replaced, CzGate(), + replacement_node = dag.substitute_node(node_to_be_replaced, CZGate(), inplace=inplace) raise_if_dagcircuit_invalid(dag) diff --git a/test/python/test_qasm_parser.py b/test/python/test_qasm_parser.py index 8be70a1b5f8b..761a386555e0 100644 --- a/test/python/test_qasm_parser.py +++ b/test/python/test_qasm_parser.py @@ -48,10 +48,20 @@ def test_parser(self): res = parse(self.qasm_file_path) self.log.info(res) # TODO: For now only some basic checks. - self.assertEqual(len(res), 2358) - self.assertEqual(res[:12], "OPENQASM 2.0") - self.assertEqual(res[14:41], "gate u3(theta,phi,lambda) q") - self.assertEqual(res[2342:2357], "measure r -> d;") + starts_expected = "OPENQASM 2.0;\ngate " + ends_expected = '\n'.join(['}', + 'qreg q[3];', + 'qreg r[3];', + 'h q;', + 'cx q,r;', + 'creg c[3];', + 'creg d[3];', + 'barrier q;', + 'measure q -> c;', + 'measure r -> d;', '']) + + self.assertEqual(res[:len(starts_expected)], starts_expected) + self.assertEqual(res[-len(ends_expected):], ends_expected) def test_parser_fail(self): """should fail a for a not valid circuit.""" @@ -61,6 +71,7 @@ def test_parser_fail(self): def test_all_valid_nodes(self): """Test that the tree contains only Node subclasses.""" + def inspect(node): """Inspect node children.""" for child in node.children: diff --git a/test/python/transpiler/test_collect_2q_blocks.py b/test/python/transpiler/test_collect_2q_blocks.py index 5dc3c619f6b9..fa4d07aea38a 100644 --- a/test/python/transpiler/test_collect_2q_blocks.py +++ b/test/python/transpiler/test_collect_2q_blocks.py @@ -67,21 +67,21 @@ def test_block_interrupted_by_gate(self): blocks : [['cx', 'id', 'id', 'id'], ['id', 'cx']] - ┌───┐┌────┐┌─┐ ┌────┐┌───┐ - q_0: |0>┤ X ├┤ Id ├┤M├──────┤ Id ├┤ X ├ - └─┬─┘├────┤└╥┘┌────┐└────┘└─┬─┘ - q_1: |0>──■──┤ Id ├─╫─┤ Id ├────────■── - └────┘ ║ └────┘ - c_0: 0 ════════════╩══════════════════ + ┌───┐┌───┐┌─┐ ┌───┐┌───┐ + q_0: |0>┤ X ├┤ I ├┤M├─────┤ I ├┤ X ├ + └─┬─┘├───┤└╥┘┌───┐└───┘└─┬─┘ + q_1: |0>──■──┤ I ├─╫─┤ I ├───────■── + └───┘ ║ └───┘ + c_0: 0 ═══════════╩════════════════ """ qc = QuantumCircuit(2, 1) qc.cx(1, 0) - qc.iden(0) - qc.iden(1) + qc.i(0) + qc.i(1) qc.measure(0, 0) - qc.iden(0) - qc.iden(1) + qc.i(0) + qc.i(1) qc.cx(1, 0) dag = circuit_to_dag(qc) @@ -89,7 +89,7 @@ def test_block_interrupted_by_gate(self): pass_.run(dag) # list from Collect2QBlocks of nodes that it should have put into blocks - good_names = ["cx", "u1", "u2", "u3", "id"] + good_names = ['cx', 'u1', 'u2', 'u3', 'id'] dag_nodes = [node for node in dag.topological_op_nodes() if node.name in good_names] # we have to convert them to sets as the ordering can be different diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py index ea9df3c197d1..2f4639c71645 100644 --- a/test/python/transpiler/test_consolidate_blocks.py +++ b/test/python/transpiler/test_consolidate_blocks.py @@ -190,10 +190,10 @@ def test_node_added_before_block(self): c_0: 0 ══╩══════════════ """ qc = QuantumCircuit(2, 1) - qc.iden(0) + qc.i(0) qc.measure(1, 0) qc.cx(1, 0) - qc.iden(1) + qc.i(1) # can't just add all the nodes to one block as in other tests # as we are trying to test the block gets added in the correct place @@ -224,9 +224,9 @@ def test_node_added_after_block(self): """ qc = QuantumCircuit(3) qc.cx(1, 2) - qc.iden(1) + qc.i(1) qc.cx(0, 1) - qc.iden(2) + qc.i(2) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) @@ -255,13 +255,13 @@ def test_node_middle_of_blocks(self): qc = QuantumCircuit(4) qc.cx(0, 1) qc.cx(3, 2) - qc.iden(1) - qc.iden(2) + qc.i(1) + qc.i(2) qc.swap(1, 2) - qc.iden(1) - qc.iden(2) + qc.i(1) + qc.i(2) qc.cx(0, 1) qc.cx(3, 2) diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py index c09952c514ad..7cd4ee0f997c 100644 --- a/test/python/transpiler/test_decompose.py +++ b/test/python/transpiler/test_decompose.py @@ -20,7 +20,7 @@ from qiskit.transpiler.passes import Decompose from qiskit.converters import circuit_to_dag from qiskit.extensions.standard import HGate -from qiskit.extensions.standard import ToffoliGate +from qiskit.extensions.standard import CCXGate from qiskit.test import QiskitTestCase @@ -63,7 +63,7 @@ def test_decompose_toffoli(self): circuit = QuantumCircuit(qr1, qr2) circuit.ccx(qr1[0], qr1[1], qr2[0]) dag = circuit_to_dag(circuit) - pass_ = Decompose(ToffoliGate) + pass_ = Decompose(CCXGate) after_dag = pass_.run(dag) op_nodes = after_dag.op_nodes() self.assertEqual(len(op_nodes), 15) diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py index d94689aea7e8..1922f522d779 100644 --- a/test/python/transpiler/test_optimize_1q_gates.py +++ b/test/python/transpiler/test_optimize_1q_gates.py @@ -39,8 +39,8 @@ def test_dont_optimize_id(self): """ qr = QuantumRegister(1, 'qr') circuit = QuantumCircuit(qr) - circuit.iden(qr) - circuit.iden(qr) + circuit.i(qr) + circuit.i(qr) dag = circuit_to_dag(circuit) pass_ = Optimize1qGates() diff --git a/test/python/transpiler/test_passmanager_run.py b/test/python/transpiler/test_passmanager_run.py index 8d2c9911ef18..b9e1b522d705 100644 --- a/test/python/transpiler/test_passmanager_run.py +++ b/test/python/transpiler/test_passmanager_run.py @@ -15,7 +15,7 @@ """Tests PassManager.run()""" from qiskit import QuantumRegister, QuantumCircuit -from qiskit.extensions.standard import CnotGate +from qiskit.extensions.standard import CXGate from qiskit.transpiler.preset_passmanagers import level_1_pass_manager from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeMelbourne @@ -64,7 +64,7 @@ def test_default_pass_manager_single(self): new_circuit = pass_manager.run(circuit) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_default_pass_manager_two(self): @@ -111,5 +111,5 @@ def test_default_pass_manager_two(self): for new_circuit in new_circuits: for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index ef77cb7f8198..7d9c8b8d13bb 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -219,7 +219,7 @@ class TesRemoveDiagonalControlGatesBeforeMeasure(QiskitTestCase): """ Test remove diagonal control gates before measure. """ def test_optimize_1cz_2measure(self): - """ Remove a single CzGate + """ Remove a single CZGate qr0:--Z--m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -244,7 +244,7 @@ def test_optimize_1cz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) def test_optimize_1crz_2measure(self): - """ Remove a single CrzGate + """ Remove a single CRZGate qr0:-RZ--m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -269,7 +269,7 @@ def test_optimize_1crz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) def test_optimize_1cu1_2measure(self): - """ Remove a single Cu1Gate + """ Remove a single CU1Gate qr0:-CU1-m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -323,7 +323,7 @@ class TestRemoveDiagonalGatesBeforeMeasureOveroptimizations(QiskitTestCase): """ Test situations where remove_diagonal_gates_before_measure should not optimize """ def test_optimize_1cz_1measure(self): - """ Do not remove a CzGate because measure happens on only one of the wires + """ Do not remove a CZGate because measure happens on only one of the wires Compare with test_optimize_1cz_2measure. qr0:--Z--m--- diff --git a/test/python/transpiler/test_unroller.py b/test/python/transpiler/test_unroller.py index f4ec79eef7bf..5c1c4d883ce2 100644 --- a/test/python/transpiler/test_unroller.py +++ b/test/python/transpiler/test_unroller.py @@ -125,7 +125,7 @@ def test_unroll_all_instructions(self): circuit.cy(qr[1], qr[2]) circuit.cz(qr[2], qr[0]) circuit.h(qr[1]) - circuit.iden(qr[0]) + circuit.i(qr[0]) circuit.rx(0.1, qr[0]) circuit.ry(0.2, qr[1]) circuit.rz(0.3, qr[2]) @@ -220,7 +220,7 @@ def test_unroll_all_instructions(self): ref_circuit.u3(0, 0, pi/2, qr[2]) ref_circuit.cx(qr[2], qr[0]) ref_circuit.u3(pi/2, 0, pi, qr[0]) - ref_circuit.iden(qr[0]) + ref_circuit.i(qr[0]) ref_circuit.u3(0.1, -pi/2, pi/2, qr[0]) ref_circuit.cx(qr[1], qr[0]) ref_circuit.u3(0, 0, 0.6, qr[0]) diff --git a/test/python/visualization/references/circuit_text_ref.txt b/test/python/visualization/references/circuit_text_ref.txt index e2a89e2967bf..4edf26f864f6 100644 --- a/test/python/visualization/references/circuit_text_ref.txt +++ b/test/python/visualization/references/circuit_text_ref.txt @@ -1,13 +1,13 @@ - ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌────┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ -q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ Id ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── - └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└────┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ -q_1: |0>──░─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── - ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ -q_2: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ - ░ └───┘ ║ ║ └╥┘ - c_0: 0 ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ - ║ ║ - c_1: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ - ║ - c_2: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ - \ No newline at end of file + ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌───┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ +q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ I ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── + └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└───┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ +q_1: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── + ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ +q_2: |0>──░───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ + ░ └───┘ ║ ║ └╥┘ + c_0: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ + ║ ║ + c_1: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ + ║ + c_2: 0 ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ + \ No newline at end of file diff --git a/test/python/visualization/references/circuit_text_ref_original.txt b/test/python/visualization/references/circuit_text_ref_original.txt new file mode 100644 index 000000000000..c4c693a71966 --- /dev/null +++ b/test/python/visualization/references/circuit_text_ref_original.txt @@ -0,0 +1,12 @@ + ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌────┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ +q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ Id ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── + └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└────┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ +q_1: |0>──░─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── + ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ +q_2: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ + ░ └───┘ ║ ║ └╥┘ + c_0: 0 ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ + ║ ║ + c_1: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ + ║ + c_2: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index 676379f65101..eda1cb68e424 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -29,7 +29,7 @@ from qiskit.transpiler import Layout from qiskit.visualization import text as elements from qiskit.visualization.circuit_visualization import _text_circuit_drawer -from qiskit.extensions import HGate, U2Gate, XGate, UnitaryGate, CzGate +from qiskit.extensions import HGate, U2Gate, XGate, UnitaryGate, CZGate class TestTextDrawerElement(QiskitTestCase): @@ -965,8 +965,8 @@ def test_label_turns_to_box_2286(self): " └───────────┘"]) qr = QuantumRegister(2, 'q') circ = QuantumCircuit(qr) - circ.append(CzGate(), [qr[0], qr[1]]) - circ.append(CzGate(label='cz label'), [qr[0], qr[1]]) + circ.append(CZGate(), [qr[0], qr[1]]) + circ.append(CZGate(label='cz label'), [qr[0], qr[1]]) self.assertEqual(str(_text_circuit_drawer(circ)), expected) diff --git a/test/python/visualization/test_circuit_visualization_output.py b/test/python/visualization/test_circuit_visualization_output.py index a255c7b0bbbe..7c9bce3e7423 100644 --- a/test/python/visualization/test_circuit_visualization_output.py +++ b/test/python/visualization/test_circuit_visualization_output.py @@ -52,7 +52,7 @@ def sample_circuit(self): circuit.sdg(qr[0]) circuit.t(qr[0]) circuit.tdg(qr[0]) - circuit.iden(qr[0]) + circuit.i(qr[0]) circuit.reset(qr[0]) circuit.rx(pi, qr[0]) circuit.ry(pi, qr[0]) @@ -100,7 +100,7 @@ def test_matplotlib_drawer(self): def test_text_drawer(self): filename = self._get_resource_path('current_textplot.txt') qc = self.sample_circuit() - output = circuit_drawer(qc, filename=filename, output="text", line_length=-1) + output = circuit_drawer(qc, filename=filename, output="text", fold=-1) self.assertFilesAreEqual(filename, self.text_reference) os.remove(filename) try: diff --git a/test/randomized/test_transpiler_equivalence.py b/test/randomized/test_transpiler_equivalence.py index 7b0d6cd1050f..c3f5dee31243 100644 --- a/test/randomized/test_transpiler_equivalence.py +++ b/test/randomized/test_transpiler_equivalence.py @@ -36,16 +36,16 @@ # pylint: disable=wildcard-import,unused-wildcard-import from qiskit.extensions.standard import * -oneQ_gates = [HGate, IdGate, SGate, SdgGate, TGate, TdgGate, XGate, YGate, ZGate, Reset] -twoQ_gates = [CnotGate, CyGate, CzGate, SwapGate, CHGate] -threeQ_gates = [ToffoliGate, FredkinGate] +oneQ_gates = [HGate, IGate, SGate, SdgGate, TGate, TdgGate, XGate, YGate, ZGate, Reset] +twoQ_gates = [CXGate, CYGate, CZGate, SwapGate, CHGate] +threeQ_gates = [CCXGate, CSwapGate] oneQ_oneP_gates = [U1Gate, RXGate, RYGate, RZGate] oneQ_twoP_gates = [U2Gate] oneQ_threeP_gates = [U3Gate] -twoQ_oneP_gates = [CrzGate, RZZGate, Cu1Gate] -twoQ_threeP_gates = [Cu3Gate] +twoQ_oneP_gates = [CRZGate, RZZGate, CU1Gate] +twoQ_threeP_gates = [CU3Gate] oneQ_oneC_gates = [Measure] variadic_gates = [Barrier] From 994b2224ad1c455e25d112c66ea72ccd84fc4bd2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sun, 23 Feb 2020 23:39:34 -0500 Subject: [PATCH 11/12] Add random circuit function to api docs (#3881) This commit adds the docs for random_circuit to the Quantum Circuit docs so that the docstring is usable from the rendered docs. It previously was not being included in any documentation which meant the only documentation about it was in the code. --- qiskit/circuit/__init__.py | 7 +++++++ qiskit/circuit/random/utils.py | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index f23a7dfed9b3..72cde06fe2af 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -54,6 +54,13 @@ ParameterVector ParameterExpression +Random Circuits +=============== + +.. autosummary:: + :toctree: ../stubs/ + + random.random_circuit """ from .quantumcircuit import QuantumCircuit from .classicalregister import ClassicalRegister, Clbit diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py index 31306c52c878..e969c0a9a1f3 100644 --- a/qiskit/circuit/random/utils.py +++ b/qiskit/circuit/random/utils.py @@ -31,6 +31,16 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, conditional=False, reset=False, seed=None): """Generate random circuit of arbitrary size and form. + This function will generate a random circuit by randomly selecting gates + from the set of standard gates in :mod:`qiskit.extensions`. For example: + + .. jupyter-execute:: + + from qiskit.circuit.random import random_circuit + + circ = random_circuit(2, 2, measure=True) + circ.draw(output='mpl') + Args: n_qubits (int): number of quantum wires depth (int): layers of operations (i.e. critical path length) From d47f82738fbed7b7fdd6278acab3f206946a4edc Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Mon, 24 Feb 2020 15:43:39 +0100 Subject: [PATCH 12/12] fix use of deprecated argument (#3882) --- qiskit/extensions/standard/i.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/extensions/standard/i.py b/qiskit/extensions/standard/i.py index 77b079f384c3..e14a00edd2c6 100644 --- a/qiskit/extensions/standard/i.py +++ b/qiskit/extensions/standard/i.py @@ -101,7 +101,7 @@ def iden(self, qubit, *, q=None): # pylint: disable=unused-argument 'will be removed no earlier than 3 months after that release date. ' 'You should use the QuantumCircuit.i() method instead.', DeprecationWarning, stacklevel=2) - return self.append(IGate(), [q], []) + return self.append(IGate(), [qubit], []) # support both i and id as methods of QuantumCircuit