# Source code for pennylane.ops.qubit

# Copyright 2018 Xanadu Quantum Technologies Inc.

# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# Unless required by applicable law or agreed to in writing, software
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
"""
This module contains the available built-in discrete-variable
quantum operations supported by PennyLane, as well as their conventions.
"""
import numpy as np

from pennylane.operation import All, Any, Observable, Operation

.. math:: H = \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1\\ 1 & -1\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class PauliX(Observable, Operation):
r"""PauliX(wires)
The Pauli X operator

.. math:: \sigma_x = \begin{bmatrix} 0 & 1 \\ 1 & 0\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class PauliY(Observable, Operation):
r"""PauliY(wires)
The Pauli Y operator

.. math:: \sigma_y = \begin{bmatrix} 0 & -i \\ i & 0\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class PauliZ(Observable, Operation):
r"""PauliZ(wires)
The Pauli Z operator

.. math:: \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class S(Operation):
r"""S(wires)
The single-qubit phase gate

.. math:: S = \begin{bmatrix}
1 & 0 \\
0 & i
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class T(Operation):
r"""T(wires)
The single-qubit T gate

.. math:: T = \begin{bmatrix}
1 & 0 \\
0 & e^{\frac{i\pi}{4}}
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 0
num_wires = 1
par_domain = None

[docs]class CNOT(Operation):
r"""CNOT(wires)
The controlled-NOT operator

.. math:: CNOT = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wires the operation acts on
"""
num_params = 0
num_wires = 2
par_domain = None

[docs]class CZ(Operation):
r"""CZ(wires)
The controlled-Z operator

.. math:: CZ = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & -1
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wires the operation acts on
"""
num_params = 0
num_wires = 2
par_domain = None

[docs]class SWAP(Operation):
r"""SWAP(wires)
The swap operator

.. math:: SWAP = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 0 & 1 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1
\end{bmatrix}.

**Details:**

* Number of wires: 2
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wires the operation acts on
"""
num_params = 0
num_wires = 2
par_domain = None

[docs]class CSWAP(Operation):
r"""CSWAP(wires)
The controlled-swap operator

.. math:: CSWAP = \begin{bmatrix}
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
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 3
* Number of parameters: 0

Args:
wires (Sequence[int] or int): the wires the operation acts on
"""
num_params = 0
num_wires = 3
par_domain = None

[docs]class RX(Operation):
r"""RX(phi, wires)
The single qubit X rotation

