Source code for graphqomb.gates

"""Module for gates used in circuit representation.

This module provides:

- `Gate`: Abstract class for gates.
- `UnitGate`: Abstract class for unit gates.
- `J`: Class for the J gate.
- `CZ`: Class for the CZ gate.
- `PhaseGadget`: Class for the PhaseGadget gate.
- `Identity`: Class for the Identity gate.
- `X`: Class for the X gate.
- `Y`: Class for the Y gate.
- `Z`: Class for the Z gate.
- `H`: Class for the H gate.
- `S`: Class for the S gate.
- `T`: Class for the T gate.
- `Tdg`: Class for the Tdg gate.
- `Rx`: Class for the Rx gate.
- `Ry`: Class for the Ry gate.
- `Rz`: Class for the Rz gate.
- `U3`: Class for the U3 gate.
- `CNOT`: Class for the CNOT gate.
- `SWAP`: Class for the SWAP gate.
- `CRz`: Class for the CRz gate.
- `CRx`: Class for the CRx gate.
- `CU3`: Class for the CU3 gate.
- `Toffoli`: Class for the Toffoli gate.
- `CCZ`: Class for the CCZ gate.
"""

from __future__ import annotations

import math
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING, TypeAlias

import numpy as np

if TYPE_CHECKING:
    from numpy.typing import NDArray


