Source code for graphix.instruction

"""Instruction classes."""

from __future__ import annotations

import enum
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import TYPE_CHECKING, ClassVar, Literal, SupportsFloat, TypeAlias

# Self introduced in Python 3.11
# override introduced in Python 3.12
from typing_extensions import Self, override

from graphix import utils
from graphix.fundamentals import (
    Axis,
    ParameterizedAngle,
    Plane,
)
from graphix.pretty_print import OutputFormat, angle_to_str
from graphix.repr_mixins import DataclassReprMixin


[docs] def repr_angle(angle: ParameterizedAngle) -> str: """ Return the representation string of an angle in radians. This is used for pretty-printing instructions with ``angle`` parameters. Delegates to :func:`pretty_print.angle_to_str`. """ # Non-float-supporting objects are returned as-is if not isinstance(angle, SupportsFloat): return str(angle) return angle_to_str(angle, OutputFormat.ASCII)
[docs] class InstructionKind(Enum): """Tag for instruction kind.""" CCX = enum.auto() RZZ = enum.auto() CNOT = enum.auto() SWAP = enum.auto() CZ = enum.auto() H = enum.auto() S = enum.auto() X = enum.auto() Y = enum.auto() Z = enum.auto() J = enum.auto() I = enum.auto() M = enum.auto() RX = enum.auto() RY = enum.auto() RZ = enum.auto() SDG = enum.auto() T = enum.auto() TDG = enum.auto() SX = enum.auto() SXDG = enum.auto() CY = enum.auto() P = enum.auto() U = enum.auto() CJ = enum.auto() CP = enum.auto() CRX = enum.auto() CRY = enum.auto() CRZ = enum.auto() CU = enum.auto() CSWAP = enum.auto() GPHASE = enum.auto()
class _KindChecker: """Enforce tag field declaration.""" def __init_subclass__(cls) -> None: """Validate that subclasses define the ``kind`` attribute.""" super().__init_subclass__() utils.check_kind(cls, {"InstructionKind": InstructionKind, "Plane": Plane})
[docs] class InstructionVisitor: """Visitor for instruction. This base class can be subclassed to rewrite instructions by overriding some of the following functions: - ``visit_qubit``: rewrite qubit indices. - ``visit_angle``: rewrite angles. - ``visit_axis``: rewrite axes. """
[docs] def visit_qubit(self, qubit: int) -> int: """Rewrite a qubit index.""" return qubit
[docs] def visit_angle(self, angle: ParameterizedAngle) -> ParameterizedAngle: """Rewrite an angle.""" return angle
[docs] def visit_axis(self, axis: Axis) -> Axis: """Rewrite an axis.""" return axis
[docs] class BaseInstruction(ABC, DataclassReprMixin): """Base class for circuit instructions."""
[docs] @abstractmethod def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: """Rewrite the instruction according to the given visitor. Parameters ---------- visitor : InstructionVisitor The visitor specifying the rewriting. copy : bool, optional If ``True``, the current instruction remains unchanged, and a new instruction is returned. The default is ``False``, meaning that changes are performed in place. Returns ------- Self The rewritten instruction. Equal to ``self`` if ``copy`` is ``False``. """
[docs] @dataclass(repr=False) class CCX(_KindChecker, BaseInstruction): r"""Toffoli circuit instruction. The CCX gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``controls[0]``, ``controls[1]``, ``target``. Attributes ---------- controls : tuple[int, int] Index of the control qubits. target : int Index of the target qubit. """ target: int controls: tuple[int, int] kind: ClassVar[Literal[InstructionKind.CCX]] = field(default=InstructionKind.CCX, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> CCX: u, v = self.controls target = visitor.visit_qubit(self.target) controls = (visitor.visit_qubit(u), visitor.visit_qubit(v)) if copy: return CCX(target, controls) self.target = target self.controls = controls return self
[docs] @dataclass(repr=False) class RZZ(_KindChecker, BaseInstruction): r"""RZZ circuit instruction. The :math:`\mathrm{RZZ}(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} \mathrm e^{-\mathrm i \frac \theta 2} & 0 & 0 & 0\\ 0 & \mathrm e^{\mathrm i \frac \theta 2} & 0 & 0\\ 0 & 0 & \mathrm e^{\mathrm i \frac \theta 2} & 0\\ 0 & 0 & 0 & \mathrm e^{-\mathrm i \frac \theta 2} \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. We have :math:`\mathrm{RZZ}(\theta) = \mathrm{CNOT} (I \otimes \mathrm{RZ}(\theta)) \mathrm{CNOT}`. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Rotation angle. """ target: int control: int angle: ParameterizedAngle = field(metadata={"repr": repr_angle}) kind: ClassVar[Literal[InstructionKind.RZZ]] = field(default=InstructionKind.RZZ, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> RZZ: target = visitor.visit_qubit(self.target) control = visitor.visit_qubit(self.control) angle = visitor.visit_angle(self.angle) if copy: return RZZ(target, control, angle) self.target = target self.control = control self.angle = angle return self
[docs] @dataclass(repr=False) class ControlledSingleTargetInstruction(BaseInstruction): """Base class for controlled single-target circuit instructions.""" target: int control: int
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: target = visitor.visit_qubit(self.target) control = visitor.visit_qubit(self.control) if copy: return type(self)(target, control) self.target = target self.control = control return self
[docs] @dataclass(repr=False) class CY(_KindChecker, ControlledSingleTargetInstruction): r"""CY circuit instruction. The CY gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & -\mathrm i\\ 0 & 0 & \mathrm i & 0 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. """ kind: ClassVar[Literal[InstructionKind.CY]] = field(default=InstructionKind.CY, init=False)
[docs] @dataclass(repr=False) class CNOT(_KindChecker, ControlledSingleTargetInstruction): r"""CNOT circuit instruction. The CNOT gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. """ kind: ClassVar[Literal[InstructionKind.CNOT]] = field(default=InstructionKind.CNOT, init=False)
# CZ is not defined as a ControlledSingleTargetInstruction because of # the symmetry between the control and the target.
[docs] @dataclass(repr=False) class CZ(_KindChecker, BaseInstruction): r"""CZ circuit instruction. The CZ gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & -1 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``targets[0]``, ``targets[1]``. Attributes ---------- targets : tuple[int, int] Index of the target qubits. """ targets: tuple[int, int] kind: ClassVar[Literal[InstructionKind.CZ]] = field(default=InstructionKind.CZ, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> CZ: u, v = self.targets targets = (visitor.visit_qubit(u), visitor.visit_qubit(v)) if copy: return CZ(targets) self.targets = targets return self
[docs] @dataclass(repr=False) class SWAP(_KindChecker, BaseInstruction): r"""SWAP circuit instruction. The SWAP gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``targets[0]``, ``targets[1]``. Attributes ---------- targets : tuple[int, int] Index of the target qubits. """ targets: tuple[int, int] kind: ClassVar[Literal[InstructionKind.SWAP]] = field(default=InstructionKind.SWAP, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> SWAP: u, v = self.targets targets = (visitor.visit_qubit(u), visitor.visit_qubit(v)) if copy: return SWAP(targets) self.targets = targets return self
[docs] @dataclass(repr=False) class CSWAP(_KindChecker, BaseInstruction): r"""CSWAP circuit instruction. The CSWAP gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``targets[0]``, ``targets[1]``. Attributes ---------- control : int Index of the control qubit. targets : tuple[int, int] Indices of the two target qubits. """ control: int targets: tuple[int, int] kind: ClassVar[Literal[InstructionKind.CSWAP]] = field(default=InstructionKind.CSWAP, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> CSWAP: control = visitor.visit_qubit(self.control) u, v = self.targets targets = (visitor.visit_qubit(u), visitor.visit_qubit(v)) if copy: return CSWAP(control, targets) self.control = control self.targets = targets return self
[docs] @dataclass(repr=False) class SingleTargetInstruction(BaseInstruction): """Base class for single-target circuit instructions.""" target: int
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: target = visitor.visit_qubit(self.target) if copy: return type(self)(target) self.target = target return self
[docs] @dataclass(repr=False) class H(_KindChecker, SingleTargetInstruction): r"""H circuit instruction. The Hadamard gate applies the matrix :math:`\frac 1 {\sqrt 2}\left[\begin{matrix}1 & 1\\1 & -1\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.H]] = field(default=InstructionKind.H, init=False)
[docs] @dataclass(repr=False) class S(_KindChecker, SingleTargetInstruction): r"""S circuit instruction. The S gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\0 & \mathrm i\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.S]] = field(default=InstructionKind.S, init=False)
[docs] @dataclass(repr=False) class SDG(_KindChecker, SingleTargetInstruction): r"""SDG circuit instruction. The :math:`S^\dagger` gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\0 & - \mathrm i\end{matrix}\right]`. We have :math:`S^\dagger = \mathrm e^{\mathrm i \frac \pi 4} R_Z(-\frac \pi 2)`. """ kind: ClassVar[Literal[InstructionKind.SDG]] = field(default=InstructionKind.SDG, init=False)
[docs] @dataclass(repr=False) class T(_KindChecker, SingleTargetInstruction): r"""T circuit instruction. The :math:`T` gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\0 & \mathrm e^{\mathrm i \frac \pi 4}\end{matrix}\right]`. We have :math:`T = \mathrm e^{\mathrm i \frac \pi 8} R_Z(\frac \pi 4)`. """ kind: ClassVar[Literal[InstructionKind.T]] = field(default=InstructionKind.T, init=False)
[docs] @dataclass(repr=False) class TDG(_KindChecker, SingleTargetInstruction): r"""TDG circuit instruction. The :math:`T^\dagger` gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\0 & \mathrm e^{- \mathrm i \frac \pi 4}\end{matrix}\right]`. We have :math:`T^\dagger = \mathrm e^{\mathrm i \frac \pi 8} R_Z(- \frac \pi 4)`. """ kind: ClassVar[Literal[InstructionKind.TDG]] = field(default=InstructionKind.TDG, init=False)
[docs] @dataclass(repr=False) class SX(_KindChecker, SingleTargetInstruction): r"""SX circuit instruction. The :math:`SX` (:math:`\sqrt X`) gate applies the matrix :math:`\frac 1 2 \left[\begin{matrix}1 + \mathrm i & 1 - \mathrm i\\1 - \mathrm i & 1 + \mathrm i\end{matrix}\right]`. We have :math:`SX = \mathrm e^{\mathrm i \frac \pi 4} R_X(\frac \pi 2)`. """ kind: ClassVar[Literal[InstructionKind.SX]] = field(default=InstructionKind.SX, init=False)
[docs] @dataclass(repr=False) class SXDG(_KindChecker, SingleTargetInstruction): r"""SXDG circuit instruction. The :math:`SX^\dagger` (:math:`{\sqrt X}^\dagger`) gate applies the matrix :math:`\frac 1 2 \left[\begin{matrix}1 - \mathrm i & 1 + \mathrm i\\1 + \mathrm i & 1 - \mathrm i\end{matrix}\right]`. We have :math:`SX^\dagger = \mathrm e^{\mathrm i \frac \pi 4} R_X(-\frac \pi 2)`. """ kind: ClassVar[Literal[InstructionKind.SXDG]] = field(default=InstructionKind.SXDG, init=False)
[docs] @dataclass(repr=False) class X(_KindChecker, SingleTargetInstruction): r"""X circuit instruction. The X gate applies the matrix :math:`\left[\begin{matrix}0 & 1\\1 & 0\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.X]] = field(default=InstructionKind.X, init=False)
[docs] @dataclass(repr=False) class Y(_KindChecker, SingleTargetInstruction): r"""Y circuit instruction. The Y gate applies the matrix :math:`\left[\begin{matrix}0 & -\mathrm i\\\mathrm i & 0\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.Y]] = field(default=InstructionKind.Y, init=False)
[docs] @dataclass(repr=False) class Z(_KindChecker, SingleTargetInstruction): r"""Z circuit instruction. The Z gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\\mathrm 0 & -1\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.Z]] = field(default=InstructionKind.Z, init=False)
[docs] @dataclass(repr=False) class I(_KindChecker, SingleTargetInstruction): r"""I circuit instruction. The identity gate applies the matrix :math:`\left[\begin{matrix}1 & 0\\\mathrm 0 & 1\end{matrix}\right]`. """ kind: ClassVar[Literal[InstructionKind.I]] = field(default=InstructionKind.I, init=False)
[docs] @dataclass(repr=False) class M(_KindChecker, BaseInstruction): """M circuit instruction.""" target: int axis: Axis kind: ClassVar[Literal[InstructionKind.M]] = field(default=InstructionKind.M, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> M: target = visitor.visit_qubit(self.target) axis = visitor.visit_axis(self.axis) if copy: return M(target, axis) self.target = target self.axis = axis return self
[docs] @dataclass(repr=False) class RotationInstruction(BaseInstruction): """Base class for rotation instructions.""" target: int angle: ParameterizedAngle = field(metadata={"repr": repr_angle})
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: target = visitor.visit_qubit(self.target) angle = visitor.visit_angle(self.angle) if copy: return type(self)(target, angle) self.target = target self.angle = angle return self
[docs] @dataclass(repr=False) class P(_KindChecker, RotationInstruction): r"""P rotation circuit instruction. The :math:`P(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0\\ 0 & \mathrm e^{\mathrm i \theta} \end{matrix}\right] We have :math:`P(\theta) = \mathrm e^{\theta/2} R_Z(\theta)`. """ kind: ClassVar[Literal[InstructionKind.P]] = field(default=InstructionKind.P, init=False)
[docs] @dataclass(repr=False) class RX(_KindChecker, RotationInstruction): r"""X rotation circuit instruction. The :math:`RX(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} \cos \frac \theta 2 & -\mathrm i \sin \frac \theta 2\\ -\mathrm i \sin \frac \theta 2 & \cos \frac \theta 2 \end{matrix}\right] """ kind: ClassVar[Literal[InstructionKind.RX]] = field(default=InstructionKind.RX, init=False)
[docs] @dataclass(repr=False) class RY(_KindChecker, RotationInstruction): r"""Y rotation circuit instruction. The :math:`RY(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} \cos \frac \theta 2 & - \sin \frac \theta 2\\ \sin \frac \theta 2 & \cos \frac \theta 2 \end{matrix}\right] """ kind: ClassVar[Literal[InstructionKind.RY]] = field(default=InstructionKind.RY, init=False)
[docs] @dataclass(repr=False) class RZ(_KindChecker, RotationInstruction): r"""Z rotation circuit instruction. The :math:`RZ(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} \mathrm e^{-\mathrm i \frac \theta 2} & 0\\ 0 & \mathrm e^{\mathrm i \frac \theta 2} \end{matrix}\right] """ kind: ClassVar[Literal[InstructionKind.RZ]] = field(default=InstructionKind.RZ, init=False)
[docs] @dataclass(repr=False) class J(_KindChecker, RotationInstruction): r"""J circuit instruction. The :math:`J(\theta)` gate applies the matrix .. math:: \frac 1 {\sqrt 2} \left[\begin{matrix} 1 & \mathrm e^{\mathrm i \theta}\\ 1 & - \mathrm e^{\mathrm i \theta} \end{matrix}\right] We have :math:`J(\theta) = \mathrm e^{\mathrm i \theta/2} H \mathrm{RZ}(\theta)`. """ kind: ClassVar[Literal[InstructionKind.J]] = field(default=InstructionKind.J, init=False)
[docs] @dataclass(repr=False) class U(_KindChecker, BaseInstruction): r"""U circuit instruction. The :math:`U(\theta, \phi, \lambda)` gate applies the matrix .. math:: \left[\begin{matrix} \cos \frac \theta 2 & - \mathrm e^{\mathrm i\lambda} \sin \frac \theta 2 \\ \mathrm e^{\mathrm i \phi} \sin \frac \theta 2 & \mathrm e^{\mathrm i (\phi + \lambda)} \cos \frac \theta 2 \end{matrix}\right] It can be decomposed as .. math:: U(\theta, \phi, \lambda) = \mathrm e^{\mathrm i\frac{\phi + \lambda}{2}} R_Z(\phi) R_Y(\theta) R_Z(\lambda) = \mathrm e^{\mathrm i\frac{\theta}{2}} H J\left(\phi + \frac{\pi}{2}\right) J(\theta) J\left(\lambda - \frac{\pi}{2}\right) """ target: int theta: ParameterizedAngle = field(metadata={"repr": repr_angle}) phi: ParameterizedAngle = field(metadata={"repr": repr_angle}) lambda_: ParameterizedAngle = field(metadata={"repr": repr_angle}) kind: ClassVar[Literal[InstructionKind.U]] = field(default=InstructionKind.U, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: target = visitor.visit_qubit(self.target) theta = visitor.visit_angle(self.theta) phi = visitor.visit_angle(self.phi) lambda_ = visitor.visit_angle(self.lambda_) if copy: return type(self)(target, theta, phi, lambda_) self.target = target self.theta = theta self.phi = phi self.lambda_ = lambda_ return self
[docs] @dataclass(repr=False) class CU(_KindChecker, BaseInstruction): r"""Controlled-U circuit instruction. The :math:`CU(\theta, \phi, \lambda, \gamma)` gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \mathrm e^{\mathrm i\gamma} \cos\left(\frac{\theta}{2}\right) & -\mathrm e^{\mathrm i(\gamma + \lambda)} \sin\left(\frac{\theta}{2}\right) \\ 0 & 0 & \mathrm e^{\mathrm i(\gamma + \phi)} \sin\left(\frac{\theta}{2}\right) & \mathrm e^{\mathrm i(\gamma + \phi + \lambda)} \cos\left(\frac{\theta}{2}\right) \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. It can be decomposed as .. math:: CU(\theta, \phi, \lambda, \gamma) = \left(P\left(\frac{\gamma - \theta} 2\right) \otimes I\right) CJ(0) CJ\left(\phi + \frac \pi 2\right) CJ(\theta) CJ\left(\lambda - \frac \pi 2\right) Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. theta : ParameterizedAngle Rotation angle around the Y axis. phi : ParameterizedAngle Rotation angle around the Z axis after the Y rotation. lambda_ : ParameterizedAngle Rotation angle around the Z axis before the Y rotation. gamma : ParameterizedAngle Global phase angle. """ control: int target: int theta: ParameterizedAngle = field(metadata={"repr": repr_angle}) phi: ParameterizedAngle = field(metadata={"repr": repr_angle}) lambda_: ParameterizedAngle = field(metadata={"repr": repr_angle}) gamma: ParameterizedAngle = field(metadata={"repr": repr_angle}) kind: ClassVar[Literal[InstructionKind.CU]] = field(default=InstructionKind.CU, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: control = visitor.visit_qubit(self.control) target = visitor.visit_qubit(self.target) theta = visitor.visit_angle(self.theta) phi = visitor.visit_angle(self.phi) lambda_ = visitor.visit_angle(self.lambda_) gamma = visitor.visit_angle(self.gamma) if copy: return type(self)(control, target, theta, phi, lambda_, gamma) self.control = control self.target = target self.theta = theta self.phi = phi self.lambda_ = lambda_ self.gamma = gamma return self
[docs] @dataclass(repr=False) class ControlledRotationInstruction(BaseInstruction): """Base class for rotation instructions.""" target: int control: int angle: ParameterizedAngle = field(metadata={"repr": repr_angle})
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> Self: target = visitor.visit_qubit(self.target) control = visitor.visit_qubit(self.control) angle = visitor.visit_angle(self.angle) if copy: return type(self)(target, control, angle) self.target = target self.control = control self.angle = angle return self
[docs] @dataclass(repr=False) class CP(_KindChecker, ControlledRotationInstruction): r"""Controlled-P circuit instruction. The :math:`CP(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & \mathrm e^{\mathrm i \theta} \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Phase angle. """ kind: ClassVar[Literal[InstructionKind.CP]] = field(default=InstructionKind.CP, init=False)
[docs] @dataclass(repr=False) class CRX(_KindChecker, ControlledRotationInstruction): r"""Controlled-X rotation circuit instruction. The :math:`CRX(\theta)` gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & \cos \frac \theta 2 & -\mathrm i \sin \frac \theta 2\\ 0 & 0 & -\mathrm i \sin \frac \theta 2 & \cos \frac \theta 2 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Rotation angle. """ kind: ClassVar[Literal[InstructionKind.CRX]] = field(default=InstructionKind.CRX, init=False)
[docs] @dataclass(repr=False) class CRY(_KindChecker, ControlledRotationInstruction): r"""Controlled-Y rotation circuit instruction. .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & \cos \frac \theta 2 & - \sin \frac \theta 2\\ 0 & 0 & \sin \frac \theta 2 & \cos \frac \theta 2 \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Rotation angle. """ kind: ClassVar[Literal[InstructionKind.CRY]] = field(default=InstructionKind.CRY, init=False)
[docs] @dataclass(repr=False) class CRZ(_KindChecker, ControlledRotationInstruction): r"""Controlled-Z rotation circuit instruction. .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & \mathrm e^{-\mathrm i \frac \theta 2} & 0\\ 0 & 0 & 0 & \mathrm e^{\mathrm i \frac \theta 2} \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Rotation angle. """ kind: ClassVar[Literal[InstructionKind.CRZ]] = field(default=InstructionKind.CRZ, init=False)
[docs] @dataclass(repr=False) class CJ(_KindChecker, ControlledRotationInstruction): r"""Controlled-J circuit instruction. The :math:`CJ(\alpha)` gate applies the matrix .. math:: \left[\begin{matrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & \frac 1 {\sqrt 2} & \frac 1 {\sqrt 2} \mathrm e^{\mathrm i \alpha}\\ 0 & 0 & \frac 1 {\sqrt 2} & - \frac 1 {\sqrt 2} \mathrm e^{\mathrm i \alpha} \end{matrix}\right] in the computational basis. The basis states use big-endian ordering, with the most significant qubit first. The qubits are numbered in the order ``control``, ``target``. Following Lemmas 4.3 and 5.1 of Barenco et al. (1995), we define: .. math:: \begin{aligned} A &= R_Y\left(\frac \pi 4\right),\\ B &= R_Y\left(- \frac \pi 4\right) R_Z(- \delta),\\ C &= R_Z(\delta),\\ \delta &= \frac {\alpha + \pi} 2 \end{aligned} These operators satisfy :math:`ABC = I` and :math:`AXBXC = \mathrm e^{-\mathrm i \delta} J(\alpha)`. Consequently, :math:`CJ(\alpha)` can be decomposed as: .. math:: CJ(\alpha) = (P(\delta) \otimes I) \, (I \otimes A) \, CX \, (I \otimes B) \, CX \, (I \otimes C) Attributes ---------- control : int Index of the control qubit. target : int Index of the target qubit. angle : ParameterizedAngle Rotation angle. References ---------- Barenco, A., Bennett, C. H., Cleve, R., DiVincenzo, D. P., Margolus, N., Shor, P., Sleator, T., Smolin, J. A., & Weinfurter, H. (1995). Elementary gates for quantum computation. Physical Review A, 52(5), 3457-3467. https://doi.org/10.1103/physreva.52.3457 """ kind: ClassVar[Literal[InstructionKind.CJ]] = field(default=InstructionKind.CJ, init=False)
[docs] @dataclass(repr=False) class GPHASE(_KindChecker, BaseInstruction): """GPHASE circuit instruction. Attributes ---------- angle : ParameterizedAngle Phase angle. """ angle: ParameterizedAngle = field(metadata={"repr": repr_angle}) kind: ClassVar[Literal[InstructionKind.GPHASE]] = field(default=InstructionKind.GPHASE, init=False)
[docs] @override def visit(self, visitor: InstructionVisitor, *, copy: bool = False) -> GPHASE: angle = visitor.visit_angle(self.angle) if copy: return GPHASE(angle) self.angle = angle return self
[docs] class Instruction: """Grouping of all instructions for namespace exposure. Notes ----- This class is not meant to be instantiated, but rather serves as a namespace for all instructions except RZZ. The type alias for "any command" is :data:`InstructionKind`. """ I: TypeAlias = I X: TypeAlias = X Y: TypeAlias = Y Z: TypeAlias = Z H: TypeAlias = H S: TypeAlias = S SDG: TypeAlias = SDG T: TypeAlias = T TDG: TypeAlias = TDG SX: TypeAlias = SX SXDG: TypeAlias = SXDG J: TypeAlias = J P: TypeAlias = P RX: TypeAlias = RX RY: TypeAlias = RY RZ: TypeAlias = RZ U: TypeAlias = U CJ: TypeAlias = CJ CP: TypeAlias = CP CRX: TypeAlias = CRX CRY: TypeAlias = CRY CRZ: TypeAlias = CRZ CU: TypeAlias = CU CNOT: TypeAlias = CNOT CY: TypeAlias = CY CZ: TypeAlias = CZ CCX: TypeAlias = CCX RZZ: TypeAlias = RZZ SWAP: TypeAlias = SWAP CSWAP: TypeAlias = CSWAP M: TypeAlias = M GPHASE: TypeAlias = GPHASE
[docs] def __init__(self) -> None: raise TypeError("Instruction is a namespace, not a class.")
if TYPE_CHECKING: InstructionType = ( I | X | Y | Z | H | S | SDG | T | TDG | SX | SXDG | J | P | RX | RY | RZ | U | CJ | CP | CRX | CRY | CRZ | CU | CNOT | CY | CZ | CCX | RZZ | SWAP | CSWAP | M | GPHASE )