.. math:: R_x(\phi) = e^{-i\phi\sigma_x/2} = \begin{bmatrix}
\cos(\phi/2) & -i\sin(\phi/2) \\
-i\sin(\phi/2) & \cos(\phi/2)
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(R_x(\phi)) = \frac{1}{2}\left[f(R_x(\phi+\pi/2)) - f(R_x(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:R_x(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 1
par_domain = "R"
generator = [PauliX, -1/2]

[docs]class RY(Operation):
r"""RY(phi, wires)
The single qubit Y rotation

.. math:: R_y(\phi) = e^{-i\phi\sigma_y/2} = \begin{bmatrix}
\cos(\phi/2) & -\sin(\phi/2) \\
\sin(\phi/2) & \cos(\phi/2)
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(R_y(\phi)) = \frac{1}{2}\left[f(R_y(\phi+\pi/2)) - f(R_y(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:R_y(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 1
par_domain = "R"
generator = [PauliY, -1/2]

[docs]class RZ(Operation):
r"""RZ(phi, wires)
The single qubit Z rotation

.. math:: R_z(\phi) = e^{-i\phi\sigma_z/2} = \begin{bmatrix}
e^{-i\phi/2} & 0 \\
0 & e^{i\phi/2}
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(R_z(\phi)) = \frac{1}{2}\left[f(R_z(\phi+\pi/2)) - f(R_z(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:R_z(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 1
par_domain = "R"
generator = [PauliZ, -1/2]

[docs]class PhaseShift(Operation):
r"""PhaseShift(phi, wires)
Arbitrary single qubit local phase shift

.. math:: R_\phi(\phi) = e^{i\phi/2}R_z(\phi) = \begin{bmatrix}
1 & 0 \\
0 & e^{i\phi}
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(R_\phi(\phi)) = \frac{1}{2}\left[f(R_\phi(\phi+\pi/2)) - f(R_\phi(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:R_{\phi}(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 1
par_domain = "R"
generator = [np.array([[0, 0], [0, 1]]), 1]

[docs]class Rot(Operation):
r"""Rot(phi, theta, omega, wires)
Arbitrary single qubit rotation

.. math:: R(\phi,\theta,\omega) = RZ(\omega)RY(\theta)RZ(\phi)= \begin{bmatrix}
e^{-i(\phi+\omega)/2}\cos(\theta/2) & -e^{i(\phi-\omega)/2}\sin(\theta/2) \\
e^{-i(\phi-\omega)/2}\sin(\theta/2) & e^{i(\phi+\omega)/2}\cos(\theta/2)
\end{bmatrix}.

**Details:**

* Number of wires: 1
* Number of parameters: 3
* Gradient recipe: :math:\frac{d}{d\phi}f(R(\phi, \theta, \omega)) = \frac{1}{2}\left[f(R(\phi+\pi/2, \theta, \omega)) - f(R(\phi-\pi/2, \theta, \omega))\right]
where :math:f is an expectation value depending on :math:R(\phi, \theta, \omega).
This gradient recipe applies for each angle argument :math:\{\phi, \theta, \omega\}.

Args:
phi (float): rotation angle :math:\phi
theta (float): rotation angle :math:\theta
omega (float): rotation angle :math:\omega
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 3
num_wires = 1
par_domain = "R"

[docs]class CRX(Operation):
r"""CRX(phi, wires)
The controlled-RX operator

.. math:: CR_x(\phi) = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & \cos(\phi/2) & -i\sin(\phi/2)\\
0 & 0 & -i\sin(\phi/2) & \cos(\phi/2)
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(CR_x(\phi)) = \frac{1}{2}\left[f(CR_x(\phi+\pi/2)) - f(CR_x(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:CR_x(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 2
par_domain = "R"
generator = [np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]), -1/2]

[docs]class CRY(Operation):
r"""CRY(phi, wires)
The controlled-RY operator

.. math:: CR_y(\phi) = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & \cos(\phi/2) & -\sin(\phi/2)\\
0 & 0 & \sin(\phi/2) & \cos(\phi/2)
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(CR_y(\phi)) = \frac{1}{2}\left[f(CR_y(\phi+\pi/2)) - f(CR_y(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:CR_y(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 2
par_domain = "R"
generator = [np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1j, 0]]), -1/2]

[docs]class CRZ(Operation):
r"""CRZ(phi, wires)
The controlled-RZ operator

.. math:: CR_z(\phi) = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & e^{-i\phi/2} & 0\\
0 & 0 & 0 & e^{i\phi/2}
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 1
* Gradient recipe: :math:\frac{d}{d\phi}f(CR_z(\phi)) = \frac{1}{2}\left[f(CR_z(\phi+\pi/2)) - f(CR_z(\phi-\pi/2))\right]
where :math:f is an expectation value depending on :math:CR_z(\phi).

Args:
phi (float): rotation angle :math:\phi
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 1
num_wires = 2
par_domain = "R"
generator = [np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]), -1/2]

[docs]class CRot(Operation):
r"""CRot(phi, theta, omega, wires)
The controlled-Rot operator

.. math:: CR(\phi, \theta, \omega) = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0\\
0 & 0 & e^{-i(\phi+\omega)/2}\cos(\theta/2) & -e^{i(\phi-\omega)/2}\sin(\theta/2)\\
0 & 0 & e^{-i(\phi-\omega)/2}\sin(\theta/2) & e^{i(\phi+\omega)/2}\cos(\theta/2)
\end{bmatrix}.

.. note:: The first wire provided corresponds to the **control qubit**.

**Details:**

* Number of wires: 2
* Number of parameters: 3
* Gradient recipe: :math:\frac{d}{d\phi}f(CR(\phi, \theta, \omega)) = \frac{1}{2}\left[f(CR(\phi+\pi/2, \theta, \omega)) - f(CR(\phi-\pi/2, \theta, \omega))\right]
where :math:f is an expectation value depending on :math:CR(\phi, \theta, \omega).
This gradient recipe applies for each angle argument :math:\{\phi, \theta, \omega\}.

Args:
phi (float): rotation angle :math:\phi
theta (float): rotation angle :math:\theta
omega (float): rotation angle :math:\omega
wires (Sequence[int] or int): the wire the operation acts on
"""
num_params = 3
num_wires = 2
par_domain = "R"

# =============================================================================
# Arbitrary operations
# =============================================================================

[docs]class QubitUnitary(Operation):
r"""QubitUnitary(U, wires)
Apply an arbitrary fixed unitary matrix.

**Details:**

* Number of wires: The operation can act on any number of wires.
* Number of parameters: 1

Args:
U (array[complex]): square unitary matrix
wires (Sequence[int] or int): the wire(s) the operation acts on
"""
num_params = 1
num_wires = Any
par_domain = "A"

# =============================================================================
# State preparation
# =============================================================================

[docs]class BasisState(Operation):
r"""BasisState(n, wires)
Prepares a single computational basis state.

**Details:**

* Number of wires: All (applied to the entire system)
* Number of parameters: 1
* Gradient recipe: None (integer parameters not supported)

Args:
n (array): prepares the basis state :math:\ket{n}, where n is an
array of integers from the set :math:\{0, 1\}, i.e.,
if n = np.array([0, 1, 0]), prepares the state :math:|010\rangle.
wires (Sequence[int] or int): the wire(s) the operation acts on
"""
num_params = 1
num_wires = Any
par_domain = "A"

[docs]class QubitStateVector(Operation):
r"""QubitStateVector(state, wires)
Prepare subsystems using the given ket vector in the computational basis.

**Details:**

* Number of wires: All (applied to the entire system)
* Number of parameters: 1

Args:
state (array[complex]): a state vector of size 2**len(wires)
wires (Sequence[int] or int): the wire(s) the operation acts on
"""
num_params = 1
num_wires = All
par_domain = "A"

# =============================================================================
# Observables
# =============================================================================

[docs]class Hermitian(Observable):
r"""Hermitian(A, wires)
An arbitrary Hermitian observable.

For a Hermitian matrix :math:A, the expectation command returns the value

.. math::
\braket{A} = \braketT{\psi}{\cdots \otimes I\otimes A\otimes I\cdots}{\psi}

where :math:A acts on the requested wires.

If acting on :math:N wires, then the matrix :math:A must be of size
:math:2^N\times 2^N.

**Details:**

* Number of wires: Any
* Number of parameters: 1

Args:
A (array): square hermitian matrix
wires (Sequence[int] or int): the wire(s) the operation acts on
"""
num_wires = Any
num_params = 1
par_domain = "A"

ops = {
"PauliX",
"PauliY",
"PauliZ",
"S",
"T",
"CNOT",
"CZ",
"SWAP",
"CSWAP",
"RX",
"RY",
"RZ",
"PhaseShift",
"Rot",
"CRX",
"CRY",
"CRZ",
"CRot",
"BasisState",
"QubitStateVector",
"QubitUnitary",
}

obs = {"Hadamard", "PauliX", "PauliY", "PauliZ", "Hermitian"}

__all__ = list(ops | obs)