# 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)