Source code for braket.circuits.gates

# Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import math
from typing import Iterable

import braket.ir.jaqcd as ir
import numpy as np
from braket.circuits import circuit
from braket.circuits.angled_gate import AngledGate
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.quantum_operator_helpers import (
    is_unitary,
    verify_quantum_operator_matrix_dimensions,
)
from braket.circuits.qubit import QubitInput
from braket.circuits.qubit_set import QubitSet, QubitSetInput

"""
To add a new gate:
    1. Implement the class and extend `Gate`
    2. Add a method with the `@circuit.subroutine(register=True)` decorator. Method name
       will be added into the `Circuit` class. This method is the default way
       clients add this gate to a circuit.
    3. Register the class with the `Gate` class via `Gate.register_gate()`.
"""

# Single qubit gates #


[docs]class H(Gate): """Hadamard gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["H"])
[docs] def to_ir(self, target: QubitSet): return ir.H.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return 1.0 / np.sqrt(2.0) * np.array([[1.0, 1.0], [1.0, -1.0]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def h(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of H instructions. Examples: >>> circ = Circuit().h(0) >>> circ = Circuit().h([0, 1, 2]) """ return [Instruction(Gate.H(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(H)
[docs]class I(Gate): # noqa: E742, E261 """Identity gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["I"])
[docs] def to_ir(self, target: QubitSet): return ir.I.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def i(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of I instructions. Examples: >>> circ = Circuit().i(0) >>> circ = Circuit().i([0, 1, 2]) """ return [Instruction(Gate.I(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(I)
[docs]class X(Gate): """Pauli-X gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["X"])
[docs] def to_ir(self, target: QubitSet): return ir.X.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def x(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of X instructions. Examples: >>> circ = Circuit().x(0) >>> circ = Circuit().x([0, 1, 2]) """ return [Instruction(Gate.X(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(X)
[docs]class Y(Gate): """Pauli-Y gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["Y"])
[docs] def to_ir(self, target: QubitSet): return ir.Y.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def y(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of Y instructions. Examples: >>> circ = Circuit().y(0) >>> circ = Circuit().y([0, 1, 2]) """ return [Instruction(Gate.Y(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Y)
[docs]class Z(Gate): """Pauli-Z gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["Z"])
[docs] def to_ir(self, target: QubitSet): return ir.Z.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def z(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of Z instructions. Examples: >>> circ = Circuit().z(0) >>> circ = Circuit().z([0, 1, 2]) """ return [Instruction(Gate.Z(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Z)
[docs]class S(Gate): """S gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["S"])
[docs] def to_ir(self, target: QubitSet): return ir.S.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, 1.0j]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def s(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of S instructions. Examples: >>> circ = Circuit().s(0) >>> circ = Circuit().s([0, 1, 2]) """ return [Instruction(Gate.S(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(S)
[docs]class Si(Gate): """Conjugate transpose of S gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["Si"])
[docs] def to_ir(self, target: QubitSet): return ir.Si.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1, 0], [0, -1j]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def si(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: Iterable of Si instructions. Examples: >>> circ = Circuit().si(0) >>> circ = Circuit().si([0, 1, 2]) """ return [Instruction(Gate.Si(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Si)
[docs]class T(Gate): """T gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["T"])
[docs] def to_ir(self, target: QubitSet): return ir.T.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, np.exp(1j * np.pi / 4)]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def t(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of T instructions. Examples: >>> circ = Circuit().t(0) >>> circ = Circuit().t([0, 1, 2]) """ return [Instruction(Gate.T(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(T)
[docs]class Ti(Gate): """Conjugate transpose of T gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["Ti"])
[docs] def to_ir(self, target: QubitSet): return ir.Ti.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, np.exp(-1j * np.pi / 4)]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def ti(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of Ti instructions. Examples: >>> circ = Circuit().ti(0) >>> circ = Circuit().ti([0, 1, 2]) """ return [Instruction(Gate.Ti(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Ti)
[docs]class V(Gate): """Square root of not gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["V"])
[docs] def to_ir(self, target: QubitSet): return ir.V.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array([[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def v(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of V instructions. Examples: >>> circ = Circuit().v(0) >>> circ = Circuit().v([0, 1, 2]) """ return [Instruction(Gate.V(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(V)
[docs]class Vi(Gate): """Conjugate transpose of square root of not gate.""" def __init__(self): super().__init__(qubit_count=1, ascii_symbols=["Vi"])
[docs] def to_ir(self, target: QubitSet): return ir.Vi.construct(target=target[0])
[docs] def to_matrix(self) -> np.ndarray: return np.array(([[0.5 - 0.5j, 0.5 + 0.5j], [0.5 + 0.5j, 0.5 - 0.5j]]), dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def vi(target: QubitSetInput) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target (Qubit, int, or iterable of Qubit / int): Target qubit(s) Returns: Iterable[Instruction]: `Iterable` of Vi instructions. Examples: >>> circ = Circuit().vi(0) >>> circ = Circuit().vi([0, 1, 2]) """ return [Instruction(Gate.Vi(), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Vi) # Single qubit gates with rotation #
[docs]class Rx(AngledGate): """X-axis rotation gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__(angle=angle, qubit_count=1, ascii_symbols=["Rx({:.3g})".format(angle)])
[docs] def to_ir(self, target: QubitSet): return ir.Rx.construct(target=target[0], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: cos = np.cos(self.angle / 2) sin = np.sin(self.angle / 2) return np.array([[cos, -1j * sin], [-1j * sin, cos]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def rx(target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: Rx instruction. Examples: >>> circ = Circuit().rx(0, 0.15) """ return [Instruction(Gate.Rx(angle), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Rx)
[docs]class Ry(AngledGate): """Y-axis rotation gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__(angle=angle, qubit_count=1, ascii_symbols=["Ry({:.3g})".format(angle)])
[docs] def to_ir(self, target: QubitSet): return ir.Ry.construct(target=target[0], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: cos = np.cos(self.angle / 2) sin = np.sin(self.angle / 2) return np.array([[cos, -sin], [+sin, cos]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def ry(target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: Ry instruction. Examples: >>> circ = Circuit().ry(0, 0.15) """ return [Instruction(Gate.Ry(angle), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Ry)
[docs]class Rz(AngledGate): """Z-axis rotation gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__(angle=angle, qubit_count=1, ascii_symbols=["Rz({:.3g})".format(angle)])
[docs] def to_ir(self, target: QubitSet): return ir.Rz.construct(target=target[0], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.array( [[np.exp(-1j * self.angle / 2), 0], [0, np.exp(1j * self.angle / 2)]], dtype=complex )
[docs] @staticmethod @circuit.subroutine(register=True) def rz(target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: Rz instruction. Examples: >>> circ = Circuit().rz(0, 0.15) """ return [Instruction(Gate.Rz(angle), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(Rz)
[docs]class PhaseShift(AngledGate): """Phase shift gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__(angle=angle, qubit_count=1, ascii_symbols=["PHASE({:.3g})".format(angle)])
[docs] def to_ir(self, target: QubitSet): return ir.PhaseShift.construct(target=target[0], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, np.exp(1j * self.angle)]], dtype=complex)
[docs] @staticmethod @circuit.subroutine(register=True) def phaseshift(target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: PhaseShift instruction. Examples: >>> circ = Circuit().phaseshift(0, 0.15) """ return [Instruction(Gate.PhaseShift(angle), target=qubit) for qubit in QubitSet(target)]
Gate.register_gate(PhaseShift) # Two qubit gates #
[docs]class CNot(Gate): """Controlled NOT gate.""" def __init__(self): super().__init__(qubit_count=2, ascii_symbols=["C", "X"])
[docs] def to_ir(self, target: QubitSet): return ir.CNot.construct(control=target[0], target=target[1])
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def cnot(control: QubitInput, target: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. Returns: Instruction: CNot instruction. Examples: >>> circ = Circuit().cnot(0, 1) """ return Instruction(Gate.CNot(), target=[control, target])
Gate.register_gate(CNot)
[docs]class Swap(Gate): """Swap gate.""" def __init__(self): super().__init__(qubit_count=2, ascii_symbols=["SWAP", "SWAP"])
[docs] def to_ir(self, target: QubitSet): return ir.Swap.construct(targets=[target[0], target[1]])
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def swap(target1: QubitInput, target2: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. Returns: Instruction: Swap instruction. Examples: >>> circ = Circuit().swap(0, 1) """ return Instruction(Gate.Swap(), target=[target1, target2])
Gate.register_gate(Swap)
[docs]class ISwap(Gate): """ISwap gate.""" def __init__(self): super().__init__(qubit_count=2, ascii_symbols=["ISWAP", "ISWAP"])
[docs] def to_ir(self, target: QubitSet): return ir.ISwap.construct(targets=[target[0], target[1]])
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0j, 0.0], [0.0, 1.0j, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def iswap(target1: QubitInput, target2: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. Returns: Instruction: ISwap instruction. Examples: >>> circ = Circuit().iswap(0, 1) """ return Instruction(Gate.ISwap(), target=[target1, target2])
Gate.register_gate(ISwap)
[docs]class PSwap(AngledGate): """PSwap gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["PSWAP({:.3g})".format(angle), "PSWAP({:.3g})".format(angle)], )
[docs] def to_ir(self, target: QubitSet): return ir.PSwap.construct(targets=[target[0], target[1]], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, np.exp(1j * self.angle), 0.0], [0.0, np.exp(1j * self.angle), 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def pswap(target1: QubitInput, target2: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. Returns: Instruction: PSwap instruction. Examples: >>> circ = Circuit().pswap(0, 1, 0.15) """ return Instruction(Gate.PSwap(angle), target=[target1, target2])
Gate.register_gate(PSwap)
[docs]class XY(AngledGate): """XY gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["XY({:.3g})".format(angle), "XY({:.3g})".format(angle)], )
[docs] def to_ir(self, target: QubitSet): return ir.XY.construct(targets=[target[0], target[1]], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: cos = np.cos(self.angle / 2) sin = np.sin(self.angle / 2) return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, cos, 1.0j * sin, 0.0], [0.0, 1.0j * sin, cos, 0.0], [0.0, 0.0, 0.0, 1.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def xy(target1: QubitInput, target2: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. Returns: Instruction: XY instruction. Examples: >>> circ = Circuit().xy(0, 1, 0.15) """ return Instruction(Gate.XY(angle), target=[target1, target2])
Gate.register_gate(XY)
[docs]class CPhaseShift(AngledGate): """Controlled phase shift gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["C", "PHASE({:.3g})".format(angle)] )
[docs] def to_ir(self, target: QubitSet): return ir.CPhaseShift.construct(control=target[0], target=target[1], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.diag([1.0, 1.0, 1.0, np.exp(1j * self.angle)])
[docs] @staticmethod @circuit.subroutine(register=True) def cphaseshift(control: QubitInput, target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: CPhaseShift instruction. Examples: >>> circ = Circuit().cphaseshift(0, 1, 0.15) """ return Instruction(Gate.CPhaseShift(angle), target=[control, target])
Gate.register_gate(CPhaseShift)
[docs]class CPhaseShift00(AngledGate): """Controlled phase shift gate for phasing the \\|00> state. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["C", "PHASE00({:.3g})".format(angle)] )
[docs] def to_ir(self, target: QubitSet): return ir.CPhaseShift00.construct(control=target[0], target=target[1], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.diag([np.exp(1j * self.angle), 1.0, 1.0, 1.0])
[docs] @staticmethod @circuit.subroutine(register=True) def cphaseshift00(control: QubitInput, target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: CPhaseShift00 instruction. Examples: >>> circ = Circuit().cphaseshift00(0, 1, 0.15) """ return Instruction(Gate.CPhaseShift00(angle), target=[control, target])
Gate.register_gate(CPhaseShift00)
[docs]class CPhaseShift01(AngledGate): """Controlled phase shift gate for phasing the \\|01> state. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["C", "PHASE01({:.3g})".format(angle)] )
[docs] def to_ir(self, target: QubitSet): return ir.CPhaseShift01.construct(control=target[0], target=target[1], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.diag([1.0, np.exp(1j * self.angle), 1.0, 1.0])
[docs] @staticmethod @circuit.subroutine(register=True) def cphaseshift01(control: QubitInput, target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: CPhaseShift01 instruction. Examples: >>> circ = Circuit().cphaseshift01(0, 1, 0.15) """ return Instruction(Gate.CPhaseShift01(angle), target=[control, target])
Gate.register_gate(CPhaseShift01)
[docs]class CPhaseShift10(AngledGate): """Controlled phase shift gate for phasing the \\|10> state. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["C", "PHASE10({:.3g})".format(angle)] )
[docs] def to_ir(self, target: QubitSet): return ir.CPhaseShift10.construct(control=target[0], target=target[1], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.diag([1.0, 1.0, np.exp(1j * self.angle), 1.0])
[docs] @staticmethod @circuit.subroutine(register=True) def cphaseshift10(control: QubitInput, target: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. angle (float): Angle in radians. Returns: Instruction: CPhaseShift10 instruction. Examples: >>> circ = Circuit().cphaseshift10(0, 1, 0.15) """ return Instruction(Gate.CPhaseShift10(angle), target=[control, target])
Gate.register_gate(CPhaseShift10)
[docs]class CY(Gate): """Controlled Pauli-Y gate.""" def __init__(self): super().__init__(qubit_count=2, ascii_symbols=["C", "Y"])
[docs] def to_ir(self, target: QubitSet): return ir.CY.construct(control=target[0], target=target[1])
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, -1.0j], [0.0, 0.0, +1.0j, 0.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def cy(control: QubitInput, target: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. Returns: Instruction: CY instruction. Examples: >>> circ = Circuit().cy(0, 1) """ return Instruction(Gate.CY(), target=[control, target])
Gate.register_gate(CY)
[docs]class CZ(Gate): """Controlled Pauli-Z gate.""" def __init__(self): super().__init__(qubit_count=2, ascii_symbols=["C", "Z"])
[docs] def to_ir(self, target: QubitSet): return ir.CZ.construct(control=target[0], target=target[1])
[docs] def to_matrix(self) -> np.ndarray: return np.diag([1.0, 1.0, 1.0, -1.0])
[docs] @staticmethod @circuit.subroutine(register=True) def cz(control: QubitInput, target: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index. target (Qubit or int): Target qubit index. Returns: Instruction: CZ instruction. Examples: >>> circ = Circuit().cz(0, 1) """ return Instruction(Gate.CZ(), target=[control, target])
Gate.register_gate(CZ)
[docs]class XX(AngledGate): """Ising XX coupling gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["XX({:.3g})".format(angle), "XX({:.3g})".format(angle)], )
[docs] def to_ir(self, target: QubitSet): return ir.XX.construct(targets=[target[0], target[1]], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return (1 / math.sqrt(2)) * np.array( [ [1.0, 0.0, 0.0, -1.0j * np.exp(1.0j * self.angle)], [0.0, 1.0, -1.0j, 0.0], [0.0, -1.0j, 1.0, 0.0], [-1.0j * np.exp(-1.0j * self.angle), 0.0, 0.0, 1.0], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def xx(target1: QubitInput, target2: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. angle (float): Angle in radians. Returns: Instruction: XX instruction. Examples: >>> circ = Circuit().xx(0, 1, 0.15) """ return Instruction(Gate.XX(angle), target=[target1, target2])
Gate.register_gate(XX)
[docs]class YY(AngledGate): """Ising YY coupling gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["YY({:.3g})".format(angle), "YY({:.3g})".format(angle)], )
[docs] def to_ir(self, target: QubitSet): return ir.YY.construct(targets=[target[0], target[1]], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: cos = np.cos(self.angle) sin = np.sin(self.angle) return np.array( [ [cos, 0.0, 0.0, 1.0j * sin], [0.0, cos, -1.0j * sin, 0.0], [0.0, -1.0j * sin, cos, 0.0], [1.0j * sin, 0.0, 0.0, cos], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def yy(target1: QubitInput, target2: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. angle (float): Angle in radians. Returns: Instruction: YY instruction. Examples: >>> circ = Circuit().yy(0, 1, 0.15) """ return Instruction(Gate.YY(angle), target=[target1, target2])
Gate.register_gate(YY)
[docs]class ZZ(AngledGate): """Ising ZZ coupling gate. Args: angle (float): angle in radians. """ def __init__(self, angle: float): super().__init__( angle=angle, qubit_count=2, ascii_symbols=["ZZ({:.3g})".format(angle), "ZZ({:.3g})".format(angle)], )
[docs] def to_ir(self, target: QubitSet): return ir.ZZ.construct(targets=[target[0], target[1]], angle=self.angle)
[docs] def to_matrix(self) -> np.ndarray: return np.array( [ [np.exp(1j * (self.angle / 2)), 0.0, 0.0, 0.0], [0.0, np.exp(-1j * (self.angle / 2)), 0.0, 0.0], [0.0, 0.0, np.exp(-1j * (self.angle / 2)), 0.0], [0.0, 0.0, 0.0, np.exp(1j * (self.angle / 2))], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def zz(target1: QubitInput, target2: QubitInput, angle: float) -> Instruction: """Registers this function into the circuit class. Args: target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. angle (float): Angle in radians. Returns: Instruction: ZZ instruction. Examples: >>> circ = Circuit().zz(0, 1, 0.15) """ return Instruction(Gate.ZZ(angle), target=[target1, target2])
Gate.register_gate(ZZ) # Three qubit gates #
[docs]class CCNot(Gate): """CCNOT gate or Toffoli gate.""" def __init__(self): super().__init__(qubit_count=3, ascii_symbols=["C", "C", "X"])
[docs] def to_ir(self, target: QubitSet): return ir.CCNot.construct(controls=[target[0], target[1]], target=target[2])
[docs] def to_matrix(self) -> np.ndarray: return np.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], [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=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def ccnot(control1: QubitInput, control2: QubitInput, target: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: control1 (Qubit or int): Control qubit 1 index. control2 (Qubit or int): Control qubit 2 index. target (Qubit or int): Target qubit index. Returns: Instruction: CCNot instruction. Examples: >>> circ = Circuit().ccnot(0, 1, 2) """ return Instruction(Gate.CCNot(), target=[control1, control2, target])
Gate.register_gate(CCNot)
[docs]class CSwap(Gate): """Controlled Swap gate.""" def __init__(self): super().__init__(qubit_count=3, ascii_symbols=["C", "SWAP", "SWAP"])
[docs] def to_ir(self, target: QubitSet): return ir.CSwap.construct(control=target[0], targets=[target[1], target[2]])
[docs] def to_matrix(self) -> np.ndarray: return np.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], [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], ], dtype=complex, )
[docs] @staticmethod @circuit.subroutine(register=True) def cswap(control: QubitInput, target1: QubitInput, target2: QubitInput) -> Instruction: """Registers this function into the circuit class. Args: control (Qubit or int): Control qubit index target1 (Qubit or int): Target qubit 1 index. target2 (Qubit or int): Target qubit 2 index. Returns: Instruction: CSwap instruction. Examples: >>> circ = Circuit().cswap(0, 1, 2) """ return Instruction(Gate.CSwap(), target=[control, target1, target2])
Gate.register_gate(CSwap)
[docs]class Unitary(Gate): """Arbitrary unitary gate Args: matrix (numpy.ndarray): Unitary matrix which defines the gate. display_name (str): Name to be used for an instance of this unitary gate for circuit diagrams. Defaults to `U`. Raises: ValueError: If `matrix` is not a two-dimensional square matrix, or has a dimension length which is not a positive exponent of 2, or is non-unitary. """ def __init__(self, matrix: np.ndarray, display_name: str = "U"): verify_quantum_operator_matrix_dimensions(matrix) self._matrix = np.array(matrix, dtype=complex) qubit_count = int(np.log2(self._matrix.shape[0])) if not is_unitary(self._matrix): raise ValueError(f"{self._matrix} is not unitary") super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count)
[docs] def to_matrix(self): return np.array(self._matrix)
[docs] def to_ir(self, target: QubitSet): return ir.Unitary.construct( targets=[qubit for qubit in target], matrix=Unitary._transform_matrix_to_ir(self._matrix), )
@staticmethod def _transform_matrix_to_ir(matrix: np.ndarray): return [[[element.real, element.imag] for element in row] for row in matrix.tolist()]
[docs] @staticmethod @circuit.subroutine(register=True) def unitary(targets: QubitSet, matrix: np.ndarray, display_name: str = "U") -> Instruction: """Registers this function into the circuit class. Args: targets (QubitSet): Target qubits. matrix (numpy.ndarray): Unitary matrix which defines the gate. Matrix should be compatible with the supplied targets, with 2 ** len(targets) == matrix.shape[0]. display_name (str): Name to be used for an instance of this unitary gate for circuit diagrams. Defaults to `U`. Returns: Instruction: Unitary instruction. Raises: ValueError: If `matrix` is not a two-dimensional square matrix, or has a dimension length which is not compatible with the `targets`, or is non-unitary, Examples: >>> circ = Circuit().unitary(matrix=np.array([[0, 1],[1, 0]]), targets=[0]) """ if 2 ** len(targets) != matrix.shape[0]: raise ValueError("Dimensions of the supplied unitary are incompatible with the targets") return Instruction(Gate.Unitary(matrix, display_name), target=targets)
Gate.register_gate(Unitary)