# Required for Sphinx to follow autodoc_type_aliases
from __future__ import annotations
from itertools import product
from functools import partial, reduce
from operator import mul
import numpy as np
import scipy.sparse as sp
from . import Qobj, qeye, sigmax, fock_dm, qdiags, qeye_like
from .dimensions import Dimensions
from .. import settings
from . import data as _data
from ..typing import LayerType
__all__ = [
"rx",
"ry",
"rz",
"sqrtnot",
"snot",
"phasegate",
"qrot",
"cy_gate",
"cz_gate",
"s_gate",
"t_gate",
"cs_gate",
"ct_gate",
"cphase",
"cnot",
"csign",
"berkeley",
"swapalpha",
"swap",
"iswap",
"sqrtswap",
"sqrtiswap",
"fredkin",
"molmer_sorensen",
"toffoli",
"hadamard_transform",
"qubit_clifford_group",
"globalphase",
]
_DIMS_2_QB = Dimensions([[2, 2], [2, 2]])
_DIMS_3_QB = Dimensions([[2, 2, 2], [2, 2, 2]])
[docs]
def cy_gate(*, dtype: LayerType = None) -> Qobj:
"""Controlled Y gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, -1j], [0, 0, 1j, 0]],
dims=_DIMS_2_QB,
isherm=True,
isunitary=True,
).to(dtype)
[docs]
def cz_gate(*, dtype: LayerType = None) -> Qobj:
"""Controlled Z gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([1, 1, 1, -1], dims=_DIMS_2_QB, dtype=dtype)
[docs]
def s_gate(*, dtype: LayerType = None) -> Qobj:
"""Single-qubit rotation also called Phase gate or the Z90 gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing
a 90 degree rotation around the z-axis.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([1, 1j], dtype=dtype)
[docs]
def cs_gate(*, dtype: LayerType = None) -> Qobj:
"""Controlled S gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([1, 1, 1, 1j], dims=_DIMS_2_QB, dtype=dtype)
[docs]
def t_gate(*, dtype: LayerType = None) -> Qobj:
"""Single-qubit rotation related to the S gate by the relationship S=T*T.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing a phase shift of pi/4.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([1, np.exp(1j * np.pi / 4)], dtype=dtype)
[docs]
def ct_gate(*, dtype: LayerType = None) -> Qobj:
"""Controlled T gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : :class:`.Qobj`
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags(
[1, 1, 1, np.exp(1j * np.pi / 4)],
dims=_DIMS_2_QB,
dtype=dtype,
)
[docs]
def rx(phi: float, *, dtype: LayerType = None) -> Qobj:
"""Single-qubit rotation for operator sigmax with angle phi.
Parameters
----------
phi : float
Rotation angle
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : qobj
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
return Qobj(
[
[np.cos(phi / 2), -1j * np.sin(phi / 2)],
[-1j * np.sin(phi / 2), np.cos(phi / 2)],
],
isherm=(phi % (2 * np.pi) <= settings.core["atol"]),
isunitary=True,
).to(dtype)
[docs]
def ry(phi: float, *, dtype: LayerType = None) -> Qobj:
"""Single-qubit rotation for operator sigmay with angle phi.
Parameters
----------
phi : float
Rotation angle
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : qobj
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
return Qobj(
[
[np.cos(phi / 2), -np.sin(phi / 2)],
[np.sin(phi / 2), np.cos(phi / 2)],
],
isherm=(phi % (2 * np.pi) <= settings.core["atol"]),
isunitary=True,
).to(dtype)
[docs]
def rz(phi: float, *, dtype: LayerType = None) -> Qobj:
"""Single-qubit rotation for operator sigmaz with angle phi.
Parameters
----------
phi : float
Rotation angle
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : qobj
Quantum object for operator describing the rotation.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([np.exp(-1j * phi / 2), np.exp(1j * phi / 2)], dtype=dtype)
[docs]
def sqrtnot(*, dtype: LayerType = None) -> Qobj:
"""Single-qubit square root NOT gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
result : qobj
Quantum object for operator describing the square root NOT gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
return Qobj(
[[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]],
isherm=False,
isunitary=True,
).to(dtype)
[docs]
def snot(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the SNOT (Hadamard) gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
snot_gate : qobj
Quantum object representation of SNOT gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[[np.sqrt(0.5), np.sqrt(0.5)], [np.sqrt(0.5), -np.sqrt(0.5)]],
isherm=True,
isunitary=True,
).to(dtype)
[docs]
def phasegate(theta: float, *, dtype: LayerType = None) -> Qobj:
"""
Returns quantum object representing the phase shift gate.
Parameters
----------
theta : float
Phase rotation angle.
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
phase_gate : qobj
Quantum object representation of phase shift gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags([1, np.exp(1.0j * theta)], dtype=dtype)
[docs]
def qrot(theta: float, phi: float, *, dtype: LayerType = None) -> Qobj:
"""
Single qubit rotation driving by Rabi oscillation with 0 detune.
Parameters
----------
phi : float
The inital phase of the rabi pulse.
theta : float
The duration of the rabi pulse.
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
qrot_gate : :class:`.Qobj`
Quantum object representation of physical qubit rotation under
a rabi pulse.
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
return Qobj(
[
[np.cos(theta / 2), -1j * np.exp(-1j * phi) * np.sin(theta / 2)],
[-1j * np.exp(1j * phi) * np.sin(theta / 2), np.cos(theta / 2)],
],
isherm=(theta % (2 * np.pi) <= settings.core["atol"]),
isunitary=True,
).to(dtype)
#
# 2 Qubit Gates
#
[docs]
def cphase(theta: float, *, dtype: LayerType = None) -> Qobj:
"""
Returns quantum object representing the controlled phase shift gate.
Parameters
----------
theta : float
Phase rotation angle.
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
U : qobj
Quantum object representation of controlled phase gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qdiags(
[1, 1, 1, np.exp(1.0j * theta)], dims=_DIMS_2_QB, dtype=dtype
)
[docs]
def cnot(*, dtype: LayerType = None) -> Qobj:
"""
Quantum object representing the CNOT gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
cnot_gate : qobj
Quantum object representation of CNOT gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]],
dims=_DIMS_2_QB,
isherm=True,
isunitary=True,
).to(dtype)
[docs]
def csign(*, dtype: LayerType = None) -> Qobj:
"""
Quantum object representing the CSIGN gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
csign_gate : qobj
Quantum object representation of CSIGN gate
"""
return cz_gate(dtype=dtype)
[docs]
def berkeley(*, dtype: LayerType = None) -> Qobj:
"""
Quantum object representing the Berkeley gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
berkeley_gate : qobj
Quantum object representation of Berkeley gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
return Qobj(
[
[np.cos(np.pi / 8), 0, 0, 1.0j * np.sin(np.pi / 8)],
[0, np.cos(3 * np.pi / 8), 1.0j * np.sin(3 * np.pi / 8), 0],
[0, 1.0j * np.sin(3 * np.pi / 8), np.cos(3 * np.pi / 8), 0],
[1.0j * np.sin(np.pi / 8), 0, 0, np.cos(np.pi / 8)],
],
dims=_DIMS_2_QB,
isherm=False,
isunitary=True,
).to(dtype)
[docs]
def swapalpha(alpha: float, *, dtype: LayerType = None) -> Qobj:
"""
Quantum object representing the SWAPalpha gate.
Parameters
----------
alpha : float
Angle of the SWAPalpha gate.
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
swapalpha_gate : qobj
Quantum object representation of SWAPalpha gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
phase = np.exp(1.0j * np.pi * alpha)
return Qobj(
[
[1, 0, 0, 0],
[0, 0.5 * (1 + phase), 0.5 * (1 - phase), 0],
[0, 0.5 * (1 - phase), 0.5 * (1 + phase), 0],
[0, 0, 0, 1],
],
dims=_DIMS_2_QB,
isherm=(np.abs(phase.imag) <= settings.core["atol"]),
isunitary=True,
).to(dtype)
[docs]
def swap(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the SWAP gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
swap_gate : qobj
Quantum object representation of SWAP gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]],
dims=_DIMS_2_QB,
isherm=True,
isunitary=True,
).to(dtype)
[docs]
def iswap(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the iSWAP gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
iswap_gate : qobj
Quantum object representation of iSWAP gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]],
dims=_DIMS_2_QB,
isherm=False,
isunitary=True,
).to(dtype)
[docs]
def sqrtswap(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the square root SWAP gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
sqrtswap_gate : qobj
Quantum object representation of square root SWAP gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
np.array(
[
[1, 0, 0, 0],
[0, 0.5 + 0.5j, 0.5 - 0.5j, 0],
[0, 0.5 - 0.5j, 0.5 + 0.5j, 0],
[0, 0, 0, 1],
]
),
dims=_DIMS_2_QB,
isherm=False,
isunitary=True,
).to(dtype)
[docs]
def sqrtiswap(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the square root iSWAP gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
sqrtiswap_gate : qobj
Quantum object representation of square root iSWAP gate
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
np.array(
[
[1, 0, 0, 0],
[0, 1 / np.sqrt(2), 1j / np.sqrt(2), 0],
[0, 1j / np.sqrt(2), 1 / np.sqrt(2), 0],
[0, 0, 0, 1],
]
),
dims=_DIMS_2_QB,
isherm=False,
isunitary=True,
).to(dtype)
[docs]
def molmer_sorensen(theta: float, *, dtype: LayerType = None) -> Qobj:
"""
Quantum object of a Mølmer–Sørensen gate.
Parameters
----------
theta: float
The duration of the interaction pulse.
target: int
The indices of the target qubits.
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
molmer_sorensen_gate: :class:`.Qobj`
Quantum object representation of the Mølmer–Sørensen gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[
[np.cos(theta / 2.0), 0, 0, -1.0j * np.sin(theta / 2.0)],
[0, np.cos(theta / 2.0), -1.0j * np.sin(theta / 2.0), 0],
[0, -1.0j * np.sin(theta / 2.0), np.cos(theta / 2.0), 0],
[-1.0j * np.sin(theta / 2.0), 0, 0, np.cos(theta / 2.0)],
],
dims=_DIMS_2_QB,
isherm=(theta % (2 * np.pi) <= settings.core["atol"]),
isunitary=True,
).to(dtype)
#
# 3 Qubit Gates
#
[docs]
def fredkin(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the Fredkin gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
fredkin_gate : qobj
Quantum object representation of Fredkin gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[
[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],
],
dims=_DIMS_3_QB,
isherm=True,
isunitary=True,
).to(dtype)
[docs]
def toffoli(*, dtype: LayerType = None) -> Qobj:
"""Quantum object representing the Toffoli gate.
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
toff_gate : qobj
Quantum object representation of Toffoli gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return Qobj(
[
[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],
],
dims=_DIMS_3_QB,
isherm=True,
isunitary=True,
).to(dtype)
#
# Miscellaneous Gates
#
[docs]
def globalphase(theta: float, N: int = 1, *, dtype: LayerType = None) -> Qobj:
"""
Returns quantum object representing the global phase shift gate.
Parameters
----------
theta : float
Phase rotation angle.
N : int:
Number of qubits
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
phase_gate : qobj
Quantum object representation of global phase shift gate.
"""
dtype = dtype or settings.core["default_dtype"] or _data.CSR
return qeye([2] * N, dtype=dtype) * np.exp(1.0j * theta)
#
# Operation on Gates
#
def _hamming_distance(x):
"""
Calculate the bit-wise Hamming distance of x from 0: That is, the number
1s in the integer x.
"""
tot = 0
while x:
tot += 1
x &= x - 1
return tot
def _powers(op, N):
"""
Generator that yields powers of an operator `op`,
through to `N`.
"""
acc = qeye_like(op)
yield acc
for _ in range(N - 1):
acc *= op
yield acc
[docs]
def qubit_clifford_group(*, dtype: LayerType = None) -> list[Qobj]:
"""
Generates the Clifford group on a single qubit,
using the presentation of the group given by Ross and Selinger
(http://www.mathstat.dal.ca/~selinger/newsynth/).
Parameters
----------
dtype : str or type, [keyword only] [optional]
Storage representation. Any data-layer known to `qutip.data.to` is
accepted.
Returns
-------
op : list of Qobj
Clifford operators, represented as Qobj instances.
"""
dtype = dtype or settings.core["default_dtype"] or _data.Dense
# The Ross-Selinger presentation of the single-qubit Clifford
# group expresses each element in the form C_{ijk} = E^i X^j S^k
# for gates E, X and S, and for i in range(3), j in range(2) and
# k in range(4).
#
# We start by defining these gates. E is defined in terms of H,
# \omega and S, so we define \omega and H first.
w = np.exp(1j * 2 * np.pi / 8)
H = snot()
X = sigmax()
S = phasegate(np.pi / 2)
E = H @ (S**3) * w**3
# partial(reduce, mul) returns a function that takes products
# of its argument, by analogy to sum. Note that by analogy,
# sum can be written as partial(reduce, add).
# product(...) yields the Cartesian product of its arguments.
# Here, each element is a tuple (E**i, X**j, S**k) such that
# partial(reduce, mul) acting on the tuple yields E**i * X**j * S**k.
gates = [
op.to(dtype)
for op in map(
partial(reduce, mul),
product(_powers(E, 3), _powers(X, 2), _powers(S, 4)),
)
]
for gate in gates:
gate.isherm
gate._isunitary = True
return gates