# Source code for weylchamber.gates

r"""Quantum gates required for Weyl chamber calculations

**Module data:**

.. data:: Qmagic

Transformation matrix to the "magic" Bell basis:

.. math::

\Op{Q} = \frac{1}{\sqrt{2}} \begin{pmatrix}
1 & 0 &  0 &  i \\
0 & i &  1 &  0 \\
0 & i & -1 &  0 \\
1 & 0 &  0 & -i
\end{pmatrix}

See "Theorem 1" in Y. Makhlin, Quantum Inf. Process. 1, 243 (2002)

.. data:: SxSx

:math:\Op{\sigma}_x \otimes \Op{\sigma}_x gate

.. data:: SySy

:math:\Op{\sigma}_y \otimes \Op{\sigma}_y gate

.. data:: SzSz

:math:\Op{\sigma}_z \otimes \Op{\sigma}_z gate
"""
import qutip
import numpy as np

__all__ = [
'Qmagic',
'SxSx',
'SySy',
'SzSz',
'bell_basis',
'gate',
'mapped_basis',
]

Qmagic = (1.0 / np.sqrt(2.0)) * qutip.Qobj(
np.array(
[[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]],
dtype=np.complex128,
),
dims=[[2, 2], [2, 2]],
)

SxSx = qutip.Qobj(
np.array(
[[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]],
dtype=np.complex128,
),
dims=[[2, 2], [2, 2]],
)

SySy = qutip.Qobj(
np.array(
[[+0, 0, 0, -1], [+0, 0, 1, 0], [+0, 1, 0, 0], [-1, 0, 0, 0]],
dtype=np.complex128,
),
dims=[[2, 2], [2, 2]],
)

SzSz = qutip.Qobj(
np.array(
[[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]],
dtype=np.complex128,
),
dims=[[2, 2], [2, 2]],
)

[docs]def bell_basis(canonical_basis):
"""Two-qubit Bell basis associated with the given canonical basis

Example:

>>> from qutip import ket
>>> canonical_basis = [
...     ket(nums) for nums in [(0, 0), (0, 1), (1, 0), (1, 1)]
... ]
>>> bell = bell_basis(canonical_basis)
>>> _bell = [
...     (     ket((0, 0)) +      ket((1, 1))) / np.sqrt(2),
...     (1j * ket((0, 1)) + 1j * ket((1, 0))) / np.sqrt(2),
...     (     ket((0, 1)) -      ket((1, 0))) / np.sqrt(2),
...     (1j * ket((0, 0)) - 1j * ket((1, 1))) / np.sqrt(2),
... ]
>>> assert (bell[0] - _bell[0]).norm() < 1e-15
>>> assert (bell[1] - _bell[1]).norm() < 1e-15
>>> for (a, b) in zip(bell, _bell):
...     assert (a - b).norm() < 1e-15, (a - b).norm()
"""
return mapped_basis(Qmagic, canonical_basis)

[docs]def gate(basis, states):
"""Two-qubit gate that maps basis to states

Example:

>>> from qutip import ket
>>> basis = [ket(nums) for nums in [(0, 0), (0, 1), (1, 0), (1, 1)]]
>>> states = mapped_basis(qutip.qip.operations.cnot(), basis)
>>> U = gate(basis, states)
>>> assert (U - qutip.qip.operations.cnot()).norm() < 1e-15
"""
if not (len(basis) == len(states) == 4):
raise ValueError(
"Both basis and states must be a list of four states"
)
U = np.zeros((4, 4), dtype=np.complex128)
for j in range(4):
for i in range(4):
U[i, j] = basis[i].overlap(states[j])
return qutip.Qobj(U, dims=[[2, 2], [2, 2]])

[docs]def mapped_basis(gate, basis):
"""Result of applying gate to basis

Example:

>>> from qutip import ket
>>> basis = [ket(nums) for nums in [(0, 0), (0, 1), (1, 0), (1, 1)]]
>>> states = mapped_basis(qutip.qip.operations.cnot(), basis)
>>> assert (states[0] - ket((0,0))).norm() < 1e-15
>>> assert (states[1] - ket((0,1))).norm() < 1e-15
>>> assert (states[2] - ket((1,1))).norm() < 1e-15  # swap (1, 1) ...
>>> assert (states[3] - ket((1,0))).norm() < 1e-15  # ... and (1, 0)
"""
return tuple(
[
sum([complex(gate[i, j]) * basis[i] for i in range(gate.shape[0])])
for j in range(gate.shape[1])
]
)