[docs] class Gate(ABC): """Abstract class for gates."""
[docs] @abstractmethod def unit_gates(self) -> list[UnitGate]: r"""Get the unit gates that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ raise NotImplementedError
[docs] @abstractmethod def matrix(self) -> NDArray[np.complex128]: r"""Get the matrix representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ raise NotImplementedError
[docs] class SingleGate(Gate): """Base class for single qubit macro gates.""" qubit: int
[docs] class TwoQubitGate(Gate): """Base class for two qubit macro gates.""" qubits: tuple[int, int]
[docs] class MultiGate(Gate): """Base class for multi qubit macro gates.""" qubits: list[int]
[docs] @dataclass(frozen=True) class J(SingleGate): r"""Class for the J gate. Attributes ---------- qubit : `int` The qubit the gate acts on. angle : `float` The angle of the J gate. .. math:: J = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & e^{i\theta} \\ 1 & -e^{i\theta} \end{pmatrix} """ qubit: int angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the unit gates that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [self]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the matrix representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ array: NDArray[np.complex128] = np.asarray( [[1.0 + 0j, np.exp(1j * self.angle)], [1.0 + 0j, -np.exp(1j * self.angle)]], dtype=np.complex128, ) / np.sqrt(2) return array
[docs] @dataclass(frozen=True) class CZ(TwoQubitGate): r"""Class for the CZ gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on. .. math:: CZ = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \end{pmatrix} """ qubits: tuple[int, int]
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [self]
[docs] def matrix( # noqa: PLR6301 self, ) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class PhaseGadget(MultiGate): r"""Class for the PhaseGadget gate. Attributes ---------- qubits : `list`\[`int`\] The qubits the gate acts on. angle : `float` The angle of the PhaseGadget gate. .. math:: PhaseGadget(\theta) = \exp\left(-i\frac{\theta}{2}\prod_{j}Z_j\right) """ qubits: list[int] angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [self]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ def count_ones_in_binary(array: NDArray[np.int64]) -> NDArray[np.int64]: def count_ones_single(x: np.int64) -> int: return int(x).bit_count() count_ones = np.vectorize(count_ones_single) return np.asarray(count_ones(array), dtype=np.int64) index_array: NDArray[np.int64] = np.arange(2 ** len(self.qubits), dtype=np.int64) z_sign = (-1) ** count_ones_in_binary(index_array) return np.diag(np.exp(-1j * self.angle / 2 * z_sign))
UnitGate: TypeAlias = J | CZ | PhaseGadget """Unit gate type"""
[docs] @dataclass(frozen=True) class Identity(SingleGate): r"""Class for the Identity gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, 0), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Identity matrix. """ return np.eye(2, dtype=np.complex128)
[docs] @dataclass(frozen=True) class X(SingleGate): r"""Class for the X gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, 0), J(self.qubit, math.pi)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] X gate matrix. """ return np.asarray([[0, 1], [1, 0]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class Y(SingleGate): r"""Class for the Y gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [ J(self.qubit, math.pi / 2), J(self.qubit, math.pi), J(self.qubit, -math.pi / 2), J(self.qubit, 0), ]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Y gate matrix. """ return np.asarray([[0, -1j], [1j, 0]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class Z(SingleGate): r"""Class for the Z gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, math.pi), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Z gate matrix. """ return np.asarray([[1, 0], [0, -1]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class H(SingleGate): r"""Class for the H gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] H gate matrix. """ array: NDArray[np.complex128] = (1 / np.sqrt(2)) * np.asarray([[1, 1], [1, -1]], dtype=np.complex128) return array
[docs] @dataclass(frozen=True) class S(SingleGate): r"""Class for the S gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, math.pi / 2), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] S gate matrix. """ return np.asarray([[1, 0], [0, 1j]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class T(SingleGate): r"""Class for the T gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: T = \begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, math.pi / 4), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] T gate matrix. """ return np.asarray([[1, 0], [0, np.exp(1j * math.pi / 4)]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class Tdg(SingleGate): r"""Class for the Tdg gate. Attributes ---------- qubit : `int` The qubit the gate acts on. .. math:: T^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & e^{-i\pi/4} \end{pmatrix} """ qubit: int
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, -math.pi / 4), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Tdg gate matrix. """ return np.asarray([[1, 0], [0, np.exp(-1j * math.pi / 4)]], dtype=np.complex128)
[docs] @dataclass(frozen=True) class Rx(SingleGate): r"""Class for the Rx gate. Attributes ---------- qubit : `int` The qubit the gate acts on. angle : `float` The angle of the Rx gate. .. math:: R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2) \\ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} """ qubit: int angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [ J(self.qubit, 0), J(self.qubit, self.angle), ]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Rx gate matrix. """ return np.asarray( [ [np.cos(self.angle / 2), -1j * np.sin(self.angle / 2)], [-1j * np.sin(self.angle / 2), np.cos(self.angle / 2)], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class Ry(SingleGate): r"""Class for the Ry gate. Attributes ---------- qubit : `int` The qubit the gate acts on. angle : `float` The angle of the Ry gate. .. math:: R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & -\sin(\theta/2) \\ \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} """ qubit: int angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [ J(self.qubit, math.pi / 2), J(self.qubit, -self.angle), J(self.qubit, -math.pi / 2), J(self.qubit, 0), ]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Ry gate matrix. """ return np.asarray( [ [np.cos(self.angle / 2), -np.sin(self.angle / 2)], [np.sin(self.angle / 2), np.cos(self.angle / 2)], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class Rz(SingleGate): r"""Class for the Rz gate. Attributes ---------- qubit : `int` The qubit the gate acts on. angle : `float` The angle of the Rz gate. .. math:: R_z(\theta) = \begin{pmatrix} e^{-i\theta/2} & 0 \\ 0 & e^{i\theta/2} \end{pmatrix} """ qubit: int angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [J(self.qubit, self.angle), J(self.qubit, 0)]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Rz gate matrix. """ return np.asarray( [[np.exp(-1j * self.angle / 2), 0], [0, np.exp(1j * self.angle / 2)]], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class U3(SingleGate): r"""Class for the U3 gate. Attributes ---------- qubit : `int` The qubit the gate acts on. angle1 : `float` The first angle of the U3 gate. angle2 : `float` The second angle of the U3 gate. angle3 : `float` The third angle of the U3 gate. .. math:: U3(\theta, \phi, \lambda) = \begin{pmatrix} \cos(\theta/2) & -e^{i\lambda}\sin(\theta/2) \\ e^{i\phi}\sin(\theta/2) & e^{i(\phi+\lambda)}\cos(\theta/2) \end{pmatrix} """ qubit: int angle1: float angle2: float angle3: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ return [ J(self.qubit, self.angle3 - math.pi / 2), J(self.qubit, self.angle1), J(self.qubit, self.angle2 + math.pi / 2), J(self.qubit, 0), ]
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] U3 gate matrix. """ return np.asarray( [ [ np.cos(self.angle1 / 2), -np.exp(1j * self.angle3) * np.sin(self.angle1 / 2), ], [ np.exp(1j * self.angle2) * np.sin(self.angle1 / 2), np.exp(1j * (self.angle2 + self.angle3)) * np.cos(self.angle1 / 2), ], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class CNOT(TwoQubitGate): r"""Class for the CNOT gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on [control target]. .. math:: CNOT = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix} """ qubits: tuple[int, int]
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ target = self.qubits[1] return [ J(target, 0), CZ((self.qubits[0], self.qubits[1])), J(target, 0), ]
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class SWAP(TwoQubitGate): r"""Class for the SWAP gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on [control target]. .. math:: SWAP = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} """ qubits: tuple[int, int]
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ control, target = self.qubits macro_gates: list[Gate] = [ CNOT(self.qubits), CNOT((target, control)), CNOT(self.qubits), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class CRz(TwoQubitGate): r"""Class for the CRz gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on [control target]. angle : `float` The angle of the CRz gate. .. math:: CR_z(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{-i\theta/2} & 0 \\ 0 & 0 & 0 & e^{i\theta/2} \end{pmatrix} """ qubits: tuple[int, int] angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ target = self.qubits[1] macro_gates: list[Gate] = [ Rz(target, self.angle / 2), CNOT(self.qubits), Rz(target, -self.angle / 2), CNOT(self.qubits), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, np.exp(-1j * self.angle / 2), 0], [0, 0, 0, np.exp(1j * self.angle / 2)], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class CRx(TwoQubitGate): r"""Class for the CRx gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on [control target]. angle : `float` The angle of the CRx gate. .. math:: CR_x(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos(\theta/2) & -i\sin(\theta/2) \\ 0 & 0 & -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} """ qubits: tuple[int, int] angle: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ target = self.qubits[1] macro_gates: list[Gate] = [ H(target), CRz(self.qubits, self.angle), H(target), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, np.cos(self.angle / 2), -1j * np.sin(self.angle / 2)], [0, 0, -1j * np.sin(self.angle / 2), np.cos(self.angle / 2)], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class CU3(TwoQubitGate): r"""Class for the CU3 gate. Attributes ---------- qubits : `tuple`\[`int`, `int`\] The qubits the gate acts on. angle1 : `float` The first angle of the CU3 gate. angle2 : `float` The second angle of the CU3 gate. angle3 : `float` The third angle of the CU3 gate. .. math:: CU3(\theta, \phi, \lambda) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos(\theta/2) & -e^{i\lambda}\sin(\theta/2) \\ 0 & 0 & e^{i\phi}\sin(\theta/2) & e^{i(\phi+\lambda)}\cos(\theta/2) \end{pmatrix} """ qubits: tuple[int, int] angle1: float angle2: float angle3: float
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ control, target = self.qubits macro_gates: list[Gate] = [ Rz(control, self.angle3 / 2 + self.angle2 / 2), Rz(target, self.angle3 / 2 - self.angle2 / 2), CNOT(self.qubits), U3(target, -self.angle1 / 2, 0, -(self.angle2 + self.angle3) / 2), CNOT(self.qubits), U3(target, self.angle1 / 2, self.angle2, 0), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [1, 0, 0, 0], [0, 1, 0, 0], [ 0, 0, np.cos(self.angle1 / 2), -np.exp(1j * self.angle3) * np.sin(self.angle1 / 2), ], [ 0, 0, np.exp(1j * self.angle2) * np.sin(self.angle1 / 2), np.exp(1j * (self.angle2 + self.angle3)) * np.cos(self.angle1 / 2), ], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class Toffoli(MultiGate): r"""Class for the Toffoli gate. Attributes ---------- qubits : `list`\[`int`\] The qubits the gate acts on [control1, control2, target]. .. math:: Toffoli = \begin{pmatrix} 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{pmatrix} """ qubits: list[int]
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ control1, control2, target = self.qubits macro_gates: list[Gate] = [ H(target), CNOT((control2, target)), Tdg(target), CNOT((control1, target)), T(target), CNOT((control2, target)), Tdg(target), CNOT((control1, target)), T(control2), T(target), H(target), CNOT((control1, control2)), T(control1), Tdg(control2), CNOT((control1, control2)), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [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], ], dtype=np.complex128, )
[docs] @dataclass(frozen=True) class CCZ(MultiGate): r"""Class for the CCZ gate. Attributes ---------- qubits : `list`\[`int`\] The qubits the gate acts on [control1, control2, target]. .. math:: CCZ = \begin{pmatrix} 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 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & -1 \end{pmatrix} """ qubits: list[int]
[docs] def unit_gates(self) -> list[UnitGate]: r"""Get the `unit_gates` that make up the gate. Returns ------- `list`\[`UnitGate`\] List of unit gates that make up the gate. """ control1, control2, target = self.qubits macro_gates: list[Gate] = [ H(target), Toffoli([control1, control2, target]), H(target), ] unit_gates: list[UnitGate] = [] for macro_gate in macro_gates: unit_gates.extend(macro_gate.unit_gates()) return unit_gates
[docs] def matrix(self) -> NDArray[np.complex128]: # noqa: PLR6301 r"""Get the `matrix` representation of the gate. Returns ------- `numpy.typing.NDArray`\[`numpy.complex128`\] Matrix representation of the gate. """ return np.asarray( [ [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, 1, 0], [0, 0, 0, 0, 0, 0, 0, -1], ], dtype=np.complex128, )